#We're making a wrapper over the Discord API using Java!!

1 messages · Page 2 of 1

orchid portal
#

for tj bot i did all manually

#

it was a pain

#

(it never got merged)

light loom
#

oooh

orchid portal
light loom
#

why lol

orchid portal
#

it was kinda garbage

light loom
#

It's good to see we can fine tune it

orchid portal
#

not good enough quality for tj bot

orchid portal
light loom
#

Ah

#

I haven't used eclipse in over 10 years

orchid portal
orchid portal
#

i started with intellij

#

then neovim + intellij

light loom
#

I started with notepad and javac

#

Then notepad++

#

Then eclipse

#

And intellij

orchid portal
#

damn

#

@light loom ok so what needs to be changed

light loom
#

For?

orchid portal
#

i found 2

    Optional<Integer> status,
    Optional<String> image
)
    implements DiscordRequest {
public @interface EventListener {
}
orchid portal
light loom
#

Ah okay

#

Tomorrow let's discuss I'm in bed

orchid portal
#

how does it feel to sleep at night

#

i forgot

light loom
#

Try mess around with it and see what you come up with

#

Aim for the best quality formatting that we can all agree is perfect and the formatting war can be over HHeartTurtle

orchid portal
#

might as well do it for tj bot

#

or not

#

i fked

#

reverted

#

i'll make atomic commits now so it'll be easier to trace back later

orchid portal
#

@restive void @plain ledge check formatting, lmk if u feel smth needs to be changed

plain ledge
#

:(

#

I think I gotta do merge conflicts now

#

:(

orchid portal
plain ledge
#

@orchid portal Should we use Methanol just for multipart data or switch everything to it?

orchid portal
plain ledge
#

(Everything being pretty much just the HttpClient)

orchid portal
#

no

plain ledge
#

kk

orchid portal
#

lets wait for wazei

plain ledge
#

I think to integrate it (besides registering whatever we need for jackson), I will just add a set body publisher method since they already have a builder for multipart, which just returns a BodyPublisher

orchid portal
#

I think we should mandate @JsonProperty on all fields of a model, wazel is doing goofy stuff

#

rpcOrigin is not even correct

#

also always use List not array

#

idk if it works but dont

plain ledge
#

Taz can u check out that Pr I created

plain ledge
#

@orchid portal

orchid portal
plain ledge
#

ok

orchid portal
plain ledge
#

the DiscordRequestDispatcher only adds the application/json Content-Type when the headers list is empty

orchid portal
plain ledge
#

I mean if u use that method for multipart data u will need to specify the Content-Type as multipart i believe, so what I was asking is if we should just combime those methods

#

like inside the method add 'Content-Type: multipart/form-data as a header

orchid portal
plain ledge
#

ok

orchid portal
#

i'd prefer that

plain ledge
#

Yeah

orchid portal
#

lets wait for wazei's opinion as well

#

@light loom

plain ledge
#

I'll include both methods's for future use

orchid portal
#

formatting is done on my side u guys can give ur opinion

plain ledge
orchid portal
plain ledge
#

schizo

#

nvm

#

im sped

#

is the dif 1 tab instead of 2?

orchid portal
#

u blind mf

orchid portal
#

that also

#

along with many other things

plain ledge
#

what did we decide for docs? Not starlight right

orchid portal
#

whats starlight

plain ledge
#

Only the cutuest buetifulist docs site ever 😉

orchid portal
#

i'd just use mkdocs, if theres no other option

plain ledge
#

wazei was against it

#

@orchid portal I am going through guild models rn

#

and there are quite a few enums, should I just through those all in the models folder?

#

I feel like it's getting kinda cluttered

orchid portal
#

separate that

#

and see if i missed any gateway model move them to gateway.events.handlers.codec.models

plain ledge
#

im not doing that in this pr

orchid portal
#

ok then deal with it for now

#

do it in another

plain ledge
#

:(

#

Are these models user facing?

orchid portal
#

no idea

plain ledge
#

:|

orchid portal
#

my fingers hurty now

#

igo

plain ledge
#

@orchid portal am I refractoring the models to match requests package?

restive void
#

I applied spotless

#

Lmk when the PR is good to go

plain ledge
restive void
light loom
#

i have some time, what are the questions you peeps want to ask me?

plain ledge
#

Or close it if those models won’t be user facing

#

And then I left a comment on the guild api issue

light loom
#

responses - tldr; yes to both

plain ledge
light loom
#

i mean yes to both, you're right

#

we should do it

plain ledge
#

Ok

light loom
#

it might be better for our sanity to let them be user facing

#

as much as I want to create wrappers over them, it's just so much manual labour that we could probably avoid

#

let's think about it when the time comes if we get benefits over creating a wrapper

plain ledge
#

Wait

#

I just looked into Jackson

#

We can just do @JsonValue on the getValud method

#

We don’t need to write a annotation processor

#

I’ll go through tonight all the models and make sure they are complete and switch it all to enum

light loom
#

ooh thanks for looking into this! @plain ledge peepo_heart

plain ledge
#

And for the guild endpoints do everything including members and stuff

light loom
#

yes please

plain ledge
#

Ok

orchid portal
#

its better to merge it quick

plain ledge
#

And the pr for multipart data olease

light loom
#

i got pulled into a meeting

#

give me 30 mins

plain ledge
#

@light loom can you merge Taz’s spotless thing asap so that we don’t have to deal with new PRs having as much merge conflicts

light loom
#

merged

restive void
#

Man the channel API is huge lmao

#

Theres still like 10 endpoints left to implement

plain ledge
#

Tonight I’ll finish the stickers pr and then pr the enum changes

#

and then tmr I will finish guilds (still need to figure out how to handle partial channels and roles cause I’m not clear on what they are)

light loom
#

The reason it's called partial is because they don't give all the fields back which is okay for us

#

We have some future work to add caching, we'll keep a track of all objects and when we receive partials ones, we can just update the fields when we receive them later

#

And keep the cache updated

plain ledge
#

Ok

#

So just ignore them then? and put the normal channel ?

plain ledge
#

@orchid portal Make sure to run spotless on the user pr 😉

plain ledge
#

I got about 70% done with the enum PR, in doing it I went over all the models and noted missing fields. I didn't do it all for guild and message since it was intimidating but still need to do that. I also added a couple new models to missing stuff like invites and refractored the models packageto match the discord docs website (same as endpoints package)

orchid portal
#

i have like 2 endpoints left in user api

#

user api was kinda small not xl

restive void
#

Will have another PR for channel api endpoints ready later today

light loom
#

so grateful there's no conflicts on #22 from taz's PR pepekek

orchid portal
#

ur not editing any file

#

only making new ones

#

rebase and apply the new spotless

restive void
#

Ill see tho

oak flare
restive void
#

What data type are we using for snowflakes btw?

orchid portal
#

since the message is pinned now ig u should keep updating the team

#

or just remove it

#

coz ig nopox is doing the most work

#

its unfair to not have him

oak flare
#

why am I up there pepekek

#

didnt do anything

#

I would just remove the team section

#

people can look it up on discord

light loom
#

the best project in the entire #1150852739467849829 section

oak flare
#

I will be gone next week and then I still have some finals, I hope I can catch up after that

oak flare
light loom
#
  • the team bit was clickbait 😄 - you guys were just the first ones I spoke about the project with and wanted to contribte ❤️
#

I just saw the enum PR...

plain ledge
plain ledge
light loom
#

please

#

tell me it's an easy review angerysad

plain ledge
#

It should be

light loom
#

okay it is!

plain ledge
#

It's just model changes and refractors

light loom
#

tested it and it decoded properly?

plain ledge
#

shi

#

nah

#

lemme do that

light loom
#

lmaoo

plain ledge
#

when u say tested it wdym

light loom
#

the models changes

plain ledge
#

uh

#

nah

light loom
#

do it quickly!

plain ledge
#

is there a testing bot or smth

light loom
#

i have a task to create a testing framework for you peeps

plain ledge
#

arent we using imposter

light loom
#

yeah we are but it's just there and not being used by the code 😦

plain ledge
#

what so the testing framework,

  1. Spins up imposter so we can make http requests
  2. Spin up the Discord instance
  3. Call dif get requests and make sure everything works
restive void
#

@light loom Is this intentional or can i refactor it and remove the duplication

light loom
#

it's a whoops

#

yeah remove it

restive void
#

Gotcha

#

Ill probs just get the remaining channel endpoints done

#

Got like 4 or so remaining

light loom
#

thanks so much for the work

#

we're progressing so well with the project

restive void
#

Np

light loom
#

sorry multi tasking, i missed this - so yes you can run imposter up in the mocks directory, this will start the server on localhost:8080 you can then use that url to make a request to the API

#

and it'll return the fields

#

they have a java lib that you can reference in the code so we don't need to use imposter up

plain ledge
#

so is that just working with junit then to prepare the testing env

light loom
#

we just need to add the library into gradle and setup the setup the boilerplate

plain ledge
#

and then modifying the discord class to be able to work locally

light loom
#

exactly

#

in the test we'll just pass localhost as the API url

restive void
#

How should i handle a response body for an endpoint like this? Or should i just ignore it for now

plain ledge
#

ignore it for now

plain ledge
restive void
#

Ight

light loom
restive void
#

Was just wondering if i should do anything for the response body

plain ledge
#

I don't think we are doing response bodies yet

light loom
#

ohh yes

#

you're right

#

dw about bodies

restive void
#
public record ListPublicArchivedThreadsRequest(
        long channelId,
        Optional<LocalDateTime> before, // Represents ISO8601 timestamp
        Optional<Integer> limit) implements DiscordRequest {

    @Override
    public DiscordRequestBuilder create() {
        return new DiscordRequestBuilder()
                .get()
                .path("/channels/%s/threads/archived/public".formatted(channelId));
    }
}
#

Ye this is what i have rn

light loom
#

perfect, just add the query params

plain ledge
#

look at smth else that uses the time

#

I think Taz has some annotation that we do on them

restive void
#

Oh ok

plain ledge
restive void
#

Ya i didnt know what we were using for ISO8601 so i just put localdatetime for now

plain ledge
#

yup

#

Alright, I'll leave that enum pr on hold till this is done and then add tests and shi

#

@light loom Can imposter do gateway events or no

light loom
#

But you can use mockito maybe

plain ledge
#

I will work on the test thing tmr if someone wants to work with me I'll prob be in vc

light loom
#

So just to remember, using Imposter is integration testing

#

Not to be confused with unit testing

plain ledge
#

So we are not using junit to instansiate imposter correct?

#

(I am somewhat new to testing)

light loom
#

We are using it to instantiate it but maybe I can explain this tomorrow as I'm going to bed soon

#

I can give you a run down of testing and the differences between the approaches

restive void
#

With this, the channel API should be done

orchid portal
#

@plain ledge what is @JsonValue

orchid portal
#

@light loom should we have a separate record for PartialChannel/User/Guild?

light loom
#

If it's a partial object, we just update the cached object with the updated fields

orchid portal
#

what about here

plain ledge
light loom
#

Just pass a regular Channel/User

#

On Jackson the @JsonIgnoreProperties will make it not throw when fields are missing

#

But you'll still get the partial content for the other fields

plain ledge
#

wait if a field is nullable and it's a primitive wont that error

orchid portal
orchid portal
#

@plain ledge did u just removed these

plain ledge
#

wait, nvm it's msising a couple. I'll write that down

restive void
#

Since im done with channel API, ima pick up Application and Audit Logs API tasks since theyre quick if u guys dont mind assigning me

plain ledge
restive void
#

What data type should we use for image data in the application API?

#

BufferedImage or something?

plain ledge
restive void
orchid portal
plain ledge
#

@light loom when u would be able to explain to me the dif between unit and integration

light loom
#

come vc? @plain ledge

plain ledge
#

sure 1moment

plain ledge
#

@light loom

#

We could fork that imposter to add gateway

light loom
#

mocking the websocket gateway is much harder if we want it like imposter

#

instead it's better to just use mockito

#

and mock the method responses

shy notch
#

wiremock ftw. but yeah, lots of stuff

oak flare
#

What’s that?

shy notch
#

let's u mock the http responses of an api, e. g discord api

oak flare
#

That’s nice, but I would say it’s generally harder to test applications that uses some api

shy notch
#

i. e. mocking on the outermost level of ur library, so that u truly get full test coverage

#

otherwise u can't do integration tests in ur case

#

or need to leave a significant part of ur code untested

#

like, suppose u want to test ur method that bans users. how would u do that. it sends a api request to discord and interprets the returned response...

#

unless u create ur own mock layer in between somewhere and then throw mockito at it, its much easier in that case to just use wiremock to intercept and redirect the http traffic, returning a mocked response

#

depending on the details of the setup, might be worth a thought 🙂

plain ledge
#

and then we were discussing what to do for the websocket portion of it

shy notch
#

i see makes sense

light loom
#

yup so imposter uses the openapi spec to generate a mock http server that we can use for integration testing

light loom
shy notch
#

openapi is such a cool thing. so many tools build upon it

light loom
#

Your code though isn't complete? LUL

#

Better to remove that to-do and update the ticket with what's missed

orchid portal
#

ahh fk

orchid portal
light loom
#

I'll review tomorrow because I wanna look into that image code you have

orchid portal
#

@light loom ok its done now

restive void
orchid portal
restive void
#

Ight

#

I re-applied spotless

#

Should be good to go now

plain ledge
#

@orchid portal

#

I think this is present in the enum pr

orchid portal
#

i aint looking at that

plain ledge
orchid portal
#

if mine gets merged first

#

rebase basically

restive void
#

Made PRs for application api and audit log api

orchid portal
#

pr maxxing

plain ledge
#

my pr on hold till we impl testing

light loom
#

hey peeps - i'm free'ish today

#

does anybody need help?

light loom
#

@restive void had a chance to look at the comment I left? you missed one of the endpoints CEHSweatSmileTurtle

#

Updated the main post to include all the contributors so you guys don't get unnoticed peepo_heart

orchid portal
#

@plain ledge i need ur approval as well

restive void
restive void
light loom
#

this is the one you added

#

wait

#

I'm also being blind

#

can you extend it

#

When an app is performing an eligible action using the APIs, it can pass an X-Audit-Log-Reason header to indicate why the action was taken. More information is in the audit log entry section.

#

sorry it was just 1 endpoint, i thought it had 2 modes but it was just the optional header

restive void
light loom
#

hey peeps, i need your opinon

#

by using records we kinda messed up because we can't cache and update

#

so... here's my hacky solution:

#
package com.javadiscord.jdi.core.cache;

import com.javadiscord.jdi.internal.models.channel.DefaultReaction;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class Cache {
    private static final Logger LOGGER = LogManager.getLogger(Cache.class);
    private final Map<Long, Object> models = new HashMap<>();

    @SuppressWarnings("unchecked")
    public <T> T getFromCachedById(long id) {
        return (T) models.get(id);
    }

    public boolean isCached(long id) {
        return models.containsKey(id);
    }

    public void cache(long id, Object model) {
        if(models.containsKey(id)) {
            updateCachedChannel(id, model);
        } else {
            models.put(id, model);
        }
    }
    @SuppressWarnings("unchecked")
    private <T> void updateCachedChannel(long id, T model) {
        T cached = (T) models.get(id);

        try {
            Class<?> recordClass = cached.getClass();
            Constructor<?> constructor = recordClass.getDeclaredConstructors()[0];

            Object[] currentValues = new Object[constructor.getParameterCount()];
            for (int i = 0; i < currentValues.length; i++) {
                Field field = recordClass.getDeclaredFields()[i];
                field.setAccessible(true);
                currentValues[i] = field.get(cached);
            }

            // Create a new instance of the record with updated values from the model
            T updated = (T) constructor.newInstance(
                    Stream.of(constructor.getParameters())
                            .map(parameter -> {
                                try {
                                    Field field = recordClass.getDeclaredField(parameter.getName());
                                    field.setAccessible(true);
                                    Object value = field.get(model);
                                    return value != null ? value : currentValues[(int) Stream.of(constructor.getParameters()).filter(p -> p == parameter).count() - 1];
                                } catch (IllegalAccessException | NoSuchFieldException e) {
                                    LOGGER.error("Failed to update cache for model {}", id, e);
                                    return null;
                                }
                            })
                            .toArray()
            );

            models.replace(id, updated);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            LOGGER.error("Failed to update cache for model {}", id, e);
            throw new RuntimeException(e);
        }
    }


    public static void main(String[] args) {
        Cache cache = new Cache();
        DefaultReaction defaultReaction = new DefaultReaction(1, "test");
        cache.cache(defaultReaction.emojiId(), defaultReaction);

        System.out.println(cache.isCached(defaultReaction.emojiId()));
        DefaultReaction updated = new DefaultReaction(defaultReaction.emojiId(), "hello");
        cache.updateCachedChannel(defaultReaction.emojiId(), updated);

        DefaultReaction fromCache = cache.getFromCachedById(defaultReaction.emojiId());
        System.out.println(fromCache.emojiName());
    }
}
#

It's a bit crazy but it saves us from writing new objects or updating all the records

#

advice on alternatives or improvements?

oak flare
#

why cant you group them instead of using Object

light loom
#

i thought about it

oak flare
#

well let me go through the whole code snippet

light loom
#

but it would mean a new cache for ALL of these

#

I thought of maybe only caching the important ones like users, channels etc

#

but it's still a fair bit though my hack lets us cache everything and anything

#

Now I might need to do what you suggested - that is keep a separate list of models because I think Ids might clash

#

Alternatively, I can use a Key object which contains the Class and ID and use that - to not need a new List for each model

#

(or a string for perf e.g. "id|className")

oak flare
#

what kind of id is that btw, because it will clash if its like the discord id
for example a thread and its starting message have the same discord id

light loom
#

Updated it - these are discord IDs

#

so I have updated it to use the Model name as part of the ID field

#

It means passing X.class but small price?

light loom
#

Best to review via commits

plain ledge
#

@light loom will that be wrapped by the user facing api or is it intended to be used by the user?

light loom
#

It's part of the reason i put inside the core package

#

Or maybe, we'll have helper methods for the "generic" stuff and still give access to the full cache for anything else

#

Hard to tell because we don't understand the API to it's fulliest - like sure we've implemented most of the API however that knowledge is in our short term memory 😄

restive void
plain ledge
#

Since there are so many things that support it we may be better off adding something for it in the user facing api

#

like .withAuditLog reason and we just modify the DiscordRequestBuilder

restive void
#

Ya

orchid portal
#

@plain ledge do u need help finishing 66?

#

lets get it merge

plain ledge
#

that's what wazei wanted, im gonna start testing this week unless u wanna start it now

#

which pr's need reviewing?

orchid portal
orchid portal
plain ledge
#

I've been sick this weekend and just sat around

#

@orchid portal

#

request my review on user pr I can't for some reason

#

@orchid portal There is just one change then it looks good

light loom
#

no worries - i can sort it out

light loom
#

@restive void just confirm for me, all the channel endpoints are done now right?

#

commented on #66 okay to approve it @plain ledge

#

Just check the conflict

plain ledge
# light loom commented on #66 okay to approve it <@456226577798135808>

oop, those missing notes are when I was going through the models on discord docs and noting down missing fields. The auditLogChanges was becauase I wrote down a todo to ask about how we handle mixed data types (https://discord.com/developers/docs/resources/audit-log#audit-log-change-object)

Discord Developer Portal

Integrate your service with Discord — whether it's a bot or a game or whatever your wildest imagination can come up with.

light loom
#

if they're primitives but mixed e.g. string/int use String

#

if it's an object, I guess Object? Until we find a better way to enforce it?

#

we can do type checks in the constructor

#

or generics e.g. <T extends B | C>

plain ledge
#

hey @light loom can you check the docs and confirm something? I am reading the audit log docs and I am thinking that it can either be a snowflake (long) or a string, but I'm not that confident that I am reading them correcltly

light loom
#

snowflakes I interpreted as longs

plain ledge
light loom
#

what bit specifically?

#

user_id? snowflake Entries from a specific user ID
action_type? integer Entries for a specific audit log event
before? snowflake Entries with ID less than a specific audit log entry ID
after? snowflake Entries with ID greater than a specific audit log entry ID
limit? integer Maximum number of entries (between 1-100) to return, defaults to 50

plain ledge
#
Object Changed    Change Key Exceptions    Change Object Exceptions
Command Permission    snowflake as key    The changes array contains objects with a key field representing the entity whose command was affected (role, channel, or user ID), a previous permissions object (with an old_value key), and an updated permissions object (with a new_value key)
Invite and Invite Metadata    Additional channel_id key (instead of object's channel.id)    
Partial Role    $add and $remove as keys    new_value is an array of objects that contain the role id and name
Webhook    avatar_hash key (instead of avatar)
#

oop,,

#

(Looking at the audit log change structure)

light loom
#

so the Command Permission looks like an Map of Command Permission objects that use the Long id as the value

#
{
  1: {
   ...
  },
  2: {
   ...
  }
}```
#

maybe something like this

#

Since we have imposter you can also invoke the API locally and see the output

#

But this is an exception case so I'm not too sure on what the exact output could be, maybe raise it as tech debt?

plain ledge
#

Well I've got that missing thing noted down in the code so should we just ignore that for now and relook at it in the future?

light loom
#

that's fine 🙂

plain ledge
#

ima head to bed now

light loom
#

goodnight 🙂

#

looks good, cool changing the BASE_URL to an env var

#

and you're on the right track!

wicked furnace
#

i wanna get in on this but this stuff looks too difficult reeee

orchid portal
#

skill issue

light loom
#

It'll at least help you gain some knowledge on how we've designed the code and maybe give you an understanding of the Discord API

light loom
#

I'm so confused by these docs

#

okay so this one happens on something channel

plain ledge
light loom
restive void
#

And ill push up the x-audit-log reason header handling to my PR later

plain ledge
#

@light loom I think our next thing after these integration tests is response handling, should we get that done by the end of this week?

light loom
light loom
#

@plain ledge maybe you should create a PR with the current work before continuing down the path of doing all the other end points. Instead continue and create unit tests for the other bits

#

The reason being, I was thinking about this hard and I think we'll need to test more bits in the area you're currently in but we're missing implementation

#

The step we're missing is actually using the response received from the API request and I think the way we're using Jackson at the moment, everything will succeed and we'll always get 200s

plain ledge
#

Atleast going forward

light loom
#

Yeah exactly and you've done a fantastic job of setting up the infrastructure

#

So everything is in place for when we're ready

light loom
#

All I did was change a few names and moving things around

#

All the hardwork is yours

plain ledge
#

If you haven’t already I will create a Pr for the current integration testijg in a couple hours and than finish up the guilds PR and I should have everything I am assigned to off my plate

light loom
#

My cache PR is getting kinda big and out of scope lmao

#

but I added all the annotations I think we need

#

(dw it wasn't all manual, I auto generated it using the handler names)

plain ledge
#

Sweet

#

So we will just annotate functions with those and then call that function in the handler?

light loom
#

Yeah so hopefull we can do

#
@EventListener
public class ExampleListener {

    @ChannelCreate
    public void exampleChannelCreate(Channel channel) {
        System.out.printf("Channel created: %s", channel.name());
    }

#

My auto generation is sketchy though so we need to test this so hard

#

I'm parsing the classes and generating the boilerplate that way

light loom
#

streaming my development rn

light loom
#

it freaking works!!!

#
EVENT_TYPE_ANNOTATIONS.put(EventType.CHANNEL_CREATE, ChannelCreate.class);```
#

just need to add this for every event/annotation combo... more manual labour...

light loom
#

who broke this:

#

😄

#

GIT BLAME - grab the pitch forks!

#

just kidding :p

plain ledge
light loom
#

Sure thing

plain ledge
#

I’ll fix the enum pr in a sec

light loom
plain ledge
#

Sweet

#

@light loom is pr 38 ready to be reviewed

light loom
#

yup

plain ledge
#

@light loom

#

is this a mistype or is that all intentional for those all to be included if content is present

light loom
#

i think it's all intentional

#

I have open issues to address the TODOs

plain ledge
#

@light loom I left a couple of comments but the rest looks good

restive void
#

@light loom Fixed audit logs api

#

PR should be good to go

light loom
#

sweet

#

are u both available to join vc?

#

I wanna demo 😄

#

it's so cool

plain ledge
light loom
#

np np

restive void
#

Nah i gotta leave for class in a bit

light loom
#
@MessageCreate
    public void example(Message message, Discord discord) {
        if(!message.author().bot()) {
            System.out.printf("Message received %s", message.content());
            discord
                    .sendRequest(new CreateMessageRequest(message.channelId(), message.content()))
                    .onSuccess(System.out::println)
                    .onError(System.out::println);
        }
    }
#

it works so perfectly

#

I made a simple echo bot 😄

restive void
#

Is the channel branch getting merged into main soon or are there more things that need to be done for it?

light loom
#

nopox left review comments, once they're done we can merge ❤️

light loom
#
discord
                    .sendRequest(new CreateMessageRequest(message.channelId(), message.content()))
                    .onSuccess(System.out::println)
                    .onError(System.out::println);
``` we'll need to wrap this ofc
#

or... do we? 😄

#

Now a cool feature that would be nice is... Hot Reloading

#

The reason is hot reloading is totally possible to do because of how we're loading the event listeners

plain ledge
#

Awesome

#

Great work Wazei peepo_heart peepo_heart peepo_heart

plain ledge
#

Maybe we can do like discord.sendMessage which returns a MessageBuilder

#

So you can do like .withEmbeds

light loom
#

Mm yeah maybe we can

#

I did some experimenting with hot reload and I got it working with creating new files but for some reason when I modify an existing one and reload it, the changes are not picked up 😦

#

But that's because of how Java's class loader works, it caches the object once it's been loaded

#

So it means, creating a custom class loader 😅

plain ledge
#

Pain

plain ledge
light loom
#

Yeah definitely, we should use proper builders

strong heart
#

could use a Customizer/Configurer like spring does. java discord.sendMessage( (msgConfig) -> msgConfig .sendRequest(new CreateMessageRequest(message.channelId(), message.content())) .onSuccess(System.out::println) .onError(System.out::println) );

#

(i have no context on this discussion)

light loom
#

thought about it and the spring way is yuck

#

thanks for the suggestion though

light loom
#

@orchid portal ur spotless config sucks

#

it's not formatting everything and leaves inconsistencies

#

for example

#

it won't format the second one

#

or the first one

plain ledge
#

If you update the config

#

Can you wait til we merge enum PR so there aren’t a ton of conflicts

light loom
#

yup

#

ur PR is approved so hit merge!

plain ledge
#

Sweet, thx

light loom
#

@plain ledge i'm just doing your review comments on /channel

#

can you show me a snippet of where the MultiPartBodyHandler is used?

#

oh i found it need to rebase

#

can you explain the update I need to do?

#
    @Override
    public DiscordRequestBuilder create() {
        return new DiscordRequestBuilder()
            .patch()
            .path("/channels/%s/messages/%s".formatted(channelId, messageId))
            .body(
                Map.of(
                    "content", content,
                    "embeds", embeds,
                    "flats", flags,
                    "allowed_mentions", allowedMentions,
                    "components", components,
                    "files", files,
                    "payload_json", payloadJson,
                    "attachments", attachments
                )
            )
            .putHeader(
                "Content-Type",
                files.isEmpty() || attachments.isEmpty()
                    ? "application/json"
                    : "multipart/form-data"
            );
    }
#

I also wanna change the project structure a little bit - and make the models a seperate gradle module

plain ledge
#

@light loom look at the sticker pr

#

Cause it doesn’t use a body you interact directly with the Multipart data handler

light loom
#

ah cool thanks

plain ledge
#

Also maybe while your at it, would you be able to write a integration test for that just to make sure it’s doing the thing properly (assuming imposter checks the headers and stuff)

light loom
#

I don't understand this api

#

I pushed broken code for the EditMessageRequest

#

Let's get back to it later? 🙂

plain ledge
light loom
plain ledge
#

Gimme 35m until lunch starts

light loom
#

(this PR is mega and still work in progress but i'll split it out later)

orchid portal
#

give me a deadline

light loom
orchid portal
#

done

light loom
#

Give this man a raise 👏👏

#

Good job

orchid portal
#

and ig u were talking about formatting

#

yes its not very strict

#

coz then it starts to get bad

light loom
#

It needs to be strict because now we have inconsistency

orchid portal
#

i'll try fix the thing u were talking about

light loom
#

Because it's not formatting parts so they all look different as if a formatter wasn't even used

plain ledge
#

@light loom how much longer are you gonna be on tonight

light loom
#

I'm in bed, what's up

plain ledge
light loom
#

What is itt

plain ledge
orchid portal
orchid portal
plain ledge
#

wtf

orchid portal
#

idk

plain ledge
restive void
plain ledge
#

ah

light loom
#

Once all the PRs are merged (or at least the big ones) I'm gonna do a codebase tidy up

plain ledge
#

@light loom I just put the final touches on guild pr

#

except for one thing, in one of the requests I need to pass in a timestamp in that ISO whatever the full name is format. I think I saw you annotate that inside the request previously which didn't make much sense to me since that shouldn't work.

light loom
#

@plain ledge i approved your PR

#

when we write tests for all this stuff, we can fix it then

#

I think we have like 2 small endpoints left to implement then we've at least got everything in code ❤️

orchid portal
light loom
#

well more so acceptance testing

#

the bits we have all been working on are fundamental components, if they don't work or work as expected, they're high priority fixes for a go-live

#

we have 1 more core component to add which is the wrapper/response handling over these DiscordRequest classes we've made

#

once that's done - we have something that has fully integrated with discord!

#

and given we've only been working on this project for a month, our progress has been really flipping amazing so big thank you to all for the contributions!

#

ready for review

orchid portal
#

i reviewed a merged pr

light loom
#

myb

#

it's okay, I am doing another refactoring

#

it's unclear at the moment which models are used for the gateway specifically and for the API

#

and there's shared models that we can't see clearly

#

so I'll do that

plain ledge
light loom
#

I like the simplicity we have and creating a new class that contains a map of requests <-> model is easier

#

@plain ledge what are ur ideas on the user facing models?

plain ledge
#

Yeah so what we discussed in the enum pr was the models being user facing so we don't have a bunch of repeated models. and then what I was just saying to wazei is if they are gonna be user facing we should move them out of the internal package

light loom
#

besides the package, do have any benefit to writing a wrapper over them?

restive void
#

I'll pick up implementing the webhook api if someone wants to assign me later

restive void
#

Who gave the Small label to the webhook api 💀 its got like 15 endpoints

light loom
#

it does!?

restive void
#

Ya

#

Im 1/3rd of the way in though so its not too bad

light loom
#

oh yeah

#

it does

#

compared to something like channel

#

15 isn't too bad

restive void
#

Ya

light loom
#

it's xs if it's like less than 10 i think

#

or 5

restive void
#

Gotcha

light loom
#

but i've just been going on "feeling"

#

what's ur gh username again?

restive void
#

tferdous17

restive void
#

There

light loom
#

ty done 🙂

plain ledge
#

@light loom So we are creating a mediator DiscordResponse between the request and model

#

Likely a blank interface, just to identify that that record represents a response?

light loom
#

yeah

#

no

#

sorry

#

ill send sample code soon

plain ledge
#

Aight

restive void
#

Do we have something to handle X-Audit-Log-Reason better?

#

Theres a lot of endpoints in webhook api that supports it

light loom
#

mm wasn't it just give the optional param and if it's present, set the header?

restive void
#

Ya

#

Idk if theres a better way to handle it than that

#

Some short .withAuditLogReason() thing (I believe Nopox mentioned it before)

plain ledge
#

Or we can go back and add it to all the endpoints if we decide to later

restive void
#

Is that in the plans or nah

#

Doing it that way

plain ledge
light loom
#
public class DiscordResponseParser {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final Map<Class<? extends DiscordRequest>, Class<?>> map = new HashMap<>();
    static {
        map.put(ChannelCreateInviteRequest.class, Channel.class);
    }

    public static Object parse(Class<?> clazz, DiscordResponse response) throws JsonProcessingException {
        return OBJECT_MAPPER.readValue(response.body(), map.get(clazz));
    }

    DiscordRequestDispatcher discordRequestDispatcher = new DiscordRequestDispatcher("");

    public void sendRequest(DiscordRequest request) {
        discordRequestDispatcher.queue(request).onSuccess(res-> {
            try {
                Channel channel = (Channel) parse(Channel.class, res);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        });
    }
}
#

i was thinking this

#

but we have to do this casting

plain ledge
light loom
#

mm no, it's still internal, we'll need builders for the DiscordRequests we have created

#

e.g.

#

or helpers

#

e.g. discord.getGuild("..").getChannel("...").sendMessage(..);

plain ledge
#

I see

#

I am not gonna be able to work on thst for a couple days since I need to make a wrapper for Roblox api

light loom
#

np 🙂

light loom
teal cargo
#

Was checking your gradle file

#

Only add logging api's

#

implementation is a runtime dependency at best

teal cargo
#

no, no time right now

#

Just letting you know

#

I'll give it a quick look over

#

(god I hate gradle)

light loom
#

there's nothing to hate lol

teal cargo
#

gradle is too complicated for it's own good

#

anyway

light loom
#

Why do you think that?

#

dependency management it's easy :p

teal cargo
#

Yeah, it's called MAVEN central for a reason 😄

light loom
#

yeah because maven created the repo

teal cargo
#

yeah, exactly, use that 😄

light loom
#

we are?

#
repositories {
    mavenCentral()
}```
teal cargo
#

I mean, use actual mvn

light loom
#

the xml is too verbose and messy

teal cargo
#

"ewww it's xml"

#

I do NOT get what people like about gradle so much

light loom
#

the 94 lines of gradle would be 1000 lines of xml

teal cargo
#

If that's your only complaint...

light loom
#

a pretty valid complaint actually

#

also, we're kinda just using it for dependency management and creation of the jar/deployment to maven

#

so the 1-liners are better for us implementation 'org.javassist:javassist:3.30.2-GA' for example

#

or being able to specify the group and version by just doing

teal cargo
#

Hmm, so you don't like verbosity? But you did decide to implement the entire discord api manually yourself instead of generating it from the open api spec?

light loom
#
group = 'com.javadiscord'
version = project.version
light loom
teal cargo
#

Which you even use in your tests...

light loom
#

using the openapi spec, we can create mock endpoints

#

you can't write actual functions automatically to call those endpoints in java

#

if you're thinking like swagger's generator or something familiar, it wouldn't give us the level of control we need, we'd still need to refactor all the classes

plain ledge
light loom
light loom
#

I wanna do a big code cleanup

#

more so, the models and gateway and just re-look at them, see what's missing

plain ledge
#

Ok

light loom
#

lots of files because of the repackage - but it's a small PR in the nature of changes

#

also implemented Application Role Connection Metadata Records

light loom
#
    @Test
    void testDecodingThreadMember() {
        String input = """
                {
                  "id": 1,
                  "user_id": 10,
                  "join_timestamp": "2024-04-25T21:37:44Z",
                  "flags": 0
                }
                """;
        try {
            ThreadMember threadMember = OBJECT_MAPPER.readValue(input, ThreadMember.class);
            assertEquals(1, threadMember.threadId());
            assertEquals(10, threadMember.userId());
            assertEquals(OffsetDateTime.parse("2024-04-25T21:37:44Z"), threadMember.joinTime());
            assertEquals(0, threadMember.flags());
        } catch (JsonProcessingException e) {
            fail(e.getMessage());
        }
    }
#

also added a unit test for one of the models

#

and found the date parsing that was added didn't work so fixed those 🙂

#

(got the sample response body from invoking the mock endpoint)

#

And created a utility for handling those images 🙂

plain ledge
#

Sweeeeet

light loom
#

bro the code looks so nice rn

#

for example

#

this is almost mirroring the API structure perfectly

#

and it's super nice because there's a 1-2-1 match

oak flare
#

the second package lmao

light loom
#

yeah _ for readability

oak flare
#

long ass name

#

is there a convention for long package names btw?

light loom
#

_ or abbrivate

#

see the match? 🙂

oak flare
#

yea

light loom
#

and we can clearly see what's missing

oak flare
#

nice progress

light loom
#

it's all thanks to the contributors!!

#

i just realized the time

#

lel

#

low-key just wanna spend all night working on it pepe_sad

plain ledge
light loom
#
package com.javadiscord.jdi.internal.api.audit_logs;

import java.util.Optional;

import com.javadiscord.jdi.internal.api.DiscordRequest;
import com.javadiscord.jdi.internal.api.DiscordRequestBuilder;

public record GetGuildAuditLogRequest(
    long guildId,
    Optional<Long> userId,
    Optional<Integer> actionType,
    Optional<Long> before,
    Optional<Long> after,
    Optional<Integer> limit,
    Optional<String> reason
)
    implements DiscordRequest {

    @Override
    public DiscordRequestBuilder create() {
        DiscordRequestBuilder discordRequestBuilder = new DiscordRequestBuilder().get()
            .path("/guilds/%s/audit-logs".formatted(guildId));

        userId.ifPresent(val -> discordRequestBuilder.queryParam("user_id", val));
        actionType.ifPresent(val -> discordRequestBuilder.queryParam("action_type", val));
        before.ifPresent(val -> discordRequestBuilder.queryParam("before", val));
        after.ifPresent(val -> discordRequestBuilder.queryParam("after", val));
        limit.ifPresent(val -> discordRequestBuilder.queryParam("limit", val));

        reason.ifPresent(reason -> discordRequestBuilder.putHeader("X-Audit-Log-Reason", reason));

        return discordRequestBuilder;
    }

    public static class Builder {
        private final long guildId;
        private Optional<Long> userId;
        private Optional<Integer> actionType;
        private Optional<Long> before;
        private Optional<Long> after;
        private Optional<Integer> limit;
        private Optional<String> reason;

        public Builder(long guildId) {
            this.guildId = guildId;
            this.userId = Optional.empty();
            this.actionType = Optional.empty();
            this.before = Optional.empty();
            this.after = Optional.empty();
            this.limit = Optional.empty();
            this.reason = Optional.empty();
        }

        public Builder setUserId(long userId) {
            this.userId = Optional.of(userId);
            return this;
        }

        public Builder setActionType(int actionType) {
            this.actionType = Optional.of(actionType);
            return this;
        }

        public Builder setBefore(long before) {
            this.before = Optional.of(before);
            return this;
        }

        public Builder setAfter(long after) {
            this.after = Optional.of(after);
            return this;
        }

        public Builder setLimit(int limit) {
            if (limit > 100 || limit < 1) {
                throw new IllegalArgumentException("limit must be between 1-100");
            }
            this.limit = Optional.of(limit);
            return this;
        }

        public Builder setReason(String reason) {
            this.reason = Optional.of(reason);
            return this;
        }

        public GetGuildAuditLogRequest build() {
            return new GetGuildAuditLogRequest(
                guildId,
                userId,
                actionType,
                before,
                after,
                limit,
                reason
            );
        }
    }
}
#

thinking we do this

#

and for the request sending

#
discord.getGuild("...").getAuditLogRequest(GetGuildAuditLogRequest.Builder);```
#

no lombok because we can't handle do the validation or use optionals like we have

#

or we coul do

#
package com.javadiscord.jdi.internal.api.audit_logs;

import java.util.Optional;

import com.javadiscord.jdi.internal.api.DiscordRequest;
import com.javadiscord.jdi.internal.api.DiscordRequestBuilder;

public class GetGuildAuditLogRequest implements DiscordRequest {
    private record Record(
            long guildId,
            Optional<Long> userId,
            Optional<Integer> actionType,
            Optional<Long> before,
            Optional<Long> after,
            Optional<Integer> limit,
            Optional<String> reason
    ) {}

    private final Record record;

    public GetGuildAuditLogRequest(GetGuildAuditLogRequest.Builder builder) {
        record = builder.build();
    }

    @Override
    public DiscordRequestBuilder create() {
        DiscordRequestBuilder discordRequestBuilder = new DiscordRequestBuilder().get()
            .path("/guilds/%s/audit-logs".formatted(record.guildId));

        record.userId.ifPresent(val -> discordRequestBuilder.queryParam("user_id", val));
        record.actionType.ifPresent(val -> discordRequestBuilder.queryParam("action_type", val));
        record.before.ifPresent(val -> discordRequestBuilder.queryParam("before", val));
        record.after.ifPresent(val -> discordRequestBuilder.queryParam("after", val));
        record.limit.ifPresent(val -> discordRequestBuilder.queryParam("limit", val));
        record.reason.ifPresent(reason -> discordRequestBuilder.putHeader("X-Audit-Log-Reason", reason));

        return discordRequestBuilder;
    }

    public static class Builder {
        private final long guildId;
        private Optional<Long> userId;
        private Optional<Integer> actionType;
        private Optional<Long> before;
        private Optional<Long> after;
        private Optional<Integer> limit;
        private Optional<String> reason;

        public Builder(long guildId) {
            this.guildId = guildId;
            this.userId = Optional.empty();
            this.actionType = Optional.empty();
            this.before = Optional.empty();
            this.after = Optional.empty();
            this.limit = Optional.empty();
            this.reason = Optional.empty();
        }

        public Builder setUserId(long userId) {
            this.userId = Optional.of(userId);
            return this;
        }

        public Builder setActionType(int actionType) {
            this.actionType = Optional.of(actionType);
            return this;
        }

        public Builder setBefore(long before) {
            this.before = Optional.of(before);
            return this;
        }

        public Builder setAfter(long after) {
            this.after = Optional.of(after);
            return this;
        }

        public Builder setLimit(int limit) {
            if (limit > 100 || limit < 1) {
                throw new IllegalArgumentException("limit must be between 1-100");
            }
            this.limit = Optional.of(limit);
            return this;
        }

        public Builder setReason(String reason) {
            this.reason = Optional.of(reason);
            return this;
        }

        private Record build() {
            return new Record(
                guildId,
                userId,
                actionType,
                before,
                after,
                limit,
                reason
            );
        }
    }
}
#

though...

#

it conflicts with the design of this being a internal class

#

so we could make a core.builder package that houses the builders instead of doing it within the records

#

might be better so we don't have to touch the records

#

so this instead:

#
package com.javadiscord.jdi.core.builder;

import com.javadiscord.jdi.internal.api.audit_logs.GetGuildAuditLogRequest;

import java.util.Optional;

public class GetGuildAuditLogRequestBuilder {
    private final long guildId;
    private Optional<Long> userId;
    private Optional<Integer> actionType;
    private Optional<Long> before;
    private Optional<Long> after;
    private Optional<Integer> limit;
    private Optional<String> reason;

    public GetGuildAuditLogRequestBuilder(long guildId) {
        this.guildId = guildId;
        this.userId = Optional.empty();
        this.actionType = Optional.empty();
        this.before = Optional.empty();
        this.after = Optional.empty();
        this.limit = Optional.empty();
        this.reason = Optional.empty();
    }

    public GetGuildAuditLogRequestBuilder setUserId(long userId) {
        this.userId = Optional.of(userId);
        return this;
    }

    public GetGuildAuditLogRequestBuilder setActionType(int actionType) {
        this.actionType = Optional.of(actionType);
        return this;
    }

    public GetGuildAuditLogRequestBuilder setBefore(long before) {
        this.before = Optional.of(before);
        return this;
    }

    public GetGuildAuditLogRequestBuilder setAfter(long after) {
        this.after = Optional.of(after);
        return this;
    }

    public GetGuildAuditLogRequestBuilder setLimit(int limit) {
        if (limit > 100 || limit < 1) {
            throw new IllegalArgumentException("limit must be between 1-100");
        }
        this.limit = Optional.of(limit);
        return this;
    }

    public GetGuildAuditLogRequestBuilder setReason(String reason) {
        this.reason = Optional.of(reason);
        return this;
    }

    public GetGuildAuditLogRequest build() {
        return new GetGuildAuditLogRequest(
                guildId,
                userId,
                actionType,
                before,
                after,
                limit,
                reason
        );
    }
}
#
discord.sendRequest(
                new GetGuildAuditLogRequestBuilder(1L)
                        .setLimit(10)
                        .setBefore(99999999)
                        .build());
#

i don't like the syntax, might be too complicated for a user

#

I'll sleep on it

plain ledge
#

like

#

that returns an instance of the builder, with the guildid and stuff already ste

#

guild.sendMessage() <-- returns a CreateMessageRequestBuilder

light loom
#

I thought of a solution

#

Subconscious working overtime CEHSweatSmileTurtle

plain ledge
light loom
#

I'll explain it in detail later but essentially, in the cache, we have a Guild model. That's essentially our way into getting guild specific data (cache)

#

Now what if, instead of returning the data model, we return a different Guild object

#

One that has all the helper methods we need

#

However, we can future create a hierarchy

plain ledge
#

Like a GuildWrapper(Guild guild) and then delegates all the methods to guild?

light loom
#

Yeah

#

But the guildwrapper would be a proxy object (or similar)

#

To the real handlers

#

So in each API package, we can have a class that contains all the actual request builders

#

Now for syntax we have 2 choices:

guild.sendMessage(channel, message)

#

Or

#

guild.getChannel(id).sendMessage(message)

#

or if those methods are not appealing, we can do something else

#

Alternatively, we could try get very black-magic

#

And try something using reflection and under the hood do some wacky complex logic

#

What a hacky black magic solution looks like? Idk

sharp ferry
light loom
#

Yeah maybe? I was thinking the same but I'm not sure if it's the "best approach" yet

#

Since, I've only identified 2 ways

light loom
#

peeps don't hateme

#

big PR:

orchid portal
#

projects*

light loom
#

Here's a breakdown

#

annotations: independent - just contains stuff like @EventListener or @MessageCreate

#

models: independent - just contains records

#

api: semi-independent - requires :models

#

cache: independent - unit tests require :models

#

gateway: not-independent - requires :models :annotations :cache

orchid portal
#

without the rest of the spring

light loom
#

wym it can't do anything?

orchid portal
light loom
#

no so this modularity is for us

#

the end user will import

#

:core

#

core is the final result of everything

#

which is what we deploy to maven

#

this is just modularising the project for development - not for end user dependencies

#

the only module that's not independent is :gateway

#

actually

#

it is

orchid portal
#

none of the module is independent

light loom
#

how are they not independent?

orchid portal
orchid portal
light loom
#

they can function on their own they just have dependencies on the non-functional components i.e. models and annotations

#

there's a little dependency on :cache but is its own thing that can be deployed as a separate artifact for anybody to use

#

it's either we duplicate all the models/annotations into each module or we just put them in their own jar (like we are now)

#

however once we generate the final artifacts

#

gateway can run on its own

#

api can run on its own

#

they can be deployed and used separately by other applications

#

e.g. if somebody wanted to make their own discord framework but did not want to implement the gateway themselves, they can just add the :gateway dependency and start using it

#

or if they want to use our models so they don't need to write it themselves, they can use :models in their own projects

#

:core is our framework which creates our specific discord framework

#

sure the modules are not 100% independent of each other - but that's normal - often in the real world you'll see shared or utils being modules that are used between sub-projects

#

if we're making independent artifacts, they would then be inside their own repository

restive void
#

Unsure if I should be putting more than just payload_json into multipartbody for Execute Webhook request

@Override
    public DiscordRequestBuilder create() {
        DiscordRequestBuilder discordRequestBuilder =
                new DiscordRequestBuilder()
                        .post()
                        .path("/webhooks/%s/%s".formatted(webhookId, webhookToken))
                        .multipartBody(
                                MultipartBodyPublisher.newBuilder()
                                        .textPart("payload_json", payloadJson)
                                        .build()
                        );

        waits.ifPresent(val -> discordRequestBuilder.queryParam("wait", val));
        threadId.ifPresent(val -> discordRequestBuilder.queryParam("thread_id", val));

        return discordRequestBuilder;
    }
light loom
#

@restive void

#

Leave it :3 it's already been done pepe_sad pepe_sad peepo_heart peepo_heart

restive void
#

Wym

#

Wats already been done lol

light loom
#

The endpoint

restive void
#

Someone did Execute Webhook before me?

light loom
#

oh it's webhook

#

i thought it was poll

restive void
#

No lol

#

Im still on the webhook API

#

Did u include everything into mulitpartbodyhandler for the poll api?

light loom
restive void
#

Ight u guys can double check it later when i open a PR anyway

light loom
#

of course!

#

the reason being, it's easier to rebase your PR then will be mine pepe_sad

restive void
#

Ya im not finished with the webhook api anyway

light loom
#

ty

plain ledge
#

wazie,shouldn't the annotations be in gateway since that's their only use?

light loom
#

for example

#
import com.javadiscord.jdi.core.Guild;
import com.javadiscord.jdi.core.annotations.EventListener;
import com.javadiscord.jdi.core.annotations.MessageCreate;
import com.javadiscord.jdi.internal.models.channel.Channel;
import com.javadiscord.jdi.internal.models.message.Message;

@EventListener
public class Example {

    @MessageCreate
    public void onMessageCreate(Message message, Guild guild) {
        Channel channel = guild.getChannel(message.channelId());
        System.out.println(channel.name());
    }
}
#

I added a new Guild object :3

#

the logic behind getChannel is kinda messyyyyyyyyyyyyyyyyyy

#
    public Channel getChannel(long channelId) {
        CompletableFuture<Channel> future = new CompletableFuture<>();
        if (cache.isCached(channelId, Channel.class)) {
            future.complete((Channel) cache.get(channelId, Channel.class));
        } else {
            discord.sendRequest(new FetchChannelRequest(channelId))
                    .onSuccess(
                            res -> {
                                if (res.status() == 200) {
                                    try {
                                        Channel channel =
                                                OBJECT_MAPPER.readValue(res.body(), Channel.class);
                                        cache.add(channelId, channel);
                                        future.complete(channel);
                                    } catch (JsonProcessingException e) {
                                        future.completeExceptionally(e);
                                    }
                                } else {
                                    future.completeExceptionally(
                                            new RuntimeException(
                                                    """
                                    Failed to fetch channel %s, received status %s
                                    %s
                                    """
                                                            .formatted(
                                                                    channelId,
                                                                    res.status(),
                                                                    res.body())));
                                }
                            })
                    .onError(future::completeExceptionally);
        }
        try {
            return future.get();
        } catch (Exception e) {
            return null;
        }
    }
#

fkin ugly right?/

plain ledge
light loom
#

it's not used at all by the gateway

#

my bad

#

the module is

#

it's used by :core and :gateway

plain ledge
#

The other modules make sense:

  • models are used in api and gateway,
  • but annotations are only used ingateway
light loom
#

you're right but

#

consider this:

#
import com.javadiscord.jdi.core.annotations.EventListener;
#
import com.javadiscord.jdi.internal.annotations.EventListener;
plain ledge
#

I don't think I understand what you are showing

light loom
#

ah when a user imports the annotation

#

which package should they import from?

plain ledge
#

1st

#

obv users shouldnt use internal

light loom
#

but :gateway is com.javadiscord.jdi.internal

plain ledge
#

hm

#

ig

light loom
#

yeah unless we just create a core package inside :gateway?

#

the idea is "bad", maybe it leaks a bad design?

#

though, i have been thinking of what taz said

plain ledge
#

ig

light loom
#

maybe we should make annotations a thing where a user adds it and then they have access to annotation based processing

#

and without it, they can do manual binding?

#

have to think about it hard and what that means for refactoring

#

but it's minor organisational topic not really affecting functionality

#

we can decide how to organise and structure folders etc later ig

plain ledge
#

alright

plain ledge
#

@light loom

#

Is it ready for review?

light loom
#

no

#

:3

#

I forced merged 😄

#

but wait - i am doing some stuff

#

I have another one

#

I'm also making it so that :annotations is independent i.e. if a user does not include the lib then they cannot use annotation based processing. instead they have to do discord.registerListener(new SomeClass())

plain ledge
steep spruce
#

do they just add the annotations to methods and go on with their day?

steep spruce
oak flare
#

return null 💀

light loom
#

I've untrashed it don't worry pepekek

steep spruce
#

good

steep spruce
#

how is it handled internally

light loom
#

Scan class path, find annotations, instantiate them

steep spruce
light loom
#

Not really, it's pretty quick

steep spruce
#

meh idrc about performance anymore I'm less of a premature optimizer now lol

quicker is better tho
looking forward to using this and asking questions

light loom
#

It takes me maybe 5 seconds to compile and have the bot online

#

Scanning the class path takes 1ms since it's already available

#

It's only just reading the bytecode but there's a library that does it quick af for us

steep spruce
light loom
#

Yeah there's reflection involved

steep spruce
#

whats missing in this library rn

steep spruce
light loom
#

Java has built in stuff e.g. method.hasAnnotation(..)

light loom
#

And gosh this one is gonna take ages even with a bunch of us working on it

steep spruce
#

well idk much about creating internals of a discord bot wrapper

#

I tried to make cooldowns and concurrency for a popular python discord lib but I didn't even know about concurrency back then or even a simple data structure like a HashMap lol

restive void
#

Opened a PR for webhook api

plain ledge
#

@restive void

#

In this what are those Object's

#

also I think for files you need to use filePart

#

It should take in Path

orchid portal
#

also ig hes using old spotless?

plain ledge
orchid portal
#

also ig for payloadjson we should accept Optional<Map<String, Object>>

orchid portal
plain ledge
orchid portal
#

it uses the branch spotless config

restive void
#

Ill try incorporating the filePart stuff tmrw

plain ledge
restive void
#

Gotcha

oak flare
oak flare
#

Then give me context

light loom
#

if it's present the API will respond differently i.e. the data is going to be different (structure the same)

#

so we only include the fields in the request when optional.ifPresent

light loom
#

Hi peeps, can we do a little retrospective? it's been a month and here's our current achievements:

  • Implemented the websocket gateway
  • Implemented almost ALL of discord API
  • Created an annotation processing system
  • A good start on the user interface
  • Create and followed an agile board on GitHub

We have done a really good job for only working on this for 1 month.

For the retrospective, could you just drop a message on:

  1. What went well?
  2. What could be done better?
  3. Any problems you have with the current management of this project? Is it working? Can we improve?
  4. Any other comments?

Thanks!

@orchid portal @restive void @plain ledge @sand peak

light loom
#

(take that back, I forgot to finish GuildRequest)

plain ledge
#

@light loom

#

what are we using javaassist for?

light loom
#

uh I think I was reading bytecode when I was doing the hot reloading

#

Or I'm using it to get the class name from a .class file as part of the annotation processing

#

Will confirm when I get out of bed

sharp ferry
light loom
#

Right now, we've done development using java 21, but I'll work on migrating down to java 8 so I can support everyone

light loom
#

Why? 👀

sharp ferry
#

Keeping projects with old version make everything go backward because nobody will update

#

Ah and also, java 8 is no longer supported

light loom
#

Ah is it not? Then at least java 11, the reason being, AWS for example don't give latest java unless you manually install it

#

And not being able to run on older versions might be a deal breaker for people wanting to host bots on servers that haven't got updated versions

plain ledge
orchid portal
#

ok probably 8 wont work with 22

light loom
#

can somebody finish the GuildRequest class for me? pepe_sad

steep spruce
#

use java 21

#

it's so much better

light loom
#

no

#

the reason being, not everyone is using java 21 so anybody on an older version cannot use this framework we're making

oak flare
#

ok but? why should we care, they should simply use java 21 then

steep spruce
#

use java 17 minimum

light loom
#

heck, I don't even use java 21 on some projects because intellij defaults

oak flare
#

enforce latest version

steep spruce
#

yes what squid said

light loom
#

it's a simple "oh i need java 21? dunno how to install, cya"

oak flare
#

?

steep spruce
#

?

oak flare
#

dunno how to install
no one ever

light loom
#

they do wym lol

#

have you not seen #1051826284008853505 ?

oak flare
#

especially not people that are working with java frameworks and maven/gradle

light loom
#

AWS doesn't even support java 21 unless you manually install it

oak flare
#

and?

steep spruce
#

also all you do is go to the website

#

and install it

light loom
#

the point is, everyone should be able to use it

oak flare
#

yeah they can

light loom
#

without being forced to change their current system

steep spruce
#

literally if someone doesn't know how to install java they wouldn't be learning it because they can't use it

#

simple as that

oak flare
#

well everyone should have latest version anyways, so they dont need to change

light loom
#

but they don't

steep spruce
#

sure maybe they exist

oak flare
#

but why should we care

#

it makes no sense

light loom
#

we care because we want everyone to use our framework?

oak flare
#

its not our part to use outdated stuff

oak flare
light loom
#

they won't

oak flare
#

bro I wont contribute to some project using outdated java ngl

steep spruce
oak flare
#

there is literally 0 reason to not use latest version

#

enforce latest version, they need to update sooner or later anyways

steep spruce
light loom
#

you can still write in java 21

steep spruce
#

but in those very very rare senarios there is like 0 discord development

light loom
#

there's just going to be a java 17 branch alongside it

steep spruce
#

infact that makes it worse

light loom
#

i just looked at what's LTS

oak flare
#

dumb imo 🤷‍♂️

steep spruce
#

anything you make to 1 branch you make for the other

plain ledge
#

Yeah, I think supporting java 8, 11 is useless

light loom
#

yeah i didn't realise 11 ended

plain ledge
#

It’s gonna be aids to maintain