Compare commits

..

127 commits

Author SHA1 Message Date
OTKUSteyler
3c250f9081
Merge branch 'kmmiio99o:main' into main 2026-08-18 10:05:14 +02:00
OTKUSteyler
27dc770a14 Merge branch 'main' of https://github.com/OTKUSteyler/GoonCord 2026-07-31 23:34:58 +02:00
OTKUSteyler
513d9b63fa
Update README.md 2026-07-31 21:29:10 +00:00
OTKUSteyler
f916820d9b
Update release.yml 2026-07-31 21:23:56 +00:00
OTKUSteyler
3569666df2
Rename output file from shiggycord.js to gooncord.js 2026-07-31 21:23:07 +00:00
OTKUSteyler
8f272d57ef Merge remote-tracking branch 'origin/dev' 2026-07-31 23:15:37 +02:00
OTKUSteyler
8d4aa61ebd
Update index.ts 2026-07-14 17:22:13 +02:00
OTKUSteyler
0968e8b1fe
Update index.tsx 2026-07-01 16:16:42 +02:00
OTKUSteyler
380a7d8cf8
Update index.tsx 2026-07-01 16:06:48 +02:00
OTKUSteyler
6c68f5b5d6
Refactor badge data structure and fetching logic 2026-07-01 15:59:43 +02:00
OTKUSteyler
60f9e9e872
Update index.tsx 2026-07-01 14:45:36 +02:00
OTKUSteyler
992d80a6d5
Update index.tsx 2026-07-01 14:43:31 +02:00
OTKUSteyler
790157f32b
Update index.ts 2026-06-16 21:00:41 +02:00
OTKUSteyler
c356d72b00
Delete src/core/plugins/lovefemboys directory 2026-06-16 21:00:20 +02:00
OTKUSteyler
17409439cb
Update index.ts 2026-06-16 20:59:34 +02:00
OTKUSteyler
c7369fe2a9
Delete src/core/plugins/lovefemboys/utils.ts 2026-06-16 20:56:19 +02:00
OTKUSteyler
a7073e3e8a
Update index.ts 2026-06-16 20:56:11 +02:00
OTKUSteyler
972c87ec33
Update index.ts 2026-06-16 20:52:47 +02:00
OTKUSteyler
e38c36aa78
Create utils.ts 2026-06-16 20:52:36 +02:00
OTKUSteyler
a4fb30ebbf
Update index.ts 2026-06-16 20:44:29 +02:00
OTKUSteyler
254ef099f6
Delete src/core/plugins/femboy directory 2026-06-16 20:20:11 +02:00
OTKUSteyler
3e39a3a4b4
Update index.ts 2026-06-16 20:16:44 +02:00
OTKUSteyler
c310ca3128
Update index.ts 2026-06-16 20:14:07 +02:00
OTKUSteyler
282eddf544
Create index.ts 2026-06-16 20:11:42 +02:00
OTKUSteyler
cbd12e8447
Create Nyaa 2026-06-16 20:08:57 +02:00
OTKUSteyler
2697587953
Update index.tsx 2026-06-14 18:44:28 +02:00
OTKUSteyler
01e8fe8793
Delete src/core/plugins/badges/Stuff directory 2026-06-14 18:42:28 +02:00
OTKUSteyler
dc4d0049b6
Delete src/core/plugins/badges/Stuff/roles.json 2026-06-14 18:42:17 +02:00
OTKUSteyler
e5ae029e1c
Merge branch 'kmmiio99o:dev' into dev
Some checks failed
Node.js CI / CD (Bun + Forgejo) / buildAndRelease (push) Has been cancelled
2026-06-13 12:00:56 +02:00
OTKUSteyler
f9a8e9d493
Update index.tsx 2026-06-09 09:48:56 +02:00
OTKUSteyler
de20126f36
Update index.tsx 2026-06-09 08:19:29 +02:00
OTKUSteyler
de509f83b3
Update index.tsx 2026-06-09 07:53:51 +02:00
OTKUSteyler
8bd8ab2039
Update index.tsx 2026-06-09 07:35:54 +02:00
OTKUSteyler
3ba90b941e
Update index.tsx 2026-06-09 07:34:31 +02:00
OTKUSteyler
d21eea3e9b
Add files via upload 2026-06-09 07:33:13 +02:00
OTKUSteyler
0ba2694f01
Update index.tsx 2026-06-09 07:28:53 +02:00
OTKUSteyler
45b9a9c8e6
Rename badges_minified (1).json to badges_minified.json 2026-06-09 07:26:58 +02:00
OTKUSteyler
a326d43d2c
Add files via upload 2026-06-09 07:26:03 +02:00
OTKUSteyler
e217f9c1ce
Create Badges.json 2026-06-09 07:25:26 +02:00
OTKUSteyler
f5e11a6552
Update index.tsx 2026-06-09 07:10:45 +02:00
OTKUSteyler
2c260c153f
Update index.tsx 2026-06-09 07:07:06 +02:00
OTKUSteyler
d981f80666
Update index.tsx 2026-06-09 07:00:43 +02:00
OTKUSteyler
452d2e67f1
Update index.tsx 2026-06-09 04:10:57 +02:00
OTKUSteyler
b9153c23b7
Update index.tsx 2026-06-08 20:26:55 +02:00
OTKUSteyler
1cc7a8a9d5
Merge branch 'kmmiio99o:main' into main 2026-05-30 02:50:39 +02:00
OTKUSteyler
b04ce41839
Update index.ts 2026-05-20 07:15:41 +02:00
OTKUSteyler
a4e2407ae6
Update index.ts 2026-05-20 07:13:27 +02:00
OTKUSteyler
25ba7226ac
Update index.ts 2026-05-20 07:08:41 +02:00
OTKUSteyler
8d0f313630
Merge branch 'kmmiio99o:main' into main 2026-05-17 03:54:24 +02:00
OTKUSteyler
4ad9614ccb
Update README with serving instructions and image 2026-05-13 17:11:47 +02:00
OTKUSteyler
6552142c80
Update README.md 2026-05-12 20:12:31 +02:00
OTKUSteyler
6cbd9a1b95
Update index.ts 2026-05-11 12:03:15 +02:00
OTKUSteyler
4c9fc328db
Update index.ts 2026-05-11 11:58:06 +02:00
OTKUSteyler
3dcb0bcf00
Update index.ts 2026-05-11 11:49:22 +02:00
OTKUSteyler
396fd96c22
Update index.ts 2026-05-11 11:41:03 +02:00
OTKUSteyler
c2413969f3
Update index.ts 2026-05-11 11:37:10 +02:00
OTKUSteyler
86963d096c
Update index.ts 2026-05-11 11:30:17 +02:00
OTKUSteyler
cc1153d38f
Update index.ts 2026-05-11 11:24:41 +02:00
OTKUSteyler
f58841a2df
Update index.ts 2026-05-11 11:16:01 +02:00
OTKUSteyler
a2cb96f3aa
Merge branch 'kmmiio99o:main' into main 2026-05-10 17:27:08 +02:00
OTKUSteyler
d7255b2d39
Merge branch 'kmmiio99o:main' into main 2026-05-07 15:55:10 +02:00
OTKUSteyler
4894043612
Update README.md 2026-05-07 13:54:00 +02:00
OTKUSteyler
82cb9823bf
Merge branch 'kmmiio99o:main' into main 2026-04-27 18:23:13 +02:00
OTKUSteyler
6709c4929c Update loader.ts 2026-04-27 18:20:38 +02:00
OTKUSteyler
4d6a23f1f8
Update Loader.ts 2026-04-27 18:20:11 +02:00
OTKUSteyler
e599664b34
Update index.ts 2026-04-07 18:02:29 +02:00
OTKUSteyler
2f28882fa2
Update index.ts 2026-04-07 09:59:27 +02:00
OTKUSteyler
b0fbeb40c3
Update index.ts 2026-04-07 00:35:40 +02:00
OTKUSteyler
0ad2557275
Update index.ts 2026-04-07 00:29:44 +02:00
OTKUSteyler
b77421f3ce
Update index.ts 2026-04-07 00:15:49 +02:00
OTKUSteyler
0ad0b79091
Update index.ts 2026-04-07 00:15:16 +02:00
OTKUSteyler
51daf66491
Add files via upload 2026-04-07 00:14:46 +02:00
OTKUSteyler
9d65780297
Update index.ts 2026-04-07 00:14:19 +02:00
OTKUSteyler
08a0f04922
Delete src/core/plugins/max verstappen directory 2026-04-07 00:14:03 +02:00
OTKUSteyler
fb729d429e
Update index.ts 2026-04-07 00:00:43 +02:00
OTKUSteyler
30f9238e9d
Rename index.ts to index.ts 2026-04-06 23:59:28 +02:00
OTKUSteyler
20ac74cfe7
Update index.ts 2026-04-06 23:56:45 +02:00
OTKUSteyler
982fba36f6
Add files via upload 2026-04-06 23:55:49 +02:00
OTKUSteyler
f3864b7b6b
Delete src/core/plugins/max verstappen/aa 2026-04-06 23:55:26 +02:00
OTKUSteyler
c8282b30d8
Create index.ts 2026-04-06 23:54:30 +02:00
OTKUSteyler
6650f6d1db
Create aa 2026-04-06 23:53:57 +02:00
OTKUSteyler
d685025ce9
Delete src/core/plugins/max verstappen 2026-04-06 23:52:48 +02:00
OTKUSteyler
eeb9ec6ff1
Create max verstappen 2026-04-06 23:51:55 +02:00
OTKUSteyler
1332fa1fc2
Delete src/core/plugins/Max Verstappen directory 2026-04-06 23:51:15 +02:00
OTKUSteyler
76d411cbe0
Update index.ts 2026-04-06 23:49:58 +02:00
OTKUSteyler
ff53c6176c
Update index.ts 2026-04-06 23:48:24 +02:00
OTKUSteyler
739e6744c7
Add files via upload 2026-04-06 23:47:03 +02:00
OTKUSteyler
2ca955c46a
Delete src/core/plugins/Max Verstappen/tutuututu 2026-04-06 23:46:45 +02:00
OTKUSteyler
f4b15f3945
Create index.ts 2026-04-06 23:46:34 +02:00
OTKUSteyler
cc7e9712e8
Create tutuututu 2026-04-06 23:43:45 +02:00
OTKUSteyler
f4adb93c98
Rename index.js to index.ts 2026-04-02 19:43:14 +02:00
OTKUSteyler
84cbee35e7
Update index.ts 2026-04-02 19:40:49 +02:00
OTKUSteyler
fbd5bbe72c
Update index.ts 2026-04-02 19:39:49 +02:00
OTKUSteyler
2e9bfdfe0f
Update index.js 2026-04-02 19:35:57 +02:00
OTKUSteyler
4dbaddf579
Update index.js 2026-04-02 19:30:06 +02:00
OTKUSteyler
4e8a302268
Delete src/core/plugins/nodelete/a 2026-04-02 19:29:51 +02:00
OTKUSteyler
021e0c15fc
Add files via upload 2026-04-02 19:29:07 +02:00
OTKUSteyler
d45e9ef61f
Create a 2026-04-02 19:28:45 +02:00
OTKUSteyler
eb6dc9ad58
Update index.ts 2026-04-02 19:03:39 +02:00
OTKUSteyler
5fcc13cae5
Delete src/core/plugins/favouriteanything/a 2026-04-02 19:01:54 +02:00
OTKUSteyler
3c86ab104e
Update index.ts 2026-04-02 19:00:13 +02:00
OTKUSteyler
c14c714745
Add files via upload 2026-04-02 18:55:02 +02:00
OTKUSteyler
d48a8634d6
Create a 2026-04-02 18:54:26 +02:00
OTKUSteyler
fcd396602d
Delete src/core/plugins/favouriteanything 2026-04-02 18:53:59 +02:00
OTKUSteyler
2227e0d328
Create 2026-04-02 18:52:46 +02:00
OTKUSteyler
3aafa28a3b
Update index.ts 2026-04-02 17:26:36 +02:00
OTKUSteyler
4854b998a2
Delete src/core/plugins/NoDelete directory 2026-04-02 17:26:06 +02:00
OTKUSteyler
a731776639
Update index.js 2026-04-02 17:22:53 +02:00
OTKUSteyler
6c475e69be
Update index.ts 2026-04-02 17:16:22 +02:00
OTKUSteyler
3a3d61971a
Update index.ts 2026-04-02 17:12:51 +02:00
OTKUSteyler
b604a02fbc
Delete src/core/plugins/NoDelete/a 2026-04-02 17:09:00 +02:00
OTKUSteyler
ba3499834e
Add files via upload 2026-04-02 17:05:54 +02:00
OTKUSteyler
b63941bc3c
Create a 2026-04-02 17:05:34 +02:00
OTKUSteyler
9fd5d46f19
Update debug.ts 2026-03-23 14:46:35 +01:00
OTKUSteyler
597a871bd5
Update debug.ts 2026-03-23 14:43:23 +01:00
OTKUSteyler
49447a7be7
Update reload.ts 2026-03-23 14:42:03 +01:00
OTKUSteyler
a7bff4c900
Update index.ts 2026-03-23 06:43:38 +01:00
OTKUSteyler
c7c8b800ee
Update index.ts 2026-03-21 21:43:51 +01:00
OTKUSteyler
0885fe0c7a
Update index.tsx 2026-03-21 21:34:36 +01:00
OTKUSteyler
8e89a70e6a
Update text in About component 2026-03-21 21:32:30 +01:00
OTKUSteyler
f477466fdf
Add files via upload 2026-03-21 21:30:57 +01:00
OTKUSteyler
4cb42a13e0
Update build.mjs 2026-03-21 21:20:52 +01:00
OTKUSteyler
d5143127d5
Update index.tsx 2026-03-21 21:18:37 +01:00
OTKUSteyler
f8a1154083
Update release.yml 2026-03-21 21:14:52 +01:00
OTKUSteyler
4df48b31c1
Update default.json 2026-03-21 21:14:16 +01:00
OTKUSteyler
42238a9421
Update README for GoonCord installation instructions 2026-03-21 21:09:29 +01:00
OTKUSteyler
43c667838f
Update README.md 2026-03-21 21:04:31 +01:00
15 changed files with 301 additions and 111 deletions

View file

@ -1,9 +1,9 @@
{
"name": "GoonCord",
"name": "ShiggyCord",
"private": true,
"author": {
"name": "OTKUSteyler",
"url": "https://github.com/OTKUSteyler"
"name": "kmmiio99o",
"url": "https://github.com/kmmiio99o"
},
"scripts": {
"build": "node scripts/build.mjs",

View file

@ -77,7 +77,7 @@ const config = {
constModules: {
globals: {
"bunny-build-info": {
version: `"1.4.1.8"`,
version: `"1.4.1.7"`,
},
},
},

View file

@ -18,20 +18,16 @@ export default () =>
},
],
execute([ephemeral], ctx) {
const info = getDebugInfo();
if(info.device.codename === undefined) { info.device.codename = ""}
else{ info.device.codename = "(" + info.device.codename + ")" }
const content = [
"**💦GoonCord💦 Debug Info**",
`> GoonCord: ${info.bunny.version} (${info.bunny.loader.name} ${info.bunny.loader.version})`,
`> Discord: ${info.discord.version} (${info.discord.build})`,
`> React: ${info.react.version} (RN ${info.react.nativeVersion})`,
`> Hermes: ${info.hermes.version} (bcv${info.hermes.bytecodeVersion})`,
`> System: ${info.os.name} ${info.os.version} ${info.os.sdk ? `(SDK ${info.os.sdk})` : ""}`.trimEnd(),
`> Device: ${info.device.model} ${info.device.codename}`,
].join("\n");
const info = getDebugInfo();
const content = [
"**💦GoonCord💦 Debug Info**",
`> 💦GoonCord💦: ${info.bunny.version} (${info.bunny.loader.name} ${info.bunny.loader.version})`,
`> Discord: ${info.discord.version} (${info.discord.build})`,
`> React: ${info.react.version} (RN ${info.react.nativeVersion})`,
`> Hermes: ${info.hermes.version} (bcv${info.hermes.bytecodeVersion})`,
`> System: ${info.os.name} ${info.os.version} ${info.os.sdk ? `(SDK ${info.os.sdk})` : ""}`.trimEnd(),
`> Device: ${info.device.model}`,
].join("\n");
if (ephemeral?.value) {
messageUtil.sendBotMessage(ctx.channel.id, content);

View file

@ -0,0 +1,112 @@
import { instead } from "@lib/api/patcher";
import { logger } from "@lib/utils/logger";
import { _lazyContextSymbol } from "@metro/lazy";
import { requireModule } from "@metro/internals/modules";
import { LazyModuleContext } from "@metro/types";
import { findByNameLazy } from "@metro/wrappers";
function log(...messages: any[]) {
try {
logger.warn("[ShiggyCord] patchAnimatedValue:", ...messages);
} catch {}
}
/**
* Discord's bundled React Native throws `AnimatedValue: Attempting to set value
* to undefined` when a `undefined` value reaches an `Animated.Value` — either
* through `setValue()` (which calls `_updateValue`) or through the constructor.
* Some of Discord's own flows (and third-party plugins) hit this during
* animation setup, which bubbles up to the React ErrorBoundary and makes
* ShiggyCord render its custom crash screen.
*
* Fix the root cause instead of masking the symptom:
* - `_updateValue` is a prototype method, so one patch covers every instance no
* matter which module captured the class reference.
* - The constructor is guarded by swapping the public `Animated.Value`
* reference (and the module's `default` export) for a patched proxy so that
* `new Animated.Value(undefined)` can no longer throw.
*/
export default function patchAnimatedValue() {
const unpatchers: Array<() => boolean> = [];
const install = (Class: any, source: string) => {
try {
if (
!Class?.prototype ||
typeof Class.prototype._updateValue !== "function"
) {
log(`${source}: resolved value is not AnimatedValue, skipping`);
return;
}
// setValue(undefined) → _updateValue(undefined). Keep the last value.
unpatchers.push(
instead(
"_updateValue",
Class.prototype,
function (this: any, args: any[], origFunc: Function) {
if (args[0] === undefined) args[0] = this?._value ?? 0;
return origFunc(...args);
},
),
);
// Constructor guard: coerce non-number to 0 so `new Animated.Value(undefined)` can't throw.
const guard = (args: any[], origFunc: Function) => {
if (typeof args[0] !== "number") args[0] = 0;
return origFunc(...args);
};
// 1. Module exports (`require(...).default`).
try {
const ctxt: LazyModuleContext | undefined =
findByNameLazy("AnimatedValue")[_lazyContextSymbol];
if (ctxt?.moduleId != null) {
const moduleExports = requireModule(ctxt.moduleId);
if (moduleExports?.default === Class) {
unpatchers.push(instead("default", moduleExports, guard));
}
}
} catch {}
// 2. The public `Animated.Value` namespace reference.
try {
const ReactNative: any = require("react-native");
const Animated = ReactNative?.Animated;
if (Animated && Animated.Value === Class) {
unpatchers.push(instead("Value", Animated, guard));
}
} catch {}
log(`${source}: patched _updateValue + constructor guard`);
} catch (e) {
log(`${source}: patch failed`, e);
}
};
// Primary: Discord's own RN Animated namespace (most reliable, no full scan).
try {
const ReactNative: any = require("react-native");
install(ReactNative?.Animated?.Value, "react-native.Animated.Value");
if (unpatchers.length > 0) {
return () => unpatchers.forEach((unpatch) => unpatch());
}
} catch (e) {
log("react-native.Animated path failed", e);
}
// Fallback: byName lookup.
try {
const ctxt: LazyModuleContext =
findByNameLazy("AnimatedValue")[_lazyContextSymbol];
if (ctxt) {
ctxt.getExports((Class: any) => install(Class, "findByNameLazy"));
} else {
log("no lazy context found");
}
} catch (e) {
log("findByNameLazy path failed", e);
}
return () => unpatchers.forEach((unpatch) => unpatch());
}

View file

@ -0,0 +1,141 @@
import React from "react";
import { View } from "react-native";
import { byProps } from "@metro/filters";
import { findExports } from "@metro/finders";
import { findByStoreNameLazy } from "@metro/wrappers";
import { instead } from "@lib/api/patcher";
import { logger } from "@lib/utils/logger";
import { isSemanticColor, resolveSemanticColor, semanticColors } from "@ui/color";
const ThemeStore = findByStoreNameLazy("ThemeStore");
function log(...messages: any[]) {
try {
logger.warn("[ShiggyCord] patchSafeCard:", ...messages);
} catch {}
}
let cardHealthy = false;
const FALLBACK_STYLE = {
borderRadius: 8,
padding: 16,
};
function resolveCardBackground(): string | undefined {
try {
const color = semanticColors?.CARD_BACKGROUND_DEFAULT;
if (isSemanticColor(color)) return resolveSemanticColor(color);
if (typeof color === "string") return color;
} catch {}
return undefined;
}
export default function patchSafeCard() {
const unpatchers: Array<() => boolean> = [];
try {
const cardModule = findExports(byProps("Card"));
const origCard = cardModule?.Card;
if (typeof origCard !== "function") {
log("no Card function on the resolved module, skipping");
return () => unpatchers.forEach((unpatch) => unpatch());
}
log("resolved design Card from first-match module");
let cardBackground = resolveCardBackground();
const unsubscribeTheme = ThemeStore?.addChangeListener?.(() => {
const resolved = resolveCardBackground();
if (resolved) cardBackground = resolved;
});
if (typeof unsubscribeTheme === "function") {
unpatchers.push(() => {
unsubscribeTheme();
return true;
});
}
class CardProbeBoundary extends React.Component<
{
fallback: React.ReactNode;
children?: React.ReactNode;
},
{ hasError: boolean }
> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: unknown) {
try {
log("Card render threw, using View fallback", error);
} catch {}
}
render() {
if (this.state.hasError) return this.props.fallback;
return this.props.children;
}
}
class HealthyProbe extends React.Component<{
onHealthy: () => void;
children?: React.ReactNode;
}> {
componentDidMount() {
this.props.onHealthy();
}
render() {
return this.props.children;
}
}
const SafeCard = function SafeCard(props: any) {
if (cardHealthy) return React.createElement(origCard, props);
return React.createElement(
CardProbeBoundary,
{
fallback: React.createElement(
View,
{
style: [
FALLBACK_STYLE,
...(cardBackground
? [{ backgroundColor: cardBackground }]
: []),
props?.style,
],
},
props?.children,
),
},
React.createElement(
HealthyProbe,
{
onHealthy: () => {
cardHealthy = true;
},
},
React.createElement(origCard, props),
),
);
};
unpatchers.push(
instead("Card", cardModule, (args: any[]) => SafeCard(args[0] ?? {})),
);
log("patched design Card (adaptive probe on first mount)");
} catch (e) {
log("patch failed", e);
}
return () => unpatchers.forEach((unpatch) => unpatch());
}

View file

@ -11,7 +11,7 @@
"IMAGE_FILES": "Image Files",
"TEXT_FILES": "Text Files",
"BRAND": "Brand",
"SHIGGYCORD": "GoonCord",
"SHIGGYCORD": "💦GoonCord💦",
"SHIGGYCORD_URL": "GoonCord URL",
"BROWSER": "Browser",
"BYTECODE": "Bytecode",
@ -94,7 +94,7 @@
"PLUGINS": "Plugins",
"REFETCH": "Refetch",
"RELOAD": "Reload",
"RELOAD_DISCORD": "ReGoon your Discord",
"RELOAD_DISCORD": "💦ReGoon💦 your Discord",
"RELOAD_IN_NORMAL_MODE": "Reload in Normal Mode",
"RELOAD_IN_NORMAL_MODE_DESC": "Safe mode currently enabled, tap to reload in normal mode",
"RELOAD_IN_SAFE_MODE": "Reload in Safe Mode",

View file

@ -11,7 +11,7 @@ const { TableRowGroup, TableRow, TableSwitchRow, Stack } = findByProps(
"TableSwitchRow",
"Stack",
);
const { ScrollView, Text } = require("react-native");
const { ScrollView, Text, Linking } = require("react-native");
type FixEmbedSettings = {
twitter: boolean;
@ -45,11 +45,11 @@ function transformLinks(content: string, config: FixEmbedSettings): string {
"https://kkinstagram.com$1",
);
}
// TikTok: Replace domain, keep path
// TikTok: Replace domain, keep path (use embedez.com for all TikTok links)
if (config.tiktok) {
result = result.replace(
/https?:\/\/(?:www\.)?tiktok\.com(\/[^\s]+)/gi,
"https://tiktokez.com$1",
"https://embedez.com$1",
);
// TikTok short links (vm.tiktok.com)
result = result.replace(
@ -99,7 +99,6 @@ export default defineCorePlugin({
setConfig((prev) => ({ ...prev, [key]: value }));
};
// Prefer table-style rows (TableRowGroup / TableSwitchRow) and Stack layout similar to other core plugins.
const {
TableRowGroup: _TRG,
TableRow: _TR,
@ -107,7 +106,6 @@ export default defineCorePlugin({
Stack: _S,
} = findByProps("TableRowGroup", "TableRow", "TableSwitchRow", "Stack");
// Fallback if the table-style components are not available in the host environment
if (!_TRG || !_TSR || !_TR || !_S) {
return React.createElement(
ScrollView,
@ -120,7 +118,6 @@ export default defineCorePlugin({
);
}
// Use the resolved components
const TableRowGroup = _TRG;
const TableRow = _TR;
const TableSwitchRow = _TSR;
@ -164,6 +161,12 @@ export default defineCorePlugin({
"This plugin automatically converts social media links to privacy-friendly alternative frontends for better embeds.",
disabled: true,
}),
React.createElement(TableRow, {
label: "EmbedEZ",
subLabel: "TikTok embed fix powered by embedez.com — tap to open",
onPress: () => Linking.openURL("https://embedez.com/"),
arrow: true,
}),
),
),
]);

View file

@ -97,7 +97,7 @@ export default function About() {
variant="text-md/medium"
style={{ textAlign: "center", marginTop: 4 }}
>
💦Goon💦 your discord client!
💦Goon your discord client💦!
</Text>
</View>

View file

@ -125,7 +125,7 @@ export default function General() {
>
<TableRowGroup title="App Information">
<TableRow
label="GoonCord"
label="💦GoonCord💦"
icon={<TableRow.Icon source={{ uri: shiggycordIcon ?? "" }} />}
trailing={<TableRow.TrailingText text={debugInfo.bunny.version} />}
/>

View file

@ -11,7 +11,7 @@ async function initializeShiggyCord() {
Object.freeze = Object.seal = Object;
await require("@metro/internals/caches").initMetroCache();
require(".").default();
await require(".").default();
} catch (e) {
const { ClientInfoManager } = require("@lib/api/native/modules");
const stack = e instanceof Error ? e.stack : undefined;

View file

@ -1,4 +1,6 @@
import patchAnimatedValue from "@core/debug/patches/patchAnimatedValue";
import patchErrorBoundary from "@core/debug/patches/patchErrorBoundary";
import patchSafeCard from "@core/debug/patches/patchSafeCard";
import initFixes from "@core/fixes";
import { initFetchI18nStrings } from "@core/i18n";
import initSettings from "@core/ui/settings";
@ -30,6 +32,8 @@ export default async () => {
["patchCommands", () => patchCommands()],
["patchJsx", () => patchJsx()],
["patchErrorBoundary", () => patchErrorBoundary()],
["patchAnimatedValue", () => patchAnimatedValue()],
["patchSafeCard", () => patchSafeCard()],
["initVendettaObject", () => initVendettaObject()],
["initFetchI18nStrings", () => initFetchI18nStrings()],
["initSettings", () => initSettings()],
@ -88,5 +92,5 @@ export default async () => {
}
// Final ready log for basic UI availability.
logger.log("ShiggyCord is ready.");
logger.log("GoonCord is ready.");
};

View file

@ -40,55 +40,19 @@ export function updateBunnyColor(colorManifest: ColorManifest | null, { update =
});
if (internalDef != null) {
// Register the synthetic key BEFORE calling native, and verify it actually
// landed in every table native reads from. If any registration step fails,
// bail out to a known-good theme instead of handing native an unknown key.
try {
tokenRef.Theme[ref.key.toUpperCase()] = ref.key;
tokenRef.Theme[ref.key.toUpperCase()] = ref.key;
FormDivider.DIVIDER_COLORS[ref.key] = FormDivider.DIVIDER_COLORS[ref.current!.reference];
if (!FormDivider.DIVIDER_COLORS[ref.current!.reference]) {
throw new Error(`Missing divider color reference: ${ref.current!.reference}`);
}
FormDivider.DIVIDER_COLORS[ref.key] = FormDivider.DIVIDER_COLORS[ref.current!.reference];
Object.keys(tokenRef.Shadow).forEach(k => {
if (!(ref.current!.reference in tokenRef.Shadow[k])) {
throw new Error(`Missing shadow reference: ${ref.current!.reference}`);
}
tokenRef.Shadow[k][ref.key] = tokenRef.Shadow[k][ref.current!.reference];
});
Object.keys(tokenRef.SemanticColor).forEach(k => {
if (!(ref.current!.reference in tokenRef.SemanticColor[k])) {
throw new Error(`Missing semantic color reference: ${ref.current!.reference}`);
}
tokenRef.SemanticColor[k][ref.key] = {
...tokenRef.SemanticColor[k][ref.current!.reference]
};
});
} catch (e) {
console.error("Failed to register custom theme key, falling back", e);
ref.current = null; // force fallback path below
}
Object.keys(tokenRef.Shadow).forEach(k => tokenRef.Shadow[k][ref.key] = tokenRef.Shadow[k][ref.current!.reference]);
Object.keys(tokenRef.SemanticColor).forEach(k => {
tokenRef.SemanticColor[k][ref.key] = {
...tokenRef.SemanticColor[k][ref.current!.reference]
};
});
}
if (update) {
AppearanceManager.setShouldSyncAppearanceSettings(false);
const targetTheme = ref.current != null ? ref.key : ref.lastSetDiscordTheme;
// This is the actual crash site: a raw call into native code with no
// guard. Wrap it so a native-side rejection can't take the whole
// bridge/thread down — fall back to the last known-good Discord theme.
try {
AppearanceManager.updateTheme(targetTheme);
} catch (e) {
console.error(`Native rejected theme "${targetTheme}", reverting to "${ref.lastSetDiscordTheme}"`, e);
try {
AppearanceManager.updateTheme(ref.lastSetDiscordTheme);
} catch (e2) {
console.error("Fallback theme also failed to apply", e2);
}
}
AppearanceManager.updateTheme(internalDef != null ? ref.key : ref.lastSetDiscordTheme);
}
}

View file

@ -1,64 +1,49 @@
import { ImageLoader } from "./";
import type { LoaderPayload } from "./types";
type InteropReturn = Promise<{ ret: string } | { err: string } | { cancelled: true; reason: string }>;
export const loaderPayload = window.__PYON_LOADER__ as LoaderPayload;
export function isModuleRegistered(module: string) {
return loaderPayload.loader.modules[module] !== undefined;
}
export function isFunctionRegistered(module: string, function_: string) {
return loaderPayload.loader.modules[module]?.functions[function_] !== undefined;
}
export async function callFunction(module: string, functionName: string, args: unknown[]) {
if (!isFunctionRegistered(module, functionName)) {
throw new Error(`Function ${module}.${functionName} is not registered`);
}
const promise: InteropReturn = ImageLoader.queryCache([
"__pyon_bridge",
JSON.stringify({ m: module, f: functionName, a: args }),
]);
const result = await promise;
if ("ret" in result) {
return JSON.parse(result.ret);
}
if ("cancelled" in result) {
throw new Error(`Function ${module}.${functionName} was cancelled: ${result.reason}`);
}
if ("err" in result) {
throw new Error(result.err);
}
}
export interface LoaderModule {
hasFunction(functionName: string): boolean;
callFunction(functionName: string, args: unknown[]): Promise<unknown>;
}
type ModuleProps<T extends LoaderModule> = {
name: string;
argumentProcessors?: Partial<{ [key in keyof T]: (args: any[]) => any[] }>;
};
export function getModule<T extends LoaderModule>({ name, argumentProcessors: processArgs }: ModuleProps<T>) {
const module = loaderPayload.loader.modules[name];
if (!module) {
throw new Error(`Module ${name} is not registered`);
}
const baseObject: Record<string, any> = {
hasFunction: (functionName: string) => isFunctionRegistered(name, functionName),
callFunction: (functionName: string, args: unknown[]) => callFunction(name, functionName, args),
};
for (const [functionName] of Object.entries(module.functions)) {
baseObject[functionName] = (...args: unknown[]) =>
callFunction(
@ -68,10 +53,8 @@ export function getModule<T extends LoaderModule>({ name, argumentProcessors: pr
processArgs && functionName in processArgs ? processArgs[functionName](args) : args,
);
}
return baseObject as T;
}
/**
* Determines if safe mode is currently enabled. The result remains consistent over time.
* This will not change until the app is restarted. For draft changes, use the InitConfigStore.

View file

@ -1,64 +1,49 @@
import { ImageLoader } from "./";
import type { LoaderPayload } from "./types";
type InteropReturn = Promise<{ ret: string } | { err: string } | { cancelled: true; reason: string }>;
export const loaderPayload = window.__PYON_LOADER__ as LoaderPayload;
export function isModuleRegistered(module: string) {
return loaderPayload.loader.modules[module] !== undefined;
}
export function isFunctionRegistered(module: string, function_: string) {
return loaderPayload.loader.modules[module]?.functions[function_] !== undefined;
}
export async function callFunction(module: string, functionName: string, args: unknown[]) {
if (!isFunctionRegistered(module, functionName)) {
throw new Error(`Function ${module}.${functionName} is not registered`);
}
const promise: InteropReturn = ImageLoader.queryCache([
"__pyon_bridge",
JSON.stringify({ m: module, f: functionName, a: args }),
]);
const result = await promise;
if ("ret" in result) {
return JSON.parse(result.ret);
}
if ("cancelled" in result) {
throw new Error(`Function ${module}.${functionName} was cancelled: ${result.reason}`);
}
if ("err" in result) {
throw new Error(result.err);
}
}
export interface LoaderModule {
hasFunction(functionName: string): boolean;
callFunction(functionName: string, args: unknown[]): Promise<unknown>;
}
type ModuleProps<T extends LoaderModule> = {
name: string;
argumentProcessors?: Partial<{ [key in keyof T]: (args: any[]) => any[] }>;
};
export function getModule<T extends LoaderModule>({ name, argumentProcessors: processArgs }: ModuleProps<T>) {
const module = loaderPayload.loader.modules[name];
if (!module) {
throw new Error(`Module ${name} is not registered`);
}
const baseObject: Record<string, any> = {
hasFunction: (functionName: string) => isFunctionRegistered(name, functionName),
callFunction: (functionName: string, args: unknown[]) => callFunction(name, functionName, args),
};
for (const [functionName] of Object.entries(module.functions)) {
baseObject[functionName] = (...args: unknown[]) =>
callFunction(
@ -68,10 +53,8 @@ export function getModule<T extends LoaderModule>({ name, argumentProcessors: pr
processArgs && functionName in processArgs ? processArgs[functionName](args) : args,
);
}
return baseObject as T;
}
/**
* Determines if safe mode is currently enabled. The result remains consistent over time.
* This will not change until the app is restarted. For draft changes, use the InitConfigStore.
@ -79,4 +62,4 @@ export function getModule<T extends LoaderModule>({ name, argumentProcessors: pr
*/
export function isSafeModeEnabled() {
return Boolean(loaderPayload.loader.initConfig.safeMode);
}
}

View file

@ -117,13 +117,17 @@ for (const [id, moduleExports] of cacher.getModules()) {
}
const exportsKeysLength = Reflect.ownKeys(actualExports).length;
if (_source[prop] && exportsKeysLength <= _source[prop]) {
if (_source[prop] && exportsKeysLength >= _source[prop]) {
continue;
}
_module[prop] = actualExports[prop];
_source[prop] = Reflect.ownKeys(actualExports).length;
cacher.cacheId(id);
if (exportsKeysLength === 1) {
redesignProps.delete(prop);
}
}
}