Plugin

ReverseImageSearch

Adds ImageSearch to image context menus

Media Utility
index.tsx
Download

Source

src/plugins/reverseImageSearch/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
8import { Flex } from "@components/Flex";
9import { OpenExternalIcon } from "@components/Icons";
10import { Devs } from "@utils/constants";
11import definePlugin from "@utils/types";
12import { Menu } from "@webpack/common";
13
14const Engines = {
15 Google: "https:class="ts-cmt">//lens.google.com/uploadbyurl?url=",
16 Yandex: "https:class="ts-cmt">//yandex.com/images/search?rpt=imageview&url=",
17 SauceNAO: "https:class="ts-cmt">//saucenao.com/search.php?url=",
18 IQDB: "https:class="ts-cmt">//iqdb.org/?url=",
19 Bing: "https:class="ts-cmt">//www.bing.com/images/search?view=detailv2&iss=sbi&q=imgurl:",
20 TinEye: "https:class="ts-cmt">//www.tineye.com/search?url=",
21 ImgOps: "https:class="ts-cmt">//imgops.com/start?url="
22} as const;
23
24function search(src: string, engine: string) {
25 open(engine + encodeURIComponent(src), "_blank");
26}
27
28function makeSearchItem(src: string) {
29 return (
30 <Menu.MenuItem
31 label="Search Image"
32 key="search-image"
33 id="search-image"
34 >
35 {Object.keys(Engines).map((engine, i) => {
36 const key = "search-image-" + engine;
37 return (
38 <Menu.MenuItem
39 key={key}
40 id={key}
41 label={
42 <Flex alignItems="center" gap="0.5em">
43 <img
44 style={{
45 borderRadius: "50%",
46 }}
47 aria-hidden="true"
48 height={16}
49 width={16}
50 src={`https:class="ts-cmt">//icons.duckduckgo.com/ip3/${new URL(Engines[engine]).host}.ico`}
51 />
52 {engine}
53 </Flex>
54 }
55 action={() => search(src, Engines[engine])}
56 />
57 );
58 })}
59 <Menu.MenuItem
60 key="search-image-all"
61 id="search-image-all"
62 label={
63 <Flex alignItems="center" gap="0.5em">
64 <OpenExternalIcon height={16} width={16} />
65 All
66 </Flex>
67 }
68 action={() => Object.values(Engines).forEach(e => search(src, e))}
69 />
70 </Menu.MenuItem>
71 );
72}
73
74const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
75 if (props?.reverseImageSearchType !== "img") return;
76
77 const src = props.itemHref ?? props.itemSrc;
78
79 const group = findGroupChildrenByChildId("copy-link", children);
80 group?.push(makeSearchItem(src));
81};
82
83const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
84 if (!props?.src) return;
85
86 const group = findGroupChildrenByChildId("copy-native-link", children) ?? children;
87 group.push(makeSearchItem(props.src));
88};
89
90export default definePlugin({
91 name: "ReverseImageSearch",
92 description: "Adds ImageSearch to image context menus",
93 tags: ["Media", "Utility"],
94 authors: [Devs.Ven, Devs.Nuckyz],
95 searchTerms: ["ImageUtilities"],
96
97 patches: [
98 {
99 find: "#{intl::MESSAGE_ACTIONS_MENU_LABEL}),shouldHideMediaOptions:",
100 replacement: {
101 match: /favoriteableType:\i,(?<=(\i)\.getAttribute\("data-type"\).+?)/,
102 replace: (m, target) => `${m}reverseImageSearchType:${target}.getAttribute("data-role"),`
103 }
104 }
105 ],
106 contextMenus: {
107 "message": messageContextMenuPatch,
108 "image-context": imageContextMenuPatch
109 }
110});
111