From e5194d2f9b2dfc3a29e38d48eb1091eef29b5165 Mon Sep 17 00:00:00 2001 From: everestmcarthur Date: Sun, 2 Aug 2026 00:21:20 +0000 Subject: [PATCH] deploy: fb0649015d354bab196f7e369315f04e2f0d63f3 --- key-inspector/install/index.js | 41 +++++++++++++++++++++-------- key-inspector/install/manifest.json | 2 +- plugins-data.json | 2 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/key-inspector/install/index.js b/key-inspector/install/index.js index fa75cc5..0735bfc 100644 --- a/key-inspector/install/index.js +++ b/key-inspector/install/index.js @@ -1,13 +1,32 @@ -(function(B,n,a,d,P,f){"use strict";const{ScrollView:v,View:T}=n.ReactNative;function E({children:e}){return n.React.createElement(v,{style:{flex:1}},e,n.React.createElement(T,{style:{height:24}}))}const{View:m,Text:O}=n.ReactNative;function F({children:e,style:t}){return n.React.createElement(m,{style:[{marginHorizontal:16,marginVertical:8,padding:10,borderRadius:8,borderWidth:1,borderColor:"rgba(128,128,128,0.25)"},t]},n.React.createElement(O,{style:{fontSize:12.5,lineHeight:18,opacity:.75}},e))}const{TouchableOpacity:k,Text:A}=n.ReactNative;function p({label:e,onPress:t,disabled:r,style:o}){return n.React.createElement(k,{disabled:r,onPress:t,style:[{backgroundColor:"#5865F2",borderRadius:8,padding:10,alignItems:"center",opacity:r?.5:1},o]},n.React.createElement(A,{style:{color:"white",fontWeight:"600"}},e))}const _=[{label:'findByProps("computePermissions", "canEveryoneRole")',run:function(){return a.findByProps("computePermissions","canEveryoneRole")}},{label:'findByProps("getBotLabel")',run:function(){return a.findByProps("getBotLabel")}},{label:'findByName("DisplayName", false)',run:function(){return a.findByName("DisplayName",!1)}},{label:'findByName("HeaderName", false)',run:function(){return a.findByName("HeaderName",!1)}},{label:'findByName("getTagProperties", false)',run:function(){return a.findByName("getTagProperties",!1)}},{label:'findByTypeNameAll("UserRow")',run:function(){return a.findByTypeNameAll("UserRow")}},{label:'findByStoreName("ThemeStore")',run:function(){return a.findByStoreName("ThemeStore")}},{label:'findByStoreName("GuildMemberStore")',run:function(){return a.findByStoreName("GuildMemberStore")}},{label:'findByStoreName("GuildStore")',run:function(){return a.findByStoreName("GuildStore")}},{label:'findByStoreName("ChannelStore")',run:function(){return a.findByStoreName("ChannelStore")}},{label:'findByStoreName("UserStore")',run:function(){return a.findByStoreName("UserStore")}},{label:'findByStoreName("RelationshipStore")',run:function(){return a.findByStoreName("RelationshipStore")}},{label:'findByName("RowManager", false)',run:function(){return a.findByName("RowManager",!1)}},{label:'findByProps("TYPING_WRAPPER_HEIGHT")',run:function(){return a.findByProps("TYPING_WRAPPER_HEIGHT")}},{label:'findByName("VoiceUserConnected", false)',run:function(){return a.findByName("VoiceUserConnected",!1)}},{label:"find(resolveSemanticColor via default.internal)",run:function(){return a.find(function(e){return e?.default?.internal?.resolveSemanticColor})}},{label:"find(resolveSemanticColor via meta)",run:function(){return a.find(function(e){return e?.meta?.resolveSemanticColor})}},{label:"find(UserProfile)",run:function(){return a.find(function(e){return e?.type?.name==="UserProfile"})}},{label:'findByName("UserProfileSection", false)',run:function(){return a.findByName("UserProfileSection",!1)}},{label:'findByProps("sendMessage", "sendBotMessage")',run:function(){return a.findByProps("sendMessage","sendBotMessage")}},{label:'findByProps("openLazy", "hideActionSheet")',run:function(){return a.findByProps("openLazy","hideActionSheet")}},{label:'findByProps("push", "pushLazy", "pop")',run:function(){return a.findByProps("push","pushLazy","pop")}},{label:'findByProps("getRenderCloseButton")',run:function(){return a.findByProps("getRenderCloseButton")}},{label:'findByProps("getHeaderCloseButton")',run:function(){return a.findByProps("getHeaderCloseButton")}},{label:'findByName("Navigator")',run:function(){return a.findByName("Navigator")}},{label:'findByName("ThemedRolePill", false)',run:function(){return a.findByName("ThemedRolePill",!1)}},{label:'findByProps("saveProfileChanges")',run:function(){return a.findByProps("saveProfileChanges")}},{label:'findByProps("useAvatarColors")',run:function(){return a.findByProps("useAvatarColors")}},{label:'findByProps("useThemeContext")',run:function(){return a.findByProps("useThemeContext")}},{label:'findByProps("getProfileTheme")',run:function(){return a.findByProps("getProfileTheme")}},{label:'findByProps("ThemeContextProvider")',run:function(){return a.findByProps("ThemeContextProvider")}},{label:"semanticColors.HEADER_SECONDARY",run:function(){return f.semanticColors?.HEADER_SECONDARY}},{label:"semanticColors.BACKGROUND_ACCENT",run:function(){return f.semanticColors?.BACKGROUND_ACCENT}},{label:"semanticColors.BACKGROUND_PRIMARY",run:function(){return f.semanticColors?.BACKGROUND_PRIMARY}},{label:"semanticColors.BACKGROUND_FLOATING",run:function(){return f.semanticColors?.BACKGROUND_FLOATING}},{label:"semanticColors.BUTTON_OUTLINE_BRAND_BORDER_ACTIVE",run:function(){return f.semanticColors?.BUTTON_OUTLINE_BRAND_BORDER_ACTIVE}},{label:"semanticColors.TEXT_NORMAL",run:function(){return f.semanticColors?.TEXT_NORMAL}}];function D(e){if(e==null)return"NOT FOUND";if(typeof e=="object"){const t=Object.keys(e);return`found (${t.length} key${t.length===1?"":"s"})`}return`found (${typeof e})`}function j(){return _.map(function({label:e,run:t}){try{const r=t();return{label:e,found:r!=null,detail:D(r)}}catch(r){return{label:e,found:!1,detail:`threw: ${r}`}}})}function U(e){const t=e.filter(function(r){return!r.found});return[`Key Inspector report - ${new Date().toISOString()}`,`${e.length} checked, ${t.length} missing`,"",...e.map(function(r){return`[${r.found?"OK":"MISSING"}] ${r.label} -> ${r.detail}`}),"",`semanticColors keys (${Object.keys(f.semanticColors??{}).length}):`,...Object.keys(f.semanticColors??{}).sort(),"",`rawColors keys (${Object.keys(f.rawColors??{}).length}):`,...Object.keys(f.rawColors??{}).sort()].join(` -`)}const I=new Set(["vendetta.metro.modules"]);function M(e){return e===null?"null":e===void 0?"undefined":Array.isArray(e)?`array(${e.length})`:typeof e}function z(e=3){const t=new WeakSet,r=[];function o(i,s,u){if(i==null||typeof i!="object"&&typeof i!="function")return;if(I.has(s)){r.push(`${s} -> (skipped - Discord's full module registry, use the search tools for this)`);return}if(typeof i=="object"){if(t.has(i)){r.push(`${s} -> [already listed elsewhere, circular]`);return}t.add(i)}let l;try{l=Object.keys(i).sort()}catch(b){r.push(`${s} -> [couldn't read keys: ${b}]`);return}const c=l.slice(0,60);for(const b of c){const $=`${s}.${b}`;let S;try{S=i[b]}catch(re){r.push(`${$} -> [threw on access: ${re}]`);continue}const C=M(S);r.push(`${$} (${C})`),C==="object"&&u60&&r.push(`${s} -> (+${l.length-60} more keys not shown at this level)`)}try{o(globalThis.vendetta,"vendetta",0)}catch(i){r.push(`Error walking the vendetta object: ${i}`)}return r.length?r.join(` -`):"vendetta object not found or empty"}function y(e){const t=window?.modules;if(t)for(const r in t){const o=t[r];if(!o?.isInitialized)continue;const i=o.publicModule?.exports;if(i)try{if(e(i))return i;if(i.default!=null&&e(i.default))return i.default}catch{}}}function w(e){return y(function(t){return t?.name===e||t?.displayName===e||t?.type?.name===e||t?.type?.displayName===e})}function H(e){return y(function(t){return t?.name===e||t?.displayName===e})}function x(...e){return y(function(t){return e.every(function(r){return t?.[r]!==void 0})})}function G(e){return y(function(t){return typeof t?.getName=="function"&&t.getName.length===0&&t.getName()===e})}const L={0:"FunctionComponent",1:"ClassComponent",3:"HostRoot",5:"HostComponent",6:"HostText",7:"Fragment",8:"Mode",9:"ContextConsumer",10:"ContextProvider",11:"ForwardRef",13:"SuspenseComponent",14:"MemoComponent",15:"SimpleMemoComponent"};function V(e){return L[e]??`tag ${e}`}function Y(){const e=[`YouBar+ diagnostics - ${new Date().toISOString()}`,""],t=w("YouBarNotificationsButton");e.push(`1. rawFindByTypeName("YouBarNotificationsButton") -> ${t?"FOUND":"NOT FOUND"}`),t&&(e.push(` typeof target = ${typeof t}`),e.push(` has .type (inner render fn) = ${typeof t?.type=="function"}`),e.push(` has .compare = ${"compare"in(t??{})}`),e.push(` $$typeof = ${String(t?.$$typeof??"none")}`)),e.push("");const r=window?.__REACT_DEVTOOLS_GLOBAL_HOOK__;e.push(`2. window.__REACT_DEVTOOLS_GLOBAL_HOOK__ -> ${r?"present":"MISSING"}`),r&&(e.push(` typeof getFiberRoots = ${typeof r.getFiberRoots}`),e.push(` renderers = ${r.renderers?`Map(${r.renderers.size??"?"})`:"MISSING"}`)),e.push("");let o=null;if(r?.getFiberRoots&&r?.renderers?.keys&&t){let s=0;for(const u of r.renderers.keys()){s++;let l;try{l=r.getFiberRoots(u)}catch(c){e.push(`3. renderer ${u}: getFiberRoots threw: ${c}`);continue}if(e.push(`3. renderer ${u}: ${l?l.size:0} fiber root(s)`),l)for(const c of l)o||(o=K(c.current,t))}s===0&&e.push("3. hook.renderers is empty - React never registered with it")}else e.push("3. skipped (no hook, no renderers, or component not found in step 1)");if(e.push(` target fiber found by tree walk -> ${o?"YES":"NO"}`),e.push(""),o){e.push("4. Ancestor chain from target fiber up to root:");let s=o,u=0,l=!1;for(;s&&u<60;){const c=typeof s.stateNode?.forceUpdate=="function";c&&(l=!0),e.push(` [${u}] ${V(s.tag)}${c?" <- has forceUpdate()":""}`),s=s.return,u++}e.push(` class-component ancestor with forceUpdate found -> ${l?"YES":"NO"}`)}else e.push("4. skipped (no fiber found in step 3)");e.push("");const i=a.findByProps("getRootNavigationRef");if(e.push(`5. findByProps("getRootNavigationRef") -> ${i?"FOUND":"NOT FOUND"}`),i)try{const s=i.getRootNavigationRef?.();if(e.push(` getRootNavigationRef() -> ${s?"returned a ref":"null/undefined"}`),s){e.push(` typeof isReady = ${typeof s.isReady}`),e.push(` isReady() = ${function(){try{return s.isReady?.()}catch(l){return`threw: ${l}`}}()}`),e.push(` typeof getCurrentRoute = ${typeof s.getCurrentRoute}`);const u=function(){try{return s.getCurrentRoute?.()}catch{return}}();e.push(` getCurrentRoute() -> ${u?JSON.stringify({name:u.name,params:u.params}):"none"}`),e.push(` typeof navigate = ${typeof s.navigate}`),e.push(` typeof setParams = ${typeof s.setParams}`)}}catch(s){e.push(` threw: ${s}`)}return e.join(` -`)}function K(e,t){const r=e?[e]:[];let o=0;for(;r.length;){if(++o>2e5)return null;const i=r.pop();if(i){if(i.type===t||i.elementType===t)return i;i.child&&r.push(i.child),i.sibling&&r.push(i.sibling)}}return null}function W(e){if(e===void 0)return"undefined";if(e===null)return"null";if(typeof e=="function")return`[Function: ${e.name||"anonymous"}]`;if(typeof e!="object")return String(e);const t=new WeakSet;try{return JSON.stringify(e,function(r,o){if(typeof o=="function")return`[Function: ${o.name||"anonymous"}]`;if(typeof o=="symbol")return o.toString();if(typeof o=="object"&&o!==null){if(t.has(o))return"[Circular]";t.add(o)}return o},2)}catch{try{return`[unserializable object, keys: ${Object.keys(e).join(", ")}]`}catch(r){return`[unserializable, and Object.keys threw: ${r}]`}}}async function J(e){try{const t=await new Function("findByProps","findByName","find","rawFind","rawFindByTypeName","rawFindByProps","rawFindByName","rawFindByStoreName","React","ReactNative","FluxDispatcher","window",`return (async () => { -${e} -})();`)(a.findByProps,a.findByName,a.find,y,w,x,H,G,n.React,n.ReactNative,n.FluxDispatcher,window);return W(t)}catch(t){return`Error: ${t instanceof Error?`${t.message} -${t.stack??""}`:String(t)}`}}const{View:h,Text:g}=n.ReactNative,{FormSection:R,FormInput:N}=P.Forms;function X(){const[e,t]=n.React.useState(null);return n.React.createElement(R,{title:"Full diagnostic scan"},n.React.createElement(h,{style:{paddingHorizontal:16,paddingVertical:8}},n.React.createElement(p,{label:"Run scan & copy full report",onPress:function(){const r=j(),o=r.filter(function(i){return!i.found}).length;n.clipboard.setString(U(r)),t(`${r.length} checked, ${o} missing - full report copied`),d.showToast(`${o} missing out of ${r.length} - report copied`,void 0)}}),e&&n.React.createElement(g,{style:{marginTop:8,fontSize:12.5,opacity:.7}},e)))}function q(){return n.React.createElement(R,{title:"YouBar+ diagnostics"},n.React.createElement(h,{style:{paddingHorizontal:16,paddingVertical:8}},n.React.createElement(p,{label:"Run YouBar+ diagnostics & copy report",onPress:function(){const e=Y();n.clipboard.setString(e),d.showToast("Diagnostics copied - paste into chat",void 0)}}),n.React.createElement(g,{style:{marginTop:8,fontSize:12.5,opacity:.7}},"Checks whether YouBarNotificationsButton can be found live, whether the React DevTools fiber-root registry forceRerender depends on is actually populated, and whether a class-component ancestor or the navigation ref fallback exists at all - run this right after toggling YouBar+ on/off to see exactly which step is failing.")))}function Q(){const[e,t]=n.React.useState(""),[r,o]=n.React.useState(null),[i,s]=n.React.useState(!1),u=async function(){if(!(!e.trim()||i)){s(!0);try{const l=await J(e);o(l),n.clipboard.setString(l),d.showToast("Result copied",void 0)}finally{s(!1)}}};return n.React.createElement(R,{title:"Eval"},n.React.createElement(h,{style:{paddingHorizontal:16,paddingVertical:8}},n.React.createElement(N,{title:"Code",placeholder:'e.g. return findByProps("getRootNavigationRef")',value:e,onChange:t,multiline:!0}),n.React.createElement(p,{label:i?"Running...":"Run & copy result",style:{marginTop:8},disabled:i,onPress:u}),r!=null&&n.React.createElement(g,{style:{marginTop:8,fontSize:12.5,opacity:.85},selectable:!0},r.length>2e3?`${r.slice(0,2e3)} -... (truncated, full result copied)`:r),n.React.createElement(g,{style:{marginTop:8,fontSize:12.5,opacity:.7}},"Runs as the body of an async function - a bare expression, `return ...`, or `await ...` all work. findByProps/findByName/find, the raw* passive variants (rawFind/rawFindByTypeName/rawFindByProps/rawFindByName/rawFindByStoreName), React, ReactNative, FluxDispatcher, and window are already in scope. Nothing runs until you tap the button.")))}function Z(){return n.React.createElement(R,{title:"Full Vendetta API tree"},n.React.createElement(h,{style:{paddingHorizontal:16,paddingVertical:8}},n.React.createElement(p,{label:"Copy the entire plugin API tree",onPress:function(){const e=z(3),t=e.split(` -`).length;n.clipboard.setString(e),d.showToast(`Copied ${t} lines - every key path under window.vendetta`,void 0)}}),n.React.createElement(g,{style:{marginTop:8,fontSize:12.5,opacity:.7}},"Every key path under the stable plugin API (window.vendetta), 3 levels deep - this is everything Revenge itself exposes to plugins, not Discord's raw internals (those are searchable individually below, since dumping all of them would be enormous).")))}function ee(){const[e,t]=n.React.useState(""),[r,o]=n.React.useState(""),i=function(){const u=e.split(",").map(function(l){return l.trim()}).filter(Boolean);if(u.length)try{const l=a.findByProps(...u);if(!l){n.clipboard.setString(`findByProps(${u.join(", ")}) -> not found`),d.showToast("No module found - copied that result",void 0);return}const c=Object.keys(l).sort();n.clipboard.setString(`findByProps(${u.join(", ")}) -> found, keys: +(function($,e,s,p,k,R,E){"use strict";const{ScrollView:F,View:j}=e.ReactNative;function A({children:t}){return e.React.createElement(F,{style:{flex:1}},t,e.React.createElement(j,{style:{height:24}}))}const{View:O,Text:z}=e.ReactNative;function D({children:t,style:r}){return e.React.createElement(O,{style:[{marginHorizontal:16,marginVertical:8,padding:10,borderRadius:8,borderWidth:1,borderColor:"rgba(128,128,128,0.25)"},r]},e.React.createElement(z,{style:{fontSize:12.5,lineHeight:18,opacity:.75}},t))}const{TouchableOpacity:I,Text:x}=e.ReactNative;function h({label:t,onPress:r,disabled:n,style:l}){return e.React.createElement(I,{disabled:n,onPress:r,style:[{backgroundColor:"#5865F2",borderRadius:8,padding:10,alignItems:"center",opacity:n?.5:1},l]},e.React.createElement(x,{style:{color:"white",fontWeight:"600"}},t))}function w(t){const r=window?.modules;if(r)for(const n in r){const l=r[n];if(!l?.isInitialized)continue;const i=l.publicModule?.exports;if(i)try{if(t(i))return i;if(i.default!=null&&t(i.default))return i.default}catch{}}}function T(t){return w(function(r){return r?.name===t||r?.displayName===t||r?.type?.name===t||r?.type?.displayName===t})}function m(t){return w(function(r){return r?.name===t||r?.displayName===t})}function P(...t){return w(function(r){return t.every(function(n){return r?.[n]!==void 0})})}function C(t){return w(function(r){return typeof r?.getName=="function"&&r.getName.length===0&&r.getName()===t})}const _=[{label:'findByProps("computePermissions", "canEveryoneRole")',run:function(){return s.findByProps("computePermissions","canEveryoneRole")}},{label:'findByProps("getBotLabel")',run:function(){return s.findByProps("getBotLabel")}},{label:'findByName("DisplayName", false)',run:function(){return s.findByName("DisplayName",!1)}},{label:'findByName("HeaderName", false)',run:function(){return s.findByName("HeaderName",!1)}},{label:'findByName("getTagProperties", false)',run:function(){return s.findByName("getTagProperties",!1)}},{label:'findByTypeNameAll("UserRow")',run:function(){return s.findByTypeNameAll("UserRow")}},{label:'findByStoreName("ThemeStore")',run:function(){return s.findByStoreName("ThemeStore")}},{label:'findByStoreName("GuildMemberStore")',run:function(){return s.findByStoreName("GuildMemberStore")}},{label:'findByStoreName("GuildStore")',run:function(){return s.findByStoreName("GuildStore")}},{label:'findByStoreName("ChannelStore")',run:function(){return s.findByStoreName("ChannelStore")}},{label:'findByStoreName("UserStore")',run:function(){return s.findByStoreName("UserStore")}},{label:'findByStoreName("RelationshipStore")',run:function(){return s.findByStoreName("RelationshipStore")}},{label:'findByName("RowManager", false)',run:function(){return s.findByName("RowManager",!1)}},{label:'findByProps("TYPING_WRAPPER_HEIGHT")',run:function(){return s.findByProps("TYPING_WRAPPER_HEIGHT")}},{label:'findByName("VoiceUserConnected", false)',run:function(){return s.findByName("VoiceUserConnected",!1)}},{label:"find(resolveSemanticColor via default.internal)",run:function(){return s.find(function(t){return t?.default?.internal?.resolveSemanticColor})}},{label:"find(resolveSemanticColor via meta)",run:function(){return s.find(function(t){return t?.meta?.resolveSemanticColor})}},{label:"find(UserProfile)",run:function(){return s.find(function(t){return t?.type?.name==="UserProfile"})}},{label:'findByName("UserProfileSection", false)',run:function(){return s.findByName("UserProfileSection",!1)}},{label:'findByProps("sendMessage", "sendBotMessage")',run:function(){return s.findByProps("sendMessage","sendBotMessage")}},{label:'findByProps("openLazy", "hideActionSheet")',run:function(){return s.findByProps("openLazy","hideActionSheet")}},{label:'findByProps("push", "pushLazy", "pop")',run:function(){return s.findByProps("push","pushLazy","pop")}},{label:'findByProps("getRenderCloseButton")',run:function(){return s.findByProps("getRenderCloseButton")}},{label:'findByProps("getHeaderCloseButton")',run:function(){return s.findByProps("getHeaderCloseButton")}},{label:'findByName("Navigator")',run:function(){return s.findByName("Navigator")}},{label:'findByName("ThemedRolePill", false)',run:function(){return s.findByName("ThemedRolePill",!1)}},{label:'findByProps("saveProfileChanges")',run:function(){return s.findByProps("saveProfileChanges")}},{label:'findByProps("useAvatarColors")',run:function(){return s.findByProps("useAvatarColors")}},{label:'findByProps("useThemeContext")',run:function(){return s.findByProps("useThemeContext")}},{label:'findByProps("getProfileTheme")',run:function(){return s.findByProps("getProfileTheme")}},{label:'findByProps("ThemeContextProvider")',run:function(){return s.findByProps("ThemeContextProvider")}},{label:"semanticColors.HEADER_SECONDARY",run:function(){return R.semanticColors?.HEADER_SECONDARY}},{label:"semanticColors.BACKGROUND_ACCENT",run:function(){return R.semanticColors?.BACKGROUND_ACCENT}},{label:"semanticColors.BACKGROUND_PRIMARY",run:function(){return R.semanticColors?.BACKGROUND_PRIMARY}},{label:"semanticColors.BACKGROUND_FLOATING",run:function(){return R.semanticColors?.BACKGROUND_FLOATING}},{label:"semanticColors.BUTTON_OUTLINE_BRAND_BORDER_ACTIVE",run:function(){return R.semanticColors?.BUTTON_OUTLINE_BRAND_BORDER_ACTIVE}},{label:"semanticColors.TEXT_NORMAL",run:function(){return R.semanticColors?.TEXT_NORMAL}}];function M(t){if(t==null)return"NOT FOUND";if(typeof t=="object"){const r=Object.keys(t);return`found (${r.length} key${r.length===1?"":"s"})`}return`found (${typeof t})`}function U(){return _.map(function({label:t,run:r}){try{const n=r();return{label:t,found:n!=null,detail:M(n)}}catch(n){return{label:t,found:!1,detail:`threw: ${n}`}}})}function G(t){const r=t.filter(function(n){return!n.found});return[`Key Inspector report - ${new Date().toISOString()}`,`${t.length} checked, ${r.length} missing`,"",...t.map(function(n){return`[${n.found?"OK":"MISSING"}] ${n.label} -> ${n.detail}`}),"",`semanticColors keys (${Object.keys(R.semanticColors??{}).length}):`,...Object.keys(R.semanticColors??{}).sort(),"",`rawColors keys (${Object.keys(R.rawColors??{}).length}):`,...Object.keys(R.rawColors??{}).sort()].join(` +`)}const H=new Set(["vendetta.metro.modules"]);function L(t){return t===null?"null":t===void 0?"undefined":Array.isArray(t)?`array(${t.length})`:typeof t}function V(t=3){const r=new WeakSet,n=[];function l(i,f,d){if(i==null||typeof i!="object"&&typeof i!="function")return;if(H.has(f)){n.push(`${f} -> (skipped - Discord's full module registry, use the search tools for this)`);return}if(typeof i=="object"){if(r.has(i)){n.push(`${f} -> [already listed elsewhere, circular]`);return}r.add(i)}let u;try{u=Object.keys(i).sort()}catch(a){n.push(`${f} -> [couldn't read keys: ${a}]`);return}const o=u.slice(0,60);for(const a of o){const c=`${f}.${a}`;let y;try{y=i[a]}catch(v){n.push(`${c} -> [threw on access: ${v}]`);continue}const b=L(y);n.push(`${c} (${b})`),b==="object"&&d60&&n.push(`${f} -> (+${u.length-60} more keys not shown at this level)`)}try{l(globalThis.vendetta,"vendetta",0)}catch(i){n.push(`Error walking the vendetta object: ${i}`)}return n.length?n.join(` +`):"vendetta object not found or empty"}function Y(t){if(t===void 0)return"undefined";if(t===null)return"null";if(typeof t=="function")return`[Function: ${t.name||"anonymous"}]`;if(typeof t!="object")return String(t);const r=new WeakSet;try{return JSON.stringify(t,function(n,l){if(typeof l=="function")return`[Function: ${l.name||"anonymous"}]`;if(typeof l=="symbol")return l.toString();if(typeof l=="object"&&l!==null){if(r.has(l))return"[Circular]";r.add(l)}return l},2)}catch{try{return`[unserializable object, keys: ${Object.keys(t).join(", ")}]`}catch(n){return`[unserializable, and Object.keys threw: ${n}]`}}}async function K(t){try{const r=await new Function("findByProps","findByName","find","rawFind","rawFindByTypeName","rawFindByProps","rawFindByName","rawFindByStoreName","React","ReactNative","FluxDispatcher","window",`return (async () => { +${t} +})();`)(s.findByProps,s.findByName,s.find,w,T,P,m,C,e.React,e.ReactNative,e.FluxDispatcher,window);return Y(r)}catch(r){return`Error: ${r instanceof Error?`${r.message} +${r.stack??""}`:String(r)}`}}const W=500;function q(t,r){return new Promise(function(n){const l=new Map;let i=function(){},f=!1;const d=function(){if(f)return;f=!0,i();const u=r?.trim(),o=[...l.entries()].sort(function(a,c){return c[1]-a[1]}).map(function([a,c]){return`${String(c).padStart(5)} ${a}`});n(o.length?`${l.size} distinct type(s)${u?` matching "${u}"`:""} over ${t}ms: + +${o.join(` +`)}`:`(no${u?` matching "${u}"`:""} events fired during the ${t}ms capture window)`)};try{i=E.before("dispatch",e.FluxDispatcher,function(u){const o=u[0]?.type??"(no type)",a=r?.trim();a&&!o.includes(a)||!l.has(o)&&l.size>=W||l.set(o,(l.get(o)??0)+1)})}catch(u){n(`Failed to patch FluxDispatcher.dispatch: ${u}`);return}setTimeout(d,t)})}function J(t){try{return JSON.stringify(t,function(r,n){return typeof n=="function"?`[Function: ${n.name||"anonymous"}]`:n},2)}catch{try{return`[unserializable, keys: ${Object.keys(t??{}).join(", ")}]`}catch{return String(t)}}}function X(t,r,n){return new Promise(function(l){const i=t.trim(),f=s.findByTypeName(i)??s.findByName(i,!1);if(!f){l(`"${i}" not found via findByTypeName or findByName.`);return}if(typeof f.type!="function"){l(`Found "${i}", but it has no callable .type property (not a memo/forwardRef wrapper), so its own render calls can't be intercepted this way. Its keys: ${Object.keys(f).join(", ")}`);return}const d=[];let u=!1,o=function(){};const a=function(){u||(u=!0,o(),l(d.length?d.join(` + +--- + +`):`"${i}" never rendered during the ${n}ms capture window.`))};try{o=E.before("type",f,function(c){d.push(`Call ${d.length+1} props: +${J(c[0])}`),d.length>=r&&a()})}catch(c){l(`Failed to patch "${i}": ${c}`);return}setTimeout(a,n)})}const{View:N,Text:B}=e.ReactNative,{FormSection:S,FormInput:g}=k.Forms;function Q(){const[t,r]=e.React.useState(null);return e.React.createElement(S,{title:"Full diagnostic scan"},e.React.createElement(N,{style:{paddingHorizontal:16,paddingVertical:8}},e.React.createElement(h,{label:"Run scan & copy full report",onPress:function(){const n=U(),l=n.filter(function(i){return!i.found}).length;e.clipboard.setString(G(n)),r(`${n.length} checked, ${l} missing - full report copied`),p.showToast(`${l} missing out of ${n.length} - report copied`,void 0)}}),t&&e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.7}},t)))}function Z(){const[t,r]=e.React.useState(""),[n,l]=e.React.useState(null),[i,f]=e.React.useState(!1),d=async function(){if(!(!t.trim()||i)){f(!0);try{const u=await K(t);l(u),e.clipboard.setString(u),p.showToast("Result copied",void 0)}finally{f(!1)}}};return e.React.createElement(S,{title:"Eval"},e.React.createElement(N,{style:{paddingHorizontal:16,paddingVertical:8}},e.React.createElement(g,{title:"Code",placeholder:'e.g. return findByProps("getRootNavigationRef")',value:t,onChange:r,multiline:!0}),e.React.createElement(h,{label:i?"Running...":"Run & copy result",style:{marginTop:8},disabled:i,onPress:d}),n!=null&&e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.85},selectable:!0},n.length>2e3?`${n.slice(0,2e3)} +... (truncated, full result copied)`:n),e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.7}},"Runs as the body of an async function - a bare expression, `return ...`, or `await ...` all work. findByProps/findByName/find, the raw* passive variants (rawFind/rawFindByTypeName/rawFindByProps/rawFindByName/rawFindByStoreName), React, ReactNative, FluxDispatcher, and window are already in scope. Nothing runs until you tap the button.")))}function ee(){const[t,r]=e.React.useState(""),[n,l]=e.React.useState("10"),[i,f]=e.React.useState(null),[d,u]=e.React.useState(!1),o=async function(){if(d)return;const a=Math.max(1,Number(n)||10)*1e3;u(!0),f(null),p.showToast(`Capturing Flux events for ${a/1e3}s - go do the thing...`,void 0);try{const c=await q(a,t);f(c),e.clipboard.setString(c),p.showToast("Capture done - report copied",void 0)}finally{u(!1)}};return e.React.createElement(S,{title:"Flux event logger"},e.React.createElement(N,{style:{paddingHorizontal:16,paddingVertical:8}},e.React.createElement(g,{title:"Filter (optional substring, e.g. GUILD)",placeholder:"Leave blank to capture everything",value:t,onChange:r}),e.React.createElement(g,{title:"Capture duration (seconds)",placeholder:"10",value:n,onChange:l,keyboardType:"numeric"}),e.React.createElement(h,{label:d?"Capturing...":"Start capture",style:{marginTop:8},disabled:d,onPress:o}),i!=null&&e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.85},selectable:!0},i.length>2e3?`${i.slice(0,2e3)} +... (truncated, full report copied)`:i),e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.7}},"Tap start, then go do the thing you're trying to understand (open a screen, send a message, tap a button) before the window closes. Reports every action type that dispatched, sorted by count - the fastest way to find which event to hook into instead of guessing from decompiled source.")))}function te(){const[t,r]=e.React.useState(""),[n,l]=e.React.useState("3"),[i,f]=e.React.useState("15"),[d,u]=e.React.useState(null),[o,a]=e.React.useState(!1),c=async function(){const y=t.trim();if(!y||o)return;const b=Math.max(1,Number(i)||15)*1e3;a(!0),u(null);try{const v=await X(y,Math.max(1,Number(n)||3),b);u(v),e.clipboard.setString(v),p.showToast("Capture done - result copied",void 0)}finally{a(!1)}};return e.React.createElement(S,{title:"Component render/props logger"},e.React.createElement(N,{style:{paddingHorizontal:16,paddingVertical:8}},e.React.createElement(g,{title:"Component name (findByTypeName / findByName)",placeholder:"e.g. YouBarNotificationsButton",value:t,onChange:r}),e.React.createElement(g,{title:"Max renders to capture",placeholder:"3",value:n,onChange:l,keyboardType:"numeric"}),e.React.createElement(g,{title:"Give up after (seconds)",placeholder:"15",value:i,onChange:f,keyboardType:"numeric"}),e.React.createElement(h,{label:o?"Waiting for renders...":"Start capture",style:{marginTop:8},disabled:o,onPress:c}),d!=null&&e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.85},selectable:!0},d.length>2e3?`${d.slice(0,2e3)} +... (truncated, full result copied)`:d),e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.7}},"Patches the component to log its actual props on every render, up to the call cap or the time limit. Only works for React.memo/forwardRef-wrapped components (most of what findByTypeName/findByName return) - a bare function component has no property to intercept its own calls through.")))}function ne(){const[t,r]=e.React.useState(""),[n,l]=e.React.useState(""),[i,f]=e.React.useState(""),[d,u]=e.React.useState(""),o=function(a,c){try{const y=c();if(!y){e.clipboard.setString(`${a} -> not found`),p.showToast("Not found - copied that result",void 0);return}const b=Object.keys(y).sort();e.clipboard.setString(`${a} -> found, keys: +${b.join(` +`)}`),p.showToast(`Found it - copied ${b.length} key(s)`,void 0)}catch(y){e.clipboard.setString(`Error running ${a}: ${y}`),p.showToast("Search failed - copied the error instead",void 0)}};return e.React.createElement(S,{title:"Raw search (bypasses the negative-result cache)"},e.React.createElement(N,{style:{paddingHorizontal:16,paddingVertical:8}},e.React.createElement(g,{title:"rawFindByProps - comma-separated prop names",placeholder:"e.g. getRootNavigationRef",value:t,onChange:r}),e.React.createElement(h,{label:"Search & copy result",style:{marginTop:8,marginBottom:16},onPress:function(){const a=t.split(",").map(function(c){return c.trim()}).filter(Boolean);a.length&&o(`rawFindByProps(${a.join(", ")})`,function(){return P(...a)})}}),e.React.createElement(g,{title:"rawFindByName",placeholder:"e.g. YouBarNotificationsButton",value:n,onChange:l}),e.React.createElement(h,{label:"Search & copy result",style:{marginTop:8,marginBottom:16},onPress:function(){return n.trim()&&o(`rawFindByName("${n.trim()}")`,function(){return m(n.trim())})}}),e.React.createElement(g,{title:"rawFindByTypeName",placeholder:"e.g. YouBarNotificationsButton",value:i,onChange:f}),e.React.createElement(h,{label:"Search & copy result",style:{marginTop:8,marginBottom:16},onPress:function(){return i.trim()&&o(`rawFindByTypeName("${i.trim()}")`,function(){return T(i.trim())})}}),e.React.createElement(g,{title:"rawFindByStoreName",placeholder:"e.g. GuildStore",value:d,onChange:u}),e.React.createElement(h,{label:"Search & copy result",style:{marginTop:8},onPress:function(){return d.trim()&&o(`rawFindByStoreName("${d.trim()}")`,function(){return C(d.trim())})}}),e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.7}},`Revenge's own findByProps/findByName/findByTypeName/findByStoreName cache a negative result forever the first time a search comes up empty, and never rescan - so if a module hasn't been required yet when you search, it can look "missing" permanently for the rest of the session even after it registers. These raw* variants only ever read modules Metro's own isInitialized flag says are already loaded (never force-requiring anything), so re-running one after doing the thing that loads your target module will actually find it.`)))}function re(){return e.React.createElement(S,{title:"Full Vendetta API tree"},e.React.createElement(N,{style:{paddingHorizontal:16,paddingVertical:8}},e.React.createElement(h,{label:"Copy the entire plugin API tree",onPress:function(){const t=V(3),r=t.split(` +`).length;e.clipboard.setString(t),p.showToast(`Copied ${r} lines - every key path under window.vendetta`,void 0)}}),e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.7}},"Every key path under the stable plugin API (window.vendetta), 3 levels deep - this is everything Revenge itself exposes to plugins, not Discord's raw internals (those are searchable individually below, since dumping all of them would be enormous).")))}function ae(){const[t,r]=e.React.useState(""),[n,l]=e.React.useState(""),i=function(){const o=t.split(",").map(function(a){return a.trim()}).filter(Boolean);if(o.length)try{const a=s.findByProps(...o);if(!a){e.clipboard.setString(`findByProps(${o.join(", ")}) -> not found`),p.showToast("No module found - copied that result",void 0);return}const c=Object.keys(a).sort();e.clipboard.setString(`findByProps(${o.join(", ")}) -> found, keys: ${c.join(` -`)}`),d.showToast(`Found it - copied ${c.length} key(s)`,void 0)}catch(l){n.clipboard.setString(`Error searching for ${u.join(", ")}: ${l}`),d.showToast("Search failed - copied the error instead",void 0)}},s=function(){const u=r.trim();if(u)try{const l=a.findByName(u,!1);if(!l){n.clipboard.setString(`findByName("${u}") -> not found`),d.showToast("No component found - copied that result",void 0);return}const c=Object.keys(l).sort();n.clipboard.setString(`findByName("${u}") -> found, keys: +`)}`),p.showToast(`Found it - copied ${c.length} key(s)`,void 0)}catch(a){e.clipboard.setString(`Error searching for ${o.join(", ")}: ${a}`),p.showToast("Search failed - copied the error instead",void 0)}},f=function(){const o=n.trim();if(o)try{const a=s.findByName(o,!1);if(!a){e.clipboard.setString(`findByName("${o}") -> not found`),p.showToast("No component found - copied that result",void 0);return}const c=Object.keys(a).sort();e.clipboard.setString(`findByName("${o}") -> found, keys: ${c.join(` -`)}`),d.showToast(`Found it - copied ${c.length} key(s)`,void 0)}catch(l){n.clipboard.setString(`Error searching for "${u}": ${l}`),d.showToast("Search failed - copied the error instead",void 0)}};return n.React.createElement(R,{title:"One-off search"},n.React.createElement(h,{style:{paddingHorizontal:16,paddingVertical:8}},n.React.createElement(N,{title:"findByProps - comma-separated prop names",placeholder:"e.g. sendMessage, sendBotMessage",value:e,onChange:t}),n.React.createElement(p,{label:"Search & copy result",style:{marginTop:8,marginBottom:16},onPress:i}),n.React.createElement(N,{title:"findByName - component name",placeholder:"e.g. ThemedRolePill",value:r,onChange:o}),n.React.createElement(p,{label:"Search & copy result",style:{marginTop:8},onPress:s})))}function te(){return n.React.createElement(E,null,n.React.createElement(X,null),n.React.createElement(q,null),n.React.createElement(Q,null),n.React.createElement(Z,null),n.React.createElement(ee,null),n.React.createElement(F,null,"The full scan checks every internal lookup this repo's plugins depend on (plus a few for plugins currently being fixed) in one pass, and includes the complete list of semanticColors/rawColors keys at the end. Paste the report back into chat when reporting a bug - it tells us exactly what's missing on your Discord version."))}var ne={settings:te};return B.default=ne,Object.defineProperty(B,"__esModule",{value:!0}),B})({},vendetta.metro.common,vendetta.metro,vendetta.ui.toasts,vendetta.ui.components,vendetta.ui); +`)}`),p.showToast(`Found it - copied ${c.length} key(s)`,void 0)}catch(a){e.clipboard.setString(`Error searching for "${o}": ${a}`),p.showToast("Search failed - copied the error instead",void 0)}},d=function(){const o=t.split(",").map(function(a){return a.trim()}).filter(Boolean);if(o.length)try{const a=s.findByPropsAll(...o)??[],c=a.map(function(y,b){return`[${b}] keys: ${Object.keys(y).sort().join(", ")}`}).join(` + +`);e.clipboard.setString(`findByPropsAll(${o.join(", ")}) -> ${a.length} match(es) + +${c}`),p.showToast(`${a.length} match(es) - report copied`,void 0)}catch(a){e.clipboard.setString(`Error searching for ${o.join(", ")}: ${a}`),p.showToast("Search failed - copied the error instead",void 0)}},u=function(){const o=n.trim();if(o)try{const a=s.findByNameAll(o,!1)??[],c=a.map(function(y,b){return`[${b}] keys: ${Object.keys(y).sort().join(", ")}`}).join(` + +`);e.clipboard.setString(`findByNameAll("${o}") -> ${a.length} match(es) + +${c}`),p.showToast(`${a.length} match(es) - report copied`,void 0)}catch(a){e.clipboard.setString(`Error searching for "${o}": ${a}`),p.showToast("Search failed - copied the error instead",void 0)}};return e.React.createElement(S,{title:"One-off search"},e.React.createElement(N,{style:{paddingHorizontal:16,paddingVertical:8}},e.React.createElement(g,{title:"findByProps - comma-separated prop names",placeholder:"e.g. sendMessage, sendBotMessage",value:t,onChange:r}),e.React.createElement(h,{label:"Search first match & copy",style:{marginTop:8},onPress:i}),e.React.createElement(h,{label:"Search ALL matches & copy",style:{marginTop:8,marginBottom:16},onPress:d}),e.React.createElement(g,{title:"findByName - component name",placeholder:"e.g. ThemedRolePill",value:n,onChange:l}),e.React.createElement(h,{label:"Search first match & copy",style:{marginTop:8},onPress:f}),e.React.createElement(h,{label:"Search ALL matches & copy",style:{marginTop:8},onPress:u}),e.React.createElement(B,{style:{marginTop:8,fontSize:12.5,opacity:.7}},'"ALL matches" is worth checking even when the first-match search succeeds - more than one module matching the same name/props is a common source of "found the wrong one" bugs (patching a re-export or an unrelated duplicate instead of the component actually on screen).')))}function ie(){return e.React.createElement(A,null,e.React.createElement(Q,null),e.React.createElement(ne,null),e.React.createElement(ae,null),e.React.createElement(ee,null),e.React.createElement(te,null),e.React.createElement(Z,null),e.React.createElement(re,null),e.React.createElement(D,null,"The full scan checks every internal lookup this repo's plugins depend on (plus a few for plugins currently being fixed) in one pass, and includes the complete list of semanticColors/rawColors keys at the end. Paste the report back into chat when reporting a bug - it tells us exactly what's missing on your Discord version."))}var oe={settings:ie};return $.default=oe,Object.defineProperty($,"__esModule",{value:!0}),$})({},vendetta.metro.common,vendetta.metro,vendetta.ui.toasts,vendetta.ui.components,vendetta.ui,vendetta.patcher); diff --git a/key-inspector/install/manifest.json b/key-inspector/install/manifest.json index 8da26d4..d30a202 100644 --- a/key-inspector/install/manifest.json +++ b/key-inspector/install/manifest.json @@ -1 +1 @@ -{"name":"Key Inspector","description":"Dev utility: copies live Discord/Revenge color token keys to your clipboard, for diagnosing plugin crashes without needing a computer.","authors":[{"name":"everestmcarthur"}],"main":"index.js","hash":"01f11ef4106e0ab1fbd2de01c537acfeedd2b5fb0c4c7891718f73f5bf0fd448"} \ No newline at end of file +{"name":"Key Inspector","description":"Plugin-dev toolkit: on-device module search (incl. a cache-safe raw mode), a Flux event logger, a component render/props logger, an eval console, and full API/color-token dumps - for diagnosing and building plugins without needing a computer.","authors":[{"name":"everestmcarthur"}],"main":"index.js","hash":"8829da888bd0dda5e69aee1f960a9ca4de2d79691a1d741c12e83350bd9c154a"} \ No newline at end of file diff --git a/plugins-data.json b/plugins-data.json index 4eecd85..d05755a 100644 --- a/plugins-data.json +++ b/plugins-data.json @@ -352,7 +352,7 @@ { "id": "key-inspector", "name": "Key Inspector", - "description": "Dev utility: copies live Discord/Revenge color token keys to your clipboard, for diagnosing plugin crashes without needing a computer.", + "description": "Plugin-dev toolkit: on-device module search (incl. a cache-safe raw mode), a Flux event logger, a component render/props logger, an eval console, and full API/color-token dumps - for diagnosing and building plugins without needing a computer.", "authors": [ "everestmcarthur" ],