Giter VIP home page Giter VIP logo

crbt-team / purplet Goto Github PK

View Code? Open in Web Editor NEW
64.0 5.0 1.0 2.15 MB

A framework for quickly building Discord bots + more packages relating to the Discord API, with a primary goal on a rich developer experience.

Home Page: https://purplet.js.org

License: Apache License 2.0

TypeScript 80.38% JavaScript 18.77% CSS 0.82% Shell 0.03%
framework typescript discord-api discord-interactions discordbot vite bun discord discord-bot discord-bot-framework

purplet's People

Contributors

clembs avatar morganlabs avatar paperdave avatar trubiso avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Forkers

arbawistudio

purplet's Issues

create purplet ignores the prettier option

i was told by a friend that the bootstrapper doesn't actually work with the prettier option.

"do you want to add prettier"

*[Y]*

doesnt get installed

i think they used npm but idk

Guild Manager app

the guild manager is meant to be a small tui utility that logs in with the current bot token, and shows a list of guilds that the bot is in, and allows the user to leave any, or add them to the current PURPLET_{INCLUDE,EXCLUDE}_GUILDS variable. this is important as in dev mode, there is a hard limit of 75 guilds, and you get a warning at 5 guilds. this tool will be pointed at devs who are unsure what bots their development bot is in and want to make it leave guilds.

CLI Docs

The cli is documented when you run it, but docs/70-cli.md does not have the documentation. A simple copy and paste with proper markdown headers would suffice.

Due to this issue, this page is empty https://purplet.js.org/docs/cli

the Tutorial For Absolute Beginners

I forgot about this until just now. We have a broken link at the start of the docs about this.

Clembs has said they can get working on this, and last i've heard this may include video tutorials as well as text based ones.

I just want to throw out there that Docusaurus supports react for pages, so that may be useful maybe not idk.

Our own rest client

working with @discordjs/rest is less than ideal, and glancing at it's code makes me feel like it's overcomplicated. I would like to have our own rest client, simply a set of functions that wrap fetch, implement rate limiting, errors, and (most important) provide typedefs for each known API route, because discord.js' doesn't do this for some reason.

if anyone wants to take this task up, I think a good name would be @purplet/rest (then a future @purplet/gateway), but the exact API I'm unsure of, maybe something like rest.resourceName.actionName() where these just mirror the discord api docs, such as rest.channel.createMessage({ opts }) but also rest.methodName(url, opts)

Deno builds for purplet's libraries

I'll be honest, i do not care to make the framework work on Deno. It's kind of slow runtime. But, I think it would be valuable if libraries like @purplet/gateway could run on Deno. The main hurdle is packaging.

If anyone knows about publishing deno packages, I would love some help out there

Implement all data model classes, with methods that wrap the REST api

the structure/"data model" classes are things like Interaction, and all of them primarily function by wrapping a raw data object passed to the constructor then accessor methods for every property, converting sub objects into more rich types & adding methods that wrap the rest api. these structures are mostly immutable.

one system I have to get better typed classes for things such as a partial channel (where you only have a string id), I have a typescript type that casts a class to an empty looking one, where supported properties and methods are available. for example, the PartialUser class does not have banner data but it does have a name and discriminator, and the fetch method will fetch the full user object.

before we go and blindly implement everything under this system, it might be worth a lot to consider a different approach to creating all these rich data models (I now really like that name name, "rich data models"). this might involve a mini library that assists in creating classes that wrap a data object, where each computed property defines a list of dependencies, and then with typescript types we can create an infinite amount of partials in any shape we need, with an interface like PartialModel<User, "id">, where id refers to the raw data, generating a class definition that only shows properties and methods that would be safe to call. under this system and the current one, the actual class object that js runs would always be the same.

I dont have a code example of what the library would look like, but it should be easy to add new properties to models, and hopefully not perform too slowly compared to just writing a plain class (maybe run faster if we allow computed props to be cached, maybe not).

  • decide if we want a different system for defining rich models
  • TODO, insert a checklist of every class we need. and all rest methods.

Rest: Support shared/external rate limit tracking.

Something that might be interesting is if you could pass some object as a rateLimitTracker, which would be an interface that could accept, store, and return rate limit data observed by the fetcher. The idea being if you had rate limits stored on each shard of a bot (where shards are separate instances, maybe even on separate computers), you could optionally link them up using something like Redis. The idea being if all instances of a bot share the same rate limit store, it should reduce the chance of a 429 being returned during high usage.

Opt in behavior, we'd essentially refactor our rate limiting system to a MemoryRateLimitTracker which would be the default for the rest client.

Internationalization/Bot Translations

I would like to have a built in system for bot devs to translate their bot easily. We would use Project Fluent to allow dev-set translation keys but also purplet provided keys for stuff such as command metadata.

Each project would have a primary language, currently available as config.lang. Hooks like $slashCommand would use data passed to it as the primary language, and other locales would have a key such as command.hello-world.name, alongside description and option metadata. I think FTL has good syntax for these object style keys as well.

The purplet core will provide loading (and hot reloading) for the locale files, as well as a function "t" which has the following API:

  • a familiar t(lang, key, opts?) -> string result
  • an Interaction object (or anything with a .locale) is valid to pass as lang
  • alternate t(key, opts?) -> LocalizedStringwhere passing the resultingLocalizedString` to an interaction response or any other method where we know what the locale is, it will resolve to the right format

As for implementing the last point of the API, I've already done some work with the toJSONValue method but that might need some modifications (cause type errors where JSONResolvable is allowed but LocaleJSONResolvable might not.

As for the rest, I would like someone else's thoughs before moving on, but also this is a huge task to do so it has a low priority.

separate builders to another package (`@purplet/builders`), and also re-export/add other builders

Two points:

  • The docs mention ButtonBuilder, which isn't something we export. It's from @discordjs/builders.
  • I would like to move purplet's src/builders folder into it's own package.

This potential package would either re-export useful builders like ButtonBuilder, or just re-implement them. I'm kind of siding on the re-implement side of things, especially since djs builders has 5 dependencies, while the builders we have made ourself only depend on discord-api-types.

Though, we probably would keep the util types OptionBuilderEntryTo* from OptionBuilder inside of purplet itself as those refer to other parts of purplet.

Testing (developers)

i would like to wrap vitest or whatever is needed to allow a purplet test. the minimum would allow you to test your $lib functions, the ideal would be the ability to mock and simulate slash command runs to ensure their responses are correct.

build command for HTTP Interaction Endpoints

so far we have HTTPBot, which (untested) can handle loading features then running the $interaction core hook. we just need

  • to ensure httpbot is finished and functional
  • a build command
  • adapters to make actual http servers and cloud functions.

I'm unsure on the exact structure the adapters should follow but sveltekit's adapter system might be something good to look at, though we are different as we have two main build types (and different gateway adapters might be a worthwhile feature.)

Shared rollup configurations

Besides a couple of minor differences, all of our packages share the same rollup configuration. As we add more packages, this would become more useful.

$mentionCommand does not work at all

Once #9 is implemented, we can fix the issue present in $mentionCommand ever since we dropped discord.js. I believe there is just a null in place of botUser.id

Document the built-in hooks.

Only the larger and most important topics like slash commands and buttons are actually documented, but the small hooks like $intents are not. It can just be a page of misc hooks that don't need a whole page explaining.

for the tsdocs, we can just copy paste each hook's description.

Gateway: `zlib-stream` compression on Bun

i've been told about fflate which works on Bun, though so far I haven't gotten it working. Either way, we don't have zlib or etf support on Gateway on Bun.

  • todo: see if fflate.Unzlib is the actual class i should be using.

Small improvement to how we encode custom_id

Right now our custom ids are composed of these elements:

  • the purple circle emoji ๐ŸŸฃ (to tell our components against something developer custom)
  • a single character describing the length of the id
  • serialized string containing the feature id
  • serialized generic containing the context object

we could save a very small amount of compression and improve speeds by using one serialization containing the feature id and context in one bit buffer. the string serializer contains the length so we wouldn't need the extra work.

Testing (purplet itself)

It would be very good if our own code could be unit tested. There's some tests already but I wasn't happy with the testing framework setup. If anyone knows how to setup a good testing foundation that would be a very nice addition to our codebase. Not to be confused with #12 which is testing for projects using purplet.

This is something we could consider using bun:test for, at least the stuff in our framework that can run within bun, but for now I think the proper thing to use is vitest.

Test all rest route's functionality

At the rest branch we currently are tracking implementation and functionality of the routes, this file specifically.

I will publish a package soon but there's some things I need to finalize first, but if anyone would like to help test route functions by actually writing a code example for them and testing practically with a real token, that would be appreciated. I doubt any routes will fail and if the types were wrong, that's on discord-api-types and not us.

Interaction error responses

If an interaction handler throws and the interaction wasn't responded to, we could consider calling .showMessage with a formatted error.

Two forms of errors should be accepted, one being a custom error class like InteractionError, which contains CreateMessageData and can be passed directly as the response, with normal errors having a preset formatter.

This behavior should be configurable within the config, so we might be depending on #9. Config should follow this schema:

  • interactionErrors.enable if this behavior is enabled or not
  • interactionErrors.formatter a function to format an Error to a CreateMessageData
  • interactionErrors.ephemeral (true default), specifies what the ephemeral flag is default to, if the formatter doesn't specify this flag.
  • interactionErrors: false disable entirely
  • interactionErrors: true enable with default settings

Support running with bun

bun is a new runtime, package manager, transpiler, and bundler- all in one. still experimental, but I believe it is the future as it's current benchmarks crush node.

some people in the bun discord were thinking about was some bun version of discord.js (I heard buncord as a name being thrown around), but i believe the best approach would be to make something that would work in both node AND bun, as bun just implements web standards like WebSocket and fetch, so we would just have to polyfill these types of things with the npm packages we use currently. then we would also have to take a look at replacing packages that do not run in bun, but jarred is constantly making things work so we also have the option to wait.

this issue can be two things

  • the creation of a separate package that acts as a discord api library instead of a full framework
  • making the framework as a whole run in bun (and node)

list of packages that we depend on that do not work in bun

I tested this on July 7th:

  • @discordjs/rest, but I plan to ship my own rest client
  • yargs, has a process.version check. since I overwrite our cli formatting I think it would be best we ship our own arg parser or switch to another one. I don't know any other arg parsers so I would love any suggestions.
  • chalk, due to a not implemented feature in bun with package json imports field
  • ora as it depends on chalk
  • fast-equals due to a bug in bun loading esm files but in cjs mode breaking.
  • vite and rollup, but our usage of this is for the framework which I think would be better to use Bun's transpiler and bundle API, but this means we'd have to maintain multiple versions of the dev and build process. I have a feeling that we might just see a bun-accelerated polyfill for these packages later on.
  • prompts, I don't know why it just fails at runtime
  • chokidar, it works but never emits events when I tried it.

I'm going to post some issues relating to splitting packages off, and if any of those happen they should have this issue in mind when they do so.

Shard Management

currently, there is no system in place for shards: calculating them, and also spawning them. we should do this through the /gateway/bot route, but it should also have some API to allow user defined behavior with this when you intend to run on multiple threads/instances/computers. (edit, Gateway supports a shard property but this is only for one instance and you must manually spawn multiple instances of it)

idk the proper API for this or the exact implementation of a shard manager, and would like some feedback frol others. one idea I have is a simple property/env with a machine id+total count can be given, then each instance of the shard manager would equally login with the specified shards (aka the same system actual shards use but abstracted). I'll have to look into existing systems for how it works.

replace discord link in cli and create

we split off from crbt community as the scale of this project is growing immensely and most people interested in this are primarily interested in this project itself.

I'm on my phone so I don't have a url on me to join, but anyone who'd contrib this basic chore besides me is gonna already be in that server lol.

Separate `utils` and `logger` into separate packages.

I was avoiding this because I felt it was overkill, but it might not be since we are planning on splitting a lot of code up. I might also consider moving some of them into my personal utility package @davecode/utils as i did with asyncMap.

move global state into "purplet/env"

purplet has a global variable "rest" which is the rest client, but there are a couple more I'd like to have (config, botUser, gateway, env, and maybe some other useful things). I want these as a separate export so we can throw an error if they are imported outside of the context of the framework, since they will all be undefined then.

rough steps to implement:

  • expand global.ts to have extra variables, and modify the set function inside of it to be setEnv(object) instead. rename env (the file we already have with setupEnv should be renamed to something about polyfilling)
  • add a file that immediately throws an error, named like env-stub.ts or something fitting in the root. add this to the rollup build for env, implementing the error.
  • add the env.ts to the rollup build as env-internal or something
  • edit the vite or purplet config resolver to include the alias from purplet/env to purplet/internal-env, so the imports resolve properly. there's a bug with build aliases not working, ignore that for now unless it's fixed.
  • edit the build so that internal d.ts files are not generated and the one for the env (1) appears, and (2) is correct.

I consider this to be low hanging fruit, besides the part where we populate the variables with content and fix the build. feel free to submit a pr without these two things.

create-purplet eslint support

I would like there to be an option when using create-purplet: Add ESLint for code linting?. Its already there but commented on line 201, mainly because we don't have it implemented if they were to say yes.

Update to vite stable

We currently pin vite@alpha as we need the ssr.external config option. Once vite@3 is released, we should install it. While it's proved to be highly stable in this project, we still should wait for this before publishing Purplet 2.0 stable.

Rest: Rate limiting issues on Bearer token requests

reported by hycord via discord

const restClient = new Rest({ token: access_token, tokenType: token_type })
  let userData = {};
  try {
  console.log('restClient > getCurrentUser : getCurrentUser()')
  userData = await restClient.user.getCurrentUser() } catch (e) {
    return {
      status: e.status,
      body: { message: e.message }
    }
  }
  console.log('restClient > getCurrentUser : getCurrentUserGuilds()')
  userData.guilds = (await restClient.user.getCurrentUserGuilds()).sort((a) => a.owner ? -1 : 0);โ€Š

for some reason it is throwing rate limit on getCurrentUserGuilds

I am also reminded I never handled global rate limiting so lol

Setup ESLint

eslint is setup, however we do not use it due to paperdave/various#1, which is from my personal eslint config. i never set it up to use typescript properly so we get a lot of lint errors when it comes to it.

once that closes i'll setup eslint project wide

Split the Gateway Client into it's own package

Related heavily to #6 but this will track progress towards just extracting the gateway client it's own package instead of how it is right now, heavily integrated. One thing on my mind is the fact it can throw CLIError, which is related to the cli.

Long term plans outline + Name discussion

My goal with this project has always been to give developers a full-framework experience for building Discord bots, though I have started splitting things from the megapackage that purplet is into simpler and smaller chunks like @purplet/gateway, @purplet/polyfill, soon coming @purplet/rest, and in the next weeks we will see a ton more, including a library that replaces the use-case of discord.js without being the full framework that purplet is. I guess my new goals have changed to "giving TS developers really good interfaces to interact with the Discord API".

With these packages I also started to support the Bun runtime, as I believe it is the future, though my methods of supporting are only to make my code not depend too tightly onto Node APIs, but rather Web Standards. We then add missing web APIs like fetch, WebSocket, and FormData via the @purplet/polyfill package. When it comes time to making the purplet, the framework itself work, that may involve a different approach as we have the opportunity to bun-accelerate compliation using Bun.Transpiler, and so on. For now, I'm holding off on making the framework cli work in Bun and will probably wait for upstream to implement vite support and use that. In the future, we might go bun-only, but node needs to be completely replacable which it is not and will not be for at least a few years.

So back to that package that replaces discord.js. It's going to wrap the gateway, rest, and structures packages we already have; being a library exporting an object similar to Discord.Client, opens a gateway connection and emits structure classes for gateway events. I think a fun name to call that package is buncord- it was something @Hycord mentioned somewhere on Discord as a hypothetical. It definitely beats @purplet/<any string> for sure. I'm unsure if that package will be depended on by purplet, probably not as the interface given might be too strict to cover gateway + endpoint uses across that package. However, the important thing about these two packages is that code written within a purplet $interaction should be identical to a buncord .on('interactionCreate') event, due to shared dependencies on @purplet/structures.

We are now at a weird situation where this project has two top-level packages: purplet and buncord. Hycord has wanted to go embrace buncord as the npm scope we use for publishing, and other branding changes, but I'm not so sure that would be a smart move, as it would include a lot of package movement. But if this is truly the right call, it would be best to make it before we continue. I am holding back publishing our rest client until this is resolved.

My personal opinion is Purplet should be the overarching name of the project, keeping buncord as one public component of the larger picture. I also don't think it makes sense to rename (again) @purplet/serialize, since buncord shouldn't be depending on this at all (similar situation with future packages I am planning on like logger from #28). We also already have an amount, but not absurdly large amount of branding and a set of users are already aware of this project as a whole to be "Purplet".

Also, the buncord package, depending how we make it work, might be an extremely minimal implementation, as it will primarily be wrapping our other packages into a more useful tool for people who desire a library as opposed to a framework.

I would like to hear other opinions before I use my position as 'code owner / maintainer' to go forward with Purplet being the core name of all of these Discord API projects. In the end, it's not the name that matters, just the actual code we're making. We've grown to a codebase of over 9300 lines, and we're only at the start of a very promising toolkit.

$modal hook

modals are very similar to message components except for a couple of differences. for our $modal hook, it would work extremely similar to the message components, so they should definitely be using a shared API. all that needs to change to allow this is loosen the types, mainly making the interaction type be { customId: string } instead of a component interaction, as a modal submit is a completely different interaction type.

another thing I would like to do (but we can move it to a separate issue) is the creation of a ModalBuilder which functions similar to the OptionBuilder, being able to pass field IDs and their data types to the modal submit handler, though this might be weird if the modal create function is dynamic, so whatever is passed to the handler should have easy methods to get and assert existence of stuff on modals.

Rest client has untyped methods for some thread actions

The following methods do not have types unless they are named something nonstandard.

  • channel.listPublicArchivedThreads docs
  • channel.listPrivateArchivedThreads docs
  • channel.listJoinedPrivateArchivedThreads docs

I have an issue open upstream at discordjs/discord-api-types#526 inquiring about this. if enough time goes by, i'll take a deep look though api types and figure out how to implement them.

I would add the low hanging fruit tag but i don't think any action of touching gen-routes.js is low hanging haha.

`No such file or directory, open '...'` on every command

Full error:

[ Error ] ENOENT: no such file or directory, open 'mnt/Data/.../interaction.ts'
    at Object.openSync (node.js)
    at Object.readFileSync (node.js)
    at getModuleName (purplet)
    at file:///mnt/Data/CRBT/node_modules/purplet/dist/index.mjs:879:34
    at String.replace (<anonymous>)
    at file:///mnt/Data/CRBT/node_modules/purplet/dist/index.mjs:853:21
    at Array.map (<anonymous>)
    at cleanStackTraces (purplet)
    at process.handleError (purplet)

This file 100% exists however it throws this error after every command. I think this is because if you look where the path is, there's no / before it. Maybe it's running from a different folder and trying to find mnt within it? This error only seems to happen on Linux.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.