A modern, stable Discord API wrapper for .NET 8.0. Production-ready with automatic reconnection, proper error handling, and comprehensive Discord API coverage.
Current Version: 0.6.1-alpha1 Status: Production-ready with advanced features - Gateway reliability, REST resiliency, caching, interactions, commands, voice support, and sharding fully implemented. v0.6.1-alpha1 delivers bug fixes and null-safety hardening: correct heartbeat configuration propagation, awaited gateway event tasks, proper archived-thread deserialization, 6000-char embed limit enforcement, and nullable-clean REST content parameters. Complete documentation and examples available.
- Sharding Support - Multi-shard management with automatic reconnection, status monitoring, and event aggregation
- Automatic Reconnection - Exponential backoff with session resumption, handles network issues gracefully
- Heartbeat Health Monitoring - Detects zombie connections and reconnects automatically
- Complete REST API - All Discord endpoints (messages, channels, guilds, members, roles, webhooks, etc.)
- Real-time Events - WebSocket gateway with typed event handlers
- Typed Error Handling - Custom exception hierarchy, no null checks needed
- Input Validation - Catch mistakes before hitting Discord's API
- Smart Caching - In-memory with per-entity limits, LRU eviction, automatic TTL cleanup
- Rate Limiting - Automatic rate limit handling with proper bucket tracking
- Dependency Injection - First-class support for .NET DI container
- Fully Async - Modern async/await throughout with nullable reference types
- Typed Components - Fully typed message component hierarchy (buttons, select menus, text inputs) with polymorphic JSON deserialization
- EmbedBuilder - Fluent builder with Discord limit enforcement (title, description, fields, footer, author, and the 6000-character total limit)
Requirements: .NET 8.0 SDK or later
Install the packages you need:
# Core functionality
dotnet add package PawSharp.Core
# REST API client
dotnet add package PawSharp.API
# Gateway and events
dotnet add package PawSharp.Gateway
# Caching providers
dotnet add package PawSharp.Cache
# Unified client (includes all above)
dotnet add package PawSharp.Client
# Command framework
dotnet add package PawSharp.Commands
# Slash commands and interactions
dotnet add package PawSharp.Interactions
# Interactivity (pagination, polls)
dotnet add package PawSharp.Interactivity
# Voice support (experimental)
dotnet add package PawSharp.Voicegit clone <repository>
cd PawSharp
dotnet buildPawSharp is organized into modular packages for flexibility:
PawSharp.Core- Base entities, enums, exceptions, interfaces, models, serialization, and validationPawSharp.API- REST API client with automatic rate limiting and error handlingPawSharp.Gateway- WebSocket gateway client with event handling and reconnection logicPawSharp.Cache- Caching abstractions and providers (memory, Redis)PawSharp.Client- Unified client combining API, Gateway, and Cache functionality
PawSharp.Commands- Traditional command framework with prefix-based commandsPawSharp.Interactions- Slash commands, buttons, select menus, and interaction handlingPawSharp.Interactivity- Interactive components like paginators and pollsPawSharp.Voice- Voice connection and audio handling (experimental)
src/- Source code for all packagestests/- Unit and integration testsexamples/- Sample applications and usage patternsdocs/- Documentation and guidestools/- Build and documentation toolsnupkgs/- Published NuGet packages
Here's a bot that responds to !ping:
using PawSharp.Client;
using PawSharp.Cache.Providers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var services = new ServiceCollection();
services.AddLogging(config => config.AddConsole());
services.AddSingleton(new PawSharpOptions
{
Token = Environment.GetEnvironmentVariable("DISCORD_TOKEN")!,
Intents = GatewayIntents.Guilds | GatewayIntents.GuildMessages | GatewayIntents.MessageContent
});
services.AddSingleton<ICacheProvider, MemoryCacheProvider>();
services.AddSingleton<DiscordClient>();
var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<DiscordClient>();
client.Gateway.OnMessageCreate += async message =>
{
if (message.Author.Bot) return;
if (message.Content == "!ping")
{
try
{
await client.Rest.CreateMessageAsync(message.ChannelId, new CreateMessageRequest
{
Content = "Pong!"
});
}
catch (RateLimitException ex)
{
Console.WriteLine($"Rate limited, wait {ex.RetryAfter}s");
}
catch (DiscordApiException ex)
{
Console.WriteLine($"API error: {ex.Message}");
}
}
};
await client.ConnectAsync();
await Task.Delay(Timeout.Infinite);More examples in examples/.
Gateway Resilience:
- Automatic reconnection with exponential backoff (1s, 2s, 4s, 8s, 16s maximum)
- Session resumption within 45 seconds of disconnect
- Heartbeat ACK tracking to detect unhealthy connections
- All 12 Discord gateway opcodes handled correctly
- State machine preventing invalid state transitions
- Maximum 10 reconnection attempts before giving up
- Events for monitoring connection state and reconnection attempts
REST API:
- Messages (create, edit, delete, fetch, reactions, pins)
- Channels (CRUD, permissions, webhooks, announcement follows)
- Guilds (info, members, roles, bans, audit logs, preview, widget, vanity URL, welcome screen, channel/role position reorder)
- Users and current user endpoints
- Interactions and slash commands
- Scheduled events
- Threads and thread management
- Auto-moderation
- Invites (get with counts/expiration, delete)
- Guild Templates (list, get, create from template, sync, modify, delete)
Gateway Events:
- Message create/update/delete
- Guild and member events
- Channel and role events
- Interaction create
- Thread events
- Connection state transitions
- Ready and resume events
Caching:
- In-memory and Redis distributed cache providers
- Automatic cache for entities with gateway synchronization
- Configurable per-type size limits (10K messages, 1K guilds, 5K users, etc.)
- LRU eviction when limits hit
- TTL-based cleanup and statistics tracking
Error Handling:
ValidationException- bad input (invalid ID, text too long, etc.)RateLimitException- Discord rate limit with retry-afterDiscordApiException- API error with status codeGatewayException- WebSocket/connection issuesDeserializationException- JSON parsing failed
PawSharp.Core
+-- Entities (Guild, Channel, Message, User, components, etc.)
+-- Enums (PermissionFlags, MessageType, MessageFlags, ChannelFlags, etc.)
+-- Exceptions (custom exception hierarchy)
+-- Validation (input validators)
+-- Models (API request/response types)
+-- Builders (EmbedBuilder)
+-- Interfaces (IDiscordRestClient, etc.)
PawSharp.API
+-- REST client (all Discord HTTP endpoints)
+-- Rate limiting (per-route bucket tracking)
+-- Request/response models
PawSharp.Cache
+-- In-memory cache provider (with per-entity limits)
PawSharp.Gateway
+-- WebSocket connection management
+-- Event dispatch system
+-- Heartbeat handling
+-- Shard manager (coming Phase 2)
PawSharp.Interactions
+-- Slash command and component builders
PawSharp.Client
+-- High-level Discord client
+-- Event handlers
Use the pieces you need. Mix and match.
No more null checks. Everything throws typed exceptions:
try
{
var message = await client.Rest.CreateMessageAsync(channelId, new CreateMessageRequest
{
Content = userText
});
// message exists, no need to check
}
catch (ValidationException ex) when (ex.Message.Contains("2000"))
{
Console.WriteLine("Text exceeds 2000 characters");
}
catch (RateLimitException ex)
{
Console.WriteLine($"Rate limited, retry in {ex.RetryAfter}s");
await Task.Delay(ex.RetryAfter * 1000);
}
catch (DiscordApiException ex)
{
Console.WriteLine($"Discord error {ex.StatusCode}: {ex.Message}");
}Designed to work with .NET's DI from the start:
var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton(new PawSharpOptions { Token = token });
services.AddSingleton<IDiscordRestClient, RestClient>();
services.AddSingleton<ICacheProvider, MemoryCacheProvider>();
services.AddSingleton<DiscordClient>();
services.AddSingleton<GatewayClient>();
var provider = services.BuildServiceProvider();
var client = provider.GetRequiredService<DiscordClient>();PawSharp supports Discord's modern interaction system:
// Register slash commands
client.Interactions.RegisterCommand("ping", async interaction =>
{
var response = new InteractionResponse
{
Type = (int)InteractionResponseType.ChannelMessageWithSource,
Data = new InteractionCallbackData
{
Content = "Pong! ??"
}
};
await client.Interactions.RespondAsync(interaction.Id, interaction.Token, response);
});
// Register component handlers
client.Interactions.RegisterComponent("my_button", async interaction =>
{
// Handle button clicks
await client.Interactions.RespondAsync(interaction.Id, interaction.Token,
new InteractionResponse { /* ... */ });
});PawSharp includes comprehensive voice channel connectivity with professional-grade audio processing:
using PawSharp.Voice;
// Connect to a voice channel
var voice = client.UseVoice();
var connection = await voice.ConnectAsync(voiceChannel);
// Start capturing from microphone and sending audio
connection.StartCapture();
// Play received audio through speakers
await connection.PlayAudioAsync(receivedAudioData);
// Clean up when done
await connection.DisconnectAsync();Voice Features:
- WebSocket-based voice connections with Discord's voice gateway
- Real-time microphone capture and speaker playback infrastructure
- Voice state management and automatic server update handling
- Audio processing framework ready for codec integration
- Thread-safe voice operations with comprehensive error handling
- Opus codec preparation ( Concentus library included, encoding/decoding framework in place )
Build engaging interactive experiences with reactions, pagination, and user input collection:
using PawSharp.Interactivity.Extensions;
// Enable interactivity on your client
var interactivity = client.UseInteractivity();
// Create paginated content for long messages
var pages = interactivity.GeneratePagesInEmbed(longText);
await channel.SendPaginatedMessageAsync(user, pages);
// Wait for a specific user reaction
var result = await message.WaitForReactionAsync(user, "??");
if (!result.TimedOut)
{
await message.RespondAsync("Thanks for the thumbs up!");
}
// Collect multiple reactions for polls
var reactions = await message.CollectReactionsAsync(client, TimeSpan.FromMinutes(5));
// Create interactive polls
await message.CreatePollAsync("What's your favorite programming language?",
new[] { "C#", "Python", "JavaScript", "Rust" });Interactivity Features:
- Reaction waiting and collection with timeout support
- Automatic pagination for large content
- Poll creation with reaction-based voting
- Message-based user input collection
- Built-in cancellation and error handling
Traditional message-based commands with clean, attribute-driven syntax:
using PawSharp.Commands;
// Enable commands with your preferred prefix
var commands = client.UseCommands("!");
// Create a command module
public class UtilityCommands : BaseCommandModule
{
[Command("ping")]
[Description("Check if the bot is responsive")]
public async Task PingAsync(CommandContext ctx)
{
await ctx.RespondAsync($"?? Pong! Latency: {DateTimeOffset.Now - ctx.Message.Timestamp:hh\\:mm\\:ss}");
}
[Command("userinfo")]
[Description("Get information about a user")]
public async Task UserInfoAsync(CommandContext ctx)
{
var user = ctx.Message.Author;
var embed = new Embed
{
Title = $"{user.Username}#{user.Discriminator}",
Fields = new List<EmbedField>
{
new() { Name = "ID", Value = user.Id.ToString(), Inline = true },
new() { Name = "Joined", Value = user.CreatedAt.ToString("R"), Inline = true }
}
};
await ctx.RespondAsync(embed);
}
[Command("say")]
[Description("Make the bot say something")]
public async Task SayAsync(CommandContext ctx)
{
if (string.IsNullOrWhiteSpace(ctx.RawArguments))
{
await ctx.RespondAsync("? What do you want me to say?");
return;
}
await ctx.RespondAsync(ctx.RawArguments);
}
}
// Register your command modules
commands.RegisterModule(new UtilityCommands());Commands Features:
- Clean attribute-based command registration
- Automatic argument parsing and validation
- Built-in help system support
- Command aliases and descriptions
- Per-command execution hooks (before/after)
- Guild and channel context awareness
- Distributed clustering (single-machine and sharded bots fully supported)
- Advanced voice features (basic voice connectivity implemented)
See ROADMAP.md for the development plan and future phases.
Help wanted. Check the ROADMAP.md for what we're building.
- Discord Developer Portal � get your bot token
- Discord API Documentation � the source of truth
- Examples � real code samples
MIT License. Do what you want with it.