Skip to content

Commit f57c4c3

Browse files
author
PawSharp
committed
docs: rewrite README with logo, badges, and full feature coverage
1 parent d89d0db commit f57c4c3

2 files changed

Lines changed: 235 additions & 95 deletions

File tree

README.md

Lines changed: 181 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,122 @@
1-
# PawSharp
1+
<div align="center">
2+
<img src="assets/pawsharp-logo.svg" alt="PawSharp Logo" width="180" /><br/><br/>
23

3-
A Discord bot library for .NET 8. Handles the gateway connection, REST calls,
4-
caching, slash commands, and voice with full DAVE E2EE.
4+
# PawSharp
55

6-
**Version:** 0.11.0-alpha.1 | **Discord API:** v10 | **Status:** alpha | [Changelog](CHANGELOG.md)
6+
**A modular Discord API library for .NET**
7+
8+
[![NuGet][nuget-badge]][nuget]
9+
[![Discord API][discord-api-badge]][discord-docs]
10+
[![.NET][dotnet-badge]][dotnet-link]
11+
[![License][license-badge]][license]
12+
[![Build][build-badge]][build]
13+
14+
[Documentation][docs] &middot; [Changelog][changelog] &middot; [Examples][examples] &middot; [NuGet][nuget]
15+
16+
</div>
17+
18+
---
19+
20+
PawSharp is a feature-complete, modular Discord API library for C# and .NET. It covers the full gateway lifecycle, ~140 REST endpoints, prefix commands with preconditions, slash command routing, in-memory caching, per-route rate limiting, interactivity helpers, and full voice support including Discord's **DAVE end-to-end encryption** — all in a single cohesive package suite with zero mandatory third-party dependencies outside the .NET runtime.
21+
22+
> **Status:** `0.11.0-alpha.1` — public alpha. APIs may change between minor versions. See the [versioning policy][versioning].
23+
24+
---
25+
26+
## Packages
27+
28+
Install only what your bot needs:
29+
30+
| Package | Description |
31+
|---------|-------------|
32+
| `PawSharp.Client` | High-level `DiscordClient` — the recommended starting point |
33+
| `PawSharp.Core` | Entities, enums, exceptions, validators, CDN helpers |
34+
| `PawSharp.API` | Raw REST client with bucket-aware rate limiting |
35+
| `PawSharp.Gateway` | WebSocket gateway, heartbeat, sharding, session resume |
36+
| `PawSharp.Commands` | Attribute-based prefix command framework with preconditions |
37+
| `PawSharp.Interactions` | Slash commands, buttons, select menus, modals |
38+
| `PawSharp.Interactivity` | Reaction waiting, polls, pagination, component waiters |
39+
| `PawSharp.Voice` | Voice connections + Opus + RTP + DAVE E2EE (MLS / AES-128-GCM) |
740

841
---
942

10-
## Install
43+
## Installation
1144

1245
```bash
13-
# Everything in one package
14-
dotnet add package PawSharp.Client # 0.11.0-alpha.1
15-
16-
# Or pick only what you need
17-
dotnet add package PawSharp.API # REST endpoints only
18-
dotnet add package PawSharp.Gateway # WebSocket / gateway only
19-
dotnet add package PawSharp.Commands # Prefix-based text commands
20-
dotnet add package PawSharp.Interactions # Slash commands & components
21-
dotnet add package PawSharp.Interactivity # Reactions, polls, pagination
22-
dotnet add package PawSharp.Voice # Voice channels + DAVE E2EE
46+
# Full client (recommended)
47+
dotnet add package PawSharp.Client
48+
49+
# Or add individual packages
50+
dotnet add package PawSharp.Commands
51+
dotnet add package PawSharp.Interactions
52+
dotnet add package PawSharp.Voice
2353
```
2454

2555
---
2656

27-
## Quickstart
57+
## Quick Start
2858

2959
```csharp
60+
using PawSharp.Client;
61+
using PawSharp.Core.Enums;
62+
3063
var client = new PawSharpClientBuilder()
3164
.WithToken(Environment.GetEnvironmentVariable("DISCORD_TOKEN")!)
3265
.WithIntents(GatewayIntents.AllNonPrivileged | GatewayIntents.MessageContent)
33-
.WithPresence("pinging", status: "online")
66+
.WithPresence("with .NET", status: "online")
3467
.UseConsoleLogging()
3568
.Build();
3669

3770
client.OnMessageCreated(async msg =>
3871
{
3972
if (msg.Author?.Bot == true) return;
4073
if (msg.Content == "!ping")
41-
await client.Rest.CreateMessageAsync(msg.ChannelId, new() { Content = "Pong!" });
74+
await client.Rest.CreateMessageAsync(msg.ChannelId, new() { Content = "Pong! 🏓" });
4275
});
4376

4477
await client.ConnectAsync();
4578
await Task.Delay(Timeout.Infinite);
4679
```
4780

48-
More examples in [examples/](examples/).
49-
5081
---
5182

52-
## What it does
53-
54-
- **REST**~140 Discord API endpoints (messages, channels, guilds, roles, webhooks, threads, AutoMod, polls, stage instances, scheduled events, and more)
55-
- **Gateway** — WebSocket connection with auto-reconnect, session resume, sharding, and all opcodes handled
56-
- **Caching** — in-memory entity cache (guilds, channels, messages, members, roles) kept in sync from gateway events
57-
- **Rate limiting** — per-route bucket tracking, automatic retry on 429s
58-
- **Slash commands & interactions** — routing, response builders, and follow-up helpers
59-
- **Prefix commands** — attribute-based command modules (reflection scanner in progress, see below)
60-
- **Voice + DAVE E2EE** — Opus encode/decode (Concentus), RTP framing, AES-128-GCM per RFC 9420 MLS — zero extra crypto dependencies
61-
- **CDN helpers** — typed URL builders for avatars, guild icons, banners, emojis, and stickers
83+
## Prefix Commands
6284

63-
---
85+
PawSharp's command framework uses attributes and supports cooldowns, permission checks, and guild-only guards out of the box.
6486

65-
## Error handling
87+
```csharp
88+
var commands = client.UseCommands(prefix: "!");
89+
commands.RegisterModule(client, new ModerationCommands());
90+
commands.CommandErrored = async args =>
91+
{
92+
if (args.Exception is PreconditionFailedException ex)
93+
await args.Context.ReplyAsync(ex.Message);
94+
};
6695

67-
Everything throws typed exceptions:
96+
// ── Module ────────────────────────────────────────────────────────────────────
6897
69-
```csharp
70-
try
98+
public class ModerationCommands : BaseCommandModule
7199
{
72-
await client.Rest.CreateMessageAsync(channelId, new() { Content = text });
100+
[Command("ping")]
101+
[Description("Latency check")]
102+
public async Task PingAsync(CommandContext ctx)
103+
=> await ctx.ReplyAsync("Pong! 🏓");
104+
105+
[Command("ban")]
106+
[RequireGuild]
107+
[RequirePermissions(Permissions.BanMembers)]
108+
[Cooldown(maxUses: 3, perSeconds: 10, CooldownBucketType.User)]
109+
public async Task BanAsync(CommandContext ctx, ulong userId, string reason = "No reason")
110+
{
111+
await ctx.Client.Rest.CreateGuildBanAsync(ctx.GuildId!.Value, userId, reason: reason);
112+
await ctx.ReplyAsync($"User `{userId}` has been banned.");
113+
}
73114
}
74-
catch (ValidationException ex) { /* bad input */ }
75-
catch (RateLimitException ex) { /* slow down */ }
76-
catch (DiscordApiException ex) { /* API error */ }
77115
```
78116

79117
---
80118

81-
## Slash commands
119+
## Slash Commands
82120

83121
```csharp
84122
client.Interactions.RegisterCommand("ping", async interaction =>
@@ -87,109 +125,157 @@ client.Interactions.RegisterCommand("ping", async interaction =>
87125
new InteractionResponse
88126
{
89127
Type = (int)InteractionResponseType.ChannelMessageWithSource,
90-
Data = new InteractionCallbackData { Content = "Pong!" }
128+
Data = new InteractionCallbackData { Content = "Pong! 🏓" }
91129
});
92130
});
93131
```
94132

95133
---
96134

97-
## Voice (DAVE E2EE)
135+
## Interactivity
136+
137+
Wait for button clicks or select menu submissions directly on a message:
138+
139+
```csharp
140+
var msg = await ctx.Client.Rest.CreateMessageAsync(ctx.ChannelId, new()
141+
{
142+
Content = "Choose an option:",
143+
Components = ComponentBuilder.ActionRow(
144+
ComponentBuilder.Button("Confirm", customId: "confirm", style: ButtonStyle.Success),
145+
ComponentBuilder.Button("Cancel", customId: "cancel", style: ButtonStyle.Danger))
146+
});
147+
148+
var result = await msg.WaitForButtonAsync(ctx.Client, user: ctx.User, timeout: TimeSpan.FromSeconds(30));
149+
150+
if (result.TimedOut)
151+
await ctx.RespondAsync("Timed out.");
152+
else if (result.Result!.Data?.CustomId == "confirm")
153+
await ctx.RespondAsync("Confirmed!");
154+
```
155+
156+
---
157+
158+
## Voice + DAVE E2EE
98159

99160
```csharp
100161
var voice = client.UseVoice();
101-
var connection = await voice.ConnectAsync(voiceChannel);
162+
var connection = await voice.ConnectAsync(voiceChannelId);
102163

103-
// Tell Discord you're about to speak, then start the mic pipeline
104164
await connection.SetSpeakingAsync(true);
105-
connection.StartCapture(); // PCM captured → Opus encoded → DAVE encrypted → RTP packet → sent
106165

107-
// Push pre-recorded PCM (16-bit signed mono 48 kHz) directly
166+
// Stream pre-encoded PCM (16-bit mono 48 kHz)
108167
await connection.SendAudioAsync(pcmBytes);
109168

110-
// Incoming packets are automatically decrypted and decoded; push to speaker:
111-
await connection.PlayAudioAsync(receivedPcm);
112-
113-
await connection.SetSpeakingAsync(false);
169+
// Or capture from microphone
170+
connection.StartCapture(); // PCM → Opus → AES-128-GCM (DAVE) → RTP → UDP
171+
// ...
114172
connection.StopCapture();
173+
115174
await connection.DisconnectAsync();
116175
```
117176

118-
Each outgoing frame is a 20 ms Opus packet wrapped in a 12-byte RTP header
119-
(RFC 3550 §5.1, payload type 120). The header is passed as Additional
120-
Authenticated Data to AES-128-GCM so the auth tag covers the full packet, not
121-
just the payload. Keys are derived per-sender via HKDF-SHA256 from the MLS
122-
epoch secret — the entire crypto stack is built on `System.Security.Cryptography`
123-
primitives without any third-party crypto libraries.
177+
Each outgoing frame is a 20 ms Opus packet in a 12-byte RTP header (RFC 3550 §5.1, payload type 120). The RTP header is passed as Additional Authenticated Data to AES-128-GCM, so the auth tag covers the full packet. Keys are derived per-sender via HKDF-SHA256 from the MLS epoch secret. The full crypto stack is built on `System.Security.Cryptography` — no third-party crypto packages required.
124178

125179
---
126180

127-
## Prefix commands
181+
## Dependency Injection
128182

129-
```csharp
130-
var commands = client.UseCommands("!");
131-
132-
public class MyCommands : BaseCommandModule
133-
{
134-
[Command("ping")]
135-
public async Task PingAsync(CommandContext ctx)
136-
=> await ctx.RespondAsync("Pong!");
137-
}
183+
PawSharp integrates cleanly with `Microsoft.Extensions.DependencyInjection`:
138184

139-
commands.RegisterModule(new MyCommands());
185+
```csharp
186+
services.AddSingleton(new PawSharpOptions { Token = token });
187+
services.AddHttpClient<IDiscordRestClient, DiscordRestClient>();
188+
services.AddSingleton<DiscordClient>();
140189
```
141190

142-
> **Note:** The reflection scanner that discovers `[Command]` methods and wires them up is
143-
> not yet implemented. Registration will work once that ships in the next release.
144-
145191
---
146192

147-
## Dependency injection
193+
## Error Handling
194+
195+
All errors surface as typed exceptions:
148196

149197
```csharp
150-
services.AddSingleton(new PawSharpOptions { Token = token });
151-
services.AddSingleton<IDiscordRestClient, DiscordRestClient>();
152-
services.AddSingleton<DiscordClient>();
198+
try
199+
{
200+
await client.Rest.CreateMessageAsync(channelId, request);
201+
}
202+
catch (ValidationException ex) { /* invalid content / embed */ }
203+
catch (RateLimitException ex) { /* still rate-limited */ }
204+
catch (DiscordApiException ex) { /* non-2xx response from API */ }
153205
```
154206

155207
---
156208

157-
## What is not done yet
209+
## Features at a Glance
158210

159-
| Feature | Status |
160-
|---------|--------|
161-
| Command module auto-registration | Attribute system works; reflection scanner not shipped yet |
162-
| Slash command attribute auto-register | Manual registration works; `[SlashCommand]` scanner pending |
163-
| Redis cache as published package | Provider exists and is tested; not yet on NuGet |
211+
| Area | Detail |
212+
|------|--------|
213+
| **REST** | ~140 endpoints — messages, channels, guilds, roles, webhooks, threads, AutoMod, polls, stage instances, scheduled events |
214+
| **Gateway** | Auto-reconnect, session resume, configurable sharding, all standard opcodes |
215+
| **Caching** | In-memory entity cache (guilds, channels, messages, members, roles) kept in sync from gateway events |
216+
| **Rate Limiting** | Per-route bucket tracking, global rate limit detection, automatic 429 retry with back-off |
217+
| **Commands** | Attribute-based modules, `[RequireGuild]`, `[RequirePermissions]`, `[Cooldown]`, `ReplyAsync` |
218+
| **Interactions** | Slash command routing, button & select handlers, modal support, follow-up messages |
219+
| **Interactivity** | `WaitForReactionAsync`, `WaitForButtonAsync`, `WaitForSelectAsync`, `CollectReactionsAsync`, polls |
220+
| **Voice** | Full Opus encode/decode (Concentus), RFC 3550 RTP framing, DAVE E2EE via RFC 9420 MLS |
221+
| **CDN** | Typed URL builders for avatars, guild icons, banners, emojis, stickers |
164222

165223
---
166224

167-
## Project layout
225+
## Project Layout
168226

169227
```
170228
src/
171-
PawSharp.Core - entities, enums, exceptions, builders, validators
172-
PawSharp.API - REST client and rate limiter
173-
PawSharp.Gateway - WebSocket gateway, event dispatcher, heartbeat, sharding
174-
PawSharp.Cache - cache abstractions and in-memory provider
175-
PawSharp.Client - high-level DiscordClient combining all of the above
176-
PawSharp.Commands - prefix command framework
177-
PawSharp.Interactions - slash commands, buttons, modals
178-
PawSharp.Interactivity - reaction waiting, polls, pagination
179-
PawSharp.Voice - voice connections + DAVE E2EE (Opus + MLS + RTP)
180-
tests/ - unit and integration tests (94+ passing)
181-
examples/ - sample bots (DashboardBot, ModerationBot, MusicBot)
182-
docs/ - developer guides + DocFX API reference
229+
PawSharp.Core entities, enums, exceptions, builders, validators, CDN helpers
230+
PawSharp.API REST client + advanced rate limiter
231+
PawSharp.Gateway WebSocket gateway, event dispatcher, heartbeat, sharding
232+
PawSharp.Cache cache abstractions + in-memory provider
233+
PawSharp.Client high-level DiscordClient and fluent builder
234+
PawSharp.Commands prefix command framework with preconditions
235+
PawSharp.Interactions slash commands, components, modals
236+
PawSharp.Interactivity reaction/component waiting, polls, pagination
237+
PawSharp.Voice voice connections, Opus codec, RTP, DAVE E2EE
238+
tests/ unit and integration tests
239+
examples/ sample bots: DashboardBot, ModerationBot, MusicBot
240+
docs/ developer guides + DocFX API reference
183241
```
184242

185243
---
186244

245+
## What Is Still In Progress
246+
247+
| Feature | Status |
248+
|---------|--------|
249+
| Slash command attribute auto-register | Manual registration works; `[SlashCommand]` scanner coming in a future release |
250+
| Redis cache NuGet package | Provider exists and is tested; publication pending |
251+
252+
---
253+
187254
## Contributing
188255

189-
See [CONTRIBUTING.md](CONTRIBUTING.md).
256+
Contributions are welcome! Please read [CONTRIBUTING.md][contributing] before opening a pull request.
190257

191258
---
192259

193260
## License
194261

195-
MIT
262+
PawSharp is distributed under the [MIT License][license].
263+
264+
---
265+
266+
<!-- Reference links -->
267+
[nuget]: https://www.nuget.org/packages/PawSharp.Client
268+
[nuget-badge]: https://img.shields.io/nuget/v/PawSharp.Client?style=flat-square&color=5865F2&label=nuget
269+
[discord-api-badge]: https://img.shields.io/badge/Discord%20API-v10-5865F2?style=flat-square
270+
[discord-docs]: https://discord.com/developers/docs
271+
[dotnet-badge]: https://img.shields.io/badge/.NET-8.0-512BD4?style=flat-square
272+
[dotnet-link]: https://dotnet.microsoft.com/en-us/download/dotnet/8.0
273+
[license-badge]: https://img.shields.io/badge/license-MIT-22c55e?style=flat-square
274+
[license]: LICENSE
275+
[build-badge]: https://img.shields.io/github/actions/workflow/status/M1tsumi/PawSharp/build.yml?style=flat-square
276+
[build]: https://github.com/M1tsumi/PawSharp/actions
277+
[docs]: https://github.com/M1tsumi/PawSharp/tree/main/docs
278+
[changelog]: CHANGELOG.md
279+
[examples]: examples/
280+
[contributing]: CONTRIBUTING.md
281+
[versioning]: docs/VERSIONING_POLICY.md

0 commit comments

Comments
 (0)