#archive-library-discussion
25085 messages · Page 10 of 26
thats to change caching not to change when events are emitted 
Oh
when capping collection, you do not recieve any events any more...
obviously 
and the Dm Create Problem (Discord gives you the rawddata)
For advanced Devs, there should be a option for getting the Data, which Discord gives....
Idk if you understand what I mean
I thought you meant emitting an event when a structure is uncached
aka removed from the collection
yeah, DIscord.js doesnt
just use the raw event then lol
since you cant use any of our structures anyway
idgi
Sure, I'll do! 👍
Mmh when is Member#joinedAt null? Is it only when is partial?
Presences don't give joinedAt, but they give all others (even roles)
Also lurkers
👍
@wild flax srry for mention,
i found a typing error
fix it
#6067 in discordjs/discord.js by DTrombett opened 16 seconds ago (review required)
types(ApplicationCommand): fix option name
📥 npm i DTrombett/discord.js#permissionId-patch
@real jetty ^^
You have to fix the tests in the index.ts too
silly you
Done lol
Totally forgot about tests
Sorry
@zenith oracle thx for submit a pull for this, but don't pull requests without at least checking that everything is OK
So sorry about it. I always run the npm test command before making a pr but this time I didn't. I updated tests and everything should be ok now. However lmk if something should be changed via comment or review 👍
Isn't this exactly what partials are
what else is needed to do for v13 release? looking to help out
Discord to release features
Otherwise feel free to check the Github Issues and fix anything you think you can
ah i see that, user banners & per guild avatars
Mainly threads and stickers actually, banners and avatars are not important features we would delay for
makes sense, i was just looking at the pull requests
why not make each typings "region" a different file?
its kinda messy rn, as one huge file
can't
add timeout back to message.delete() function ?
that would be nice
[3:35 PM] KinectTheDots: It didn’t allow for proper error handling
welp
ggs
You can just use .setTimeout()?
Takes little bit extra code and allows for both a timeout and proper error handling
Even with Partials, it does not fire the event(s)...
#6073 in discordjs/discord.js by shinotheshino opened 14 seconds ago (review required)
feat(Client): add conditional ready typings
📥 npm i shinotheshino/discord.js#master
That was discussed earlier, I think it was basically "why even use the library at that point" lol.
You can also probably listen to websocket events 
true, but there should be a note on the Docs, before People wonder
why the Event does not get fired...
@oak quail PR technically done?
yeah
except for the docs link thing sugden? asked for
yeah
Ill wait anyway until discord merges the PR
in case they have a fever dream over their break
and want to break more
yeah I'll try to get it merged next week
btw does this need to be allowed for my PR?
Yes, first time contribs need to be approved for a workflow run first
Hey @copper laurel, in regards to the conditional typing pr, what do you think about making Client#token readonly? That would prevent arbitrary API calls without logging in first, in a way that is rather unintrusive.
I'm not really in favour of restricting the way the library can be used
But Im also not responsible for the direction of the lib - my main concern is simply that the types weren't correct. Not all of the time.
I really do like Client#isReady() and TypeScript users should be expecting to typeguard their stuff. I have no sympathy for TS users complaining about having to typeguard - don't use TS then.
Alright. It seems to me that the refactor of Base is outside of the scope of the PR then. I'll revert the commits that changed Base.
Dont do it on my say so
Nah, it seems to be a much bigger issue than I initially assumed it to be
Hope i didn't come across too negative, I like the concept you're going for
Im just not sure asserting it as true is the right approach, its not truly conditional if there isnt a condition being evaluated
Oh, no, don't worry, you weren't :)
Since Events became a const enum we can't use index access with them.... I mean, doing Constants.Events[eventName], where eventName is a function parameter typed as keyof Events, will now throw the error A const enum member can only be accessed using a string literal. Is this somehow intentional? Why?
I use an event handler and obviously this break a lot of things for me...
Why do you need to access it like that
Oh, I see
Hmmm
That might have been an oversight if anything
We didn’t intend to have such a breaking change here
Mmh yeah
hmm should the library reject this
Why, the API doesnt
Course it doesn't, because its a valid route. Now if we should handle it...idk.
Definitiely unexpected
That's user error if anything, so no
Just like passing a non-snowflake to members.fetch can result on a WS restart and a timeout, sanitise your input and you're gold
Typings already use ${bigint} to guard against that right?
Mhmm
When you provide invalid data to the api and get a DiscordAPIError the stack trace is lost, which can make debugging difficult. Could the APIRequest class capture the stack trace when it is created then emit it on error?
I tried to do this myself but I ended up getting a stack trace to TextBasedChannel.js (for a channel.send), the goal would be to get a stack trace back to the users code where they did the actual channel.send call
It should work fine on Node v15 or 16, forget which, with async stack traces
Im on 15.6.0, didn't realize v16 added that ill have to check it out
Hmm they should already be in place
Yeah it was actually 14
I don't think its just because its async, its because it gets pushed into a que and then the event is fired later
Right, gotcha
Im currently trying to figure out how to get a stack trace from here, to where the error is created... but I am very lost 😄
Do you actually await all your send calls
Or do you sometimes just return or smth
I normally await, and the line that is throwing the error is await'ed
Actually, the stack trace may be broken due to how the route builder works
Or... its just javascript being funny
Put a trace right here and yet the callstack is broken
Javascript must be high or something, if I write the code like this, then my stack trace is logged correctly
However like this it fails
or... maybe not?
I have no idea, ill take another look at it tomarrow
bit late but I'm not sure if I agree with this - input should indeed be sanitized on the user's end but the return value of such a function call does not match documentation nor typings at all - it is practically UB - which shouldn't happen.
I don't think the library should handle the snowflake passed in and try to validate it, but it should at the very least validate that the data returned from the API is valid to construct a User instance from
I agree with this ^^
I think it can just check if id is typeof string, and, if isn't, throw an error
Can prevent unexpected behaviour like that where the returned structure doesn't match any documented one
uh, no, that's not what this is about.
the issue is particular strings that make it hit a different route
i.e. somestring causes the library to do /users/somestring which is an actual 404, the library does throw since it could not find the user
however some/string makes it hit an entirely different route than intended (/users/some/string)
So what should the library do to prevent this?
if the route happens to exist (like @me/guilds) then U.B happens
Ohh I see what you mean
there's 2 approaches:
- make sure the string cannot include
/(or other HTTP unfriendly characters) <- not a fan of this - check the API response so one of those weird
Userinstances cannot be constructed - and instead an error is thrown
the 2nd approach makes much more sense as it doesn't do input validation for the user
it simply prevents completely unpredictable behavior
a User instance should simply never have an undefined id, that's just horrible on all fronts
Mmh do you mean this should be checked by the User constructor?
sure, it could be done in each structures' constructors, I suppose
it could also be done before one is called, it depends how wide spread such checks should become (if at all)
That's really interesting, but how should djs check if the data provided in the constructor is ok?
it doesnt
lol
I meant, how it should 😂
just how you typically do runtime checks in js. ¯\_(ツ)_/¯
typeof - in keyword etc
similar to how client options and everything else is validated
Well that's what djs does (using if "something" in data), but it also handle partial Structures so it cannot throw an error if, for example, a property is missing...
Those checks are there for different reasons and are there for mutable properties and happen in _patch typically.
its like
for users, yeah I get it
a partial user doesnt have much other than its ID as raw data
but this is mostly about data coming from GET api calls which is typically complete
hence why I was hasitant to say "do it in the constructor"
its a very tricky problem to approach, yes
Lol
I don’t think we need to do anything
We document very clearly what you can do
If you go strictly against that, dont expect anything fancy to happen
I dunno, I don't think an undefined id on a "valid" User instance is desireable
Introducing random runtime checks on every construct now seems ridiculous
The error is already not passing an id
Not the construct you get back
Feel free to PR a good solution to this, because I can’t think of one that wouldn’t either impact perf or convolute the code
👍
I just saw that interaction includes an "inGuild" function. I think that this could be great if we can add a similar function on Message and maybe on Channel
what would the inGuild method do?
i pressume itd check whether the message was sent in a guild or a channel belongs to a guild
Don't think that would be necessary as the GuildChannel class does exist
but I may be wrong
should be able to guard that with a return on !message.guild
Yuh
Well it's the same as !interaction.guild_id. Not sure why this was added effectively 
Does that actually type guard
The inGuild? No, I don't think so
I mean the others
Others what?
Jeez, the other structures, message.guild, channel.guild
Wait what ? I thought it worked like a typeguard
What should it guard? It can only ensure that guild_id is a snowflake, and you can do this by just manually checking it...
Well, same... I don't understand what should they guard from... What I was saying is that, yeah, if there is a isGuild in an interaction it can also be in message, channel etc... But I don't see what's their for... Neither in interaction nor in a Channel or Guild
I thought it worked similar to "isText()" to indicate that "guild" is not null and that "channel" is of type GuildChannel
the question wasn't "what should they guard from" but "do they guard"
Well yes but a GuildChannel can be instanceof TextChannel, DMChannel, etc... Where a lot of properties and methods change. An interaction in a guild is pretty much the same as an interaction outside the guild about properties and methods
does doing !whatever.guild have the same effect as using !inGuild()
No, since the first at least ensure that the .guild exists, while the second just returns a Boolean
Do you actually look at the typings
Hence why it’s called a type guard
Returning a bool is how type guards work
Well what I mean is that returning boolean is different from returning this is TextChannel
It's a typeguard.
So this returns to my question 😄
The inGuild, what should typeguard?
that the guild exists...
and it's notis but in
probably also why it cannot be this is whatever
If you don't use TypesScript and don't understand typeguards then why drag this out further
Okok I think I wasn't really clear but I think I'll look into it better tomorrow 😂
However what I meant is that if I use if (interaction.inGuild()), for example, am I sure that .guild or .guild_id (or was it guildId, I don't remember) are defined? That's what I mean for typeguard lol, maybe I did not say it clear enough, sry
did you look at what it actually checks?
There's no GuildInteraction you could typeguard with isGuild(): this is GuildInteraction guard that would check if there are guild and member properties.
There's just Interaction, so you have nothing to is against.
this typeguard doesn't act as a type narrower
but more of a check
That's what I meant here...
I think I understood what he meant, he expected to have a this is ...... ||too late XD||
Nvm we come back to my question, why not add this principle to the Message object? Both functions would have the same principle and I think it would homogenize the operation by having this same function in the Message class.
And that's the main question
Honestly, I thought, like D Trombett, that with this function there was a new class like GuildInteraction that was created and that overloaded some properties like guild by indicating that it was not nullable anymore
If there is a typeguard (which doesn't change the type with an is) that just check for the existence of guild_id, is it there for a specific reason? (And it's not in other structures like message or channel for another?)
why would there be a separate class that has exactly same properties
I don't pretend to create a new class or interface since, however, actually a guild can also not be cached if the bot isn't in it, so types are pretty much the same
Tbh if there's no GuildInteraction interface that is used for the inGuild() function, I don't see why it was added in the first place
The sole reason why I added isText() was to have a type guard
Indeed I did not think of that
Considering how it works, I don't find it useful since checking the presence of guildId is the same
Yeah!!! That's what I said 😂
Why is it there without a type check... But actually not in any other place (in that case we can say it's for consistency...).
Actually replying to Vaporox but forgot to add the reply woops
@copper laurel maybe knows 🤔
I forget who added it but yeah, it just typeguards a couple properties to rule out it being in a DM. I think I proposed "inGuild" rather than "isGuild"
is it actually supposed to work in this context?
I would expect it to, but since it's not a type guard it doesn't
I thought it worked like a type guard to remove the nullable status but it doesn't. So I don't see the point of this function nor the point of adding it as I proposed to the Message and Channel class...
So I have a new question, why not make this function a real typeguard to update interaction typing?
as it was already said, it would require to have a separate interface just to have any way to narrow the type. except such GuildInteraction would be identical in properties to Interaction merely changing one or two property types. Having it would also mean that you would have to have every other interaction class extending Interaction in version that extends GuildInteraction
it works like that on channel types because TextChannel extends GuildChannel, adding things related to text channels that are not found on other types of GuildChannels. Similarly, GuildChannel extends Channel adding things that are not on the base Channel. Or even ButtonInteraction adding button related stuff not valid to be placed on the base Interaction class.
It's just a fancier way to identify guild interactions than checking guildId. Nothing more really..
If it seems like a typeguard because of its name, perhaps we could rename it to fromGuild or something
If it doesn't typeguard the guild properties it's useless
Is there a reason why Options.cacheWithLimits({}) takes Record<string, number> instead of being keyed with manager names?
Right now i can search up for the PR that adds it since it shows an example, or open v13 changes showing exactly same examples, but to my knowledge there isn't a list of managers to pick from
maybe apart from opening the source code since managers are in their own directory
They're just the names of all class that extends another class called like CachedManager (totally unsure about this name). They're really a lot but yeah, a type that includes all of these name would be great
yeah, but if i am a new user, i have to click (or at least scroll) through every single class on the sidebar to see all managers. "and extend? what is that supposed to mean, how is extending relevant"

I don't see why we couldn't make a typedef/constants like ClientEvents
I personally, know where to look for managers, or how to tell which managers can be manipulated with the option.
But i was talking from perspective of a new user somewhere down the line. They might not have any idea what they can put there at all, and when they look at the guide (assuming it will land somewhere else than just v12 -> v13 page which might not even apply to them if they start with v13 directly) they might see only those 2 managers shown on the examples. then they can either assume only those 2 can have options, or that they can pass more stuff.
But what other stuff. Every manager? then they need to scroll through every single class on the docs sidebar, since there is no single list of all managers, like for a resolvable or flag list. And while doing so, they will be completely clueless that not every manager extends CachedManager or even have 0 clue what that means
having a typedef for it not only makes it appear in the docs on a single page, but also can be made to appear in your IDE as you're typing the option
I'm sorry I'm pushing a little bit for the inGuild function but I'm convinced that we can improve the typing by making a real typeGuard
I searched for several hours to find a way to make a real typeGuard that can be set up very easily. Be careful it's typescript but I think that transcribing it into javascript should be no problem:
type PropertiesNotNull<T, TProp> = { [P in keyof T]: P extends TProp ? NonNullable<T[P]> : T[P] };
function propertiesAreDefined<T, TKey extends keyof T>(object: T, ...properties: TKey[]): object is PropertiesNotNull<T, TKey> {
return properties.every( property => object[property] != null);
}
function wasSentInGuild(interaction: Interaction): interaction is PropertiesNotNull<Interaction, 'guild' | 'guildId'> {
return propertiesAreDefined(interaction, 'guild', 'guildId');
}```
||Do not pay attention to the names of the functions they were written in the night XD||
After that it can be improved because if we enter in the "if" part (and not the "else" part) the variable interaction is of type "never"
Shouldn't this just assert whether or not Interaction#guildId is not null?
Let's say we do not have the GUILDS intent for some reasons, Interaction#guild will be null no matter if it came from a guild or not.
that is what the check does right now, along also checking for member
I shouldn't work so late at night because beyond that interactions can be sent even if the bot is not in the guild...
Maybe this system can be improved but actually right now it would lead to an error...
No runtime checks apart from inGuild should be needed which is already in place
all that is missing are the proper types for it in the index.d.ts
https://github.com/discordjs/discord.js/blob/f72ce7c136cf2dfe31a67b190c00e30ba7d70bfa/src/client/actions/TypingStart.js#L12 isn't TextBasedChannelTypes an array, shouldn't this be .includes, not in?
There’s a pr for that
Ah sweet
Hm, I'm getting a load of PermissionOverwrites#type being an integer on master 
Huh
Should be a string, role or member, but getting a lot of errors thrown that its 0, which is what the API returns directly. Will dig through d.js source in a bit to find where its not being converted
Uh I can’t link because of mobile
But maybe the permission manager PR is a hint
Since that changed a bit in that regard
Could double check the files it modified
Permission Overwrite Manager sorry
that would be my suspect as well given that was in my version bump where it broke
I think the same... It's a really good idea to add these cache options, but effectively not really everyone knows all structures (eligibles for this cache limit) or know where to find.
The other day I saw that a property in my clientOptipns (regarding the max count of messages in the cache) was removed and I found this new property called "makeCache", which accepts a function. Sincerely, I totally didn't find anything about what's this and the function it accepts what should do so I tried to check the docs, but they were really generic: Function to create a cache. (-1 or Infinity for unlimited - don't do this without message sweeping, otherwise memory usage will climb indefinitely), which includes a part from the old description (which is not really useful now) and nothing that refers to Options... Nothing neither in the reference of CacheFactory, that actually only confused me more... I found Options.cacheWithLimits for a pure case, because a user asked support about it in #djs-help-v14... So I tried to use it, but saw just a record of string and numbers, so I was totally confused. After checking the pr where it was added I found more informations about it and I went to check all of these managers.
Now, I don't think that everyone can do this, and types and docs are there for a reason, so the first thing I think can be done is to improve the description of this option (replacing the old text with a word about Options) and then adding to types all of the structures that can be used in the Options.cacheWithLimits. Probably, it's an hard work 😂, but is necessary for all people who have no idea about these options and their potential, so, I totally agree with you and I can help with docs or types for this but lemme know What y'all think about this...
I wrote too much, don't read everything because it's useless probably 😂
That's some niche feature anyway, I don't think begineers would use it (I hope so at least, scared about what I could see in #djs-help-v14 )
Also except for Message and Presence managers I'm not sure where is the practical use case of restricting guild manager cache (for example)
Even if some typedef about accepted inputs should definitely be a thing, of course
I'm limiting the user and members managers iirc
To avoid having a lot of users in the cache
And Message obviously
Was mentioning this in #archive-offtopic but thought I would surface here -- strings seem to be the worst offender for mem usage (a shock to no one), but it seems d.js currently prefers to store a lot of stuff as strings internally (most enums, etc.). I'm wondering what folks would think of switching things like this to store as numbers, and have getters to retain returning strings when accessed (or just moving to numbers completely and having the user resolve the enum)?
it's just so much more annoying to use for people ugh
Like I fully agree and we should do it
If you were to add getters the end user wouldn't see much impact, but maintainer/contributor experience would suck a bit more
but people are gonna give us an earful again for having to do ChannelTypes.GUILD_TEXT and why they cant do "GUILD_TEXT"
Could just resolve that
But I'm also a fan of just moving folks to using constants like that anyway, hard-coded strings are bad
Very hard with such a close-minded userbase that mostly learned JS from bad express tutorials
Sure you can just force it onto people, but still
It could be for now that its done in a completely non-breaking way, so there are getters so accessing still returns a string, and resolvers for anywhere a user-provided value is ingested
And made more breaking with numbers-only later on
getters use memory too tho 
"Later on" will probably just be the rewrite anyway
but yeah, it will be hella annoying for people to do channel.type === ChannelTypes.GUILD_TEXT
most of our userbase is js beginners 
Honestly, you are right though
I should just not care
tbf v13 is so incredibly breaking that a change like this would fit in
But I do to a certain degree, I don't like death threats from 14yo
i struggle to keep up with master when I update once a week 
vlad wanted to do something like that
we still have approx a month or so until threads hit general availability
which is when v13 is supposed to hit anyway
I'd like to think this'd have a pretty significant impact on d.js mem usage so would be worth it
But I don't actually have anything concrete to confirm that
Its pretty clear that 700k "GUILD_TEXT" or "GUILD_VOICE" uses a lot more than 1 or 2
But not sure if it makes a huge difference
id rather be interested in extending cacheable props
so you can disable certain props on structs and theyd just return null or smth
'GUILD_TEXT'.length * 2 * 700_000 = 14_000_000, channel type is currently 14mb
A number in JS is 8 bytes iirc, so 8 * 700_000 = 5_600_000 5.6mb
Seems like a decent saving on a single prop, so across all enums would be p good imo
but what if you could just do
makeCache() {
return {
ChannelCache: {
// amount: 200 -- Defaults to Infinity
topic: false,
}
}
}
Yeah that'd be nice
You can just update the prototypes for channel to add a null'ed getter & setter for topic though, so thats less of an issue
Not as userfriendly honestly
but I guess yeah
Yeah, for a ux perspective, being able to just disable the prop would be nice
Doesn't fix things like type though, cause a lot of folks need those, but they're still strings 
Gonna test this on Res for a bit and see if I notice any diff in mem usage:
// Store channel type as an integer
Reflect.defineProperty(Channel.prototype, 'type', {
get: function () { return Constants.ChannelTypes[this._type]; },
set: function (value) { this._type = Constants.ChannelTypes[value]; },
});
// Store overwrites type as an integer
Reflect.defineProperty(PermissionOverwrites.prototype, 'type', {
get: function () { return Constants.OverwriteTypes[this._type]; },
set: function (value) { this._type = Constants.OverwriteTypes[value]; },
});

@vagrant holly i brought that up recently, Kyra said changing all types to numbers didn't impact memory usage
Dropped about 100mb for me
Maybe something about what Discord was sending me happened to change as I rolled out the above, but it would appear to me that it does help
Prove it
0 effect
It takes 10 bytes for text, 8 for length
Every reference to it is 8 bytes
Numbers inside objects are boxed, to prevent memory issues as well as preventing memory expansions in the middle of it, which means they're always doubles, and they always take 8 bytes
So the overhead of having this as a string, is a mere 18 bytes
This is assuming that v8 decides to optimise the strings?
Aren't strings in most cases stored directly, not optimised in a map with pointers?
Oh, hm, the enum they come from is a constant object, so maybe v8 does optimise them, assuming type is always set from those objects
what about making snowflakes a bunch of opaque types
so that the type of a user ID is distinct from the type of a channel ID
on what basis, because they are the same thing
@vagrant holly there's an internal hashmap for strings
Even if you don't use the constant, it'll be assigned to the same one, although there's also the one in the .data field from the compiled assembly, which ofc is a constant value
This is also the same behaviour in C# and several other languages, specially managed ones that have immutable strings
Memory usage goes to space
?
Simply, no
If this were C++ or Rust, sure, because RTTI is optional
But JS is RTTI heavy, and as such, the overhead of boxing primitives is enormous
uh
what are you talking about
opaque types
build time
typescript
RTTI = RunTime Type Information
aka information that tells what each thing is instance of
Each value in JS has a header which tells the type followed by the data of the values
Primitives are specially optimised, because they can skip some headers, but they don't work when boxed due to alignment issues that can arise from mutating them with other types
are you reading my messages at all
Yes
And it makes no sense
okay 👍 👍
So I'm trying to make sense of it
You can't just make 2 types resolving to the same one and expect them to be opaque
Simply because type is called type alias in TS
The only way to make them properly opaque is by adding type-only fields that don't exist, I think those are called brand fields
But it can be troublesome, you see
In all servers, the everyone role id is the same as the guild's
BUILD TIME
ONLY TYPESCRIPT
I DO NOT CARE ABOUT JS USERS
As well as in old ones, their created-by-default channel also has them
Dude are you even reading me?
What do you think "type-only fields" mean?
that's the only way to do opaque types
that is how everyone does it
it is a feature of the language
Yes
And I'm telling you
type Snowflake = `${bigint}`;
type Opaque<T, N extends string> = T & { __opaque: N };
type UserSnowflake = Opaque<Snowflake, "UserSnowflake">;
type ChannelSnowflake = Opaque<Snowflake, "ChannelSnowflake">;
const user = "123" as UserSnowflake;
const channel = "123" as ChannelSnowflake;
console.log(user === user);
console.log(channel === channel);
// @ts-expect-error
console.log(user === channel);
behold, 0 cost typesafety
It's baseless
IDs are shared in many places
A message ID can be a thread's ID
A role ID can be a channel's ID as well as the guild's ID
You'd have to typecast it all the time and it's annoying
I already know what branded types are, I use them in my bot to define i18next types
tell me more about your Discordjs Bot
Outside this channel, probably in DMs
I see that discord.js-modules is using tabs. Is there a reason for this switch? Can I read about discussion anywhere?
Tabs are simply superior
If you want a "real" reason read this.
But either way, point still stands. Superior.
I see, I can't imagine this was the reason that discord.js switched though. Is it just preference among the core developers and this is something that they've wanted to do but can't because it would ruin the history?
I mean youre talking to a maintainer of discord.js who just gave you the reason
and you think its not the reason because...?
In the topic of memory usage (that IPv4 has brought over), should we look into removing TextBasedChannel#_typings? Its removal has a pretty large memory impact
Alternatively, we can do a global WeakMap<ChannelId, Set<UserId>>, which has near to no memory effect for those not using typing intents, if we need it
Or a Map and remove it in CHANNEL_DELETE/GUILD_DELETE just like we do with the global channel cache
The structure would ideally self-sweep if an entry removal ends up to 0 user IDs, so we don't memory leak
@outer raven no we don't know, thats exactly why
I don't see any reason why they would do that though? The row doesn't even have any special properties it's just a place to put components in
It's Discord, they can implement new features without notice, say, padded columns within a row with a different type
And we'd have to support 2D arrays as well as different row types, but the former always resolving to the first row type
Well I assume the nested arrays would always only contain components and not those columns, and Action Rows would always be the default when a nested array is passed
Rows having a type identifier suggests they're going to add new ones in the future
They have a type identifier just like buttons and select menus, but, unlike channel types, there is no gap between the action row number type and the others
having a default to only one type of row is exactly the sort of misleading interface we wanted to remove
like the send overloads
But why remove things that make people's lives easier? I have a system rn that I genuinely don't think I can use action rows for and the nested arrays were the perfect solution
For all of the reasons listed above
Why are people like this
Because Discord might introduce new types of rows
Because we might have to support 2D arrays and cant if theyre being interpreted as rows
Because we don't want two different interfaces for different types of rows
And yes, you can easily have your system work with non-nested arrays
Because you can literally just replicate what we do when you pass a nested array
a raw object is barely more code
I'm just saying that at the moment there are no signs that Discord would ever do that and if they do I can't see that happening in the near future
If they do add those columns or whatever it might be then this can be removed in the PR that adds support for them, but why now?
because we cant make breaking changes later
dont want to have to do a v14 just for one row type
can't you just do a 13.x?
not to remove something
do you understand semver?
Removing features = semver major = v13 -> v14
What is semver?
Is v14 gonna be the TS rewrite or is it still gonna be on js

Alright then
- components: [[button]]
+ components: [{ type: 1, components: [button] }]`
I have an array of arrays of buttons though, and I can't possibly predict how many buttons I have in total
Can you at least provide an alternative in the PR description?
Taking a quick glance at source, this seems to power two things right? It backs all the methods for seeing who is typing in channels, but also backs being able to set the client to be typing somewhere?
if you want me to copy and paste that, sure
but that only works for 1 button, not for multiple buttons across multiple rows
why dont you show what youre doing now, in buttons channel, and I'll give alternative
sure
The alternative is literally just doing what the library does now internally with a nested array really, which is mapping it to components
What was the reason for not removing interaction event instead of deprecating it. We had no reason to keep support for something that wasn't even released yet 🤔
pretty sure it wasn't just a niche no one knew about. we literally made channels for v13 support
Yes a lot of people would have been affected from it but interaction were added on master and were changed on master, so there is like no need to keep the bloat around when we already are near a major release and have already broken some really big parts of the lib
and the "bloat" is singular boolean variable?
it's not how small or big it is, if we can break stuff that has been here for a long time then why keep support for something that had been just added to the lib.
just because of the scale we expect its been used at
and there isnt much harm in that existing
I understand where you're coming from but this all seems like a problem we are pushing over to the future. We have a chance to rename both the events as people will likely rewrite their bot for v13.
it isnt a problem though
it wasnt a problem for 12 versions and it isnt now
True, @dev releases have around 5000 downloads a week
It will be a problem if we decide to remove the deprecated events somewhere in future. I'm just saying that we have a chance of making breaking changes then why not break this too. It's not like users can get anymore angry, given that we already broke a lot
we'd remove them in v14
thats what deprecations are
If that's what the majority decision is then okay 👍
It will be a problem if we decide to remove the deprecated events somewhere in future.
I don't understand lol
How
Right now you get a warning if you listen to the deprecated events, just like you get when you used a deprecated function in v11 that was removed or replaced in v12. In the future, they'll probably (I'd assume so) be removed, so you have a gap of time for people to update their code
I was curious about the decision so I asked, I'm okay with the change now because it seems like everyone supports it and it wasn't something that just made it's way into the master (for eg inGuild typeguard). I would like to make myself clear tho, I was thinking that we deprecate things when we introduce a replacement without a major release and then remove it on the next major release. I get it now.
For crawl's question, I meant it would be a problem if we remove it in the next version and someone who still uses the deprecated events will have to make change but that's their problem. Yeah that's it.
Yeah but considering they get a warning in the console even
Don't get me wrong, using tabs to accommodate for the visually impaired is a pretty good reason for one to use tabs. But was that one of the arguments brought up when this was discussed and ultimately decided?
I really just want to know what lead up to this decision so that I can weigh the benefits myself and make better decisions for my own projects.
the reason that was given doesn't really have any counter argument
or maybe you have some arguments against tabs
and by the way, yes. that reddit post was brought up few times before. it's not like it was random and crawl just now searched up something to give you.
Not really no, I have no issue with the library using tabs.
I see, that was the impression that I got.
Thank you!
I think it would be quite beneficial to a lot of users if the messageReactionAdd, messageReactionRemove and typingStart events sent a GuildMember object along with the user object that is already sent so that they can more easily manipulate the member. Of course this would be nullable since the member may not be cached or the event may not be triggered in a guild. What do you guys think?
If you want a member grab it from a guild
The current event is already getting the user from a member most of the times so why not just send the member as well?
"along user"? sounds pointless when you can do member.user
and then you realize that all those events can be received in DMs
That’s exactly why I suggested for it to be sent along the user cuz it can sometimes be null
does the library even emit any empty data for current state in any place?
it would seem weird that you sometimes do get member, sometimes you don't.
with current way you are going to get an error at message.guild if you are going to carelessly assume it was in a guild and get/resolve anyway
I never said you would assume anything, I just said the member object would be sent when it makes sense for it to be sent, that is when the message is in a guild
what
I think they meant to have that kind of signature for typingStart handler
(TextChannel, User, ?GuildMember)
Or
(TextChannel, (User | GuildMember))
the latter is just going to be confusing, since you have a parameter that can have 2 completely different from each other objects
the former i think falls onto what i already asked
does the library at any place states that it can emit data that can be missing (and explicitly stating that it's not old state in *Update, which is cache dependant)
I don't really agree with that at all tbh
That job could be done by the end user with a single line
const member = channel.guild?.members.resolve(user.id) ?? null```
resolve already defaults to null 
if the concern is cache though, you want to fetch anyway
If guild not here it would set it to undefined (?)
regardless, !member
but in your own variable, obtained from objects that are always going to be given
since the member may not be cached
well this isnt an issue for message reaction add or typing start
discord never sends a user object in those, it only sends user_id and the member if it's in a guild
djs not providing the member object is inconsistent with the gw, and the fact that it caches the member when those events are received is hidden behavior
This has been discussed before and decided that we didn't want either of these interfaces
client.on("messageReactionAdd", (reaction, user, member or null) =>
client.on("messageReactionAdd", (reaction, user or member) =>
discord.js caches the member when data is received, so reaction.message.guild.members.cache.get(user.id) should always work if sent in a guild, intents etc
The UserFlags "EARLY_VERIFIED_BOT_DEVELOPER" reports to the Djs as "EARLY_VERIFIED_DEVELOPER"
I tested with two users with this flag (The second command isn't there because I used Edit), maybe this is better suited to the #archive-site-discussion, srry if is
thats because youre on v12
its EARLY_VERIFIED_BOT_DEVELOPER in v13
I just tested in a v13 bot, you are right, but the v12 docs are as EARLY_VERIFIED_BOT_DEVELOPER, but this is better suited for the other channel
well v12 is done, it wont be changed
Why was sending a MessageEmbed by itself removed from .send in favor of using an array in messageoptions? Seems like a weird choice considering more likely then not you're only sending one embed at a time.
interesting, thanks for the read
I am not sure what to do with my PR
https://github.com/discordjs/discord.js/pull/6085
Looking into the source of MessageManager, the resolve method looks like this ```js
resolve(idOrInstance) {
if (idOrInstance instanceof this.holds) return idOrInstance;
if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) ?? null;
return null;
}
There's no way to detect an API message, because it is simply an object (not an ES6 class). The only way to know is to see if it has an `id` property and then try to use that, but that has side-effects of accepting anything with an id.
So I guess I'll just add move it to this union (instead of inside MessageResolvable)? ```js
deleteMessage(message: MessageResolvable| APIMessage | '@original'): Promise<void>;
You already know the answer, do what you suggested in your last comment on the PR i.e. change the typings for deleteMessages and add APIMessage to it.
Changing MessageResolvable will create ripple effect for just a niche change.
The Webhook#send returns APIMessage along with MessageResolvable, you let deleteMessage take APIMessage along with the same
Would a CommandOptionResolver class like https://git.io/JCkad be a good addition?
Used like so: (/command single <name?: string>)
maybe it could be implemented in @discordjs/builders?
@tacit crypt what do you think about this ^?
Dunno... builders are more... take user-friendly input and emit raw API data. But I'm not opposed to utilities like that. Thoughts @wild flax @solemn oyster?
hmm
I don't mind transformers
the only requirements would be:
- Takes
interaction.optionsand returns a completely new collection (no mutation) - Using
owfor validation, since we already do that in builders
The first point might be irrelevant anyway, since all it does, as I see it, is parsing it
Yeah, the way I have it currently, it just provides methods for parsing the options into native data, and subcommands into another resolver class
Is that or a transformer of some kind preferable?
Nah, I mean thatd be fine
I just wouldnt know what to call it or where to put it
How about src/interactions/slashCommands/resolvers.ts, export class SlashCommandOptionResolver?
Also, for the types, both discord.js@amber obsidian and discord-api-types@next are required. Should these be peerDependencies? Or how should that work?
oops sorry for ping person
discord.js@dev*
djs types are required?
CommandInteractionOption
and User etc.
hmm
Maybe this would belong in the main lib instead?
probably so yeah
Should it replace CommandInteraction#options?
Where the current raw collection would be something like rawOptions
Neither are raw
Hmm, maybe optionsCollection or something then?
Or make it a property of the resolver
I think it should be opt-in if anything
alright, should it be like, manually constructed or should it be a property?
I don't necessarily wanna decide this alone so uh
cc @copper laurel @vivid field
Does Discord always resolve the data that reliably to be throwing if its not there?
This wrapper is essentially just allowing us to get a properly typed value from the options collections right?
So intead of .get("name").value: string | number | boolean etc, we'd use .channel("name"): GuildChannel | null
correct
Also has support for subcommands and subcommand groups
So, for the most part I think I like this, but I also don't really like the fact that it's a Collection at all currently, I'm not sure what benefit changing it to that in the first place provided other than .get("name") being ever so slightly easier than .find(o => o.name === "name")
It also doesn't really make sense to me to be a collection
So for me personally, I think this is a far better implementation than the options collection, except for the fact that it doesnt provide access to things like the raw values that I can see (unless you went via resolver.options.get?)
yeah I wouldn't like being forced the validation on me
I have a transformer for the current collection that just .get() values, I don't care if they are nulled
My other concern is the method names, while this does seem like the simplest naming convention I'm pretty hesitant to have methods that are name the same thing as properties all over the rest of the lib, like .user(), .channel() etc
We got rid of guild.member() and people used to use that incorrectly all the time
Definitely support the concept though
My idea would be to have
CommandInteraction#options: CommandOptionResolver
CommandOptionResolver#data: Collection<string, CommandInteractionOption> -- (or CommandInteractionOption[])
So for most users, they could use interaction.options.channel("target").
And for users that need the raw collection/array, use interaction.options.data.
Also, how about get*() instead? getUser(), getSubCommand(), getString(), etc.?
probably yeah
I'd still like to keep a generic get()
Its a little wordier but it makes them clear that theyre methods and not props
that does no validation
Wouldnt that be interaction.options.data.get
^
or do you want to promote it up
I wouldn't want to expose raw data at all
we don't do that anywhere in the lib rn
Well that "raw" data is still the parsed collection
So have CommandOptionResolver#get(name: string): CommandInteractionOption | null?
actually
Doesnt this already do that
yah
oh yeah
cool
forgot I added that lmao
id like to keep that
as long as required defaults to false
I really like the concept too, but I don't really get the purpose of validating the data again. Shouldn't you as the bot owner know which option has which type?
yeah but TS
people would (like me) have to write their own type parser or smth
or constantly cast
which is really a sub-par experience lol
Sure we can keep the methods, but e.g. the required parameter seems unnecessary to me (or maybe as a type parameter)
People like me would have to steal Crawl's type parser
It allows for more type inference (string | null for non-required strings, just string for required ones)
A lot of command options are required, so having to check in code that it isn't null gets annoying fast
It's a lot easier to just add a , true) to when you get the option
imo at least
It can also help devs catch bugs where their command code doesn't match their command option data
Yea agreed, but I think a type param would be better suited here
Instead of just getting null, they would get an error
hmm
I've run into the issue a few times where I update my command's ApplicationCommandData, but forget to change something in my actual execution code. The errors are usually really annoying to track down because I would just get back null when I tried to access an invalid option. With the required parameter, it would help catch those errors better, in addition to the inferred types without adding another generic.
I'd argue that trying to get an invalid option should be an error, and it's better for the library to handle that error than for the user to. There's no use case at all for fetching an invalid option.
But if you get cannot read property "user" of null or whatever, shouldn't you know what's going on 🤔
Well anyway, I'd say you can implement it and then it can still be changed afterwards
Before I go too far in the wrong direction, does this look alright? @copper laurel @vivid field @wild flax
https://gist.github.com/shinotheshino/845cd2fb41ca2ae2d71762c3d36fb573
(just added the property parameter to _getTypedOption, it would be added to all the calls as well as "value", "channel", "options" etc.)
maybe im too tired but
get(name, required = false) {
const option = this.options.find(opt => opt.name === name);
if (option) {
return option.value;
} else if (required) {
throw new Error(`Missing required option "${name}"`);
} else {
return null;
}
}
get(null, true) will throw?
yes
ah right, makes sense
I was reading that in a very dumb way 
yeah the private method is ok
reduces code duplication
Just a few minor nits but the concept LGTM
Actually couldn't this extend BaseManager instead of being its own structure?
Or are those meant to be for api methods only
why only return the value and not the whole option
ah yeah
it should probably do that
I really dont see how this aligns with a Manager
Otherwise yeah I think so
#6107 in discordjs/discord.js by shinotheshino created 56 seconds ago (review required)
refactor(CommandInteraction): create CommandInteractionOptionResolver
📥 npm i shinotheshino/discord.js#commandoptionresolver
lmk what changes are needed :]
@wild flax @vivid field @copper laurel
I'll look at it tomorrow 👍
yeah caught that
@bitter peak can you fixup the spacing in the typings file
we don't usually space out props/methods like that
prettier did that sorry
prettier surely didn't add newlines
:P
op lemme check
how's that?
don't really, usually, need to separate privates from publics like that
and they are usually at the very top in other typings
its a bit inconsistent, but thats better anyway
so... this?
yep yep
kk
possibly add a way to add permissions on slash command creation if possible
Iirc the API doesn't support this yet
Yeah, Discord has it as separate APIs, because it doesn't really make sense as one. At least not for globals
Hello everyone. I thought for a long time about the fact that many of my favorite structures were removed. I had an idea of how this could be altered normally so that it would be convenient and understandable to use the extension of structures. Inside the library there are managers for each available data type. But the problem is that the GuildManager does not accept the holds parameter and is substituted inside. If we had access to replace it, then we could create classes inside the cache with the required fields and methods. Also, adding your executable method inside _add will allow you to make a query to the database for further data query. Maybe I'm wrong, but I would like to express my thought here
// https://github.com/discordjs/discord.js/blob/master/src/managers/GuildManager.js
// example
class MyGuild extends Guild {
cool = true;
}
class MyGuildManager extends GuildManager {
constructor(client, holds, iterable) {
super(client, holds, iterable)
}
}
class MyClient extends Client {
constructor() {
super(options)
this.guilds = new MyGuildManager(this, MyGuild)
}
}
no dont
i have so many things i want to say
this runs into the same problems and more as the original problem
and please separate your database code from your discord code why do you want these things to be coupled so badly i dont understand
its like you intentionally want to write unmaintainable code
But on the other hand, why do we need to separate the data that logically relate to the structure and move it into a separate entity, or do you have options for how to do it more correctly and better?
- because it makes your code more maintainable
- it makes your code more readable
- it makes everyone's code more predictable
as for how, literally just put that code somewhere else; in a separate class, as separate functions, whatever
these stuff are basic software design principles
That is, create many separate functions in which, as an argument, always pass the required structure by reference? I don’t think this is the best thing to think of
it is
people who use Structures before and wish to continue it are too attached to the idea that just because its related it has to follow with a dot like foo.bar
that . is not magic
it doesnt make your code better just because its a dot
Okay, I agree with that
please look up these terms to understand where you're going wrong: coupling, single responsibility, open-closed principle
and look up these terms for what you can do: dependency inversion & injection, repository pattern, functions
Yep, i know
The extension approach itself is not good practice in general
Do you have examples of how someone has moved away from structures to another approach that is more correct in your opinion in order to study
see yuudachi/v3 @rich igloo
crawl never used structures in the first place
also its agpl so follow the license if youre gonna use its code
Thanks for the answer. Yes, definitely
Can someone tag shinotheshino please
Wanna ask them a question about their comment on their pr
@bitter peak ↑↓
Aight thanks
So what exactly is this supposed to mean? https://github.com/discordjs/discord.js/pull/6107#issuecomment-879504907
Seems like you’re agreeing with me but I’m not really sure cuz the wording is a bit weird
Yeah, I dont think that's the same thing
Theres no reason for required but there's also kinda no reason for name either
I dont really think this resolver is quite the right approach for sub-commands, at least not the one #5880 was going for
interaction.options.getSubCommand() should return the name of the sub-command used
Or do what #5880 was going to do (I really shouldnt have shut that one down) and have that already parsed
Yeah it shouldn’t have any parameters at all and find the subcommand by the type
Hmmm, @outer raven @copper laurel I feel like the way it is makes for a much better control flow when being used (see the example). Maybe name could be optional, to allow for both ways? If name is set, behave as it currently does; if it isn't set, get the current subcommand (required). Thoughts?
I don't quite understand what flow you'd have where you'd want to try to get each subcommand by name vs just... getting the one selected. It's more like string choices.
Can you explain a bit more?
In fairness, I think Discords implementation of it is kinda shit, and I think the fact we left it in options is dumb. #5880 was actually good, I misread it originally
That'd be something like https://github.com/remoji-bot/remoji-bot/blob/discordjs-13/src/commands/emotes/copy.command.ts (sorry for the messy code)
You can make it optional if you really wanna support that way but I don't see why anyone would want to do that. If you can only have one subcommand at a time and you're sure there is a subcommand then why check for all of them? What advantages would that give vs just grabbing the option with the subcommand type?
I think it's just preference tbh; although handling groups seems like it's nicer with the way it currently is
I dont see why you wouldnt rather do
const subcommand = interaction.options.getSubCommand();
switch(subcommand) {
case "single":
break;
case "double":
break
}```
etc
Especially for anything more than two sub commands
Or in this case since it is only two, you dont need to get both, you can if/else on either one of them
Well, first off you'd have to wrap the cases in braces if you want to have block-scoped variables, so switch isn't really a great option
sure
i guess
that seems like a unrelated reason
youd be putting the ifs in braces too though
So that just seems like a style preference, nothing wrong with braced cases
You don’t have to use a switch, some people might break those out into completely separate functions even
Im just really struggling to see why anyone would want to have to get every possible subcommand and then check which one is defined
Sorry 😄
I feel like I must be missing something good in that design
Especially since that requires a call to the api if you don’t already have the command in cache
Well in that case would we pull the options up to the top level, or would we return a second resolver from getSubCommand()
cause i dont see it
Im okay with either - #5880 was top level
and I was kinda an ass about it and I was wrong
Top-level seems a bit more useful to me. Although, can't you have multiple layers of subcommand groups?
nope
You can have top level subcommands, or subcommand groups
If you have top level subcommand groups, second level is subcommands
Max 3 levels, group > subcommand > options
I have no idea what the use case for groups is tbh
Stuff like this
Kinda off-topic for this channel though
a /tic tac toe command 
Wow it’d help if I sent the screenshot 
eh, that could also be new remove-embed
i guess though
or whatever passes validation
There’s a lot more options, just can’t show cause mobile
Off-topic, lets stop here
Before I push, does this seem good?
Im not sure why we need the getters
To align with the rest of the class and provide the error message
ahh you abstracted away options too
Yeah I think so
minus the typo on line 43 oops 
So coming back to this, is /buttonroles new a group with no subcommands?
yes I think so
and so... no options either?
No, it’s a subcommand
you can have a toplevel subcommand with a toplevel group?
Can’t shave a group without a subcommand, won’t even show up
Yes, you can mix and match
god damnit discord
People always get confused when I say that lmao
Okay so.... _subcommand could be defined and _group not
But if _group is defined then _subcommand has to be defined, and in that group
Yeah
/command bar foo
/command foo
These are both foo subcommand?
Hmm, that makes throwing an error in getSubCommandGroup kinda risky
but one has null group
Correct
ughghufgubfhdjgk yeah that needs required then
You cannot have a subcommand with the same name as a group IIRC
to control the throw
Fuck this design is terrible, why Discord
*at the same level
Alright so that becomes:
btw why are these functions instead of regular getters? Is it for consistency with the others?
Yes
hm i think it would make sense for those to be getters, you can easily remove the required param from the group function too
I'd even argue that you can just make subCommand and group normal properties instead of using methods/getters
Did we get a resolution on this?
Switches make for way better control flow
Yeah looks like we did, current debate seems to be it should still be a method at all
Compared to?
a getter?
Properties
Why getters at all
I don't know im just asking
Why properties
the rest of the class is methods, so it doesn't seem right to make this one a property
the rest of the class works for multiple options, you can only have 1 group and 1 subcommand
Why is APIInteractionGuildMember#permissions a Snowflake instead of Readonly<Permissions> like a normal GuildMember? Can I use new Permissions(<APIInteractionGuildMember>.permissions) to get the correct Permissions object?
Because discord only sends the bitfield, discord.js then constructs a Permissions object out of that
Well, yes about the first part, but if we have an interaction (let's assume a slash command), we may want to check the permissions of the member of the command using interaction.member.permissions.has() for example, but we can't since the .permissions can also returns a Snowflake...
Then you can construct a Permissions object manually, sure
Anything prefixed with API is usually a type from discord-api-types, which represents raw Discord data
Ok so I'll use that, thanks!
Yeah, I saw
@tacit crypt idk if you read the convo but the subcommand method isn’t the same because you don’t need either of the params
It should even be a property imo
I mean from what I saw in the code, right now they're glorified getters
Yeah, that’s what it should be
neither of them should be functions nor getters
A getter/property
no getters??
why would you make it a getter
Idrk
Idk what the advantages of getters are tbh so I won’t comment on that
declare const options: APIInteractionDataOptionsIDoNotRememberTheNameOkSueMe;
if (options[0].type === 'SUB_COMMAND_GROUP') {
this.subCommandGroup = options[0].name;
const [subCommand] = options[0].options;
this.subcommand = subCommand.name;
this._options = subCommand.options;
} else if (options[0].type === 'SUB_COMMAND') {
this.subCommand = options[0].name;
this._options = options[0].options;
}

Hey, I'm new here so this might not be the channel for it but I wanted to talk about a certain feature implemented on discord.js (cache check)
So' this is regarding intents, the cache system and fetching
Now, with intents being a thing and discord.js enforcing the idea of specifically adding the ones you need, we can limit which events the client receives, which discord.js' cache depends on to stay up to date, and fetching also checks the cache first I believe
my example would be guild.members.fetch, which (unless explicitly told to skip the cache check ) checks the cache
async _fetchSingle({ user, cache, force = false }) {
if (!force) {
const existing = this.cache.get(user);
if (existing && !existing.partial) return existing;
}
```keep this in mind
now a scenario, let's say a client has GUILDS, GUILD_MESSAGES and GUILD_MESSAGE_REACTIONS intents enabled
the (relevant) events they'll receive would be
`messageReactionAdd`, which discord.js will use to cache the GuildMember, at this point in the cache, there is a GuildMember instance for member x (our reactee)
now note that since there isn't GUILD_MEMBERS, the client won't be sent events like guildMemberUpdate, so if i were to add a role (role1) on x, that wouldn't trigger the event, discord.js wouldn't be able to internally update the cache, right?
now, the fetch, what the fetch does is that it `const existing = this.cache.get(user);` just checks if the cache has the instance, that's enough to make it not call the api, it doesn't check if the data is up to date by my understanding
so in this scenario, I am fetching a member, however, it's not giving me the up to date data , so my question is and what I wanted to talk about was if this cache check feature is useless now or if i'm misunderstanding it of course
Well, there's a force option for a reason. You (usually) don't need to fetch a user if you have it already in the cache, but if scenarios, like the one you showed, occurs, we can use the force option to ignore the cache...
Avoiding API calls you dont need to make is never useless, but yes, if you've chosen not to receive events that maintain the state of the cache, then you also need to make sure you're not relying on cache where appropriate
Another option is with custom caching if you don't set GUILD_MEMBERS intent for example you can set GuildMemberManager: () => new LimitedCollection() that way it never caches. I think that would probably be the intended use, not 100% sure though.
I know the force exists and I have mentioned it before, however,I don't think the users were made aware of this, that even the fetch which's point (i always thought) was to give you the up to date data
in this case, a possible solution may be tweaking cache check to check if the client had the necessary intents passed on
like for this, we need GUILD_MEMBERS, since we want the guildMembersUpdate event, (theoretically that should keep the member cache up to date)
so checking Client.options.intents, if it has the GUILDS and GUILD_MEMBERS intents should do it?
async _fetchSingle({ user, cache, force = false }) {
if (!force) {
const existing = this.cache.get(user);
if (existing && !existing.partial && this.client.options.intents.has(["GUILDS", "GUILD_MEMBERS"])) return existing;
}
``` is this a possible workaround to it?
Pretty sure intents doesn’t apply to fetching a single member
I will say that I don't quite understand what you're saying here 😅
I thought the whole point of limitedCollection was that it only stores a limited amount of entries, I don't think a limitedcollection would just stop caching or keep outdated instances, which was the original issue, sorry but I would need a bit more explanation on this to understand what's going on here
@copper garden the original issue was that with a few certain intents disabled, the cache stores an entry however it's no longer updated (since the cache depends on certain events to stay up to date and not having intents means the client won't get said events)
now the "cache check" only checked for const existing = this.cache.get(user); an entry
and if it found one regardless of if it's outdated, it would return that instead of an api call, yes I am aware you can force and I mentioned that above too
Yes intents may not affect calling the api, but not having them could result in discord.js returning the outdated instance even before reaching to call the api
If you set the maximum of entries to 0, then it doesn't cache anything, therefore it has no outdated instances (0 also happens to be the default)
I get what you're saying about checking intents btw, just not sure if its the right design decision
that would completely discard caching in this case, right?
yes
honestly don't know if it's better but what do you think is wrong with checking the intents?
hmm, actually I have an idea, default force to !client.options.intents.has(['GUILDS', 'OTHER_RELEVANT_INTENT']), that way the dev can still pass false or true if they want. The issue I have is the way that things are intertwined, like you need guilds for pretty much everything for example. Figuring out what depends on what isn't really something each manager should have to worry about
I get that you are trying to say, if we disable the cache, it won't store the data, since at this point if the intents aren't there, this specific cache seems useless
although to be fair, you won't have a guild member manager without a guild, so you don't necessarily need the guilds intent check, plus you could've fetched the guild
i feel like, this may have unexpected side effects
intents checking there would be safer i feel like
that is sort of what's going on with the intents check further, just that this would skip the check we know wouldn't work and would allow the user to check the api
however this brings up the question why, why would a user want to get outdated data?
You never get outdated data
Data just happens to become outdated without intents
If you do not turn on specific intents, it’s your job to make sure to constantly fetch a member IMO
I don’t think the library should hardcode intents everywhere now to circumvent that
yeah, hence this
Yeah that seems the most sensible approach
Yeah I agree that intents shouldn't be hardcoded and that was never recommended
I am still relatively new to this library when it comes to internal stuff
but judging by what I'm seeing so far, disabling this particular cache completely may be a workaround
However, there is a specific thing I used to use which I feel like is now more complicated
As you know messageReactionAdd only gives you the MessageReaction and the User, however, the API's event actually gives the client a member object( of the reactee)
client.on("messageReactionAdd", (reaction, user) => {
const member = reaction.message.guild?.members.cache.get(user.id)
})
``` I would've been pretty confident that it's cached since I know the api gives you the member
however here, I need to make the fetch
so here, I am required to make an api request I know could've been avoided
also I would like to ask why this event doesn't have an optional third parameter or something to pass on the member
Yeah this is what I don't get about this explaination
How could you be confident, maybe I'm just reading the code wrong, its early:
https://github.com/discordjs/discord.js/blob/master/src/client/actions/MessageReactionAdd.js#L21
Doesn't this indicate the member gets always freshly cached?
So you would only be hitting the problem if you try to use the members cache OUTSIDE of the event
I am talking about the suggestion that says the lack of required intents would result in the cache being disabled completely
Well I don't see this as a needed solution
I am talking about your original concern
If you use
client.on("messageReactionAdd", (reaction, user) => {
const member = reaction.message.guild?.members.cache.get(user.id)
})
the member will always be up2date in this event. No matter what.
If you however start using it in different places, without:
const member = await message.guild?.members.fetch(id, { force: true })
yes exactly, that's what i said, and never caching would break this code
I was noting this as a side effect to this suggestion
Yeah youll run into issues
But I am asking myself, why would you knowingly use the cache (.cache) if you know you won't get GUILD_MEMBER_UPDATEs
in this case because I know the api is giving me the up to date member in this event
Yes, but the member in the "event" will always be up2date
Since we update the cache when we get a new member from this event
That doesn't mean other events wouldnt update it, and since you knowingly didnt enable them..
re #archive-github-djs message should I copy the branch and make a new pr fixing the conflicts and adding banner color?
Yeah just do
I'm a bit lost here, could you give me an example of what you mean?
I am aware that outside of this event, it's outdated
Yeah, and inside of it it will always be the latest up2date member
and the cache will be updated
Since even if it exists, we patch the new data on top of it
exactly, I know that
but i was talking about the cache being disabled completely, and said that this would be a side effect
if you disable the cache completely, (limit it to 0 entries) this member won't be cached
client.on("messageReactionAdd", (reaction, user) => {
const member = reaction.message.guild?.members.cache.get(user.id)
})
``` and here, member will be undefined
Well yes
But that is a deliberate decision of yours
No?
Why would you access a cached instance if you know you disabled it
I feel, if anything, we should do more educating on that behaviour, but not change code because of it
I wouldn't, that was an example, saying that here I am avoiding to call the api since it's definitely not needed
Be right back
But you just disabled cache?
So your decision is to prefer API calls
messageReactionAdd is also kind of an odd child here because its the only event that actually gives you a member from the API, but we don't directly expose it
But thats a rather interesting design decision
If the problem exists anywhere else, I'd happy to discuss this further, but if its only this event I would rather fix the event by possibly returning (reaction, user, member?) =>
Even if its a very awkward interface lol
typing start does too iirc
this conversation has happened before btw
Well what I meant was rather if the problem exists even in events where we expose all the data 😅
But sure, preferably we should fix all of them
Yeah we've had this exact discussion and decided that was a crap interface
But there's a valid point about not receiving it or caching it at all I guess
Might be worth to revisit
Esp with disabling cache
Fetch is always an option anyway
Yeah but its kinda pointless to fetch if you technically get the data lol
We just don't expose it to you
I definitely prefer a third sometimes-there param to inconsistent types on the second
Yeah, I wouldnt wanna do user/member
This is something I completely agree with, the users should know what they're doing, they should know how intents are affecting everything
stating that when you don't have the required intents, the data is outdated in the cache and you have to force if you need the latest data in this case would help
I am new to typescript/javascript in general I guess so I don't quite get what you mean by an awkward interface
sorry if this is dumb but I would like to know what you mean by that
to me "GuildMember | null" seems fine when compared to presenceUpdate where the first parameter is Presence or null
The “User | GuildMember | null” would be a very awkward one
Since it can’t be a member in dms
Yeah that would be awkward, I thought you were talking about the one with the third parameter
for user banner color discord returns a hex string ("#123456"), which is inconsistent with embed colors. should djs just return that or also have a getter for the color as a number?
You could check on the roles
We have a displayColor thing there I think
And also a hex prop
Roles have a color property in base 10 and hexColor getter in hexadecimal
roles do the same thing embeds do ^
bc discord returns a number for role colors too
Then we should copy that behavior IMO
but banner colors are inconsistent for some reason
And just transform discord’s stuff
Or maybe ask in the docs PR
Fucking dumb
(╯°□°)╯︵ ┻━┻
so would hexColor be the property and color be the getter, or should it store the converted color and add a hexColor getter to un-convert it
i'll also comment on the docs pr
I feel like I made this conversation drag on for a long time, sorry, it's 5 am here I can't think but I'll end it with what i came out with from this
fetch should check client's intents, if it seems like the data might be outdated, skip the cache check, by my understanding, users would expect the latest data, the reason why I am not okay with completely disabling this cache is because i feel like it'll lead to unnecessary api calls like with the event I mentioned
Lastly, the users should be told that the intents don't just disable some events, it also causes cache issues internally. They should know what they're doing and what to expect.
Thanks a lot for this conversation, I explored and learned a lot thanks to all of you, goodnight
Hmmm
Probably no transform for the raw data
and transform into an int via the getter in this case
Actually annoying how backwards it is
Literally ass
oh
user#fetch(true) is broken lol
it doesnt send an options object https://advaith.is-in-hyper.space/5f70359b41.png
ig i'll fix that in a different pr so it can get merged faster
Have channel type names been made uppercase?
yes
Why so?
Consistency?
Makes sense
since u have to fetch the user to get user banner & banner color (its not always on the user object)
should I add fetchBanner, fetchBannerURL, fetchBannerColor, and fetchHexBannerColor methods, like the current fetchFlags?
Wait what
Why not just fetch banner that populates all of those
what should it return tho
just the banner hash?
void 
isnt that just fetch(true) tho
I dunno, the patched user?
i mean
I don't even know why we don't do that everywhere
fetchFlags is essentially just fetch(true) that returns .flags
i assume theres some situation where user.flags exists but isnt sent
If we need to fetch something, best case scenario we should just return the patched user
cc @tacit crypt @solemn oyster
So fetchBanner() = this.fetch()?
yeah
essentially
lol
I know its awkward but I just don't like arbitrarily returning data based on the fetch
I mean at that point we would drop fetchFlags and just tell users to call user.fetch if null
yeah thats even better
What do with emojis/stickers then
what about them
how are they relevant 🤔
fetchGuild
well for all of those
oh no not this again
we should really just return the whole instance
it feels so awkward and dumb to have 5 methods just to return 5 strings from them
we don't do tell people to do .fetchFull() either when its a partial right
and then return everything but the id
because its always there on a partial
lol
so on the banner properties should I add a "The user must be force fetched" note?
Well, force isn't really needed cuz it defaults to true anyways
But the main fun factor is how you'll differentiate from no banner and no fetched banner
cuz it defaults to true anyways
no it doesnt?
afaik its always been default false
Not in the manager function, in the user function
false there too
wait wtf is the point then
I mean it should probably default to true, because partial users
@wild flax @solemn oyster ^ thoughts
yeah imo on instances it should default to fetch(true)
but on managers it should be fetch(false)
should i change that in the fix pr too
Sure
done
made the banner pr
Why did presence get moved to member, rather than user
Even on Discord API side
What made that change
A presence is never on a user
It was in v12
That was us doing it
it was always just a getter that tried to retrieve the presence from the member on the first guild it happened to stumble across
however there were edge cases that resulted in this not working and a surplus of the suggestion "use message.member.presence instead of message.author.presence"
presence data is inherently guild based data and we probably should've never started to provide it to the user
you also get presences when being friends, but since thats not a thing with bots
is there any chance djs would add undocumented features?
no - see stuff like mobile indicator
understandable
From memory, pull requests are created before documentation but merge on the branch only when documented.
So I think DJs release doesn't support undocumented features
Would it make sense for the library to add a Message#removeComponents method, similarly to how there is Message#removeAttachments?
tbh I thought that was kinda a silly method since all it does is message.edit({ attachments: [] });
Yeah, it doesn't really make sense for only this to exist. It's the same thing with removing components. Either we create one for everything (components, embeds, attachments) or none
yea thats what i thought too
but if we keep it we might as well add the components method
what if we removed Message#removeAttachments
I guess maybe it doesn't make sense because its not causing any harm
or a singular method taking arguments as to what to remove?
would probably be the best decision, it doesn't really provide much value
that just seems like more work than doing edit({components: [] })
Then again, lib enforces using methods rather than constructor objects in guides. In embeds for ex, it's proposed to use emb.addDescription().addField() rather than passing values to embed constructor. It could make sense for a similar approach msg.removeEmbeds().removeComponents()
what about something like message.remove(["embeds", "components"]);
like Mega said
that seems kinda janky honestly, imo i would either add both methods or remove removeAttachments
msg.removeEmbeds().removeComponents() seems better tbh
No
Just confirmed - the reason that exists is because you can remove the attachments from other people's messages if you have MANAGE_MESSAGES
Much like supressEmbeds also exists
We aren't going to add helpers for every other edit of your own messages
oh well that makes sense, forgot about that
@oak quail did u change anything in the icon function?
dynamic wasnt in the typings before
that change was copied from the old banner pr
Yeah, I remember someone said to add missing Typings for it too
Found this comment: https://github.com/discordjs/discord.js/pull/5930#discussion_r659213364
alright but can you remove 'default' then? I have no clue what that's supposed to be but it doesn't work
btw @bitter peak about your command options PR, can you please make all functions return undefined instead of null just like collection.get does?
yeah should the default format be webp or something
null is more correct for those imo
why?
they arent a collection
why would it matter
they are methods, not a hashmap
because undefined is more accurate than null
no, i dont think it is
based on what
because these options were not specified by the user who ran the command
it's not like they were specified as null
imo null is always more accurate
we only use undefined for collections because map does
to not completely break the map interface
undefined is a dumb type that doesnt exist in a lot of other languages
but it's way easier to work with undefined types because if you have an optional parameter in a function you can't assign null to that
?
?
We use undefined only when exclusively needed
Null is more of an explicit empty type whereas undefined is an implicit one. It's pretty much always better to use null for things like this unless it breaks something else
Such as skipping fields from JSON.stringify
Why are subCommand/subCommandGroups not... properties 
they are. private properties lol
Not what I asked
They shouldn't be glorified getters
oh I know
I dont see why not
I dont know why theyre private and not just properties
ok so imagine you have a function with an optional parameter and you assign one of the command's options to that parameter
if the option is not required it will throw an error because you can't assign null to whatever type your parameter has
whereas with the previous .get you could and both work perfectly fine, it's just a typescript error
thats more a you problem
null can be a default value for optional params
yeah but why do this
function example(param: string | null) {}
when you can just do this
function example(param?: string) {}
bruh
we.... dont
that doesn't even make sense because you want the param to be optional, not null
wdym?
dont write shit functions
dont pass variables you havent checked to functions
function example(param?: string | null) {}
is the best of both worlds
and youd need to do it anyway
since not everything returns undefined
yeah but the | null there is redundant
no it isnt
if you ever pass a null value the ? wont allow it
well if you're only passing command options it should, and that's what happens atm
I know but allowing the null type or adding a ? essentially means the same thing but for different types
it doesnt mean the same thing
undefined is not null
then what's the difference
learn js thats the difference
I don't get why you guys like the null type so much it's just annoying to work with
undefined is not the lack of a value. Its the lack of any definition at all, a property not existing in an object, etc
null is a specific, defined return value denoting that everything was successful, but theres no value found. A function should not be returning undefined, it should return null when it has no value to return because it deliberately, knowingly has no value
Because its correct
If JS wasn't such a loose language, undefined wouldnt exist. Accessing undefined things would throw errors in a good language like C#
well but when you leave an optional parameter out wouldn't that mean it's not defined? Why are you giving an option a value that it doesn't even have? You can even try to get an option that doesn't exist on your command and it will return null which doesn't make sense to me because the option isn't there to begin with
But this is a command option resolver
A resolver function should return null
accessing a property that isnt defined yes, should return undefined but thats not what youre doing
You dont have to use the resolver if you want the map-like behaviour
You're drawing a false equivalency to an option being null, and an option resolver method returning null. They arent the same
But Discord will never allow you to pass null to any type of option, it only lets you not pass it at all and the API doesn't pass anything as null either, so why change that behavior?
You could simply try to find the option in the array and return that regardless of the outcome (meaning that you wouldn't be explicitly returning undefined, it's just what the find function returns if that makes sense)
What has that to do with sending something to discord when we are transforming received data?
We strife to write good and maintainable code, not some mess.
undefined is a mess that should not exist in the first place, the only reason we use it is because of quirks in the current language
Also this is a nice read for you too: https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-beta/#exact-optional-property-types
aight if typescript itself is doing that then sure there's no point to change to undefined
lol
It's almost like we know what we are talking about
@outer raven The reason I have methods for getSubCommand and getSubCommandGroup is to perform validation as all the other methods do. e.g. getSubCommand() asserts that there actually is a subcommand, moving the burden of checking away from end-user code.
cc @tacit crypt bc you asked too
??
but if you wanna check if there is a subcommand or a group just check if they're null ?
The same could be said for all the get* methods
But the rest of the PR follows that assertion logic