Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Github Actions -- tests Github Actions -- docs powered by potato GitHub tag (latest by date) LGTM Grade

A Discord API wrapper in Java. Fully async / reactive, built on top of RxJava. catnip tries to map roughly 1:1 to how the Discord API works, both in terms of events and REST methods available. catnip uses Java 17+.

catnip is part of the amyware Discord server.

Licensed under the BSD 3-Clause License.


Get it on Jitpack

Current version: GitHub tag (latest by date)

Can I just download a JAR directly?

No. Use a real build tool like Maven or Gradle.


Get them here.


Basic usage

This is the simplest possible bot you can make right now:

final Catnip catnip = Catnip.catnip("your token goes here");
    .filter(msg -> msg.content().equals("!ping"))
    .subscribe(msg -> {
    }, error -> error.printStackTrace());

catnip returns RxJava operators (Completable/Observable/Single/...) from all REST methods. For example, editing your ping message to include time it took to create the message:

final Catnip catnip = Catnip.catnip("your token goes here");
        .filter(msg -> msg.content().equals("!ping"))
        .subscribe(msg -> {
            long start = System.currentTimeMillis();
                    .subscribe(ping -> {
                        long end = System.currentTimeMillis();
                        ping.edit("pong! (took " + (end - start) + "ms).");
        }, error -> error.printStackTrace());

You can also create a catnip instance asynchronously:

Catnip.catnipAsync("your token here").subscribe(catnip -> {
        .filter(msg -> msg.content().equals("!ping"))
        .subscribe(msg -> {
        }, error -> error.printStackTrace());

Also check out the examples for Kotlin and Scala usage.

A note on Observable#subscribe vs. Observable#forEach

Observable#forEach seems like the obvious way to use the reactive methods, but as it turns out, it's also the wrong thing to do. Observable#forEach is generally intended for finite streams of data; the events that catnip emits aren't finite, and as such, Observable#forEach isn't the correct tool to use. In addition, Observable#forEach will stop processing events if an uncaught exception is thrown. Instead, you should use Observable#subscribe(eventCallback, exceptionCallback), which will handle exceptions properly.

Modular usage

catnip supports being used in REST-only or shards-only configurations. The nice thing about catnip is that using it like this is exactly the same as using it normally. The only difference is that to use catnip in REST-only mode, you don't call catnip.connect() and use instead.

RxJava schedulers

By default, RxJava's Observable#subscribe() and related methods will not operate on any particular scheduler by default. That is, they will run on the calling thread. catnip will automatically subscribe RxJava objects onto a scheduler provided by catnip, that defaults to being a ForkJoinPool-based scheduler. You can customize the scheduler used with the corresponding option in CatnipOptions.

Useful extensions

Why write a fourth Java lib?

  • I didn't want ten billion events for every possible case. catnip maps more/less 1:1 with the Discord API, and any "extra" events on top of that need to be user-provided via extensions or other means. I guess really I just didn't want my lib to be as "high-level" as other libs are.
  • I wanted to try to maximize extensibility / customizability, beyond just making it modular. Things like being able to intercept raw websocket messages (as JSON), write custom distributed cache handlers, ... are incredibly useful.
  • I like everything returning Rx classes instead of custom Future-like classes. I do get why other libs have them, I just wanted to not.
  • I wanted modular usage to be exactly the same more / less no matter what; everything should be doable through the catnip instance that you create.
  • I wanted to make a lib built on RxJava.
  • To take over the world and convert all Java bots. :^)