Warning
The project is under active development. Many features will change as development progresses
ButterBror2 is a modular, multi-platform chatbot framework built on .NET 10. It is designed around a plugin architecture - chat integrations (Twitch, Discord, Telegram) and commands are distributed as self-contained .pag packages and loaded at runtime without recompiling the host application
- .NET 10 SDK or later
- Python 3.10+ (only required to build
.pagmodule packages withscripts/module-compilator.py) - Git
- Windows, Linux, or macOS
Currently, the recommended way to run ButterBror2 is directly from source
1. Clone the repository
git clone https://github.com/itzkitb/butterbror2.git
cd butterbror22. Run the host
dotnet run --project ButterBror.Host/ButterBror.Host.csprojOr, if you prefer to build a binary first:
dotnet build ButterBror.Host/ButterBror.Host.csproj -c ReleaseThe compiled output will be placed in ButterBror.Host/bin/Release/net10.0/
Once the host is running, it will scan the platform-specific AppData directory for installed .pag modules:
| OS | Path |
|---|---|
| Windows | %APPDATA%\SillyApps\ButterBror2\ |
| Linux / macOS | ~/.local/share/SillyApps/ButterBror2\ |
Chat modules are loaded from the Chat/ subdirectory and command modules from Command/. Any .pag file placed in the correct folder will be picked up automatically on the next startup or reload
The solution is organized into the following groups:
Core / Host
ButterBror.Host- application entry point; wires up DI, loads modulesButterBror.Core- interfaces and abstractions consumed across all layersButterBror.Domain- domain entitiesButterBror.Application- built-in commandsButterBror.Infrastructure- service implementations, repositories, storageButterBror.Data- data access layerButterBror.Localization- translation file loading and locale resolutionButterBror.AI- chatgpt to answer your questions in chat (WIP)ButterBror.Scripting- scripting support (WIP)ButterBror.Dashboard- dashboard bridge
SDK (for authors)
ButterBror.ChatModule-IChatModuleinterface and chat module loaderButterBror.CommandModule-ICommandModule,ICommand,CommandBase, metadata and context contracts
Built-in Chat Modules
ButterBror.ChatModules.TwitchButterBror.ChatModules.DiscordButterBror.ChatModules.TelegramButterBror.ChatModules.Loader- runtime loader for.pagchat modules
Tools
ButterBror.Tools.AnalyzerButterBror.Tools.GeneratorButterBror.Tools.MigratorButterBror.Tools.Updater
Tests
ButterBror.UnitTests,ButterBror.IntegrationTests,ButterBror.PlatformTests,ButterBror.E2ETests
Modules are packaged as .pag files - ZIP archives with a .pag extension that contain compiled DLL assemblies and a module.manifest.json descriptor. The build script scripts/module-compilator.py handles the full build-and-package pipeline
Before writing a module, you need to make the ButterBror2 SDK projects available to your module's .csproj. The recommended approach is to add the main repository as a Git submodule inside a libs/ folder in your module's repository. This way you always have access to the correct SDK source without manually copying files
1. Add ButterBror2 as a submodule
git submodule add https://github.com/itzkitb/butterbror2.git libs/butterbror2
git submodule update --init --recursiveYour module project layout will look like this:
MyModule/
โโโ libs/
โ โโโ butterbror2/ - ButterBror2 repository (submodule)
โ โโโ ButterBror.ChatModule/
โ โโโ ButterBror.CommandModule/
โ โโโ ...
โโโ MyModule/
โ โโโ MyModule.csproj
โโโ MyModule.sln
2. Update the submodule at any time to pull in the latest SDK changes:
git submodule update --remote libs/butterbror2If you're using Visual Studio Code or other IDE, it will look like this:
3. Reference the SDK project from your .csproj using the relative path into libs/:
<!-- For a chat module -->
<ItemGroup>
<ProjectReference Include="../libs/butterbror2/ButterBror.ChatModule/ButterBror.ChatModule.csproj" />
</ItemGroup>
<!-- For a command module -->
<ItemGroup>
<ProjectReference Include="../libs/butterbror2/ButterBror.CommandModule/ButterBror.CommandModule.csproj" />
</ItemGroup>When running
scripts/module-compilator.py, all core ButterBror assemblies are automatically excluded from the.pagfile, since they will already be in context when the module is loaded
A chat module integrates ButterBror2 with a messaging platform (e.g. Twitch, Discord). To create one:
1. Create a new .NET class library project that references ButterBror.ChatModule:
<ItemGroup>
<ProjectReference Include="../ButterBror.ChatModule/ButterBror.ChatModule.csproj" />
</ItemGroup>2. Implement the IChatModule interface:
using ButterBror.ChatModule;
using ButterBror.Core.Interfaces;
using ButterBror.CommandModule.Commands;
public class MyPlatformModule : IChatModule
{
public string PlatformName => "myauthor:myplatform";
public IReadOnlyList<ModuleCommandExport> ExportedCommands => Array.Empty<ModuleCommandExport>();
// Optional: provide default translations bundled with the module
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> DefaultTranslations =>
new Dictionary<string, IReadOnlyDictionary<string, string>>();
public void InitializeWithServices(IServiceProvider serviceProvider)
{
// Resolve any services you need from the DI container here
}
public Task InitializeAsync(IBotCore core)
{
// Connect to the platform and start listening
return Task.CompletedTask;
}
public Task ShutdownAsync()
{
// Disconnect and clean up
return Task.CompletedTask;
}
}PlatformName must be unique across all loaded modules and follow the "author:platform" convention.
A command module exposes one or more chat commands. To create one:
1. Create a new .NET class library project that references ButterBror.CommandModule:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../libs/butterbror2/ButterBror.CommandModule/ButterBror.CommandModule.csproj" />
</ItemGroup>
</Project>2. Implement your command:
using ButterBror.CommandModule.Commands;
using ButterBror.CommandModule.Context;
public class HelloCommand : ICommand
{
public override Task<CommandResult> ExecuteAsync(
ICommandExecutionContext context,
ICommandServiceProvider serviceProvider)
{
return Task.FromResult(
CommandResult.Successfully($"Hello, {context.User.DisplayName}!"));
}
}3. Create a metadata for the command:
using ButterBror.CommandModule.Commands;
using ButterBror.CommandModule.Enums;
public class PingCommandMeta : ICommandMetadata
{
public string Name => "hello";
public List<string> Aliases => ["hi", "yo", "wrrr"];
public int CooldownSeconds => 10;
public List<string> RequiredPermissions => [];
public string ArgumentsHelpText => "";
public string Id => "author:bot:hello";
public PlatformCompatibilityType PlatformCompatibilityType => PlatformCompatibilityType.Blacklist;
public List<string> PlatformCompatibilityList => [];
}4. Implement ICommandModule to register your commands:
using ButterBror.CommandModule.CommandModule;
using ButterBror.CommandModule.Commands;
public class MyCommandModule : ICommandModule
{
public string ModuleId => "author:bot";
public string Version => "1.0.0";
public IReadOnlyList<CommandModuleExport> ExportedCommands =>
[
new CommandModuleExport(
CommandName: "hello",
Factory: () => new HelloCommand(),
Metadata: new HelloCommandMetadata()
)
];
public void InitializeWithServices(IServiceProvider serviceProvider) { }
public Task ShutdownAsync() => Task.CompletedTask;
}Use the included Python build script to compile the project and package it into a .pag archive:
# Build a chat module (Release, installs automatically)
python3 scripts/module-compilator.py \
--type chat \
--csproj MyModule/MyModule.csproj
# Build a command module (Release, installs automatically)
python3 scripts/module-compilator.py \
--type command \
--csproj MyCommandModule/MyCommandModule.csproj
# Build without installing; save the .pag to ./dist/
python3 scripts/module-compilator.py \
--type chat \
--csproj MyModule/MyModule.csproj \
--no-install \
--output ./dist
# Debug build, skip install
python3 scripts/module-compilator.py \
--type command \
--csproj MyModule/MyModule.csproj \
--config Debug \
--no-install
# Dry-run: see what would happen without doing anything
python3 scripts/module-compilator.py \
--type chat \
--csproj MyModule/MyModule.csproj \
--dry-runAll available options:
| Flag | Description |
|---|---|
--type / -t |
Module type: chat or command (required) |
--csproj / -p |
Path to the .csproj file (required) |
--config / -c |
Build configuration: Debug or Release (default: Release) |
--framework / -f |
Target framework (default: net10.0) |
--version |
Version written into the manifest (default: 1.0.0) |
--author |
Author written into the manifest |
--output / -o |
Output directory for the .pag file (default: <project_root>/dist/) |
--no-install |
Skip auto-install to AppData; only produce the .pag file |
--dry-run / -n |
Show what would happen without executing anything |
--exclude |
Additional assembly prefix(es) to exclude from the archive (repeatable) |
The script performs four steps internally: dotnet build > collect files > generate module.manifest.json > pack into .pag. Core ButterBror assemblies are automatically excluded from the archive since they are already present in the host.
The generated module.manifest.json inside the archive has the following structure:
{
"mainDll": "MyModule.dll",
"name": "MyModule",
"version": "1.0.0",
"description": "My custom module for ButterBror2",
"author": "MyName"
}If you used --no-install or want to install a pre-built package manually, copy the .pag file to the appropriate AppData directory:
| Module type | Windows | Linux / macOS |
|---|---|---|
| Chat | %APPDATA%\SillyApps\ButterBror2\Chat\ |
~/.local/share/SillyApps/ButterBror2/Chat/ |
| Command | %APPDATA%\SillyApps\ButterBror2\Command\ |
~/.local/share/SillyApps/ButterBror2/Command/ |
After placing the file, restart the host (or trigger a hot-reload if supported) and the module will be discovered automatically
ButterBror2 has a built-in localization system. Translation files are stored in the Localization/ subdirectory of the AppData folder, alongside an Available.json registry that maps locale codes to file paths and aliases
By default, the registry is bootstrapped with EN_US and RU_RU. Module authors can bundle default translations directly in their IChatModule.DefaultTranslations or ICommandModule.DefaultTranslations property - these are merged into the localization cache at load time and act as fallbacks when no translation file exists for a given key
Contributions are welcome! To get started:
- Fork the repository on GitHub
- Create a new branch from
mainfor your changes:git checkout -b feat/your-feature - Make your changes and add or update tests where relevant. The test suite uses NUnit 4 and targets
net10.0 - Run the tests to make sure nothing is broken:
dotnet test - Commit using Conventional Commits (
feat:,fix:,docs:, etc.) - Open a Pull Request against
main
Please keep PRs focused and include a clear description of what was changed and why
This project is licensed under the MIT License. See LICENSE.txt for the full text
Copyright ยฉ 2026 SillyApps (aka. ItzKITb)
If you encounter any issues or have questions:
- Create an issue in the GitHub repository
- Twitch: https://twitch.tv/itzkitb
- Email: itzkitb@gmail.com
- Donate: My Boosty
SillyApps :P