A JavaFX desktop chatbot with a reactive core, built to demonstrate practical RxJava and Streams API design. Started as a course project; the reactive architecture is intentionally built to outlive the rule-based bot currently running on top of it.
ChattyApp is a rule-based chatbot with a JavaFX interface, matching user input against regex-defined intents loaded from YAML and responding with either a canned reply or the result of a handler (weather, jokes, current time). The interesting part isn't the bot's intelligence — it's deliberately simple — it's how the app is wired together: a fully reactive pipeline built on RxJava3 that governs everything from input handling to response delivery.
- Rule-based intent matching via regex patterns, loaded from a YAML lexicon and evaluated in priority order
- Reactive input pipeline: user messages flow through a
PublishSubject, with backpressure handling, sequential processing, and thread-safe UI updates - Three working handlers — weather (via wttr.in), jokes (via an external joke API), and time
- Persistent user profile (name, location, joke preference) stored to disk, loaded via YAML
- Modular controller structure (six controllers, one orchestrator) with dependency injection
- Packaged as a standalone, shaded JAR via Maven
A few choices in the reactive pipeline that were deliberate, not accidental:
- Backpressure via drop strategy. Incoming messages are converted to a
FlowableusingBackpressureStrategy.DROP, so if the user fires off input while Chatty is still processing the previous message, the new one is discarded rather than queued. Combined withflatMapSingle(mapper, false, 1)(max concurrency of 1), this guarantees strictly sequential processing without ever blocking the UI thread. - Regex intents evaluated in priority order, first match wins. The lexicon is sorted by priority at load time; matching is a straightforward
IntStreamfiltered against each intent's compiled pattern, with the first hit selected viafindFirst(). Simple, predictable, and easy to reason about — at the cost of needing careful priority ordering as more intents are added. - Exceptions as a (deliberate, imperfect) control-flow tool.
ChatValidationSignalis thrown when a request is missing required info (e.g. asking for weather with no location set) and caught upstream viaretryWhen, which resubscribes to the stream without breaking it. Using exceptions this way isn't idiomatic, but it kept the validation logic decoupled from the main response path — a tradeoff I'd revisit given more time. - Shared state via
BehaviorSubject, not tight coupling. Handlers that need user data (location, joke preference) subscribe toBehaviorSubjects exposed by the user profile rather than being handed a reference to it directly. This keeps handlers decoupled from how or where that data is stored.
- Component ownership is a bit tangled.
UserProfileand the YAML loading logic are currently owned by the central manager class and threaded through to the controllers that need them. Instantiating them independently and injecting only what's needed would simplify the dependency graph. - Screen visibility logic doesn't scale. The chat area is currently shown whenever neither the info screen nor the user screen is active — a two-way toggle. Adding a third screen would need this rebuilt as a proper state machine rather than a boolean pair.
- Silent failures for background API errors. Errors in flows outside the main chat path (e.g. fetching joke categories) currently just print to console. The user has no idea why, say, their joke preference dropdown is empty. Surfacing these in the chat area would be a small but meaningful fix.
The rule-based engine is a proof of concept. The concrete next step would be swapping it for a local LLM — most likely something in Google's Gemma 4 family, run via Ollama, keeping the bot fully offline and consistent with the rest of the app's philosophy. The reactive pipeline itself shouldn't need to change; only what sits behind getChattyResponse does.
Longer-term, I've been sketching a bigger idea: reshaping ChattyApp into a rearrangeable panel-based dashboard — chat, notes, maybe a research timeline — with a cyberdeck-style command-center feel and shared context across panels. That's a much bigger rebuild than anything scoped here and is something more of a direction I'm drawn to than a concrete plan.
Prerequisites: Java 21+, Maven 3.6+
git clone https://github.com/J-manLans/ChattyApp.git
cd ChattyApp
mvn clean package
mvn javafx:runOr run the packaged JAR directly:
java --module-path /path/to/javafx/lib --add-modules javafx.controls,javafx.fxml -jar target/ChattyApp-1.0-SNAPSHOT.jar(This requires JavaFX 21.0.9 installed separately, with the module path pointed at its lib folder. Given the project bundles a shaded JAR with all dependencies included, java -jar target/ChattyApp-1.0-SNAPSHOT.jar on its own may also work without the module-path flags — untested as of this writing.)
All resources (FXML, YAML, CSS) are bundled in the JAR, so no extra configuration is needed beyond JavaFX for the standalone run.
- JavaFX 21 — desktop UI framework
- RxJava 3 + RxJavaFX — reactive core and JavaFX integration
- Jackson YAML — configuration and profile persistence
- org.json — API response parsing
- JUnit 5 — testing
