AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
A provider-agnostic, Discord-library-agnostic persistence layer for Discord bots, built on EF Core 10. Ships the model only — entities, conventions, and module configurations. You stay in control of t
Developer toolsC# · ShellGitHub @HandyS11OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
A provider-agnostic, Discord-library-agnostic persistence layer for Discord bots, built on EF Core 10. Ships the model only — entities, conventions, and module configurations. You stay in control of the database provider and the Discord gateway.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
A provider-agnostic, Discord-library-agnostic persistence layer for Discord bots, built on EF Core 10.
Ships the model only — entities, conventions, and module configurations. You stay in control of the
database provider and the Discord gateway.
Discord ids are 64-bit ulong snowflakes; relational providers store signed long.
Persistord handles the bit-faithful ulong ↔ long round-trip globally, models the
core Discord graph (guilds, channels, users, members, roles, messages), and adds
opt-in soft-delete and append-only history — without coupling you to a specific
database or Discord library.Read the full README ↗A provider-agnostic, Discord-library-agnostic persistence layer for Discord bots, built on EF Core 10. Ships the model only — entities, conventions, and module configurations. You stay in control of the database provider and the Discord gateway.
Getting Started · Documentation · Samples
Discord ids are 64-bit ulong snowflakes; relational providers store signed long.
Persistord handles the bit-faithful ulong ↔ long round-trip globally, models the
core Discord graph (guilds, channels, users, members, roles, messages), and adds
opt-in soft-delete and append-only history — without coupling you to a specific
database or Discord library.
| Package | Downloads | Adds | Depends on |
|---|---|---|---|
Persistord | meta package — bundles Core, Messages, and History | Core, Messages, History | |
Persistord.Core | snowflake conversion, base DiscordDbContext, opt-in skeleton graph (DiscordGraphDbContext), guild root, upsert, purge | — | |
Persistord.Messages | MessageEntity (soft-delete), embeds, attachments, reactions | Core | |
Persistord.History | append-only MessageHistoryEntity with a real FK to messages | Messages | |
Persistord.Adapters.DiscordNet | .To*Entity() mappers from Discord.Net types | Core, Messages, History |
The core packages are independent of any Discord client library. Install an adapter only if you use that library — Persistord.Adapters.DiscordNet for Discord.Net, Persistord.Adapters.DSharpPlus for DSharpPlus, Persistord.Adapters.NetCord for NetCord.
Persistord.Managed, Persistord.Protection, and Persistord.Testing are opt-in and
not part of the Persistord meta package: a bot that only mirrors Discord never
owns resources, encrypts a column, or needs the test fixtures, so the meta package
stays the library-neutral mirror stack (Core, Messages, History) and nothing more.
# Recommended: the full library-neutral stack in one package
dotnet add package Persistord
# Or install modules individually:
dotnet add package Persistord.Core
dotnet add package Persistord.Messages # optional: message persistence
dotnet add package Persistord.History # optional: requires Messages
dotnet add package Persistord.Adapters.DiscordNet # optional: Discord.Net mappers
dotnet add package Persistord.Adapters.DSharpPlus # optional: DSharpPlus mappers
dotnet add package Persistord.Adapters.NetCord # optional: NetCord mappers
DiscordDbContext applies only the snowflake conventions and maps nothing —
derive it and apply the module configurations you want in OnModelCreating:
using Microsoft.EntityFrameworkCore;
using Persistord.Core;
using Persistord.History;
using Persistord.History.Entities;
using Persistord.Messages;
using Persistord.Messages.Entities;
public sealed class MyBotContext : DiscordDbContext
{
public MyBotContext(DbContextOptions<MyBotContext> options) : base(options) { }
public DbSet<MessageEntity> Messages => Set<MessageEntity>();
public DbSet<MessageHistoryEntity> MessageHistory => Set<MessageHistoryEntity>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); // snowflake convention only
modelBuilder.ApplyMessagesModule(); // omit if you don't persist messages
modelBuilder.ApplyHistoryModule(); // requires ApplyMessagesModule()
}
}
If your bot also mirrors guilds, channels, users, members or roles, derive
DiscordGraphDbContext instead — it exposes those DbSets (Guilds,
Channels, Users, Members, Roles) automatically, on top of the same
conventions.
The consumer owns the provider choice. Any EF Core 10 relational provider works (PostgreSQL, SQL Server, SQLite, …); the snowflake conversion is applied automatically.
services.AddDbContextFactory<MyBotContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Bot")));
A bot is long-lived and concurrent; a DbContext is neither thread-safe nor meant
to live forever. Create one per unit of work via the factory and dispose it:
await using var db = await factory.CreateDbContextAsync();
db.Messages.Add(new MessageEntity
{
Id = message.Id,
ChannelId = message.ChannelId,
AuthorId = message.Author.Id,
Content = message.Content,
});
await db.SaveChangesAsync();
If you use Discord.Net, the adapter maps gateway/REST types for you:
using Persistord.Adapters.DiscordNet;
using Persistord.History.Entities;
db.Messages.Add(socketMessage.ToMessageEntity()); // embeds, attachments, reactions included
db.MessageHistory.Add(socketMessage.ToHistoryEntity(HistoryChangeType.Created));
await db.SaveChangesAsync();
Deleting a message sets IsDeleted / DeletedAt rather than removing the row, and a
default query filter hides soft-deleted messages (IgnoreQueryFilters() to include
them, or ApplyMessagesModule(filterDeleted: false) to disable). Because the row
survives, MessageHistoryEntity's foreign key to it — including the row logging the
deletion — stays valid.
Full guides and the API reference live on the documentation site (built with DocFX). Start with Getting Started, browse the Guides and Recipes, or check Troubleshooting if something isn't working.
samples/.dotnet restore
dotnet build
dotnet test
Requires the .NET 10 SDK. Formatting is enforced with ReSharper
(dotnet jb cleanupcode Persistord.slnx --profile="ReformatAndReorder").
MIT © Clergue Valentin
Persistord.Adapters.DSharpPlus | .To*Entity() mappers from DSharpPlus types | Core, Messages, History |
Persistord.Adapters.NetCord | .To*Entity() mappers from NetCord types | Core, Messages, History |
Persistord.Managed | records of the categories, channels, anchored messages, and webhooks a bot creates and owns | Core |
Persistord.Protection | encrypts [Protected] string columns at rest via ASP.NET Core Data Protection | Core |
Persistord.Testing | in-memory SQLite fixtures and EF Core model assertions for tests | Core |