A .NET Slack bot
The project uses .NET Aspire for local
orchestration. Running the Bottomly.AppHost project will spin up MongoDB, Ollama (LLM), and the bot itself:
dotnet run --project Bottomly.AppHostRun the Bottomly project directly, ensuring MongoDB and Ollama are available and all required environment variables
are set:
dotnet run --project BottomlyThe solution is divided into four projects:
- Bottomly — the main application
- Bottomly.AppHost — .NET Aspire orchestration host for local development
- Bottomly.ServiceDefaults — shared Aspire configuration (telemetry, service discovery, HTTP resilience)
- Bottomly.Tests — xUnit test suite
Within the Bottomly project, code is organised by responsibility:
Classes here implement the actions triggered by bot commands. Each command performs an action and returns a result,
delegating any persistence work to the Repositories layer.
Defines the persistence layer. All behaviours which relate to persisting information beyond a simple request/response cycle are defined here, e.g. user karma and member lists. Backed by MongoDB.
Implements the Slack delivery channel using SlackNet in Socket Mode.
SlackWorker is a background service that maintains the Slack connection and dispatches incoming events. Event handlers
are split by type:
MessageEventHandlers/— handle text messages. Each handler implementsIMessageEventHandler, which requires:CanHandle(message): returnstrueif this handler should process the given message.Handle(message): extracts relevant information and invokes the corresponding command.
ReactionHandlers/— handle emoji reactions (e.g. karma changes).MembershipEventHandlers/— handle member join events.
Wraps OllamaSharp to provide conversational AI responses via a locally-hosted Ollama instance.
Has an internal structure matching the rest of the app. Each app file should have a corresponding test file.
The test suite is split into two categories:
- Unit tests — fast, in-process tests using Moq for mocking and Shouldly for assertions. Cover commands, event handlers, and other logic that can be exercised without external dependencies.
- Integration tests (
Repositories/Integration/) — use Testcontainers to spin up a real MongoDB container and exercise the repository layer end-to-end, including aggregation pipelines. These require Docker to be running locally; they run automatically in CI onubuntu-latest.
The following secrets/environment variables must be configured for the app to run:
bottomly_env: Describes the active environment. If not set tolive, output messages will be marked as "DEBUG".bottomly_prefix: The prefix for bot commands (e.g.!). Most commands require this prefix, allowing easy switching between test and production environments.bottomly_slack_bot_token: The Slack bot token (xoxb-...) for Socket Mode access.bottomly_slack_app_token: The Slack app-level token (xapp-...) for Socket Mode.bottomly_google_api_key: A valid Google API key.bottomly_google_cse_id: A valid Google Custom Search Engine ID.bottomly_giphy_api_key: A valid Giphy API key.bottomly_github_token: A GitHub personal access token.
MongoDB and Ollama connection strings are managed automatically by Aspire in development. In production, configure them via standard .NET connection string settings.
Most additions will involve adding a handler and command pair in the relevant modules, with the command delegating to the repository layer for any persistence work. PRs will not be merged without proper test coverage.