#🧩-plugin-development
1 messages · Page 82 of 1
In probability theory and statistics, a Markov chain or Markov process is a stochastic process describing a sequence of possible events in which the probability of each event depends only on the state attained in the previous event. Informally, this may be thought of as, "What happens next depends only on the state of affairs now." A countably i...
artificial stupidity
Kevin just builds a huge probability model based on all messages in chat
if I start with this word, what words could come after it?
then it uses that model to generate a series of words
it's like pressing the middle suggestion 10 times on your phone keyboard
if it's trained in these three messages:
Hello Vencord
The vencord thing is cool
Is this thing on?
it could generate Hello Vencord thing on?
that's why sometimes it generates garbage that makes no sense and other times it generates proper sentences
AIs do not have access to discord's codebase
neither the real one, nor the minified client JS
it is not in any way possible for the documentation to be accurate, unless you get an LLM with a very large context size and feed it client JS
oh he's gone
all plugin suggestions are closed now right?
?
maybe it's the ESL or something but telling bro to make it herself when she was just asking if requests are open or not does come off a bit aggressive.
wasn't my intention, dont want to get into an argument about it
didn't figure it was and I don't either, just answering your ❓ react
Which is exactly how a llm works, just a simpler model
yeah just a few more steps to make an llm
quick question, is there any good documentation for patches? i can't figure it out at all
get started with plugin development
you need:
- a devbuild
- TypeScript knowledge
- if you want to write patches regex knowledge
https://docs.vencord.dev to get started
patch guide (ignore the "superseded by official docs" part)
https://gist.github.com/sunnniee/28bd595f8c07992f6d03289911289ba8
also, reading existing plugin code will teach you a lot!
ignore the part where it says superseded by official docs
Thank you gonna read through that!
XD
!! thanks!
this is kinda how i made my plugin
instead of using an AI to get code
just become an AI and learn what other peoples code does
that's not ai that's just i
Hi! I'm trying to make a plugin that automatically puts you into invisible (or any other status selected in the settings) on startup, even if your status was something else before. I'm patching both getLocalPresence, getStatus and the setCurrentUserOnConnectionOpen() call, it works perfectly when the last set status was online, but if it was idle or dnd, your status will get set as that, and not invisible (or the selected "spoofed" status). Checking the websocket messages, the only message sent at startup with presence/status inside is set to invisible, it even shows invisible on my client, but not to anyone else. After a while, the status gets updated, another websocket message gets sent with invisible status also, only then the real status updates to invisible for everyone. Any ideas?
Hey, I'm making a plugin to integrate with StreamController (streamdeck for linux). Is there a proper way to start a stream programatically? I've tried manually dispatching STREAM_CREATE via FluxDispatcher, but that results in a screenshare which loads forever:
Vencord.Webpack.findByCode("STREAM_START", "GUILD", "CALL", "OVERLAY")("<guildId>", "<channelId>", {
"pid": null,
"sourceId": "Screen 1",
"sourceName": null
})
// this internally just dispatches STREAM_START
I'm on Vesktop, which has the custom screenshare picker and does something extra to make screenshare work - but all of this extra logic is in the component itself, so I'm guessing that's why it results in a blank screenshare. There is also more logic here.
What's the best way I can hack this together and have it actually screenshare?
I also looked at a BD plugin which does this, all they do is call the same method I'm calling. They do pass in more options, however after inspecting it with a debugger it seems that the client itself only passes in those three.
you will probably want to checkout the source code for vesktop to find out what it does to create a stream
openScreenSharePicker is bound to a IPC command handler, the IPC command is called from electron's DisplayMediaRequest and I have no idea who calls that https://github.com/Vencord/Vesktop/blob/main/src/main/screenShare.ts#L29
screenShare.ts: Line 29
session.defaultSession.setDisplayMediaRequestHandler(async (request, callback) => {
honestly it might just be easier to find the elements and call .click on them lol
that element probably doesn't exists if you are not on the call screen and also you would have to click on the screenshare button and select the whole screen also
yeah, it wouldn't be a great solution, but I don't see any other for now
hey so im trying to get the name in react devtools but when i check the last element for the name part in dms react devtools gived me an error, heres it is: https://pastebin.com/LS0P5sZz
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
I think I fixed this for myself by deleting the extension cache and full restarting. been a while though.
Has someone documented all the known i18n keys? I'm using a random discord build from a few years ago but I'm still missing a few keys that it doesn't contain.
#👾-core-development pins
thank you
ok i just found im stupid and deleted the wrong cahce 😭 thank you
hey so what is the "menu.ts" webpack common? i didint find any plugin that uses that
ummm so my react devtools just disappeared
tried restarting and reenableing but same
After fully restarting discord, you have to reload the page once to get React developer tools due to a chromium bug.
Open the developer tools and press C-r
Hello! Has anyone ever figured out how to patch existing messages client-side?
I've already tried everything with React Dev Tools but I cant seem to get it to work 🥲
here's a rough draft of what I'm trying to do:
- Replace blacklisted words (Client-side) with something else (lalilulelo)
- Everything is configurable, I've already got that working as I've worked previously with other plugins
- The only thing remaining to get the plugin to work is figuring out the find and replace part
- TLDR: Replaces "Hello World!" with "lalilulelo World!"
patches: [
{
find: 'message.content',
replacement: {
match: /message\.content/,
replace: "$self.replaceBlacklistedWords(message.content)",
},
},
],
Imagine Stream Mode except replacing words (offensive or not) with something silly, thats essentially the idea
ain't this possible with TextReplace?
yesnt
TextReplace intercepts the message being sent by you
what I'm trying to do is essentially "Inspect Element" existing messages
uhhh
i have a plugin that was to have a typing animation, but it's kinda useless
that does effect it like inspect element
maybe that could work, but in short I'm trying to do this: (TextReplace is the "blacklisted word")
take a peek at the patches and see if it's something you're looking for
is the difference between textreplace and what you're trying to do that yours just has to scan through all messages on DOM?
probably...?
I know that TextReplace is much much simpler since it basically only targets the message you're still sending
If you need to patch existing messages, edit them in MessageStore and call emitChanges()
If you want to patch any messages received after your plugin starts (which should be after a restart), you can just intercept incoming message data and intercept it
FluxDispatcher.addInterceptor(e => {
if (e.type === 'MESSAGE_CREATE' || e.type === 'MESSAGE_UPDATE') {
console.log(e.message.content)
} else if (e.type === 'LOAD_MESSAGES_SUCCESS') {
e.messages.forEach(message => {
console.log(message.content)
});
}
})
instead of console log you would just do message.content = myReplacer(message.content)
You should patch the render, not the store
mmm
I know that ShowMeYourName intercepts everything on the current chat like this:
find: '?"@":"")',
replacement: {
match: /(?<=onContextMenu:\i,children:).*?\)}/,
replace: "$self.renderUsername(arguments[0])}"
}
const nickname: string = author.nick;
const userObj = message.author;
...
you could just target however discord renders image and insert your methods laliloolela'd string, no?
probably, what I was trying to do was this:
patches: [
{
find: 'message.content',
replacement: {
match: /\)\("li",\{(.+?),className:/,
replace: "$self.replaceBlacklistedWords(message.content)",
},
},
],
replaceBlacklistedWords(content: string): string {
if (!content) return content;
console.log("Message content intercepted:", content);
const blacklist = settings.store.Blacklist
.split(",")
.map((w) => w.trim())
.filter(Boolean);
return blacklist.reduce(
(text, word) => text.replace(new RegExp(`\\b${word}\\b`, "gi"), "lalilulelo"), content
);
}
(definitely not the best way to do it, but I'm not good at making things from scratch XD)
there's like a million places where discord renders message content, updating the source data is way easier
That does look a lot easier, now I just need to figure out how FluxDispatcher works XD
I suppose I could even do something like
message.content.replace(blacklisted_word, "lalilulelo")
yeah, just don't forget to update the original event object
taking a look at your code, but I'm not sure whats the correlation between FluxDispatcher on start() and the patch: {} section
I just dont have any clue on what I should be doing lol
like is patch mandatory or can I just use start() and FluxDispatcher
yeah you're right it looks like garbage
it's more like the best and most streamlined solution is more than likely using patches (actually more like it could be the best solution)
but for example, textreplace doesn't have patches
ummm
ok so I managed to get FluxDispatcher to work, turns out patch {} is not necessary when you do that
however
it is pulling every single message from all the Discord servers
that could be problematic
hehe yea
I believe message objects have a guildId property, which you can use to filter message events from specific guilds
I mean its not an "real" problem, I was just expecting it to run only on the current chat
but thats a problem for tomorrow me XD
(says the guy that wrote this but instead of using wait functions he counted to like 4 quadrillion one by one)
ngl im looking at my code and it is painfully obvious that this is what i made in order to remember how to program .tsx's
you could patch the same place message logger is patching.
I think that if I tried understanding that code rn I would have a seizure lol
Anyways this seems to have worked so far
I suppose to edit the message itself I would use this?
FluxDispatcher.dispatch({
type: "MESSAGE_UPDATE",
message: {
you'd just have to modify l.content or arguments[0].message.content
Ok maybe I should take a break
there are no breaks until you succeed
I have to clock in in 15 minutes XD
your boss will understand
I wish XD
something tells me that this could in theory work
if (e.type === 'LOAD_MESSAGES_SUCCESS') {
e.messages.forEach(message => {
console.log(message.content)
e.message.content.replace("tomorrow", "lalilulelo")
});
}
I just need to add a proper check so it doesnt implode on itself
(WIP)
we'll I'll be taking my break in the hopes that I wont have a brain aneurysm by the end of the day and hopefully I'll figure it out by 11PM XD
Don't forget that .replace returns a new string, and e.message is undefined if you're working with multiple messages
if (e.type === 'LOAD_MESSAGES_SUCCESS') {
e.messages.forEach(message => {
console.log(message.content)
message.content = message.content.replace("tomorrow", "lalilulelo")
});
}
Single message events (MESSAGE_CREATE and MESSAGE_UPDATE) do have e.message
does anyone know how to find whether a dm / group channel is muted?
see how discord's code does it
i'd look at the context menu
these disappear when i stop hovering so i cant take a look at the onclick or anything
check the f1 plugin
what about it
F8 not F1
thanks will take a look
mute the channel and the inspect it since it changes to "remove mute" or something, then just find the if statement which decides it
this doesnt work because the components tab also freezes
i have found this although backtracking to where "g" is doesnt really help me, and considering it is using a guild id (when i am looking for DM / group channels) then it might not be what im after but it still might help
oh yea forgot about that part, you must do it the old fashion way. as far as I remember, when you inspect the raw elements in menus usually there is an id of the button in one of fields, you could use it to search the code for anything possibly related.
I want to fact check this but eh I just cleaned up my laptop
I don't have node.js at the moment
it might not be what im after
why not? discord uses it seemingly just fine
okay then, have you got a suggestion to finding out what attribute isChannelMuted is apart of? I found another instance of it here
findByProps("isChannelMuted")Vencord.Webpack.wreq(9156)- search for
9156:
pick your poison
f8, click in the frozen client (discord portion), move mouse over dev tools, f8 again to unfreeze, dont move mouse back over to client. until you do any hovering things will remain visible. at least i think this is how I did it in the past
oh wrong order.
click in client > f8 > click in dev tools > f8
hover stuff should remain til you move your mouse back to the client
btw sadan's dev companion makes it super easy to find stuff 
also this snippet of code
let[n,l] = (0, s.Wu)([g.ZP], () => [g.ZP.isChannelMuted(t.guild_id, t.id), g.ZP.getChannelMuteConfig(t.guild_id, t.id)])
is pretty much equivalent to this
const [isChannelMuted, muteConfig] = useStateFromStores(
[UserGuildSettingsStore], // the stores you want to use
() => [ // mapper function - can return anything
UserGuildSettingsStore.isChannelMuted(channel.guild_id, channel.id),
UserGuildSettingsStore.getChannelMuteConfig(channel.guild_id, channel.id),
],
[channel.id, channel.guild_id], // dependencies
(a, b) => a.every((value, i) => b[i] === value)
/* ^ equality checker - compares the previous and next version of the mapped value
/ if you're lazy you can just use lodash.isEqual or leave this out entirely */
);
The return value will be recomputed when the dependencies change or the store emits an update, and if the contents of the array differ it will rerender the component.
👍
appreciate it
bumb!
It exports utilities and components for context menus
also there are so many plugins that use it
I would like some opinion on my code
start() {
FluxDispatcher.addInterceptor(e => {
// if (e.type === 'MESSAGE_CREATE' || e.type === 'MESSAGE_UPDATE') {
// console.log(e.message.content)
// }
if (e.type === 'LOAD_MESSAGES_SUCCESS') {
const blacklist = settings.store.Blacklist
.split(",")
.map((w) => w.trim())
.filter(Boolean);
e.messages.forEach((message, index) => {
console.log(message.content)
message.content = blacklist.reduce(
(text, word) => text.replace(new RegExp(`\\b${word}\\b`, "gi"), "lalilulelo"),
message.content
);
});
}
})
I've just tested it and its working without any issues so far
but I mean, is there anything else that can be done to improve readability/performance/overall safety?
actually I should probably move that const out of the interceptor
done
thanks y'all :D
https://github.com/Vendicated/Vencord/pull/3549
Maybe extract the entire text replacement logic outside of the event handler
So that you can then just pass the message to your own function
yeah I just did that XD
Ohhh I see
was testing my code and I realized that since I was only using LOAD_MESSAGES_SUCCESS it meant that only messages read at restart (?) were being affected
Yeah load_messages is triggered by the initial http request (when you open a channel for the first time or jump to a message), while message_create and message_update comes from the gateway
Oh nice
already submitted the pr, hopefully nothing breaks on someone elses computer XD
oh snap
I just found something
You should mark the PR as a draft since your plugin isn't finished yet
Idk if that's only for github premium users or per repo
pretty sure it was just for premium, but the plugin was finished
I just decided to add more XD
Ohh
but now I need to fix the leet_converter because I'm making all the uppercase letters lowercase by accident
ok fixed
FFS ANOTHER ERROR XD
ok all fixed
hopefully
how long does a PR normally takes to go into the Stable Channel? I konw it first goes into dev, but its been a while since I've heard from my last PR XD
Okay chat so I made this, now I do realize it’s probably too niche for Vencord*(unless it was to be merged to showhiddenthings or something)* but whats the likelihood of a #1256395889354997771 post
cant you see that at the bottom of settings
Yeah but this cool
wdym likelihood
missing whatever that in parenthesis is
you are asking as if most unofficial plugins have an actual use
Ik they don’t
I mean, I like the plug-in, but it button to send a photo of a cat is not useful
the one time I tried to make an #1256395889354997771 post V deleted the post and then told me to make a pr instead and it's been 8 months since I opened that pr so :)
if a plugin is a direct fork/patch of an existing plugin ofc it’ll be deleted
true but i do see the point of it being deleted
lgtm as a userplugin
yeah I get it just unfortunate it's been sat dead for almost a year now
true
Isn’t that just the commit hash?
GitHub stuff
idk
is it known that image zoom does some weird stuff with clear images?
weird
i dont think anybody cares enough to fix it tho
what should I be seeing
(or if it even is fixable/an issue)
?
I dont even see the issue lol
yeah looks fine to me
hey all, not totally sure where it's best to put this but I'm trying to update my custom build (only changes are the addition of one custom plugin, hence channel) and I'm getting this error preventing it from building, which I'm not sure how to fix. any ideas? ```
✘ [ERROR] Could not resolve "@vencord/discord-types/enums"
src/api/Commands/types.ts:8:98:
8 │ ...ype, ApplicationCommandType } from "@vencord/discord-types/enums";
pnpm i
ah, thank you! that fixed it!
I see
it's just the continuation of the white glow inside the magnifier itself, no?
which imo the white glow is ugly 
huh there's two effects. it's the filter one yeah
I guess it helps you see where the edge actually is when your mouse is over the edge but slightly inside the non transparent bit
feel free to make pr
Nah too niche for its own plugin
The best you could do is get it integrated into maybe show hidden things but even at that point it’s still too niche
It’s fine as a user plug-in
I feel like it would fit more in with the Experiments plugin, just purely functionality wise.
how's that related to experiments?
iirc experiments also adds other dev related studf
check settings
I'm pretty sure that's a side effect of the experiments plugin, not the main feature of it.
^^^
That’s why I said if anything that’s prolly would just fit best into ShowHiddenThings
Cause it really doesn’t have anything to do with experiments
yeah
included #2 in a commit message cause it was the 2nd related fix and it referenced the 2nd issue on the github after pushing. agony. 
so im trying to make a plugin to allow editing messages without marking it as edited (this is possible, using a bug in discord's api). Using the MessageEditListener, i can have my own code run before the code to edit a message. Is there a way to have my own code run instead of the native discord code?
the bug is, if you use the send message api call using the other message's id as the nonce, it will edit the message without marking it as edited. So, i will need to have it use the send message api call instead of the edit message api call
that sounds like a really bad idea
im having it as a toggle, it's just a fun little thing to show off like a party trick where i dont need to copy as fetch from the console every time
still sounds like a really bad idea
is there an API to add buttons to the channel name button list like so?
i know vencordtoolbox uses a patch and i don't want to try to add another patch due to future conflicts
add a patch where it sends the request
vp toolbox
Adds a button next to the inbox button in the channel header that houses Vencord quick actions
V, AutumnVN
why does this still say the inbox button its nowhere near the index button 😭 😭 😭
took me an embarassingly long time to find isSelfDeaf after finding toggleSelfDeaf 
nobody has moved it yet
my plugin yearns for vens eyes
are internationalization keywords constant or do they get obfuscated inconsistently too
"YGm6SU":"Resume"
like the key here
constant
thats useful
is there an easy way to get the values for those.
hmm found Vencord.Util.getIntlMessage("YGm6SU"); but its just saying it doesnt exist 🤔
ah Vencord.Util.getIntlMessageFromHash("YGm6SU")
Check pins in #👾-core-development
Oh yeah but the hashes are kinda ugly 😭 I prefer to use the real intl keys
I just ctrl+shift+f'd ":"Resume

so would the right part go in the first function I tried
indeed
im using it in a patch though and it expects the hash but useful for the future
For patches you can use the intl macro
#{intl::KEY} gets converted into hashfn(KEY) for better clarity
#{intl::HASH::raw} also works but it's equivalent to just HASH
match: /\["9KoPyM"\]/,
replace: "[$self.getQuestAcceptedButtonText(arguments[0].quest, \"YGm6SU\",\"9KoPyM\")]"
so the second one
dont think that applies for this patch?
match: "[\"#{intl::GAME_LIBRARY_UPDATES_ACTION_RESUME}\"]" could work I think
(forgot about the hash symbol lol)
unless I patched the whole function call I guess
oh for the match yeah I guess
match: /\[#{intl::GAME_LIBRARY_UPDATES_ACTION_RESUME}]/,
replace: "[$self.getQuestAcceptedButtonText(arguments[0].quest, #{intl::GAME_LIBRARY_UPDATES_ACTION_RESUME},#{intl::QUEST_ACCEPTED})]"
does it work in replace too?
Btw don't forget the quotation marks around the hashes
put it in for the match and it says no effect hmm
oh wrong one
match should be QUEST_ACCEPTED
still no smh
yeah idk guess ill just keep using the raw hashes
how does this even make any sense. how is it possibly resolving to the letter o.
It matches any one of the chars inside []
and if I add ::raw it resolved to an open parenthesis (
After intl replacement and stuff
I thought using quotes around the match made it treat it as a string hm
I only unregexed it cause d' did 😭
yeah even match: /\[\"#{intl::QUEST_ACCEPTED::raw}\"\]/, doesnt work just has no results. amh
how do I match the 9KoPyM format then
what's 9KoPyM
the hash
QUEST_ACCEPTED?
yes
no
yes?
Quest Accepted is the actual text
and QUEST_ACCEPTED is the intl key
9KoPyM is the hash
idk if the terms im using are right
take off ::raw
still no match
then your regex is just wrong
The intl tag already adds the [""] I'm pretty sure
remove the "
You don't need a second one
still no. also why wouldnt I need them?
im trying to match them
match: /\["9KoPyM"\]/, works fine
You are not trying to match ["["9Ko"]"] are you?
So why are you adding a second [""]
the code itself has quotes in it
still doesnt match
{
find: "platformSelectorPrimary,",
group: true,
replacement: [
{
match: /\[#{intl::QUEST_ACCEPTED}\]/,
replace: "[$self.getQuestAcceptedButtonText(arguments[0].quest, \"YGm6SU\",\"9KoPyM\")]"
}
]
},
Not [["9Ko"]] either
this
why would it expand to that. that seems inconvenient if you wanted to use the values-ah theres functions for that
and this syntax is for patches
ok
It expands to that since that's what you want to match
that is confusing
I haven't used intl::raw but my guess would be that it expands to without the wrapping
now I cant get the getIntlMessage to work agony
ah
match: /\i.intl.string\(\i.\i#{intl::QUEST_ACCEPTED}\)/,
replace: "$self.getQuestAcceptedButtonText(arguments[0].quest,$self.getIntlMessage(\"GAME_LIBRARY_UPDATES_ACTION_RESUME\"),$self.getIntlMessage(\"QUEST_ACCEPTED\"))"
ok this works.
no it doesn't
I think
actually im not that sure
then how is it matching ["..."] without me having to add those chars myself
anyway don't wrap in []
It expands to a property access, either .key or ["key"] depending on ctx
Depending on whether the computed hash begins with a digit
how is that relevant 🤔
Because you can't access the property with dot notation if it starts with a number
this is syntax error
thing.1abc

you have to thing["1abc"]
oh thats why that code was like that to begin with
is there a way to not allow a setting to be cloud backed up (eg a password) or a saner way to store such a setting
dont put it in settings
DataStore or smth
@hushed loom plz fix
huh it's in webpack but it's not letting me use it 🤔 console says undefined
not implemented yet i think
you should implement it yourself 
looks like that function is lazy loaded or smthn? cant use it in my plugin. keeps giving me undefined. sadge
you can use lazy-loaded functions lol
you just have to load them
how
export const defaultSounds = (() => {
const soundFunction = findModuleFactory("activity_end.mp3").toString();
return [...soundFunction.matchAll(/(\w+)\.mp3/g)].map(match => match[1]);
})();
trying to do this
hm cant use this in top level of code then. guess ill delay getting the default sounds til the settings modal is opened
mm might just stick to hardcodes sounds this is agony
what am I supposed to be putting in extractAndLoadChunksLazy? I assumed the same thing as extractAndLoadChunksLazy but it's not finding it 🤔
read the function documentation
I did and checked out other use cases
they seem to just be using code from the module
which is what im doing too so .-.
im baffled cause my code is literally in the same file as decor's use of extractAndLoadChunksLazy and I copied theirs into console and that worked. 
ok putting this in start() is much easier
onceReady.then(() => {
const soundFunction = findModuleFactory("activity_end.mp3")?.toString() || "";
QuestifyLogger.info([...soundFunction.matchAll(/(\w+)\.mp3/g)].map(match => match[1]));
});
find(m=>m.resolve&&m.id&&m.keys().some(k=>k.endsWith(".mp3")))
that catches
function i(e) {
return n(a(e))
}
waddahell
whats that find doing?
ok so same issue as before, I still need to put it in the onceready
did you lazy it
checks what a module exports, in this case its a function with those properties
ok putting it in the setting component actually worked. nice
just lazy it?
idk what that means
findLazy
didnt work
how
idk maybe I did it wrong. ill try again in a sec. in the middle of adding it to the component directly
so the module.resolve means the first module to be loaded which meets the other conditions as well?

resolve it just another property, its not special
then why is it necessary 🤔
to match the correct one

worked for me
on the plugin object
what?
export default definePlugin({
audios,
});
do you need to do that
to beat Read All Messages yes
otherwise it creates an ugly DMs Buton > Read All Messages > Quest Button order
cause the serverlist insert function doesnt let you pick an index
i see
how is it useful there?
just to test it worked as a lazy
does it need to be there for lazy to work
or is it not working for me due to the StartAt.Init
no
probably
right well ill just do a defaultSounds = defaultSounds || find... to only run it the first time the component loads
can you explain what .resolve actually means? or why it and .id are needed for .keys to exist 🤔
well i changed it to init and still worked
its just a thing thats exported from a module
well i mean the findlazy is already top level
correction, the module exports a function module = function() {} but the function has function.resolve = someOtherFunc. id and keys are the same
ok yeah no wonder, you are doing that way too early
brother where else would I do it 😭
just call .keys() lol
if it's not there it's not useful
?
const audios = findLazy();
// component
audioPaths = audios.keys()
the error says it's not finding the module
yeah because you call it immediatly
also part of the find is that it has keys so how would that work
so how is this different than just doing
defaultSounds = defaultSounds || (
find(module => module.resolve && module.id && module.keys().some(key => key.endsWith(".mp3")))?.keys() || []
).map(key => {
const match = key.match(/((\w|-)+)\.mp3$/);
return match ? match[1] : null;
}).filter(Boolean) as string[];
in the settings component
ugly, i think its just convention to have finds top level
if anything this seems like a better approach? so it's only evaluated once
huh
ok still not working 
const findDefaultSounds = findLazy(module => module.resolve && module.id && module.keys().some(key => key.endsWith(".mp3")));
defaultSounds = defaultSounds || (
findDefaultSounds.keys() || []
).map(key => {
const match = key.match(/((\w|-)+)\.mp3$/);
return match ? match[1] : null;
}).filter(Boolean) as string[];
is this not what you meant by delayed usage
well is defaultsounds top level or not
const findDefaultSounds = findLazy(module => module.resolve && module.id && module.keys().some(key => key.endsWith(".mp3")));
let defaultSounds: string[] = [];
empty array is still true
stop using it top level
its in a settings component?
ok its working but still also erroring in console
oh this might be the unrelated issue that's fixed in dev but isnt being pushed to main to spite me
yeah ok not checking the length was the issue
nice
thanks for the help
// top level
let audioPaths = null;
// component
audioPaths ??= audios.keys().map(k=>k.slice(2,-4))
that is gibberish
??= looks illegal
It assigns a value to a variable only if the variable is currently null or undefined.

let defaultSounds: string[] | null = null;
const findDefaultSounds = findLazy(module => module.resolve && module.id && module.keys().some(key => key.endsWith(".mp3")));
defaultSounds ??= (findDefaultSounds.keys() || []).map(key => {
const match = key.match(/((\w|-)+)\.mp3$/);
return match ? match[1] : null;
}).filter(Boolean) as string[];
works nicely
still dont get resolve or id but ill just accept it's one of those things you accept without understanding and move on :D
thats all it is doing, if we want the object with foo and baz, we cant use use .foo because it'll find something else so we narrow it down .baz, thats what resolve and id are
oh so this specific module we're after has a resolve and id attribute
oh yes I see them now
so it's just convenient no other module has a resolve and id attribute but not keys which would cause an error
yeah
makes sense, thanks
By the way, a bunch of my plugins' imports are broken, is this because of recent changes or is something wrong with my environment?
Imports such as this are broken:
Yes, everything settings related got heavily refactored
is there any tool to auto update plugins to the newer version?
just replacing old import strings to the new ones
or is the functionality also changed?
Just check the pr/commit that did it and do the same changes
okay
makeRange is now in the same file as definePlugin
thank you
i was about to do something really bad
okay everything is fixed yippie
one of the top 3 operators tbh
its so good
when is C getting ?:=
I'm sorry I may be dumb right now, is there a discord function to get the current text in the ChatInputBox? I know there is a function to insert but I'm like really dumb and cant find anything about it
Rip this doesnt work anymore

idk
look into the code and fix it yourself
if it's an issue because of the setting refactor you should pr
i can't tell if you're joking or not
Semi
can anyone tell me if i contribute to an official plugin does that get me the very cool badge
or is like hand given
if you contribute at all and your github is linked to your profile then you get the badge I think
oh maybe it is plugins only actually
hm ok cause i have a few accounts with my github added maybe it will give to em all
i made a pull request anyways
That’s not how that works, you only get the profile badge if you make a plug-in or your name is within the constants.ts file
Just opening a PR and having accepted really only gets you the server role
lol im always running my fork so locally ive always had the badge
so I wasnt sure what actually gave it
Howdy folks (and my thanks to nin0); I'm looking for a bit of guidance in an attempt to create a timestamp plugin – generally to unify the format to one of my choosing (though I'd at least try to make that configurable, so it's useful to others as well), and to eliminate relative timestamps entirely. Unfortunately, I'm an old web designer and not a programmer. As such, my js/ts experience is limited to "things I learned against my will".
I had a look at ReplyTimestamp as a starting place, which pointed me to webpack/common/utils.ts. I can probably context-read enough to put together the structure of the plugin, but I'm struggling to work out how to manipulate DateUtils to do what I'm looking for – if that's even the correct function, or if I'm going to have to format millisecondsInUnit myself. As such, any pointers/general-outlines/starting-places/domain experience y'all have would be gratefully appreciated.
Does this suit your needs?
Ah. I had checked the plugin repo first, but didn't realise there was a backlog of submissions for it as GH PRs. That is indeed close enough to what I'm looking for. Thank you kindly.
What plugin repo?
requests probably
thats not a repo thats just a list of plugins in vencord
hello im eclipsia, im super silly and im going to try to explore by making a vencord plugin which has a set chance to maybe include an emoticon in your message and other uwuification sillies :3
That is a distinction without a difference to end-users, given it's really the only source AFAICT.
you can't call it a repo because you don't install anything from there
it's just a list of text there is nothing else

when a dev and an end user interact
I guess semantically you could argue while "repository" could be used more generally per its definition, the shortening to "repo" couldn't. 🤔
I don't think you could argue that because in this context repo/repository takes on a different meaning from its definition.
Words don't exist in a vacuum and you can't ignore their context.
I think the context is an end user was referring to a website with a bunch of knowledge of a thing, aka a repo
it's not a repo of the plugin code, but it is a repo of the plugin information
#linguistics-development chat going crazy here
Aaaand this kind of pointless pedantry is why even experienced users leave projects. Ironic bringing up context, considering nobody here was honestly stupid enough to not figure out what "repo" referred to in context (considering, again, there is literally only a single user-facing source). Y'all just wanted to wave your superior technical knowledge over the 'newbie' for not using your brand of terminology – on a semantic technicality that was irrelevant to the discussion. Bye. :)
meds
i'm definitely gonna start using this
and that's an example of crashing out over something not very deep
Prob the funniest part is that he immediately leaves
I don't really disagree with him tbh
calling a website a repo 
Very much a "who gives a shit" issue
still stupid to make a huge message crying abt ppl talkin abt the repo and then outright leaving bc of that alone

Sure, I just agree with the sentiment, a lot of dev spaces online are very condescending and blunt especially towards new people
This isn't a huge example of that but y'know
the thing is that it's not that deep lol
I was just memeing
Yeah I don't think it's worth writing a paragraph and leaving over
Just learned plugins can interact with experiments
plugins can do basically anything thats the whole point
Hello, I want to run a function when the user connects or disconnects from a voice channel, I'm currently using VOICE_STATE_UPDATES and returning if the user id doesn't match, and I was wondering if there was a better way.
You can ctrl+shift+f in the dev console for async handleVoiceConnect and 287734: (disconnect function inside) and it looks like those both run when joining and leaving respectively. Then just patch in what you need.
thank you, I will check it out in my free time.
you probably want to use flux events, from what i can tell, that is the correct flux event
there's no need to use a patch for this
quicker response times maybe? but yeah probably not needed.
VOICE_CHANNEL_SELECT looks better though
it seems to only fire when you join or leave and only for you
has a currentVoiceChannelId field which is null if you're connecting and set if you're leaving
hm interesting. channelId is where you end up and currentVoiceChannelId is where you were
so if you switch between two vcs you can tell that way
does it fire for dms
does it fire when you press the reconnect button
reconnect?
does it fire when you click on the channel even if you're aready in a call
.
name is misleading it's not literally highlighting it. you have to actually do the connect action
I didnt see a button it just auto reconnected me but it didnt fire. smh
alt-f4, don't relaod
it does when you click reconnect. weirdly it even fired for leaving despite the client having already been restarted at that point?
so only downside is ctrl+r reloading not being tracked I guess
?remind tomorrow make flux-event browser for vencord companion
Alright @hushed loom, in 1 day: make flux-event browser for vencord companion
it took me too long to realize the checkbox in the dispatcher is to pause showing new events 
god this will be painful
@hoary pilot why husk
felt like it ykyk
Thanks everyone I will see what works best for my use case
@hushed loom any way to make the extension ignore a warning on a specific patch? noWarn doesnt seem to work
Modal?
yeah, i'm going to make it handle noWarn
i also want to version the plugin and extension so it warns you if you're out of date
nice. one of my patches seems to be a lazy loaded file cause it errors till I trigger the tooltip it's modifying
as well as cache the module scan so you don't have to wait 10s to find all usages
Hi! i'm trying to bypass a "index.ts" with all the references in it cause i don't wanna change it on every new command. I'm currently using fs to get files in the (sub)folders and it works fine until i try to import the file. It just skips this part.
// c-rob.ts
...
export default {...}
// index.tsx
...
const Native = VencordNative.pluginHelpers.CustomCommands as PluginNative<typeof import("./native")>;
(async () => {
const dir = await Native.getDirname()
const loc = await Native.resolve(dir, "../", "src", "userplugins", "vc-customCommands", "commands")
const files = await Native.scrapFolder(loc)
console.log(files)
})()
export default definePlugin(...)
// native.ts
import fs from 'fs'
import path from 'path'
export async function scrapFolder(_, loc:string) {
let output: Array<object> = []
fs.readdirSync(loc).forEach(async file => {
let fpath = path.resolve(loc, file)
if (fs.statSync(fpath).isDirectory()) {
output = output.concat(await scrapFolder(null, fpath))
} else {
const req = (await import (fpath)).default
if (typeof(req) == "object" && typeof(req.execute) == "function" && typeof(req.name) == "string" && typeof(req.description) == "string") {
// 0 For default "ApplicationCommandOptionType.BUILT_IN"
req.inputType = req.inputType !== null ? req.inputType : 0
output.push(req)
}
}
})
return output
}
export async function resolve(...args){
return path.resolve(...args)
}
export async function getDirname() { return __dirname }
The hard coded folder location aint perfect but it's temporary, and i don't wanna spend time finding a host for an user install bot..
Lmk if this is possible with the "native.ts" feature or if i have no other choice but to index every command manually. -# ping please
native.ts runs at execution
vencord is bundled at build time which means your commands are left behind and there is no notion of filesystem once vencord is running
Dang.. so if i wanna keep that system i gotta edit the build command? not doing this
So no choice but to do a index.ts exporting every command manually?
yes
(you could also write your own custom build script but not worth your time)
Thanks
User install the bot and host it within the discord process via a vencord plugin so it's always running when you're using discord 
we call them apps now
Feel free
@hushed loom, <t:1753491578:R>: make flux-event browser for vencord companion
progress for today
virus
I'd get the same issue but ye why not ig
However now i'm trying to use the "search" function via a plugin and after checking the react components i get to a constructor with those this function
U(this, "setSearchQuery", function(e) {
// useless shit for parameters
P.S.dispatch(F.CkL.SET_SEARCH_QUERY, {
query: e,
anchor: 0,
focus: 0,
performSearch: true
}
),
})
seems good but when using findByCodeLazy("this, \"setSearchQuery\", ") it returns nothing. I assume i'm using this wrong so i though of using the event "SET_SEARCH_QUERY" but triggering it gives.. no result (MessageActions from webpack/common has no function for it).
Is there a better way to get all messages from ONE member?
-# ping me please
What's the end goal here
Getting all messages from one user on one server then look if the content is the same as the one from a context menu and delete the found messages. Using the search function would be awesome since
from: ${message.author.id} ${message.content}
and just loop over the result
sounds like api spam/abuse
findByCodeLazy("this, \"setSearchQuery\", ") returns nothing bcause there's a space in it
discord's modules are minified
also it's not exported
so you won't be able to find it
now it properly traces key modules
Any idea on how i could do this? heard the sentence "if an ui element can do it, you can do it"
how would every message from a user only be 15?
Oh yeah i meant deleting 15 and i could only search the last 50. That’s why the search function you be really handy
you tried just using the restapi? would cut out the middle man of trying to take over the search bar. although discord is pretty strict with the search endpoints cause they're expensive so you would really need to be careful with using it and it would almost certainly not make it into an official plugin (I have not backread what your goal is)
Basically when a dumb user gets hacked and sends « mr beast gives 5k at scam.com » it would be cool to right click, « bulk delete » and it deletes all the msgs from this user with the same content. So your solution would be using direct api route without passing by the app functions? Sounds really selfbot obvious to me
and using the search bar wouldnt?
all that would do is call the endpoint itself
you're just adding a clunky layer inbetween
identical
You can just ban them and discord auto deletes all recent messages
well by default no
you have to specify delete_message_seconds
also it sounds like they want to primarily account for normal server-contributing members who get compromised so not a clean sweep of the past 2 weeks of messages but only the recent bad ones once they got compromised. and since scam bots typically spam the same mesassage by iterating the channel list it would be by searching for that term across the guild
Which delete_message_seconds can handle as well
if you find their first message and use the command on that one
so it would delete everything after it was posted
but at that point you might as well just go channel by channel manually deleting them
cause channels arent sorted in your channel list by creation date but api wise spam bots most likely receive them in that order
so you wouldnt know at a glance which channel was the first affected
though if you just have a good automod ruleset you'll almost never have to deal with it
Searching from:user and using the built in ban and message delete functions up to the point they were compromised still seems a lot less work than making a plugin
But y'know, coding is fun so I'm not gonna stop you
doesnt the built in ban have strict times? like minimum is an hour or smthn. some messages would get caught in cross fire. but really skill issue for getting compromised at that point
myep
Bots can do shorter durations
well yes but then you get back to needing to code it
I thought your point was that coding a solution to this was over engineering it lol
I see what you mean now. search for their messages, find the first scam message, then use a command to ban and delete x seconds of messages
Yeah, but even so, coding a bot with a single command and an easy to work with API is much easier than coding a vencord plugin with api spam
in most cases though you'd notice a spam of scam messages within a few minutes. they might not even be indexed yet tbh so this whole solution of using search at all is probably not a great idea
Bigger bots like circle and yagpdb have this kind of protection built in as well, even detecting the same message across multiple channels I'm pretty sure
well it wouldnt really be api spam it would at most be 2-3 search endpoint calls and a ban endpoint call but ye
Oh, yeah I meant with the original deleting messages method
again easiest solution is just automodding @ everyone and invites format and then whitelisting your staff. 99.99999% of scam messages include it

oh was their original goal to manually delete messages. right
and the bulk message delete endpoint is per channel and not supported for user accounts
so it would have to be message by message which indeed would be a bad idea to automate
I think having a plugin that sets the delete_message_seconds based on which message you've clicked to ban the user is a decent idea, would be a little bit niche though
yeah but then as I said you'd be fumbling to find that first message yourself. unless discord returns the channel list in sorted order and not by id order
wtf it does
Vencord.Webpack.Common.GuildChannelStore.getChannels("1015060230222131221", true)
first is moderator only and second is v
Yeah
hey guys, i was making my plugin and i want to get media from files.catbox.moe but the thing is that every time i get a csp error
-# Refused to load media from 'https://files.catbox.moe/....mp3' because it violates the following Content Security Policy directive: "media-src 'self' blob: disclip: https://.discordapp.net https://.discord.com https://.discordapp.com https://.youtube.com https://streamable.com https://vid.me https://twitter.com https://oddshot.akamaized.net https://*.giphy.com https://i.imgur.com https://media.tenor.co https://media.tenor.com https://c.tenor.com blob: data: vencord:".
do i need to add something to the csp policies of vencord or something?
step 1: make native.ts file in ur plugin
step 2: import the CspPolicies in native.ts (vscode should suggest from where to import)
step 3: add this
CspPolicies["example.com"] = [ "connect-src"]
step 4: rebuild
step 5: restart ur client
Something like that
Your IDE will assist you
ohhh thank you, i'll try that
is there any way to check what you're live streaming via a plugin? like if its an application or screen. application name isnt necessary
try VideoStreamStore
looks like GuildRoleStore has changed its exports
@dull magnet stabilized guildrolestore when
hmm it has some sort of record partitioning system, I wonder how that works
Oh it's probably a ts equivalent of some internal libdiscore structure
I have an open PR with the fixes for it if you need that
I'm using
const QuestsStore = findByProps("getQuest");
but sometimes the store has outdated data, is there anyway to force update it?
when is it ever out of date?
ive never experienced that
regardless you can do this
if by oudated you mean it doesnt have the newest available quest then yeah discord only auto fetches new quests when you reload or visit the quests page
if you mean the existing data is outdated then ive never had that happen
When I accept a new quest, the enrolledAt of that quest is still null
you know I did actually experience that earlier but I assumed it was because the quest object passed to my function wasnt the same as the one in the store and so I "fixed" it by just fetching it from the store but havent been able to test it til I get a new quest
how are you accessing the quest after enrolling
I fetch the object again
const QuestsStore = findByProps("getQuest");
also this only became an issue yesterday when they added a captcha to enrolling in quests im pretty sure
🤔
and it was still null?
Captcha been there since a long time and it's not really an issue since the popup actually shows up for the user to complete
Yep
captcha for completing not enrolling
ive always insta enrolled anyways
unless I set off discord's sus detetor
Both but it's on an off sometimes I get it sometimes I don't
Hey everyone, I just made my first plugin (https://discord.com/channels/1015060230222131221/1399422697301348524) and was wondering if and / or how it was possible to get it to join the big boys as an official plugin
you fork Vencord, add your plugin to it, and send a pull request. See the contributing doc: https://github.com/Vendicated/Vencord/blob/main/CONTRIBUTING.md
ooo misssed that doc. thanks
is there any existing plugin that uses the clipboard? or do i have to add that
oh there most definitely is lmao ill go looking
yeah i found it @utils/clipboard me when i ask a question and just have the answer
me when i ask a question and then immediately find the answer not long after
i do this everytime
i just stop asking questions atp
thats the thing tho you dont find the answer unless you ask
the reason you ask is cause you tried for so long to do it without asking that you finally think you're never gonna manage
paradox
is there already a package that is modifiying the toolbar like the electron app.setUserTasks() task?
does vesktop have the DiscordNative variable and can it use its functions?
oof
what exactly do u need
I was calling DiscordNative.fileManager.saveWithDialog but I'm playing with other ideas now.
Was just curious if it was a Discord client thing only or not.
electron has an api to open something like that
yeah im using that now
await dialog.showSaveDialog({
is there a way to mark plugins incompatible? I searched for it and found 1 mention of it maybe being possible but then the person stopped explaining mid way 
with what platform
add .desktop to the plugin folder
that's probably what you want
with other plugins
so like show an error if you try enabling x while y is already enabled
is there a way to emulate a ping client side? like through a plugin, have ping(s) appear on a channel, and in the server, etc.
more really - does anyone know how id do this, or do i have to try and see if i can figure it out
see how discord does it
find where the ping is renedere
see what discord checks to know if it should render it
okay 😭➕
thank you 10
is it normal for updating a plugin native file to require reinjecting to work or am I just unlucky
you need to fully restart discord
ah yeah thats probably it then. dont think i ever tried anything other than reload
hey, I'm trying to write a plugin that just remaps :3 into a glowy :3 gif, and I'm trying to intercept edited messages so if the user edits a message to fit the pattern, the plugin can interact with it. I've been looking through the APIs that vencord provides and existing plugins (mainly clearURLs) and I can't find a way to handle a message after it is edited. my index.ts is here:
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { MessageObject } from "@api/MessageEvents";
import definePlugin from "@utils/types";
export default definePlugin({
name: "ColonThree",
description: "Make your :3 glowy :3",
authors: [{
name: "void_linxu",
id: 675018004605698049n
}],
onBeforeMessageSend(_, msg) {
return this.handleMessage(msg);
},
// only handles the content *before* user edits
onBeforeMessageEdit(_cid, _mid, msg) {
return this.handleMessage(msg);
},
handleMessage(msg: MessageObject) {
if (msg.content.trim() === ":3") {
msg.content = "https://cdn.discordapp.com/attachments/1265636854431219772/1297946528643809385/silly.gif";
}
else {
// .error because red is easy to see lol
console.error("DEBUG: ", msg);
}
return msg;
}
});
use textreplace lol
textreplace doesn't do it better 💔
What do you need to handle post edit for?
<Popout
position="bottom"
align="center"
spacing={0}
animation={Popout.Animation.SCALE}
shouldShow={show}
onRequestClose={() => setShow(false)}
onRequestOpen={() => setShow(true)}
targetElementRef={buttonRef}
renderPopout={() => console.log("hello worlD")}
>
{(_, { isShown }) => (
<HeaderBarIcon
ref={buttonRef}
onClick={() => setShow((v) => !v)}
tooltip={isShown ? null : "Reports Menu"}
icon={() => MenuIcon()}
selected={isShown}
/>
)}
</Popout>```
Does anyone know why the renderPopout doesn't run when I click on the icon? I tried adding a debugger to the component I passed into renderPopout but it just doesn't run (I've not swapped it with console.log)
const [show, setShow] = useState(false);
const buttonRef = useRef(null);
The props in the inner function has an onClick that you need to pass into the icon
No errors in console either
Like in here, it's being spread so onClick, onMouseDown, etc are passed onto the span
That being said, I guess your way should still work. Does the onClick on the icon work at all?
I added useEffect and was tracking the show state, and it seems to be alternating as expected (after each click)
I've fixed it now, thanks for trying to help.
It was something to do with how my Popout component was added to the toolbar array
Where can i find info on how to use patches? looking at other plugins like the call timer plugin with a patch that looks like this
find: "renderConnectionStatus(){",
replacement: {
// in renderConnectionStatus()
match: /(lineClamp:1,children:)(\i)(?=,|}\))/,
replace: "$1[$2,$self.renderTimer(this.props.channel.id)]"
}
I have no idea where i would find out that i should use "renderConnectionStatus()" besides maybe using react devtools but inspecting with them i havent been able to find that function by focusing (? i dont know the correct term) that element, im also not sure i understand what the regex is actually doing. So is there like a guide?
The only times ive used patches there have been patches in other plugins that were just exactly what i needed anyways
There are some guides in pins, this probably explains patches best
Inspecting with react dev tools and using ctrl+shift+f to search all sources in chrome dev tools is really your best bet for finding where and what to patch
Even following that i dont see an option called pretty print and i dont see anything with "i18n"
Pretty print is in the bottom left of the editor
It’s a pair of braces as an icon
I am so lost?
In regular dev tools sources tab
Click of the pair of angled brackets to view the source
You can also right click on some functions to view their source
Also detach your dev tools
Top left three dots menu
Ah thats lovely
i18n stuff is outdated now as discord changed systems
But the concept is the same
The find locates the module, then the match and replace are a regex replacement performed on the found module
Unfortunately pretty much any example in a patching guide will end up outdated because the code is always liable to change in any update and probably will
I might just be incredibly slow? but im not exactly finding anything useful
return (0,
r.jsx)("div", f(u({
style: u({
flexShrink: d,
flexGrow: p,
flexBasis: b
}, y),
className: a()(s.flex, i, o, l, c, n)
}, O), {
children: t
}))
this is as much as i can recognize from what the guide you sent but theres nothing quite useful here i think?
What do you want to do
I can give you an up to date example
put text between here
Alr
@turbid spruce try looking at module 665149
let A = e => {
let {className: t, innerClassName: n, toolbarClassName: o, children: d, childrenBottom: f, toolbar: p, onDoubleClick: h, "aria-label": m, "aria-labelledby": g, role: E, scrollable: b, transparent: y=!1, showToolbar: O=!0} = e
, v = i.useRef(null)
, I = i.useContext(u.Z);
return (0,
r.jsx)("section", {
className: a()(t, _.container, {
[_.themed]: !y,
[_.transparent]: y,
[_.themedMobile]: l.tq,
[_.showToolbar]: O
}),
"aria-label": m,
"aria-labelledby": g,
role: E,
ref: v,
children: (0,
r.jsxs)(c.JcV, {
containerRef: v,
children: [(0,
r.jsxs)("div", {
className: a()(_.upperContainer, {
[_.showToolbar]: O
}),
children: [(0,
r.jsxs)("div", {
className: a()(_.children, n, {
[_.scrollable]: b,
[_.showToolbar]: O
}),
onDoubleClick: h,
children: [l.tq && null != I ? (0,
r.jsx)(s.r, {
onClick: I,
className: _.hamburger
}) : null, d]
}), null != p ? (0,
r.jsx)("div", {
className: a()(_.toolbar, o, {
[_.showToolbar]: O
}),
children: p
}) : null]
}), f]
})
})
}
;
Still kinda lost honestly..
what do you not understand
I dont understand what im actually looking for? some sort of className or function? i have no idea, i cant even find Module 66519 in sources
do you know how jsx transformed code looks like
finding 66519 is as simple as searching for 66519: in all the sources
Partially, i got from the outdated doc the other guy sent that "r.jsxs" is React.createElement
do you understand what children is?
I do
so you are looking for a module in the app (which is essentially one file in the actual discord code) that includes a children array which should have both logic related to the channel name and description
from the react devtools you can find which component is being passed the channel name, or the description
from the there you can look for parent components to see which one is rendering the component that has the name or the description
by finding that component you may find the children which includes both of them, the one you want to patch
That helps but i still feel lost, i still dont really understand how i know im looking at the correct thing or how to write a patch that will actually find it
im sorry if im being extremely illiterate 🙏
to know if you are looking at the right thing you need to see pay attention whether you see things related to what I said
does it render a component with a variable pointing to the channel name
the desc, etc
Looking at the code the other guy attached it mentions the toolbar which is what im looking for but i dont see any variables that explicitly are named something like channel name or description
yeah, it might not be that
so i should be searching for something with "description" or "channel name" ish?
Classnames can be a good way to find things as well as long as they're not super generic
looking, not searching
if you search you will get way too many results
as they are very generic
this too
Yeah i see 600+ results for searching
This is specifically the topic text but this is one way to find that component
Once you fuck around with it for a while it eventually just starts to sink in, it is pretty confusing to begin with though
And still confusing later on depending what it is tbh
Okay i think i understand a little more now
how do i query it with a patch tho?
if i just wanted to change the topic text for example?
You'd need to find a string of code in that module that's unique to only that module, preferably without any minified variables, but if you do have to use them, you can use \i in regex to capture them without hardcoding the var name
Once you have that, you can use the match and replace in the find object to replace that e.topic (or more around it if you want) with whatever text you want
Just make sure you replace any variable names like e, n, t, etc with \i or your patch will eventually break when discord's minifier gives it a different letter
Can the "find" just be any piece of unique code in the relevant module? would .parseTopic( be specific enough?
372564 this i think?
doesnt look specific enough
ah
is this some sort of patch helper? where can i find it
Ah yes okay
i just use it for testing finds, i do patches in other ways
If i wanted to replace the topic always to some specific text would this be sufficient?
{
find: '"highlight"===',
replacement: {
match: /\i\.topic/,
replace: "'This is a test'"
}
}
just changing "e.topic" with the magic "\i" thing?
patch helper seems happy but it doesnt seem to actually do anything when i enabled the plugin
you're replacing a condition
null will never equal This is a test
null != "this is a test"
function getTopic(channelId: string, fallback: string): string {
if (channelId === "1032770730703716362") {
return "This is the plugin development channel.";
}
return fallback;
}
export default definePlugin({
name: "...",
description: "...",
authors: [...],
settings,
getTopic,
patches: [
{
find: "topic.length>0;",
replacement: {
match: /parseTopic\((\i).topic/,
replace: "parseTopic($self.getTopic($1.id,$1.topic)"
}
},
{
find: "ChannelTopicModal",
replacement: {
match: /parseTopic\((\i).topic/,
replace: "parseTopic($self.getTopic($1.id,$1.topic)"
}
},
you'd want smthn like this
Ive messed around with this and i just cant get it to work, i have no idea why it wouldnt work, the patch helper looks right
did you rebuild and then reload discord
I did something
and it works now
i dont know what i did but it worked
Im sorry for me being a bit slow all along this short journey but i want to sincerely thank everyone who helped me 🙏
@quick zephyr 
suh
use equicord if you want it
was told it'll never be merged here so
typical lol
surely my silenttyping pr that V told me to PR 9 months ago will be merged soon though right
yes because this is way too much
yeah thats fine. glad to have an answer so I dont indefinitely have to keep updating it in 2 places
your ShowMeYourName change is also excessively large
thats why I also closed that one lol
I knew it wouldnt be getting merged either
surely my silenttyping updates and imagezoom fix are fair game though 
the imagezoom fix hasn't been merged yet?
nop
from a quick skim of your two big PRs:
- you add way too many options. no user cares about so much customisation, it's just overwhelming at that point. limit yourself to a few core customisations and do not add super minor customisations
- your code in a lot of places is very verbose and repetitive. there's not really much I can say other than try writing more concise idiomatic code. these examples are just bad style. try keeping your code similar to other existing plugins
I mean yeah I could condense the left into a single really long if but other than that I don't really agree personally. but it's fine
besides includedNames, removeDuplicates and discriminators, every single option you added to SMYN shouldn't exist
huge disagree 
sorry but you have no clue about UX if you think a plugin related to changing the username(s) displayed needs 20 switches 😭
too much customisation makes customisation less accessible because it is overwhelming to the user
if every plugin had 20 switches, people would have to invest hours to understand and configure everything
the vast majority of plugins should have at most like 5 settings
13 and they all do smthn I personally think is worth making optional. thats the point of defaults. let the user not think about it unless they really want to. but again thats fine. im not tryna convince you to merge it. we can agree to disagree. for a big public project like this I can see the argument you're making on simplicity.
If that's the case then linux (and most oss) developers have no clue about UX either 
can you give my silenttyping pr a quick skim and tell me if it's also too much or not so I know whether to keep it updated til it can be reviewed properly or close it
tbf my plugin also suffers from a bajillion options
if you're talking about window managers, these things are not comparable
you're talking about an entire program vs one single feature of a program
it would be comparable to this page having 20 different settings regarding your mouse
option fiend
right ok ill just close that one too then
is this why his pfp is cosplaying devilbro (using a tank top)
damn why tf am I catching so many strays.
i'm not saying everything is bad, but most of those arent needed
You could probably simplify this by just making left, middle and right do one of each of these and not have it cutomisable
I really do not like these "show icon" settings
we should just make these a central setting in vencord somewhere
you know how android has one central settings page to configure which icons show in your statusbar
I prefer more customizable vs less which is fine if that's just not how vencord plugins are preferred
true
we need something like this for vencord chatbar icons
yeah
Also you should #1256395889354997771 these if they won't get merged, those people are asking for more complexity anyway
this one was in there... v deleted it then told me to pr
what exactly?
silenttyping
because we don't allow modifications of official plugins in that channel
the channel is meant for original plugins
Oh right
then each plugin adding a chat bar button would automatically get a link to this page in its settings
I dont really want to maintain multiple separate repos unfortunately
Friends keep telling me to PR to equicord and I keep telling them the same thing
I barely have the energy to bother with one place
I'll redo all my readme images soon, I originally did them in the context of just showing what the thing does for the PR but I forgot they need to show on the site
Gonna PR this, easy merge, no need to read
can you dynamically create a setting? or is there a better way for persistent data storage? my use case is per account data so it doesnt get overwritten when you use the account switcher. I could just stringify some json but looking for better alternatives
just dataStore + user id
it does
just checked and plugin settings are shared between accounts which means my logic is just overwriting it to empty somewhere on accident when switching accounts I guess
you can store it in settings but you need to manage it yourself
wdym?
ive just learned that what I really need is object storage
dont need it to be per account in this case. per client like all settings is fine
🤔
const settings = defineSettings().withPrivateSettings<{ perAccountSettings?: Record<string, whatever> }>();
you can extend the settings type like that
oh nice
then just write to settings.store.perAccountSettings[userId] (and it will be undefined so you need to set it to an empty object first)
like during start()? just initialize it to empty
whenever you read or write to it, just ensure it
make a function getSettings() or smth
I've been storing quests by questname but I just learned I need to account for excluded quests which discord only sends an ID for those so im going to have to store an id and name for each quest I need to keep track of.
function getSettings() {
const s = settings.store.perAccountSettings ??= {};
return s[AuthenticationStore.getUserId()] ??= {};
}
actually idk if the return of ?? = ends up reactive
but just store it like that
const settings = defineSettings().withPrivateSettings<{
ignoredQuests: Record<string, { questId: string; questName: string }>
}>();
function getIgnoredQuests() {
return settings.store.ignoredQuests ?? {};
}
could just do this, no?
I guess when I set to it though I need to init if undefined though
function getIgnoredQuests() {
settings.store.ignoredQuests = settings.store.ignoredQuests ?? {};
return settings.store.ignoredQuests ?? {};
}
🤔
you can use ??=
function getIgnoredQuests() {
settings.store.ignoredQuests ??= {};
return settings.store.ignoredQuests;
}
yeah
nice thanks
there is no need to use withPrivateSettings
just use a normal set with type set as CUSTOM
yeah
nice
default: {} as Record<string, { questId: string; questName: string }>
for type safety
yeah that works
I tried making it a set but it seems to be storing as a Proxy(Set)? do I just use Array.from and then make a new set out of it whenever I need to access it?
that did not in fact work
use primitive types only
the whole point here was to store objects .-.
I could have just stored it as a comma separated list from the start
object is primitive
so just set is the issue then
yes
damn can I not even cast it to a set after reading it
what for
object cant have duped keys
im using an array. is that also not allowd 
why not an object?
then you have to use DataStore (indexeddb)
you can put Maps, Sets etc in there
settings is json only
json has lists doesnt it
then add an onChange to ur setting, make a set from the list, turn into array again and update the setting
reactivity isnt my issue
Questify: Error while handling QUESTS_FETCH_CURRENT_QUESTS_SUCCESS
TypeError: Method Set.prototype.values called on incompatible receiver #<Set>
const currentlyIgnored = ignoredQuests ?? new Set(Array.from(settings.store.ignoredQuestIDs));
oh is the setting still stuck as a set. do I need to go in and manually delete it
possibly
wheres that at again
appdata
yeah im in roaming
vencord folder
ok yeah was stuck as {}
there is no need for Array.from
yeah removed
damn yeah not reactive
agony
ill just do a comma separated string I guess
??
it was already a multiline string of quest names I shoulda just kept it a string when I decided to switch to ids
lmao
