Skip to content
SentorX edited this page Jan 15, 2026 · 5 revisions

Welcome to SimpleDiscordDotNet v1.8.0

A lightweight, dependency-free Discord bot SDK for .NET 10 that provides direct access to Discord API v10 (REST + Gateway).

🚀 Quick Links

📦 Installation

dotnet add package SimpleDiscordDotNet

NuGet: https://www.nuget.org/packages/SimpleDiscordDotNet/ GitHub: https://github.com/SimpleDiscordNet/SimpleDiscordNet

✨ Key Features

Core Features

  • Slash commands with full option support - Attribute-based handlers with constraints, choices, autocomplete, and all Discord option types (STRING, INTEGER, NUMBER, BOOLEAN, USER, CHANNEL, ROLE)
  • 🆕 v1.8.0: Dual-layer command permissions - Discord-level defaults + runtime per-guild custom rules
  • Components and modals - Buttons, select menus, and forms
  • Zero external dependencies - BCL only
  • Builder pattern and DI-friendly
  • Global static event hub for logs and domain events
  • Source generator for zero-reflection command/component discovery
  • Native AOT compatible - Full trimming and AOT support with 100% zero reflection
  • Memory-optimized - Span and Memory APIs for 30-50% less GC pressure
  • Horizontal sharding - 3 modes: single process, multi-shard, or distributed coordinator/worker
  • Enhanced InteractionContext - Direct Member and Guild access without cache lookups
  • HTTPS-secured sharding - TLS 1.3+ for distributed coordinator communication

Rich Discord API v10 Support

  • Messages: send, edit, delete, bulk delete, pin/unpin
  • Reactions: add, remove, clear, get users who reacted
  • Embeds & Components: rich embeds, buttons, selects, modals
  • Permissions: channel overwrites (per-role and per-member)
  • Roles: create, edit, delete, assign/remove, permission checking
  • Channels: create, edit, delete, categories, text, voice
  • Threads: create, join, leave, add/remove members
  • Moderation: kick, ban, unban, role assignment
  • Events: comprehensive gateway events for all entity changes
  • Rate Limiting: advanced bucket tracking, monitoring, and zero message loss

Ambient DiscordContext

  • Access cached Guilds/Channels/Members/Users/Roles from anywhere
  • 🆕 Live observable collections - Thread-safe INotifyCollectionChanged for WPF/MAUI/Avalonia UI binding
  • Filtered collections (Categories, TextChannels, VoiceChannels, Threads)
  • Helper methods for querying by guild, category, role
  • Type-safe, read-only snapshots (backward compatible)

📚 Documentation

Getting Started

Commands & Interactions

Messages & Communication

Server Management

Data Access & Events

Advanced Topics

💡 Quick Example

using SimpleDiscordNet;
using SimpleDiscordNet.Commands;
using SimpleDiscordNet.Primitives;

// Define your commands with options
public sealed class MyCommands
{
    [SlashCommand("greet", "Greet someone")]
    public async Task GreetAsync(
        InteractionContext ctx,
        [CommandOption("name", "Person's name", MinLength = 2, MaxLength = 32)]
        string name,
        [CommandOption("style", "Greeting style", Choices = "Friendly:👋,Formal:🤝,Casual:✌️")]
        string style)
    {
        // ✨ NEW in v1.4.0: Direct access to Member and Guild
        string memberName = ctx.Member?.User.Username ?? "Unknown";
        string guildName = ctx.Guild?.Name ?? "DM";

        await ctx.RespondAsync($"{style} Hello, {name}! Called by {memberName} in {guildName}", ephemeral: true);
    }
}

// Build and start your bot
var bot = DiscordBot.NewBuilder()
    .WithToken(File.ReadAllText("token.txt").Trim())
    .WithIntents(DiscordIntents.Guilds)
    .WithDevelopmentMode(true)
    .WithDevelopmentGuild("YOUR_DEV_GUILD_ID")
    .Build();

await bot.StartAsync();
await Task.Delay(Timeout.Infinite);

🎯 Design Philosophy

SimpleDiscordDotNet is designed with these principles:

  1. Simplicity First - Extremely easy to use with minimal boilerplate
  2. Zero Dependencies - No external packages, only BCL
  3. Performance - Memory-optimized with Span, Memory, and zero-allocation APIs
  4. Modern C# - Built with C# 14 and .NET 10 features including span-based APIs
  5. AOT Ready - Compatible with Native AOT compilation with 100% zero reflection
  6. Well Documented - Every public method has XML docs with examples

🆕 What's New in v1.8.0

Advanced Moderation & Audit Log Features

New Member Voice Control Operations:

// Deafen/undeafen members in voice channels
await bot.DeafenMemberAsync(guildId, userId);
await bot.UndeafenMemberAsync(guildId, userId);

// Move members between voice channels
await bot.MoveMemberToVoiceChannelAsync(guildId, userId, targetChannelId);

// Disconnect member from voice
await bot.DisconnectMemberFromVoiceAsync(guildId, userId);

Guild Bans Management:

// Get all banned users
var bans = await bot.GetGuildBansAsync(guildId);
foreach (var ban in bans)
{
    Console.WriteLine($"{ban.user.username}: {ban.reason}");
}

// Get specific ban
var ban = await bot.GetGuildBanAsync(guildId, userId);

Audit Log Access with Filtering:

// Get audit log with filters
var auditLog = await bot.GetAuditLogAsync(
    guildId,
    userId: moderatorId,              // Filter by who performed the action
    actionType: (int)AuditLogAction.MemberBanAdd,  // Filter by action type
    limit: 100);

// Process audit log entries
foreach (var entry in auditLog.audit_log_entries)
{
    Console.WriteLine($"Action: {entry.action_type}, Reason: {entry.reason}");
}

All operations support both string and ulong overloads for maximum flexibility!

Dual-Layer Command Permissions

Control who can use your slash commands with a powerful two-layer system:

Layer 1: Discord-Level Default Permissions

// Only visible to users with BanMembers permission
[RequirePermissions(PermissionFlags.BanMembers)]
[SlashCommand("ban", "Ban a user")]
public async Task BanCommand(InteractionContext ctx, DiscordUser user) { }

Layer 2: Runtime Per-Guild Custom Rules

// Further restrict to specific role in this guild
bot.Permissions.RegisterGuildRule("123456789", "ban", ctx =>
    ctx.Member?.Roles.Any(r => r.Id == "moderator_role_id") ?? false);

// Load from database for dynamic configuration
var guildConfigs = await database.GetAllGuildConfigsAsync();
foreach (var config in guildConfigs)
{
    bot.Permissions.RegisterGuildRule(config.GuildId, "ban", ctx =>
        config.ModeratorRoleIds.Any(roleId =>
            ctx.Member?.Roles.Any(r => r.Id == roleId) ?? false));
}

Features:

  • 100% AOT/Trim Compatible - No reflection, no dynamic code
  • Shard-Safe - Works across all sharding modes
  • Thread-Safe - Concurrent dictionary with locks
  • Beginner-Friendly - Simple attribute + delegate API
  • Dynamic - Change permissions per-guild without redeploying

See Command Permissions for complete guide and examples.


🆕 What's New in v1.6.0

Live Observable Collections for UI Binding

Thread-safe collections with INotifyCollectionChanged support for real-time UI data binding:

// WPF Example
public class MainViewModel
{
    public MainViewModel()
    {
        // Bind directly to live collections
        Guilds = DiscordContext.LiveGuilds;

        var guildId = 123456789;
        Channels = DiscordContext.GetLiveChannelsForGuild(guildId);
        Members = DiscordContext.GetLiveMembersForGuild(guildId);
    }

    public ObservableConcurrentDictionary<ulong, DiscordGuild> Guilds { get; }
    public ObservableConcurrentList<DiscordChannel> Channels { get; }
    public ObservableConcurrentList<DiscordMember> Members { get; }
}

// Configure UI thread marshaling
var bot = DiscordBot.NewBuilder()
    .WithToken(token)
    .WithSynchronizationContext(SynchronizationContext.Current) // ✨ UI thread marshaling
    .Build();

Features:

  • Thread-safe - Built on ConcurrentDictionary and ReaderWriterLockSlim
  • Batch operations - BeginBatchUpdate(), AddOrUpdateRange() to prevent UI thrashing on large servers
  • UI thread marshaling - Optional SynchronizationContext for automatic UI thread dispatching
  • Backward compatible - All existing snapshot APIs preserved

See Working with Entities for complete examples (WPF, MAUI, Avalonia).

🆕 What's New in v1.5.0

Advanced Components & Select Menus

Full support for advanced Discord component features:

// Default values in select menus
var userSelect = new UserSelect("team",
    defaultValues: new[] { SelectDefaultValue.User(userId) });

// Access resolved entity data
[UserSelectHandler("team")]
public async Task HandleTeam(InteractionContext ctx)
{
    var users = ctx.GetResolvedUsers(); // Full user objects!
    foreach (var user in users)
        Console.WriteLine($"{user.Username}#{user.Discriminator}");
}

// Emoji support in options
var options = new[]
{
    new SelectOption("Red", "red", emojiName: "🔴"),
    new SelectOption("Blue", "blue", emojiName: "🔵")
};

Specialized Component Handler Attributes

Type-safe handler registration:

[ButtonHandler("confirm")]          // Buttons only
[UserSelectHandler("users")]        // User selects only
[RoleSelectHandler("roles")]        // Role selects only
[ChannelSelectHandler("channels")]  // Channel selects only
[MentionableSelectHandler("any")]   // Users and roles

File Attachments in MessageBuilder

Send files easily with multiple overloads:

var builder = new MessageBuilder()
    .WithContent("Here are the files!")
    .AddFile("report.pdf", pdfBytes)
    .AddFile("data.json", jsonStream);

await ctx.RespondAsync(builder);

// Async stream support
await builder.AddFileAsync("download.zip", httpStream);

Message Collection Methods

Fetch and manage message history:

// Get recent messages
var messages = await channel.GetMessagesAsync(50);
foreach (var msg in messages)
    Console.WriteLine(msg.Content);

// Bulk delete
await channel.BulkDeleteMessagesAsync(10);

IEnumerable Returns

All collection methods now return IEnumerable<T> for better LINQ support:

var channels = await bot.GetGuildChannelsAsync(guildId);
var textChannels = channels.Where(c => c.Type == ChannelType.GuildText);

🤝 Contributing

Issues and PRs are welcome on GitHub! Keep the code dependency-free and aligned with the existing style.

📄 License

SimpleDiscordDotNet is licensed under the Apache License, Version 2.0.


Ready to get started? Head to Getting Started to build your first bot!

Clone this wiki locally