{"id":16374981,"url":"https://github.com/moderocky/eris","last_synced_at":"2025-10-26T08:30:51.138Z","repository":{"id":45206412,"uuid":"513411398","full_name":"Moderocky/Eris","owner":"Moderocky","description":"A library for interacting with the Discord API at a very low level.","archived":false,"fork":false,"pushed_at":"2024-09-03T13:30:56.000Z","size":492,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-31T17:12:58.038Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Moderocky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-13T06:48:02.000Z","updated_at":"2024-09-03T13:31:01.000Z","dependencies_parsed_at":"2024-08-13T13:12:30.762Z","dependency_job_id":null,"html_url":"https://github.com/Moderocky/Eris","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moderocky%2FEris","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moderocky%2FEris/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moderocky%2FEris/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Moderocky%2FEris/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Moderocky","download_url":"https://codeload.github.com/Moderocky/Eris/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238293275,"owners_count":19448156,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T03:19:02.030Z","updated_at":"2025-10-26T08:30:45.778Z","avatar_url":"https://github.com/Moderocky.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"Eris\n=====\n\n### Opus #19\n\nA library for interacting with the Discord API at a very low level.\n\n### Description\n\nEris provides simple access to the Discord API without smothering data in layers of needless, slow wrappers. \\\nIt also avoids forcing users into particular design choices (futures, lambdas.)\n\nThe library is designed to be:\n1. Simple, so that a beginner can use it.\n2. Non-restrictive, so that users are not forced to use it in a certain way.\n3. Modifiable, so that users can access it at any level.\n4. Small, so that users do not need to shade megabytes of needless third-party libraries.\n\n*No third-party libraries were harmed in the making of this.*\n\nGet started with [this introduction guide](GETTING-STARTED.md).\n\n## Maven Information\n```xml\n\u003crepository\u003e\n    \u003cid\u003ekenzie\u003c/id\u003e\n    \u003cname\u003eKenzie's Repository\u003c/name\u003e\n    \u003curl\u003ehttps://repo.kenzie.mx/releases\u003c/url\u003e\n\u003c/repository\u003e\n``` \n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003emx.kenzie\u003c/groupId\u003e\n    \u003cartifactId\u003eeris\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Design\n\nThe default design pattern has two features: listeners and entities. \\\nBoth of these are optional, and advanced users can use a different design pattern that better suits their project.\n\n### Listeners\n\nListeners can anticipate Discord **events** (dispatches.) \\\nAdvanced users can listen to raw gateway **payloads** and handle the data manually.\n\n```java \nfinal Bot bot = new Bot(\"token\");\n\nbot.registerListener(Ready.class, ready -\u003e {\n    System.out.println(\"The bot has successfully logged in!\");\n    System.out.println(\"It is called \" + ready.user.getTag());\n});\n```\n\n### Entities\n\nEntities represent Discord objects (users, guilds, members, etc.)\nThe entity structure corresponds directly to its raw data.\n\n#### Fields\nEntities have exposed, mutable fields rather than methods. \\\nThis is a design choice:\n1. Entity classes are data-stores, not high-security API.\n2. Some entities have 30+ fields - adding methods for each would increase bloat.\n3. Users are __supposed__ to edit entity objects in order to update/patch them.\n\n#### Requesting Entities\nMost entities have a `Snowflake` ID by which they can be retrieved.\n\n```java \nfinal User user = api.getUser(196709350469795841L);\nfinal User user = api.getUser(\"196709350469795841\");\n// Both ID formats are supported.\n```\n\nEntities are `Lazy`. \\\nTheir data may not be immediately available after creation, but the object can still be used.\n\nTo ensure that a `Lazy` entity has finished acquiring its data, use the `entity.await()` method. \\\nThis will **block** the current thread. An alternative `entity.\u003cCompletableFuture\u003ewhenReady()` method is available.\n\n```java \nfinal User user = api.getUser(196709350469795841L);\nassert user.id != null; // This property is already available.\nuser.await(); // Wait for all data to be loaded.\nassert user.name != null; // This property is now available.\n```\n\n```java \nfinal User pending = api.getUser(196709350469795841L);\npending.\u003cUser\u003ewhenReady().thenAccept(user -\u003e System.out.println(user.username + \" is ready!\"));\n```\n\nThis dual structure allows programs to use the most suitable design pattern.\n\nThe `Lazy` framework has an ancillary benefit: it has the smallest possible time-requirement.\n```java \nfinal User user = api.getUser(012345678910L);\nfinal Guild guild = api.getGuild(109876543210L);\nfor (int x = 0; x \u003c 10000; x++) {\n    // do something slow here, e.g. RegEx\n    // both Guild + User are populating in the background\n}\nuser.await();\nguild.await();\n// this has taken the smallest possible time for both\n// Guild + User to be ready\n```\nThis allows you to have minimal wait-times for multiple entities to be resolved without needing to use Java's complex `CompletableFuture`s.\n\n### Updating Entities\n\nIt is safe to cache or store Discord entities. \\\nAfter retrieving a stored entity it should be updated before being used to make sure all data is accurate.\n\nA Discord entity may need to be updated if:\n1. You retrieved a partial entity (e.g. partial member list in a `Guild` request) and want more data.\n2. The entity was changed by Discord (e.g. a user changes their name.)\n3. You made an entity object yourself (e.g. loaded cached `Guild` data from a file.)\n\nIf you ask Discord to `PATCH` (change) an entity it will be updated automatically on completion.\n\n### Creating Entities\n\nFor beginners, it is better practice to ask the `DiscordAPI` to do it for you. \\\nThis API object can be obtained from your `Bot` instance.\n\nFor advanced users it is safe to manually create most `Entity` objects manually, however you will need to request their data from Discord. \\\nBefore requesting the data you **must** set its snowflake `id` field.\n\nPlease note that any helper methods on the entity (e.g. `channel.send(...)`) will not function until you update the entity or attach a `DiscordAPI` object.\n\n### Generic Entities\n\nMany utility methods will accept a generic `I` entity, e.g. `api.getBan(IGuild, IUser)`.\n\nThese generic `I` entities have two special rules:\n1. They can accept an object relating to the ID of the entity rather than the entity itself. \\\n    E.g. you can provide a guild's ID in String/long form instead of a Guild object.\n2. If you use an object, it **does not** need to be finished, since only the ID is required. \\\n    E.g. you can provide the product of `api.getUser(...)` without `await`ing it first.\n\nThese objects are not strictly checked outside a test environment.\nMost methods will specify which types are permitted.\n\n## Data Acquisition\n\nAlmost all data must be acquired from Discord's API before being used.\n\nAlthough Eris does not force developers to use a particular format or method, some are recommended for simplicity or safety.\n\n### Lazy Acquisition\nMost Discord entities are provided as `Lazy` objects. \\\nYou can see [here](#entities) for an introduction to how these work.\n\nWhen using a `Lazy` entity the data may be acquired using `lazy.await()` which will **block** the current thread until **all** data is received.\nYou may also wait for the data using any method that calls `await` internally, e.g. `lazy.successful()`.\n\n\u003e Note: be careful about using `lazy.await()` on objects that were not acquired directly from the API - these may have no completion goal and so will block indefinitely.\n\nTo check whether `Lazy` acquisition is successful, a `lazy.successful()` block method is provided. \\\nIf there is an error it may be found and thrown/read using `lazy.error()`.\n\n### Future Acquisition\nRather than using the provided `Lazy` API to read data in-situ, it is possible to use Java's `CompletableFuture` system instead.\n\nThese may be used from a `Lazy` object directly (e.g. `lazy.whenReady()...`) however this is not advised in pooled environments since it busy-waits on a background thread.\n\nAlternatively, the data can be acquired from the API's `request` methods directly. Since Eris contains the Argo JSON library, the `InputStream` can be read and converted.\nThis is not advised for beginner users, since the data will have to be marshalled correctly.\n\n## Events\n\nEvents are triggered by incoming payloads from the Discord websocket.\nThese are sent when something happens that your application 1) intends to listen to and 2) has the privilege to listen to.\n\nYour application will not receive events if it was not registered with the correct intents or if it does not have privilege for those intents.\n\n### Event Structure\n\nThe event object received by listeners is designed to be easy-to-use. \\\nAll events are `Payload`s corresponding to their JSON key/value structure.\n\nSome events are designed to correspond directly with entities, such as `Channel Create`.\nThe `CreateChannel` object directly extends the `Channel` entity class, and is fully usable as a channel object.\nThis makes it easy to construct a listener with behaviour around this.\n\n```java \nbot.registerListener(CreateChannel.class, channel -\u003e {\n    channel.send(new Message(\"hello there\"));\n});\n```\n\n### Event List\n\nThis is the list of Discord events and corresponding event classes in this library.\n\nNote: Discord (unhelpfully) sends some of these events as payloads - the event class will be left blank.\n\n| Discord Event Name                     | Event Class                 |\n|----------------------------------------|-----------------------------|\n| Hello                                  |                             |\n| Ready                                  | Ready                       |\n| Resumed                                | Resumed                     |\n| Reconnect                              |                             |\n| Invalid Session                        |                             |\n| Application Command Permissions Update | UpdateCommandPermissions    |\n| Auto Moderation Rule Create            | CreateModerationRule        |\n| Auto Moderation Rule Update            | UpdateModerationRule        |\n| Auto Moderation Rule Delete            | DeleteModerationRule        |\n| Auto Moderation Action Execution       | ExecuteRule                 |\n| Channel Create                         | CreateChannel               |\n| Channel Update                         | UpdateChannel               |\n| Channel Delete                         | DeleteChannel               |\n| Channel Pins Update                    | UpdateChannelPins           |\n| Thread Create                          | CreateThread                |\n| Thread Update                          | UpdateThread                |\n| Thread Delete                          | DeleteThread                |\n| Thread List Sync                       | ThreadListSync              |\n| Thread Member Update                   | UpdateThreadMember          |\n| Thread Members Update                  | UpdateThreadMembers         |\n| Guild Create                           | IdentifyGuild               |\n| Guild Update                           | UpdateGuild                 |\n| Guild Delete                           | DeleteGuild                 |\n| Guild Ban Add                          | AddGuildBan                 |\n| Guild Ban Remove                       | RemoveGuildBan              |\n| Guild Emojis Update                    | UpdateGuildEmojis           |\n| Guild Stickers Update                  | UpdateGuildStickers         |\n| Guild Integrations Update              | UpdateGuildIntegrations     |\n| Guild Member Add                       | AddGuildMember              |\n| Guild Member Remove                    | RemoveGuildMember           |\n| Guild Member Update                    | UpdateGuildMember           |\n| Guild Members Chunk                    | IdentifyGuildMembers        |\n| Guild Role Create                      | CreateGuildRole             |\n| Guild Role Update                      | UpdateGuildRole             |\n| Guild Role Delete                      | DeleteGuildRole             |\n| Guild Scheduled Event Create           | CreateScheduledEvent        |\n| Guild Scheduled Event Update           | UpdateScheduledEvent        |\n| Guild Scheduled Event Delete           | DeleteScheduledEvent        |\n| Guild Scheduled Event User Add         | ScheduledEventAddUser       |\n| Guild Scheduled Event User Remove      | ScheduledEventRemoveUser    |\n| Integration Create                     | CreateIntegration           |\n| Integration Update                     | UpdateIntegration           |\n| Integration Delete                     | DeleteIntegration           |\n| Interaction Create                     | Interaction                 |\n| Invite Create                          | CreateInvite                |\n| Invite Delete                          | DeleteInvite                |\n| Message Create                         | ReceiveMessage              |\n| Message Update                         | UpdateMessage               |\n| Message Delete                         | DeleteMessage               |\n| Message Delete Bulk                    | BulkDeleteMessage           |\n| Message Reaction Add                   | AddMessageReaction          |\n| Message Reaction Remove                | RemoveMessageReaction       |\n| Message Reaction Remove All            | RemoveAllMessageReactions   |\n| Message Reaction Remove Emoji          | RemoveEmojiMessageReactions |\n| Presence Update                        | UpdatePresence              |\n| Stage Instance Create                  | CreateStage                 |\n| Stage Instance Update                  | UpdateStage                 |\n| Stage Instance Delete                  | DeleteStage                 |\n| Typing Start                           | StartTyping                 |\n| User Update                            | UpdateUser                  |\n| Voice State Update                     | UpdateVoiceState            |\n| Voice Server Update                    | UpdateVoiceServer           |\n| Webhooks Update                        | UpdateWebhooks              |\n\n\n### Listeners\n\nListeners can anticipate Discord **events** (dispatches.) \\\nAdvanced users can listen to raw gateway **payloads** and handle the data manually.\n\n```java \nfinal Bot bot = new Bot(\"token\");\n\nbot.registerListener(Ready.class, ready -\u003e {\n    System.out.println(\"The bot has successfully logged in!\");\n    System.out.println(\"It is called \" + ready.user.getTag());\n});\n```\n\n## Handling Entities in Bulk\n\nIt will be necessary to handle data - and its corresponding entities - in bulk.\nAn example of this would be acquiring large chunks of the member list or message history. \\\nWhile Discord does limit these to X results per call (e.g. `100` for messages) this is still a large chunk of data to read into memory all at once.\n\n\u003e Reading 100 `Message`s into memory would use: \\\n\u003e ~16 bytes for the Message object \\\n\u003e ~8 bytes of primitive field data \\\n\u003e ~72 bytes of addresses for message data (assuming these are already cached) \\\n\u003e ++ String data from each message \\\n\u003e = 12kb of memory, excluding message content.\n\nIdeally, we do not want to be using 12kb+ of memory at a time.\n\nFortunately, bulk calls provide three ways of dealing with their data.\n\n#### 1. Lazy List\nIf all 100 messages need to be in memory at once (e.g. sort/search/store/etc.) they can be acquired as a `LazyList`.\n\n```java \nfinal LazyList\u003cMessage\u003e messages = channel.getMessages()\n    .limit(100).get();\nmessages.await();\nmessages...\n```\nThis will use the most memory, but all messages will be available at once.\n\n| Pros                                                | Cons                                                        |\n|-----------------------------------------------------|-------------------------------------------------------------|\n| Can act on all the messages.                        | All of the messages go into memory.                         |\n| Able to iterate all the messages at once.           | Have to wait for all messages to arrive before reading one. |\n| Can do other tasks while the messages are arriving. |                                                             |\n\n\n#### 2. Background Consumer\nIf the messages do not need to be consumed immediately, they may be dealt with in the background using a consumer.\n\n```java \nchannel.getMessages().limit(100)\n    .forEach(message -\u003e System.out.println(message.content));\n```\nThis will use the least memory: once an entity is assembled from JSON it is immediately consumed. \\\nAfter the consumer has finished - providing the user is not storing a strong reference - the entity object is destroyed. \\\nHowever, there is a short delay (nanoseconds) between messages since they are consumed while the data stream is still incoming.\n\n| Pros                                                              | Cons                                              |\n|-------------------------------------------------------------------|---------------------------------------------------|\n| Low-memory: objects are discarded after the consumer is finished. | Can act on only one message at a time.            |\n| Fast: consumers are run as the data is read.                      | Cannot look ahead/behind at other messages.       |\n| Background: doesn't block the current thread.                     | Slow consumers will slow down the incoming data.  |\n|                                                                   | The parser must wait for each consumer to finish. |\n\n\n\u003e Note: this is not a standard for-each.\n\n#### 3. In-situ Iteration\nThe message helper object can be treated as an `Iterable` (like a list) and looped.\n\n```java \nfor (final Message message : channel.getMessages().limit(100)) {\n    System.out.println(message.content);\n}\n```\nThis is the fastest and lowest-memory approach, but it blocks the current thread.\n\nMessages are passed across a transferring queue and then iterated. This allows the messages to be read on the current thread.\n\n\n| Pros                                                    | Cons                                        |\n|---------------------------------------------------------|---------------------------------------------|\n| Low-memory: objects are discarded after each iteration. | Cannot look ahead/behind at other messages. |\n| Low-CPU: no heavy consumers or lambdas required.        |                                             |\n| In-situ: entities are in the current method.            |                                             |\n| Speedy: incoming entities are queued in the background. |                                             |\n\n## Caching\nUnlike other discord libraries, Eris operates only a very limited cache. \\\nEris is designed to function in low-memory environments, so caching large amounts of potentially-unnecessary data would be inappropriate.\n\nSome ID-based entities (Users, Guilds, Channels) are cached when requested.\nThis cache will be maintained only as long as the user keeps a reference to that entity somewhere.\nThis is to prevent having to wait to reacquire entities in multiple places at the same time. \\\nIn order to use this cache longer-term, simply store the entity references somewhere within your own project to stop them being garbage-collected from the cache.\n\nEvery time a cached entity is requested, the cached version will be returned but a call will be sent to the Discord API for an updated copy. \\\nYou do **not** need to `await` the updated copy before using the entity, however in some cases you may wish to if the cached copy is old.\n\nSome API methods allow a user to provide their own copy of an entity. This copy may be different from the cached version. \\\nIf the method returns the same type of entity it will always return the cached version (pending the result from Discord's API.) \\\nThe user-provided entity may supplant the cached version and be returned if:\n1. The cached version is marked outdated or incomplete.\n2. The cached version has no strong references and due to be garbage-collected.\n3. The cached version is implemented at a lower-level (e.g. raw `Channel` vs `Thread`.)\n\n## Chain Responses\nDiscord's message component system encourages chaining multiple actions together. \\\nE.g. `command -\u003e modal -\u003e message -\u003e button press -\u003e result`\n\nHowever, Discord's API is structured around regular listeners and callbacks. \\\nTo avoid users needing to create 5+ temporary listeners for a single interaction chain,\nthe API provides a helper process for these \"one-and-done\" callbacks.\n\n### Appropriate Settings\nThe one-and-done response system is not designed for regular or repeatable interactions like commands.\nIt is appropriate for interactions which are:\n- Unique, such as a user's modal input.\n- Require data from the previous step, e.g. a 5-page form.\n\nIt is not appropriate for interactions which are:\n- Not identifiably unique, such as commands.\n- Designed to be triggered more than once like a permanent button.\n\n### Preparing the Response\n\nSome interactive components have an `expectResult` method.\nThis tells the API you are intending to use the component for a one-and-done response.\n\nSome components have a static `auto` creator method that will **pre-trigger** this.\n\n\n#### Button Example\nAn example of responding to a button is below.\n```java \nfinal Button button = Button.auto(\"My Button\");\nfinal Message message; // store for later\nchannel.send(message = new Message(\"Hello!\", button);\nbutton.await(50000); // wait for somebody to press the button\n// a time-out is always appropriate\n// if the user does not press the button this would hang indefinitely\n    \nif (button.cancelled()) return; // the user didn't press the button or the interaction expired\nfinal Interaction press = button.result(); // this is the interaction event\npress.respond(new Message(\"You pressed the button!\").withFlag(MessageFlags.EPHEMERAL));\n// this can be triggered only once\n\nmessage.delete(); // get rid of the button so nobody else presses it!\n```\n\u003e In this example a button-message is sent to a channel.\n\u003e The **first** time a user presses the button, the bot will respond.\n\u003e After the bot has responded, the button will be deleted.\n\u003e If nobody presses the button within the 50-second time window, the interaction will not respond.\n\n#### Modal Example\nThis complex example shows creating a modal and processing its result.\n\n```java \nbot.registerCommand(Command.slash(\"bean\", \"My bean command.\"), interaction -\u003e {\n    final Modal modal = Modal.auto(\"My Modal\", new TextInput(\"text\", \"Write something!\"));\n    interaction.respond(modal);\n    \n    modal.await(30000);\n    if (modal.cancelled()) return;\n    final Interaction result = modal.result();\n    final String text = result.data.getInputValue(\"text\");\n    final Button button = Button.auto(\"My Button\");\n    result.respond(new Message(\"You wrote: `\" + text + \"`\", button).withFlag(MessageFlags.EPHEMERAL));\n    \n    button.await(50000);\n    if (button.cancelled()) return;\n    final Interaction press = button.result();\n    press.respond(new Message(\"You pressed the button!\").withFlag(MessageFlags.EPHEMERAL));\n});\n```\n\n\u003e In this example, a global command is registered.\n\u003e When the command is run, it sends a unique modal to the user asking for a text response.\n\u003e The interaction waits 30 seconds for the user to fill in the text.\n\u003e A message is sent to the user telling them what they wrote, with a button.\n\u003e If the user presses the button it will respond with a new message.\n\n\u003cimg width=\"339\" alt=\"image\" src=\"https://user-images.githubusercontent.com/14147477/180742795-81b8abea-986d-4db2-9938-94414b2c9a85.png\"\u003e\n\n## Files and Attachments\nSome Discord API endpoints support attachments.\nAs the file sizes could be greater than 100mb (with increased file limits) these files cannot be read normally.\n\n1. The 'multipart' message data is read into off-heap memory (java arrays are simply not equipped to hold the data.) \\\n    If you add multiple attachments to the message the off-heap datastore will be natively resized to fit these.\n2. The network client then requests this data in small, N-kilobyte chunks.\n3. Each chunk is copied directly from the off-heap memory into the correct heap address.\n4. That chunk is dispatched and then freed up and the next is requested, until no more remain.\n5. The off-heap memory store is freed.\n\nThis is a dangerous process since there are no limits or security checks on off-heap memory.\nCurrently, you may read files only if they are smaller than your available RAM. \\\nAccidentally reading a multi-gigabyte file would crash your application since it cannot allocate that much memory.\n\nSince Discord's file limit is fairly small, there is no reason to add an artificial restriction.\n\n## Bypassing the API\nUsers are not forced into using the provided API methods or even the `Entity` data objects.\n\nThe API provides ways to send raw requests to Discord and read their data as an `InputStream`.\n\nYou may also unregister the payload-listener that creates wrapped events and interpret payloads directly from the gateway socket.\n\n## Dependencies\n\n- **Argo** by me, found [here](https://github.com/Moderocky/Argo).\n- **Jupiter** by me, found [here](https://github.com/Moderocky/Jupiter).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoderocky%2Feris","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoderocky%2Feris","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoderocky%2Feris/lists"}