#🧩-plugin-development

1 messages · Page 82 of 1

dull magnet
#

math

#

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...

fallen sandal
#

artificial stupidity

dull magnet
#

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

clear parcel
#

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

hushed sable
#

Got humbled and skidaddled

rocky falcon
#

all plugin suggestions are closed now right?

hushed loom
#

make it yourself

rocky falcon
#

little aggressive

dull magnet
#

?

quick zephyr
#

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.

hushed loom
#

wasn't my intention, dont want to get into an argument about it

quick zephyr
#

didn't figure it was and I don't either, just answering your ❓ react

vast karma
austere mauve
#

yeah just a few more steps to make an llm

graceful hazel
#

quick question, is there any good documentation for patches? i can't figure it out at all

hoary pilot
#

ignore the part where it says superseded by official docs

graceful hazel
#

Thank you gonna read through that!

pure temple
#

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

hoary pilot
#

Okay so be intelligent

#

And think

tropic ice
pure temple
#

So true

#

the secret to programming

#

i

#

Actually it’s IT

#

intelligence and time

ruby hill
#

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?

halcyon blaze
#

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.

hushed loom
halcyon blaze
proud parrotBOT
halcyon blaze
#

honestly it might just be easier to find the elements and call .click on them lol

ruby hill
halcyon blaze
#

yeah, it wouldn't be a great solution, but I don't see any other for now

prime belfry
quick zephyr
prime belfry
#

ok

#

deleted the cache in applications and same error

#

seems like it cant hook

silk sorrel
#

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.

silk sorrel
#

thank you

prime belfry
#

hey so what is the "menu.ts" webpack common? i didint find any plugin that uses that

prime belfry
#

ummm so my react devtools just disappeared

#

tried restarting and reenableing but same

hushed loom
prime belfry
#

im in vesktop

#

how do i reload the page in vesktop?

#

f12 dosent work

hushed loom
prime belfry
#

ohhh

#

its back! thank you!

bronze shuttle
#

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

pure temple
#

ain't this possible with TextReplace?

bronze shuttle
#

yesnt

#

TextReplace intercepts the message being sent by you

#

what I'm trying to do is essentially "Inspect Element" existing messages

pure temple
#

uhhh

#

i have a plugin that was to have a typing animation, but it's kinda useless

#

that does effect it like inspect element

bronze shuttle
#

maybe that could work, but in short I'm trying to do this: (TextReplace is the "blacklisted word")

pure temple
#

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?

bronze shuttle
#

probably...?

#

I know that TextReplace is much much simpler since it basically only targets the message you're still sending

pure temple
#

mhm

#

i think it uses onMessage with flux? lemme check

silk sorrel
# bronze shuttle Hello! Has anyone ever figured out how to patch existing messages client-side?

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)

vast karma
#

You should patch the render, not the store

pure temple
#

mmm

bronze shuttle
#

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;
   ...
pure temple
#

you could just target however discord renders image and insert your methods laliloolela'd string, no?

bronze shuttle
#

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)

silk sorrel
bronze shuttle
#

I suppose I could even do something like

message.content.replace(blacklisted_word, "lalilulelo")
silk sorrel
#

yeah, just don't forget to update the original event object

bronze shuttle
pure temple
#

oh it's absolute shitcode

#

wait really? it's that bad?

#

wackyyy

bronze shuttle
#

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

pure temple
#

yeah you're right it looks like garbage

pure temple
#

but for example, textreplace doesn't have patches

bronze shuttle
#

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

pure temple
#

hehe yea

silk sorrel
#

I believe message objects have a guildId property, which you can use to filter message events from specific guilds

bronze shuttle
#

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

bronze shuttle
pure temple
#

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

quick zephyr
#

you could patch the same place message logger is patching.

bronze shuttle
#

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: {
quick zephyr
bronze shuttle
#

Ok maybe I should take a break

quick zephyr
#

there are no breaks until you succeed

bronze shuttle
#

I have to clock in in 15 minutes XD

quick zephyr
#

your boss will understand

bronze shuttle
#

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

silk sorrel
#

Single message events (MESSAGE_CREATE and MESSAGE_UPDATE) do have e.message

sullen fog
#

does anyone know how to find whether a dm / group channel is muted?

hushed loom
#

i'd look at the context menu

sullen fog
sullen fog
humble tulip
#

F8 not F1

iron epoch
#

myf

#

vp f8

shrewd tundraBOT
sullen fog
#

thanks will take a look

bronze dove
sullen fog
sullen fog
iron epoch
# sullen fog this doesnt work because the components tab also freezes

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

bronze dove
sullen fog
bronze dove
#
  1. findByProps("isChannelMuted")
  2. Vencord.Webpack.wreq(9156)
  3. search for 9156:
    pick your poison
sullen fog
#

UserGuildSettingsStore

#

is what im after

#

thank you

quick zephyr
#

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

silk sorrel
#

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.

silk sorrel
#

also there are so many plugins that use it

prime belfry
#

oh i was searching in vscode "menu.ts" my bad

#

thanks

bronze shuttle
# silk sorrel Don't forget that .replace returns a new string, and e.message is undefined if y...

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

bronze shuttle
silk sorrel
bronze shuttle
#

yeah I just did that XD

silk sorrel
#

Ohhh I see

bronze shuttle
#

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

silk sorrel
bronze shuttle
#

oh I see

#

yeah it kinda makes sense now lol

#

anyways Embeds are working now

silk sorrel
#

Oh nice

bronze shuttle
#

already submitted the pr, hopefully nothing breaks on someone elses computer XD

#

oh snap

#

I just found something

silk sorrel
#

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

bronze shuttle
#

pretty sure it was just for premium, but the plugin was finished

#

I just decided to add more XD

silk sorrel
#

Ohh

bronze shuttle
#

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

bronze shuttle
#

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

bronze shuttle
#

ok Mr. V. Did not like my sphaghetti code

#

back to the drawing board I guess

swift delta
#

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

quick zephyr
#

cant you see that at the bottom of settings

swift delta
#

Yeah but this cool

quick zephyr
#

it doesnt even show the full build string

#

stable 418516 (f170514)

quick zephyr
#

missing whatever that in parenthesis is

empty sundial
#

you are asking as if most unofficial plugins have an actual use

swift delta
#

Ik they don’t

#

I mean, I like the plug-in, but it button to send a photo of a cat is not useful

quick zephyr
empty sundial
#

insane

#

i just keep my plugins on my github now cuz i cba to wait

hoary pilot
quick zephyr
#

you're the one that approved my ticket to post it there

hoary pilot
#

true but i do see the point of it being deleted

hoary pilot
quick zephyr
#

yeah I get it just unfortunate it's been sat dead for almost a year now

hoary pilot
#

true

swift delta
#

GitHub stuff

quick zephyr
#

idk

rocky falcon
#

is it known that image zoom does some weird stuff with clear images?

hushed loom
hushed loom
#

i dont think anybody cares enough to fix it tho

quick zephyr
#

what should I be seeing

hushed loom
#

(or if it even is fixable/an issue)

quick zephyr
#

?

cedar olive
#

I dont even see the issue lol

quick zephyr
#

yeah looks fine to me

fiery berry
#

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";
cedar olive
#

pnpm i

fiery berry
#

ah, thank you! that fixed it!

cedar olive
#

I see

quick zephyr
#

it's just the continuation of the white glow inside the magnifier itself, no?

#

which imo the white glow is ugly MRun

#

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

swift delta
#

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

rain shard
tropic ice
#

how's that related to experiments?

median flare
#

check settings

hushed loom
swift delta
#

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

hushed loom
#

yeah

quick zephyr
#

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. agony

balmy oar
#

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

tropic ice
#

that sounds like a really bad idea

balmy oar
#

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

tropic ice
#

still sounds like a really bad idea

green vessel
#

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

green vessel
#

vp toolbox

shrewd tundraBOT
green vessel
# shrewd tundra

why does this still say the inbox button its nowhere near the index button 😭 😭 😭

green vessel
#

nvmd i figured it out

#

i hate discord

haughty crater
#

took me an embarassingly long time to find isSelfDeaf after finding toggleSelfDeaf wires

pure temple
#

my plugin yearns for vens eyes

quick zephyr
#

are internationalization keywords constant or do they get obfuscated inconsistently too

#

"YGm6SU":"Resume"
like the key here

dull magnet
#

constant

quick zephyr
#

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")

quick zephyr
#

the message right below that self answers SKULL_SKELETON

#

ah the whole list of terms is pinned

silk sorrel
#

Oh yeah but the hashes are kinda ugly 😭 I prefer to use the real intl keys

quick zephyr
#

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

silk sorrel
quick zephyr
#
match: /\["9KoPyM"\]/,
replace: "[$self.getQuestAcceptedButtonText(arguments[0].quest, \"YGm6SU\",\"9KoPyM\")]"
#

so the second one

#

dont think that applies for this patch?

silk sorrel
#

match: "[\"#{intl::GAME_LIBRARY_UPDATES_ACTION_RESUME}\"]" could work I think
(forgot about the hash symbol lol)

quick zephyr
#

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?

silk sorrel
#

I've never tried that tbh

#

You can try

silk sorrel
quick zephyr
#

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.

vast karma
#

It matches any one of the chars inside []

quick zephyr
#

and if I add ::raw it resolved to an open parenthesis (

vast karma
#

After intl replacement and stuff

quick zephyr
#

I thought using quotes around the match made it treat it as a string hm

vast karma
#

Stop expecting the regex mangling to be clever

#

It's not

quick zephyr
#

I only unregexed it cause d' did 😭

#

yeah even match: /\[\"#{intl::QUEST_ACCEPTED::raw}\"\]/, doesnt work just has no results. amh

cedar olive
#

that's not how you use raw

#

raw is for hashed key

quick zephyr
#

how do I match the 9KoPyM format then

cedar olive
#

what's 9KoPyM

quick zephyr
#

the hash

cedar olive
#

QUEST_ACCEPTED?

quick zephyr
#

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

cedar olive
#

take off ::raw

quick zephyr
#

still no match

cedar olive
#

then your regex is just wrong

vast karma
#

The intl tag already adds the [""] I'm pretty sure

cedar olive
#

remove the "

vast karma
#

You don't need a second one

quick zephyr
#

still no. also why wouldnt I need them?

#

im trying to match them

#

match: /\["9KoPyM"\]/, works fine

vast karma
#

You are not trying to match ["["9Ko"]"] are you?

quick zephyr
#

yes

#

no?

#

I sent above

vast karma
#

So why are you adding a second [""]

quick zephyr
#

the code itself has quotes in it

vast karma
#

Yes

#

Which intl:: adds

#

And then you add a second group

quick zephyr
#

still doesnt match

#
{
    find: "platformSelectorPrimary,",
    group: true,
    replacement: [
        {
            match: /\[#{intl::QUEST_ACCEPTED}\]/,
            replace: "[$self.getQuestAcceptedButtonText(arguments[0].quest, \"YGm6SU\",\"9KoPyM\")]"
        }
    ]
},
vast karma
#

Not [["9Ko"]] either

quick zephyr
#

?

#

wtf why did removing the surrounding [] make the patch work

dull magnet
#

because the [] make no sense

#

why did you add them

quick zephyr
#

the original code has it

#

im trying to match that

quick zephyr
vast karma
#

The intl already expands to ["..."]

#

You don't need to add extra

quick zephyr
#

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

vast karma
#

It expands to that since that's what you want to match

quick zephyr
#

that is confusing

vast karma
#

I haven't used intl::raw but my guess would be that it expands to without the wrapping

quick zephyr
#

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.

dull magnet
#

I think

#

actually im not that sure

quick zephyr
#

then how is it matching ["..."] without me having to add those chars myself

dull magnet
#

anyway don't wrap in []

marble sorrel
#

It expands to a property access, either .key or ["key"] depending on ctx

vast karma
#

Depending on whether the computed hash begins with a digit

quick zephyr
#

how is that relevant 🤔

humble tulip
#

Because you can't access the property with dot notation if it starts with a number

dull magnet
quick zephyr
dull magnet
#

you have to thing["1abc"]

quick zephyr
#

oh thats why that code was like that to begin with

hoary pilot
#

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

dull magnet
#

DataStore or smth

quick zephyr
#

@hushed loom plz fix

#

huh it's in webpack but it's not letting me use it 🤔 console says undefined

hushed loom
#

you should implement it yourself blobcatcozy

quick zephyr
#

looks like that function is lazy loaded or smthn? cant use it in my plugin. keeps giving me undefined. sadge

hushed loom
#

you just have to load them

quick zephyr
#

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

hushed loom
#

extractAndLoadChunksLazy

#

webpack.ts:710

quick zephyr
#

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

quick zephyr
#

what am I supposed to be putting in extractAndLoadChunksLazy? I assumed the same thing as extractAndLoadChunksLazy but it's not finding it 🤔

cedar olive
#

read the function documentation

quick zephyr
#

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 .-.

quick zephyr
#

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. agony

#

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]));
});
bronze dove
quick zephyr
bronze dove
quick zephyr
#

waddahell

#

whats that find doing?

#

ok so same issue as before, I still need to put it in the onceready

bronze dove
#

did you lazy it

bronze dove
quick zephyr
#

ok putting it in the setting component actually worked. nice

quick zephyr
#

idk what that means

bronze dove
#

findLazy

quick zephyr
#

didnt work

bronze dove
#

how

quick zephyr
#

idk maybe I did it wrong. ill try again in a sec. in the middle of adding it to the component directly

quick zephyr
quick zephyr
bronze dove
quick zephyr
#

then why is it necessary 🤔

bronze dove
#

to match the correct one

quick zephyr
bronze dove
quick zephyr
#

where you putting it

#

oh yeah my plugin runs at StartAt.Init

bronze dove
quick zephyr
#

what?

bronze dove
#
export default definePlugin({
    audios,
});
bronze dove
quick zephyr
#

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

bronze dove
#

i see

quick zephyr
bronze dove
#

just to test it worked as a lazy

quick zephyr
#

does it need to be there for lazy to work

#

or is it not working for me due to the StartAt.Init

bronze dove
quick zephyr
#

right well ill just do a defaultSounds = defaultSounds || find... to only run it the first time the component loads

quick zephyr
bronze dove
quick zephyr
#

inside the plugin def though?

#

what about in start or just top level

bronze dove
bronze dove
quick zephyr
#

idk then I guess im cursed

bronze dove
bronze dove
quick zephyr
#

brother where else would I do it 😭

bronze dove
#

just call .keys() lol

quick zephyr
#

if it's not there it's not useful

quick zephyr
bronze dove
#
const audios = findLazy();
// component
audioPaths = audios.keys()
quick zephyr
#

the error says it's not finding the module

bronze dove
#

yeah because you call it immediatly

quick zephyr
#

also part of the find is that it has keys so how would that work

bronze dove
#

because its evaluated when you first use it

#

thats the Lazy part

quick zephyr
# bronze dove yeah because you call it immediatly

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

bronze dove
#

ugly, i think its just convention to have finds top level

quick zephyr
#

if anything this seems like a better approach? so it's only evaluated once

bronze dove
#

huh

quick zephyr
#

ok still not working agony

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

bronze dove
#

well is defaultsounds top level or not

quick zephyr
#
const findDefaultSounds = findLazy(module => module.resolve && module.id && module.keys().some(key => key.endsWith(".mp3")));
let defaultSounds: string[] = [];
quick zephyr
#

?

#

ah right

#

still doesnt explain the console error saying the module doesnt exist

bronze dove
#

stop using it top level

quick zephyr
#

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

#

thanks for the help

bronze dove
quick zephyr
#

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

bronze dove
quick zephyr
#

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

bronze dove
#

yeah

quick zephyr
#

makes sense, thanks

inner monolith
#

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:

vast karma
#

Yes, everything settings related got heavily refactored

inner monolith
#

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?

vast karma
#

Just check the pr/commit that did it and do the same changes

inner monolith
#

okay

dull magnet
#

makeRange is now in the same file as definePlugin

inner monolith
#

thank you

#

i was about to do something really bad

#

okay everything is fixed yippie

austere mauve
#

its so good

#

when is C getting ?:=

turbid glen
#

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

swift delta
#

Rip this doesnt work anymore

hushed loom
swift delta
#

Rip

hushed loom
#

idk

#

look into the code and fix it yourself

#

if it's an issue because of the setting refactor you should pr

swift delta
#

Nah, you where the one whom told me how to it

#

You should see why it no longer works

hushed loom
#

i can't tell if you're joking or not

swift delta
#

Semi

proud sonnet
#

can anyone tell me if i contribute to an official plugin does that get me the very cool badge

#

or is like hand given

quick zephyr
#

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

proud sonnet
#

i made a pull request anyways

swift delta
#

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

quick zephyr
#

lol im always running my fork so locally ive always had the badge SKULL_SKELETON so I wasnt sure what actually gave it

fleet venture
#

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.

humble tulip
fleet venture
#

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.

vast karma
#

What plugin repo?

oak sundial
#

requests probably

fleet venture
oak sundial
#

thats not a repo thats just a list of plugins in vencord

noble scarab
#

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

fleet venture
dull magnet
#

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

quick zephyr
#

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. 🤔

hushed loom
#

Words don't exist in a vacuum and you can't ignore their context.

quick zephyr
#

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

pure temple
#

#linguistics-development chat going crazy here

fleet venture
#

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. :)

hardy adder
#

wait i thought that was a copypasta

#

it should be

river talon
#

i'm definitely gonna start using this

amber mantle
#

and that's an example of crashing out over something not very deep

hushed sable
humble tulip
#

I don't really disagree with him tbh

honest stump
#

calling a website a repo broken_love

humble tulip
#

Very much a "who gives a shit" issue

hushed sable
humble tulip
#

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

dull magnet
#

I was just memeing

humble tulip
#

Yeah I don't think it's worth writing a paragraph and leaving over

swift delta
#

Just learned plugins can interact with experiments

oak sundial
#

plugins can do basically anything thats the whole point

silk pewter
#

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.

quick zephyr
#

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.

silk pewter
#

thank you, I will check it out in my free time.

hushed loom
#

there's no need to use a patch for this

quick zephyr
#

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

hushed loom
#

does it fire when you press the reconnect button

quick zephyr
#

reconnect?

hushed loom
#

does it fire when you click on the channel even if you're aready in a call

quick zephyr
#

name is misleading it's not literally highlighting it. you have to actually do the connect action

hushed loom
quick zephyr
#

I didnt see a button it just auto reconnected me but it didnt fire. smh

quick zephyr
#

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

hushed loom
#

?remind tomorrow make flux-event browser for vencord companion

fathom pivotBOT
#

Alright @hushed loom, in 1 day: make flux-event browser for vencord companion

quick zephyr
#

it took me too long to realize the checkbox in the dispatcher is to pause showing new events SKULL_SKELETON

hushed loom
hushed loom
#

@hoary pilot why husk

hoary pilot
#

felt like it ykyk

swift delta
#

Whats the word im looking for

#

The plugins setting module

#

Sounds werid

silk pewter
#

Thanks everyone I will see what works best for my use case

quick zephyr
#

@hushed loom any way to make the extension ignore a warning on a specific patch? noWarn doesnt seem to work

humble tulip
hushed loom
#

i also want to version the plugin and extension so it warns you if you're out of date

quick zephyr
#

nice. one of my patches seems to be a lazy loaded file cause it errors till I trigger the tooltip it's modifying

hushed loom
#

as well as cache the module scan so you don't have to wait 10s to find all usages

bleak falcon
#

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

hoary pilot
bleak falcon
#

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?

hushed loom
#

(you could also write your own custom build script but not worth your time)

bleak falcon
#

Thanks

humble tulip
humble tulip
#

Feel free

fathom pivotBOT
#

@hushed loom, <t:1753491578:R>: make flux-event browser for vencord companion

hoary pilot
#

virus

bleak falcon
#

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

bleak falcon
# oak sundial 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

silk sorrel
#

sounds like api spam/abuse

bleak falcon
#

It'll be 15 msgs max tho

#

And that's why i don't wanna fetch msgs in every channel

hushed loom
#

discord's modules are minified

#

also it's not exported

#

so you won't be able to find it

hushed loom
bleak falcon
quick zephyr
bleak falcon
quick zephyr
bleak falcon
quick zephyr
#

and using the search bar wouldnt?

#

all that would do is call the endpoint itself

#

you're just adding a clunky layer inbetween

bleak falcon
#

Too many nerd new words at 0.13 for me.. i’ll check tmr

#

(Can’t even type anymore)

quick zephyr
#

identical

humble tulip
quick zephyr
#

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

humble tulip
#

Which delete_message_seconds can handle as well

quick zephyr
#

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

humble tulip
#

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

quick zephyr
#

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

quick zephyr
#

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

humble tulip
#

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

quick zephyr
#

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

humble tulip
#

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

quick zephyr
#

well it wouldnt really be api spam it would at most be 2-3 search endpoint calls and a ban endpoint call but ye

humble tulip
#

Oh, yeah I meant with the original deleting messages method

quick zephyr
#

again easiest solution is just automodding @ everyone and invites format and then whitelisting your staff. 99.99999% of scam messages include it

humble tulip
quick zephyr
#

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

humble tulip
#

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

quick zephyr
#

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

humble tulip
#

Yeah

lean acorn
#

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?

swift delta
#

Something like that

#

Your IDE will assist you

tidal zenith
#

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

silk sorrel
#

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

hushed loom
leaden fable
#

I'm using
const QuestsStore = findByProps("getQuest");
but sometimes the store has outdated data, is there anyway to force update it?

quick zephyr
#

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

leaden fable
quick zephyr
#

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

quick zephyr
leaden fable
quick zephyr
#

also this only became an issue yesterday when they added a captcha to enrolling in quests im pretty sure

#

🤔

leaden fable
leaden fable
quick zephyr
#

captcha for completing not enrolling

#

ive always insta enrolled anyways

#

unless I set off discord's sus detetor

leaden fable
split aspen
river talon
rapid crag
#

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

amber mantle
#

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

quick zephyr
#

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

amber mantle
#

unfortunately lol
feels awkward having that happen everytime

mortal marsh
#

is there already a package that is modifiying the toolbar like the electron app.setUserTasks() task?

quick zephyr
#

does vesktop have the DiscordNative variable and can it use its functions?

dull magnet
#

no

#

there's VesktopNative but it's very different

quick zephyr
#

oof

quaint cipher
quick zephyr
#

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.

hushed loom
quick zephyr
#

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 SKULL_SKELETON

hushed loom
#

add .desktop to the plugin folder

#

that's probably what you want

quick zephyr
#

so like show an error if you try enabling x while y is already enabled

hushed loom
#

no

#

fix the compatability

rocky falcon
#

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

hushed loom
#

see how discord does it

#

find where the ping is renedere

#

see what discord checks to know if it should render it

rocky falcon
#

okay 😭➕

cedar olive
#

It's probably something to do with ReadStateStore

#

if I were to guess

rocky falcon
#

thank you 10

quick zephyr
#

is it normal for updating a plugin native file to require reinjecting to work or am I just unlucky

dull magnet
#

you need to fully restart discord

quick zephyr
#

ah yeah thats probably it then. dont think i ever tried anything other than reload

humble tulip
#

smh discord broke my customtimestamps plugin

#

Clearly a personal attack

humble tulip
#

nvm I was the problem

#

hardcoded var

sullen widget
#

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;
    }
});
sullen widget
#

textreplace doesn't do it better 💔

humble tulip
#

What do you need to handle post edit for?

dry reef
#
  <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);
humble tulip
#

The props in the inner function has an onClick that you need to pass into the icon

dry reef
#

No errors in console either

humble tulip
#

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?

dry reef
dry reef
#

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

turbid spruce
#

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

humble tulip
#

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

turbid spruce
hushed loom
#

It’s a pair of braces as an icon

turbid spruce
humble tulip
#

In regular dev tools sources tab

hushed loom
# turbid spruce I am so lost?

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

turbid spruce
#

Ah thats lovely

humble tulip
#

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

turbid spruce
#

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?

hushed loom
#

I can give you an up to date example

turbid spruce
#

put text between here

hushed loom
#

@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]
                    })
                })
            }
            ;
turbid spruce
#

Still kinda lost honestly..

cedar olive
#

what do you not understand

turbid spruce
cedar olive
#

do you know how jsx transformed code looks like

cedar olive
turbid spruce
cedar olive
#

do you understand what children is?

turbid spruce
#

I do

cedar olive
#

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

turbid spruce
#

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 🙏

cedar olive
#

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

turbid spruce
#

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

cedar olive
#

yeah, it might not be that

turbid spruce
#

so i should be searching for something with "description" or "channel name" ish?

humble tulip
#

Classnames can be a good way to find things as well as long as they're not super generic

cedar olive
#

if you search you will get way too many results

#

as they are very generic

turbid spruce
#

Yeah i see 600+ results for searching

humble tulip
#

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

turbid spruce
#

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?

humble tulip
#

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

turbid spruce
#

Ah okay i think i get it now

#

Thank you for being patient with me 🙏

turbid spruce
hushed loom
#

yes, but there might be a better find than that

#

what is the module id

turbid spruce
#

372564 this i think?

quick zephyr
#

doesnt look specific enough

turbid spruce
#

ah

hushed loom
#

try .matches("a")

#

"highlight"=== also works and is better

turbid spruce
# hushed loom

is this some sort of patch helper? where can i find it

hushed loom
#

build with --dev

#

it's in settings

turbid spruce
#

Ah yes okay

hushed loom
#

i just use it for testing finds, i do patches in other ways

turbid spruce
#

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

quick zephyr
#

null will never equal This is a test

hushed loom
#

null != "this is a test"

quick zephyr
# turbid spruce If i wanted to replace the topic always to some specific text would this be suff...
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

turbid spruce
quick zephyr
#

did you rebuild and then reload discord

turbid spruce
#

ive had watch on

#

and ive reloaded discord every change

#

plugin is enabled too

quick zephyr
#

is the plugin enabled

#

new plugins start disabled

turbid spruce
#

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 🙏

acoustic nest
#

@quick zephyr BetterTTV_NOOO

quick zephyr
#

suh

acoustic nest
quick zephyr
#

use equicord if you want it Shrug was told it'll never be merged here so

acoustic nest
#

typical lol

quick zephyr
#

surely my silenttyping pr that V told me to PR 9 months ago will be merged soon though right

quick zephyr
#

yeah thats fine. glad to have an answer so I dont indefinitely have to keep updating it in 2 places

dull magnet
#

your ShowMeYourName change is also excessively large

quick zephyr
#

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 Clueless

hushed loom
#

the imagezoom fix hasn't been merged yet?

quick zephyr
#

nop

dull magnet
#

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
quick zephyr
#

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

dull magnet
#

besides includedNames, removeDuplicates and discriminators, every single option you added to SMYN shouldn't exist

quick zephyr
#

huge disagree SKULL_SKELETON

dull magnet
#

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

quick zephyr
#

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.

silk sorrel
quick zephyr
#

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

silk sorrel
#

tbf my plugin also suffers from a bajillion options

dull magnet
#

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

hushed loom
#

INSANE

humble tulip
#

option fiend

quick zephyr
austere mauve
quick zephyr
#

damn why tf am I catching so many strays.

hushed loom
humble tulip
# hushed loom

You could probably simplify this by just making left, middle and right do one of each of these and not have it cutomisable

dull magnet
#

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

quick zephyr
austere mauve
#

true

dull magnet
#

we need something like this for vencord chatbar icons

humble tulip
#

Also you should #1256395889354997771 these if they won't get merged, those people are asking for more complexity anyway

quick zephyr
#

this one was in there... v deleted it then told me to pr

dull magnet
quick zephyr
#

silenttyping

dull magnet
#

because we don't allow modifications of official plugins in that channel

#

the channel is meant for original plugins

humble tulip
#

Oh right

dull magnet
quick zephyr
humble tulip
#

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

quick zephyr
#

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

humble tulip
#

DataStore I think

#

I don't know if it persists a logout

hushed loom
quick zephyr
#

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

dull magnet
quick zephyr
#

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

#

🤔

dull magnet
#
const settings = defineSettings().withPrivateSettings<{ perAccountSettings?: Record<string, whatever> }>();
#

you can extend the settings type like that

quick zephyr
#

oh nice

dull magnet
#

then just write to settings.store.perAccountSettings[userId] (and it will be undefined so you need to set it to an empty object first)

quick zephyr
#

like during start()? just initialize it to empty

dull magnet
#

whenever you read or write to it, just ensure it

#

make a function getSettings() or smth

quick zephyr
#

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.

dull magnet
#
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

quick zephyr
#
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 ?? {};
}
#

🤔

dull magnet
#

you can use ??=

quick zephyr
#
function getIgnoredQuests() {
  settings.store.ignoredQuests ??= {};
  return settings.store.ignoredQuests;
}
dull magnet
#

yeah

quick zephyr
#

nice thanks

cedar olive
#

just use a normal set with type set as CUSTOM

quick zephyr
#

and I can just define a default for that like normal?

#

default: {}

cedar olive
#

yeah

quick zephyr
#

nice

cedar olive
#

default: {} as Record<string, { questId: string; questName: string }>

#

for type safety

dull magnet
#

yeah that works

quick zephyr
#

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

cedar olive
#

use primitive types only

quick zephyr
#

the whole point here was to store objects .-.

#

I could have just stored it as a comma separated list from the start

cedar olive
#

object is primitive

quick zephyr
#

so just set is the issue then

cedar olive
#

yes

quick zephyr
#

damn can I not even cast it to a set after reading it

cedar olive
#

what for

quick zephyr
#

idk just dupe id prevention

#

read, cast set, do stuff, cast list, write

cedar olive
#

object cant have duped keys

quick zephyr
#

im using an array. is that also not allowd SKULL_SKELETON

cedar olive
#

why not an object?

quick zephyr
#

I dont need to map anything

#

im literally just storing ids

dull magnet
#

you can put Maps, Sets etc in there

#

settings is json only

quick zephyr
#

json has lists doesnt it

dull magnet
#

yes array

#

but it won't be reactive

cedar olive
#

then add an onChange to ur setting, make a set from the list, turn into array again and update the setting

quick zephyr
#

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

cedar olive
#

possibly

quick zephyr
#

wheres that at again

cedar olive
#

appdata

quick zephyr
#

yeah im in roaming

cedar olive
#

vencord folder

quick zephyr
#

ok yeah was stuck as {}

cedar olive
#

there is no need for Array.from

quick zephyr
#

yeah removed

#

damn yeah not reactive

#

agony

#

ill just do a comma separated string I guess

cedar olive
#

you are doing it wrong

#

dont overcomplicate

quick zephyr
#

??

#

it was already a multiline string of quest names I shoulda just kept it a string when I decided to switch to ids

dull magnet
#

lmao