commit 9a4639194ed5f972927e7c5263da00d18b58cce8 Author: milky Date: Sat Jul 16 13:13:14 2022 -0700 ♻️ Cleaned up Signed-off-by: milky diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..006a270 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022 Milky +Copyright (c) 2021 TaiAurori + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..793b0a5 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# powercord-redirects +🔎 Privacy-orientated plugin + +## Why? + +This was forked due to the hardcoded initial values being outdated and offline. This might be something that'll end up maintained, with my own self-hosted alternatives. + +## Supported services +- Youtube -> [yewtu.be](https://yewtu.be) +- Twitter -> [nitter.net](https://nitter.net) +- Reddit -> [libreddit.spike.codes](https://libreddit.spike.codes) +- Instagram -> [bibliogram.pussthecat.org](https://bibliogram.pussthecat.org) +- Wikipedia -> [wikiless.org](https://wikiless.org) +- Medium -> [scribe.rip](https://scribe.rip) diff --git a/Settings.jsx b/Settings.jsx new file mode 100644 index 0000000..6b3ad32 --- /dev/null +++ b/Settings.jsx @@ -0,0 +1,68 @@ +var settings; + +const { React, getModuleByDisplayName } = require("powercord/webpack"); +const { Category, TextInput, SwitchItem } = require("powercord/components/settings"); +const FormText = getModuleByDisplayName("FormText", false); + +const services = require("./services.js") + +module.exports = class Settings extends React.PureComponent { + constructor(props) { + super(props) + + this.state = {} + settings = this.props; + } + render() { + return ( + <> +
+
+
+ {services.map(s => { + return ( + { + this.setState({ ["opened_" + s.name]: !this.state["opened_" + s.name] }); + }}> + Toggles whether {s.replaces} embeds will be replaced with {s.name}.} + value={settings.getSetting(s.name.toLowerCase() + "Active", true)} + onChange={() => { + settings.toggleSetting(s.name.toLowerCase() + "Active", true); + }} + > + Replace Embeds + + Toggles whether {s.replaces} links will be replaced with {s.name}.} + value={settings.getSetting(s.name.toLowerCase() + "LinkActive", true)} + onChange={() => { + settings.toggleSetting(s.name.toLowerCase() + "LinkActive", true); + }} + > + Replace Links + + + {settings.getSetting(s.name.toLowerCase() + "Instance", s.default) == s.replacedURL + ? You think you're real funny, don't you? + : ""}This is the {s.name} instance that will replace all {s.replaces} links/embeds. + + } + defaultValue={settings.getSetting(s.name.toLowerCase() + "Instance", s.default)} + onChange={(val) => settings.updateSetting(s.name.toLowerCase() + "Instance", val)} + > + {s.name} Instance + + + ) + })} +
+ + ); + } +}; diff --git a/index.js b/index.js new file mode 100644 index 0000000..0a1f6d4 --- /dev/null +++ b/index.js @@ -0,0 +1,135 @@ +/* Copyright (C) 2020 TaiAurori (Gabriel Sylvain) - All Rights Reserved + * You may use, distribute and modify this code under the + * terms of the MIT license. + * Basically, you can change and redistribute this code + * but this copyright notice must remain unmodified. + */ + +let settings; + +const { Plugin } = require("powercord/entities"); +const { inject, uninject } = require("powercord/injector"); +const { getModule, React } = require("powercord/webpack"); +const { findInReactTree } = require("powercord/util"); +const { clipboard } = getModule(["clipboard"], false) || {}; + +const Settings = require("./Settings"); + +const services = require("./services.js"); + +const redirectLinkColor = "#0091ff" + +module.exports = class EmbedRedirect extends Plugin { + startPlugin() { + settings = this.settings; + powercord.api.settings.registerSettings(this.entityID, { + category: this.entityID, + label: this.manifest.name, + render: Settings + }); + this.initInject(); + } + + trimLink(link) { + let trimmed = link + if (trimmed.startsWith("https://") || trimmed.startsWith("http://")) trimmed = trimmed.split("://")[1] + if (trimmed.endsWith("/")) trimmed = trimmed.slice(0, trimmed.length - 1) + if (trimmed.includes("/")) trimmed = trimmed.split("/")[0] + trimmed = trimmed.trim() // trimmed x1000 + return trimmed + } + + async initInject() { + inject("embed-redirect", (await getModule(["MessageAccessories"])), "default", (args, res) => { + res.props.message.embeds = res.props.message.embeds.map((embed) => { + services.forEach((s) => { + if (settings.get(s.name.toLowerCase() + "Active", true)) { + if (s.embedMatches(embed)) { + s.replaceEmbed(embed, settings) + } + } + }) + return embed + }) + return res; + }) + + let Anchor = await getModule(m => m.default?.displayName === "Anchor") + inject("embed-redirect-link", Anchor, "default", (args, res) => { + if (res.props.href) { + let trimmed = this.trimLink(res.props.href) + if (trimmed in services.guide) { + let service = services[services.guide[trimmed]] + if (service) { + if (settings.get(service.name.toLowerCase() + "LinkActive", true)) { + service.replaceLink(res, settings) + } + } + } + } + return res + }) + Anchor.default.displayName = "Anchor" + + inject("embed-redirect-textbox-link", (await getModule(m => m.default?.displayName === "SlateChannelTextArea")).default.prototype, "render", (args, res) => { + if (settings.get("enableCosmetics", true)) { + setTimeout(() => { // yes this is a dumb workaround, no i dont care + let inputItems = res.props.children[1].ref.current.children[0]?.children[0]?.children[0]?.children + if (inputItems) { + for (let item in inputItems) { + if (!isNaN(new Number(item).valueOf())) { + if (inputItems[item].children[0].className.includes("fakeLink")) { + let trimmed = this.trimLink(inputItems[item].children[0].children[0].innerText) + if (trimmed in services.guide) { + let service = services[services.guide[trimmed]] + if (service) { + if (settings.get(service.name.toLowerCase() + "LinkActive", true)) { + inputItems[item].children[0].children[0].style.color = redirectLinkColor + } + } + } + } + } + } + } + }, 0) + } + return res + }) + + const Menu = await getModule(['MenuGroup', 'MenuItem']) + const MessageContextMenu = await getModule(m => m.default && m.default.displayName == 'MessageContextMenu') + inject('embed-redirect-context-menu', MessageContextMenu, 'default', (args, res) => { + if (args[0].target.tagName.toLowerCase() == "a" && args[0].target.getAttribute("originallink")) { + if (!findInReactTree(res, e => e.props && e.props.id == 'copy-redirected-link')) { + let copyLink = findInReactTree(res, e => e.props && e.props.id == 'copy-native-link'); + let openLink = findInReactTree(res, e => e.props && e.props.id == 'open-native-link'); + let copyLinkGroup = findInReactTree(res, e => e.props && e.props.children && e.props.children[0] && e.props.children[0].props && e.props.children[0].props.id == 'copy-native-link'); + if (copyLink) { + (copyLinkGroup ? copyLinkGroup : res).props.children.splice((copyLinkGroup ? 1 : 2), 0, + React.createElement(Menu.MenuItem, + { + action: () => { clipboard.copy(args[0].target.getAttribute("href")) }, + id: 'copy-redirected-link', + label: 'Copy Redirected Link' + } + ) + ) + copyLink.props.action = () => { clipboard.copy(args[0].target.getAttribute("originallink")) } + openLink.props.action = () => { window.open(args[0].target.getAttribute("originallink")) } + } + } + } + return res + }) + MessageContextMenu.default.displayName = 'MessageContextMenu' + } + + pluginWillUnload() { + powercord.api.settings.unregisterSettings(this.entityID); + uninject("embed-redirect"); + uninject("embed-redirect-link"); + uninject("embed-redirect-context-menu"); + uninject("embed-redirect-textbox-link"); + }; +}; diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..5361b0b --- /dev/null +++ b/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Embed Redirect", + "version": "1.0.0", + "description": "Changes the destination of embeds for some track-happy services.", + "author": "TaiAurori#6781", + "license": "MIT", + "optionalDependencies": [ + "mtega" + ] +} \ No newline at end of file diff --git a/services.js b/services.js new file mode 100644 index 0000000..6b1b4b5 --- /dev/null +++ b/services.js @@ -0,0 +1,175 @@ +const { React } = require("powercord/webpack") + +const redirectLinkColor = "#0091ff" + +function setting(settings, one, two) { + let instance = settings.get(one, two) + if (instance.startsWith("https://") || instance.startsWith("http://")) instance = instance.split("://")[1] + if (instance.endsWith("/")) instance = instance.slice(0, instance.length - 1) + return instance +} + +module.exports = [ + { + name: "Invidious", + replaces: "YouTube", + instances: "https://github.com/iv-org/documentation/blob/master/Invidious-Instances.md", + default: "yewtu.be", + replacedURL: "youtube.com", + embedMatches: (embed) => { return embed.video && (embed.video.originalURL ? embed.video.originalURL : embed.video.url).includes("youtube") }, + replaceEmbed: (embed, settings) => { + if (!embed.video.originalURL) embed.video.originalURL = embed.video.url; + let instance = setting(settings, "invidiousInstance", "yewtu.be") + if (!instance) { + embed.video.url = "data:text/plain,No Invidious instance selected. You can either:\n- Go to the settings page for Embed Redirect and select one\n- Turn off Embed Redirect\n\n\n" + } else { + if (!settings.get("redirectColorEmbeds", true)) { + embed.provider.url = "https://" + instance + embed.author.url = embed.author.url.replace("www.", "").replace("youtube.com", instance) + embed.url = embed.url.replace("www.", "").replace("youtube.com", instance) + } + embed.video.url = embed.video.url.replace("www.", "").replace("youtube.com", instance) + if (settings.get("enableCosmetics", true)) { + embed.provider.name = "Invidious 🠔 YouTube" + embed.color = "#0091ff" + } + } + }, + replaceLink: (link, settings) => { + if (!link.props.originallink) link.props.originallink = link.props.href + link.props.href = link.props.href.replace(/(www\.)?youtube\.com|youtu\.be/, setting(settings, "invidiousInstance", "invidious.kavin.rocks")) + link.props.onClick = (e) => { } + if (settings.get("enableCosmetics", true)) link.props.style = { color: redirectLinkColor } + link.props.title = link.props.href + } + }, + { + name: "Nitter", + replaces: "Twitter", + instances: "https://github.com/zedeus/nitter/wiki/Instances", + default: "nitter.net", + replacedURL: "twitter.com", + embedMatches: (embed) => { return embed.footer && embed.footer.text == "Twitter" }, + replaceEmbed: (embed, settings) => { + let instance = setting(settings, "nitterInstance", "nitter.net") + if (instance) { + if (!settings.get("redirectColorEmbeds", true)) { + embed.url = embed.url.replace("twitter.com", instance) + embed.author.url = embed.author.url.replace("twitter.com", instance) + } + if (embed.mtega) embed.video.url = embed.video.url.replace("twitter.com", instance) + if (settings.get("enableCosmetics", true)) { + embed.footer.text = "Nitter 🠔 Twitter" + delete embed.footer.iconProxyURL + delete embed.footer.iconURL + embed.color = "#0091ff" + } + } + }, + replaceLink: (link, settings) => { + if (!link.props.originallink) link.props.originallink = link.props.href + link.props.href = link.props.href.replace(link.props.href.includes("fxtwitter.com") ? "fxtwitter.com" : "twitter.com", setting(settings, "nitterInstance", "nitter.moomoo.me")) + link.props.onClick = (e) => { } + if (settings.get("enableCosmetics", true)) link.props.style = { color: redirectLinkColor } + link.props.title = link.props.href + } + }, + { + name: "Libreddit", + replaces: "reddit", + instances: "https://github.com/spikecodes/libreddit#instances", + default: "libreddit.spike.codes", + replacedURL: "reddit.com", + embedMatches: (embed) => { return embed.provider && embed.provider.name == "reddit" }, + replaceEmbed: (embed, settings) => { + let instance = setting(settings, "libredditInstance", "libreddit.spike.codes") + if (instance) { + if (!settings.get("redirectColorEmbeds", true)) embed.url = embed.url.replace("reddit.com", instance) + if (settings.get("enableCosmetics", true)) { + embed.provider.name = "Libreddit 🠔 reddit" + embed.color = "#0091ff" + } + } + }, + replaceLink: (link, settings) => { + if (!link.props.originallink) link.props.originallink = link.props.href + link.props.href = link.props.href.replace(/((old|new|www)\.)?reddit\.com|redd\.it/, setting(settings, "libredditInstance", "libreddit.silkky.cloud")) + link.props.onClick = (e) => { } + if (settings.get("enableCosmetics", true)) link.props.style = { color: redirectLinkColor } + link.props.title = link.props.href + } + }, + { + name: "Bibliogram", + replaces: "Instagram", + instances: "https://git.sr.ht/~cadence/bibliogram-docs/tree/master/docs/Instances.md", + default: "bibliogram.pussthecat.org", + replacedURL: "instagram.com", + embedMatches: (embed) => { return false }, + replaceEmbed: (embed, settings) => { }, + replaceLink: (link, settings) => { + if (!link.props.originallink) link.props.originallink = link.props.href + let subpage = link.props.href.split("://")[1].split("/")[1] + if (!(["", "p", "about"].includes(subpage))) { + link.props.href = link.props.href.replace("www.instagram", "instagram").replace("instagram.com/", "instagram.com/u/") + } + link.props.href = link.props.href.replace("www.instagram", "instagram").replace("instagram.com", setting(settings, "bibliogramInstance", "bibliogram.pussthecat.org")) + link.props.onClick = (e) => { } + if (settings.get("enableCosmetics", true)) link.props.style = { color: redirectLinkColor } + link.props.title = link.props.href + } + }, + { + name: "Wikiless", + replaces: "Wikipedia", + instances: "https://codeberg.org/orenom/Wikiless#instances", + default: "wikiless.org", + replacedURL: "en.wikipedia.org", + embedMatches: (embed) => { return false }, + replaceEmbed: (embed, settings) => { }, + replaceLink: (link, settings) => { + if (!link.props.originallink) link.props.originallink = link.props.href + link.props.href = link.props.href.replace("en.wikipedia.org", setting(settings, "wikilessInstance", "wikiless.org")) + link.props.onClick = (e) => { } + if (settings.get("enableCosmetics", true)) link.props.style = { color: redirectLinkColor } + link.props.title = link.props.href + } + }, + { + name: "Scribe", + replaces: "Medium", + instances: null, + default: "scribe.rip", + replacedURL: "medium.com", + embedMatches: (embed) => { return embed.provider && embed.provider.name == "Medium" }, + replaceEmbed: (embed, settings) => { + if (settings.get("enableCosmetics", true)) { + embed.provider.name = "Scribe 🠔 Medium" + } + }, + replaceLink: (link, settings) => { + if (!link.props.originallink) link.props.originallink = link.props.href + link.props.href = link.props.href.replace("medium.com", setting(settings, "scribeInstance", "scribe.rip")) + link.props.onClick = (e) => { } + if (settings.get("enableCosmetics", true)) link.props.style = { color: redirectLinkColor } + link.props.title = link.props.href + } + }, +] + +module.exports.guide = { + "www.youtube.com": 0, + "youtube.com": 0, + "youtu.be": 0, + "twitter.com": 1, + "fxtwitter.com": 1, + "www.reddit.com": 2, + "old.reddit.com": 2, + "new.reddit.com": 2, + "reddit.com": 2, + "redd.it": 2, + "instagram.com": 3, + "www.instagram.com": 3, + "en.wikipedia.org": 4, + "medium.com": 5, +}