Skip to content

mewna/catnip

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PROJECT STATUS

This project is no longer maintained. I don't have time to maintain it anymore, and I don't really use Discord's API much anymore anyway. If you want to maintain this library, please see issue #729.

catnip

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.

Installation

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.

Javadocs?

Get them here.

Features

Basic usage

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

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

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");
catnip.observable(DiscordEvent.MESSAGE_CREATE)
        .filter(msg -> msg.content().equals("!ping"))
        .subscribe(msg -> {
            long start = System.currentTimeMillis();
            msg.respond("pong!")
                    .subscribe(ping -> {
                        long end = System.currentTimeMillis();
                        ping.edit("pong! (took " + (end - start) + "ms).");
                    });
        }, error -> error.printStackTrace());
catnip.connect();

You can also create a catnip instance asynchronously:

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

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 catnip.rest().whatever() 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. :^)