Skip to content

Commit dad6694

Browse files
committed
Merge branch 'develop'
2 parents 15c1ac8 + 56a714e commit dad6694

72 files changed

Lines changed: 1608 additions & 580 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/dotnet.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This workflow will build a .NET project
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3+
4+
name: .NET
5+
6+
on:
7+
push:
8+
branches: [ "develop" ]
9+
pull_request:
10+
branches: [ "develop" ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
- name: Setup .NET
20+
uses: actions/setup-dotnet@v4
21+
with:
22+
dotnet-version: 8.0.x
23+
- name: Restore dependencies
24+
working-directory: ./src/
25+
run: dotnet restore
26+
- name: Build
27+
working-directory: ./src/
28+
run: dotnet build --no-restore
29+
- name: Test
30+
working-directory: ./src/
31+
run: dotnet test --no-build --verbosity normal
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Publish NuGet Packages
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
14+
defaults:
15+
run:
16+
working-directory: ./src
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Setup .NET
23+
uses: actions/setup-dotnet@v4
24+
with:
25+
dotnet-version: 8.0.x
26+
27+
- name: Restore
28+
run: dotnet restore Disc.NET.sln
29+
30+
- name: Build package version
31+
shell: bash
32+
run: |
33+
VERSION="1.0.${{ github.run_number }}"
34+
echo "PACKAGE_VERSION=$VERSION" >> "$GITHUB_ENV"
35+
36+
- name: Pack
37+
run: dotnet pack Disc.NET.sln --configuration Release --no-restore --output ./artifacts -p:Version=${{ env.PACKAGE_VERSION }}
38+
39+
- name: Publish to NuGet
40+
run: dotnet nuget push "./artifacts/*.nupkg" --api-key "${{ secrets.NUGET_API_KEY }}" --source "https://api.nuget.org/v3/index.json" --skip-duplicate

README.md

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,62 @@
1-
# Disc.NET
1+
# Disc.NET
2+
3+
**Disc.NET** é um projeto experimental para a construção de uma biblioteca de interação com a API do Discord, utilizando .NET 8+. O objetivo é criar uma alternativa modular, focada em performance e facilidade de uso através de padrões modernos de desenvolvimento.
4+
5+
O projeto ainda está em **fase ativa de desenvolvimento** e explora arquiteturas desacopladas para lidar com a complexidade da Gateway e das interações do Discord.
6+
7+
---
8+
9+
## Como o projeto funciona?
10+
11+
A arquitetura do Disc.NET gira em torno de três pilares principais:
12+
13+
### Event Dispatcher & Handlers
14+
O coração do sistema é o `EventDispatcher`. Ele recebe os payloads brutos da Gateway e os roteia para **Handlers** específicos.
15+
- Cada Handler é responsável por um tipo de evento (ex: `InteractionCreate`, `MessageCreate`).
16+
- Isso permite que a lógica de processamento seja isolada e fácil de estender sem sujar o código principal da conexão.
17+
18+
### Comandos via Attributes
19+
Chega de `if/else` gigantes para validar comandos. O Disc.NET utiliza **Attributes** para mapear classes de comando automaticamente:
20+
- Basta decorar sua classe com `[SlashCommand]` ou `[PrefixCommand]`.
21+
- O sistema de registro faz o *scan* das classes e vincula a execução ao trigger correto via Reflection.
22+
23+
### Service Container (DI)
24+
Utilizamos um `DiscNetContainer` interno para gerenciar dependências. Isso garante que seus comandos tenham acesso fácil a serviços de configuração, clientes de API e bancos de dados de forma nativa.
25+
26+
---
27+
28+
## Exemplo de Uso (Experimental)
29+
30+
```csharp
31+
// 1. Defina o comando usando atributos
32+
[SlashCommand("ping", InteractionType.ApplicationCommand, "Testa a latência")]
33+
public class PingCommand : ISlashCommand
34+
{
35+
public async Task RunAsync(InteractionContext context, CancellationToken ct = default)
36+
{
37+
await context.Response.SendMessageAsync(new Message()
38+
{
39+
Content = "Hello World!",
40+
});
41+
}
42+
}
43+
44+
// 2. Inicialize o AppBuilder
45+
var app = new AppBuilder()
46+
.AddConfiguration(new AppConfiguration("TOKEN") { ApplicationId = 123 })
47+
.Build();
48+
49+
await app.RunAsync();
50+
```
51+
52+
---
53+
54+
## 📦 Módulos Atuais
55+
- **Disc.NET.Client.SDK**: Abstração da API REST.
56+
- **Disc.NET.Commands**: Motor de execução de comandos e contextos.
57+
- **Disc.NET.Shared**: Utilitários de serialização e extensões de sistema.
58+
- **Disc.NET.Components**: Builders para botões, selects e embeds.
59+
60+
---
61+
62+
> ⚠️ **Aviso:** Por ser um projeto experimental, mudanças drásticas na API podem ocorrer a qualquer momento.

src/Directory.Build.props

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project>
2+
<PropertyGroup>
3+
<PackageId>$(AssemblyName)</PackageId>
4+
<Authors>01Dri</Authors>
5+
<Company>01Dri</Company>
6+
<Description>Disc.NET libraries for building Discord applications with .NET.</Description>
7+
<PackageReadmeFile>README.md</PackageReadmeFile>
8+
<RepositoryUrl>https://github.com/01Dri/Disc.NET</RepositoryUrl>
9+
<RepositoryType>git</RepositoryType>
10+
<PackageProjectUrl>https://github.com/01Dri/Disc.NET</PackageProjectUrl>
11+
<PackageTags>discord;dotnet;net8;bot</PackageTags>
12+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
13+
<IncludeSymbols>false</IncludeSymbols>
14+
</PropertyGroup>
15+
16+
<ItemGroup>
17+
<None Include="$(MSBuildThisFileDirectory)..\README.md" Pack="true" PackagePath="\" Link="README.md" Visible="false" />
18+
</ItemGroup>
19+
</Project>

src/Disc.NET.Client.SDK/Client.cs

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,83 @@
11
using Disc.NET.Client.SDK.Interfaces;
22
using Disc.NET.Client.SDK.Messages;
3-
using Disc.NET.Shared.Configurations;
43
using Disc.NET.Shared.Exceptions;
54
using Disc.NET.Shared.Serializer;
6-
using System;
7-
using System.Net;
85
using System.Text;
9-
using System.Text.Json;
106

117
namespace Disc.NET.Client.SDK;
128

13-
public class Client : ClientBase,IClient
9+
public sealed class Client : ClientBase, IClient
1410
{
15-
private readonly AppConfiguration _appConfiguration;
16-
public Client(AppConfiguration appConfiguration, HttpClient client) : base(appConfiguration, client)
11+
private readonly ClientConfiguration _clientConfiguration;
12+
private readonly DiscNetSerializer _serializer = DiscNetSerializer.GetInstance();
13+
public Client(ClientConfiguration clientConfiguration, HttpClient client) : base(clientConfiguration, client)
1714
{
18-
_appConfiguration = appConfiguration;
15+
_clientConfiguration = clientConfiguration;
1916
}
2017

2118
public async Task SendMessageAsync(string channelId, ApiMessage message, CancellationToken cancellation = default)
2219
{
23-
var serializer = DiscNetSerializer.GetInstance();
24-
var json = serializer.Serialize(message);
25-
var content = new StringContent(json, Encoding.UTF8, "application/json");
26-
var response = await HttpClient.PostAsync($"channels/{channelId}/messages", content, cancellation).ConfigureAwait(false);
27-
if (!response.IsSuccessStatusCode)
28-
{
29-
var error = await response.Content.ReadAsStringAsync(cancellation).ConfigureAwait(false);
30-
throw new DiscNetClientSdkException(error, response.StatusCode);
31-
}
20+
await SendMessageAsync(message, channelId, cancellation);
3221
}
3322

3423
public async Task<ApiMessage?> GetMessageAsync(string channelId, string messageId, CancellationToken cancellation = default)
3524
{
36-
var serializer = DiscNetSerializer.GetInstance();
3725

38-
var response = await HttpClient.GetAsync($"channels/{channelId}/messages/{messageId}",cancellation)
26+
var response = await HttpClient.GetAsync($"channels/{channelId}/messages/{messageId}", cancellation)
3927
.ConfigureAwait(false);
4028
if (!response.IsSuccessStatusCode)
4129
{
4230
var error = await response.Content.ReadAsStringAsync(cancellation).ConfigureAwait(false);
4331
throw new DiscNetClientSdkException(error, response.StatusCode);
4432
}
4533
var content = await response.Content.ReadAsStreamAsync(cancellation).ConfigureAwait(false);
46-
return await serializer.DeserializeAsync<ApiMessage>(content, cancellation).ConfigureAwait(false);
34+
return await _serializer.DeserializeAsync<ApiMessage>(content, cancellation).ConfigureAwait(false);
4735
}
4836

4937
public async Task RegisterGlobalSlashCommandAsync(string commandJson, CancellationToken cancellation = default)
5038
{
51-
await PostAsync(commandJson, $"applications/{_appConfiguration.ApplicationId}/commands",
39+
await PostAsync(commandJson, $"applications/{_clientConfiguration.ApplicationId}/commands",
5240
cancellation);
5341
}
5442

5543
public async Task RegisterGuildSlashCommandAsync(string commandJson, string guildId, CancellationToken cancellation = default)
5644
{
57-
await PostAsync(commandJson, $"applications/{_appConfiguration.ApplicationId}/guilds/{guildId}/commands",
45+
await PostAsync(commandJson, $"applications/{_clientConfiguration.ApplicationId}/guilds/{guildId}/commands",
5846
cancellation);
5947
}
6048

61-
public async Task InteractionRespondingAsync(string interactionId, string interactionToken, string responseJson,
49+
public async Task SendInteractionResponseAsync(string interactionId, string interactionToken, string responseJson,
6250
CancellationToken cancellation = default)
6351
{
6452
var url = $"https://discord.com/api/v10/interactions/{interactionId}/{interactionToken}/callback";
6553
await PostAsync(responseJson, url, cancellation);
6654
}
6755

56+
public async Task SendInteractionResponseAsync(string interactionId, string interactionToken, ApiMessage message,
57+
bool isEphemeral = false, CancellationToken cancellation = default)
58+
{
59+
var teste = _serializer.Serialize(new Teste()
60+
{
61+
Type = message.Type!.Value,
62+
Data = message
63+
});
64+
var url = $"https://discord.com/api/v10/interactions/{interactionId}/{interactionToken}/callback";
65+
66+
await PostAsync(teste, url, cancellation);
67+
}
6868

6969

70+
private async Task SendMessageAsync(ApiMessage message, string channelId, CancellationToken cancellation = default)
71+
{
72+
var messageJson = _serializer.Serialize(message);
73+
var content = new StringContent(messageJson, Encoding.UTF8, "application/json");
74+
var response = await HttpClient.PostAsync($"channels/{channelId}/messages", content, cancellation).ConfigureAwait(false);
75+
if (!response.IsSuccessStatusCode)
76+
{
77+
var error = await response.Content.ReadAsStringAsync(cancellation).ConfigureAwait(false);
78+
throw new DiscNetClientSdkException(error, response.StatusCode);
79+
}
80+
}
7081
private async Task PostAsync(string json, string uri, CancellationToken cancellation = default)
7182
{
7283
var content = new StringContent(json, Encoding.UTF8, "application/json");
@@ -77,5 +88,6 @@ private async Task PostAsync(string json, string uri, CancellationToken cancella
7788
var error = await response.Content.ReadAsStringAsync(cancellation).ConfigureAwait(false);
7889
throw new DiscNetClientSdkException(error, response.StatusCode);
7990
}
80-
}
91+
}
92+
8193
}
Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
using Disc.NET.Shared.Configurations;
2-
3-
namespace Disc.NET.Client.SDK
1+
namespace Disc.NET.Client.SDK
42
{
53
public abstract class ClientBase
64
{
75

86
protected readonly HttpClient HttpClient;
9-
protected AppConfiguration AppConfiguration { get; }
7+
protected ClientConfiguration ClientConfiguration { get; }
108

11-
12-
protected ClientBase(AppConfiguration appConfiguration, HttpClient client)
9+
protected ClientBase(ClientConfiguration clientConfiguration, HttpClient client)
1310
{
14-
AppConfiguration = appConfiguration;
11+
ClientConfiguration = clientConfiguration;
1512
HttpClient = client;
1613
HttpClient.BaseAddress = new Uri("https://discord.com/api/v10/");
1714
HttpClient.DefaultRequestHeaders.Authorization =
18-
new System.Net.Http.Headers.AuthenticationHeaderValue("Bot", AppConfiguration.Token);
15+
new System.Net.Http.Headers.AuthenticationHeaderValue("Bot", ClientConfiguration.Token);
1916
}
2017
}
2118
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace Disc.NET.Client.SDK
2+
{
3+
public class ClientConfiguration
4+
{
5+
public string Token { get; set; }
6+
public long ApplicationId { get; set; }
7+
public ClientConfiguration(string token, long applicationId)
8+
{
9+
Token = token;
10+
ApplicationId = applicationId;
11+
}
12+
}
13+
}
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,32 @@
11
using Disc.NET.Client.SDK.Interfaces;
2-
using Disc.NET.Shared.Configurations;
32

43
namespace Disc.NET.Client.SDK
54
{
65
public sealed class ClientSingleton
76
{
87
private static IClient? _instance;
8+
private static ClientConfiguration _clientConfiguration;
99

10-
public static IClient GetInstance(AppConfiguration appConfiguration)
10+
public static IClient GetInstance()
1111
{
12+
if (_clientConfiguration == null)
13+
{
14+
throw new InvalidOperationException("Client configuration is not set. Please call Configure method before getting the instance.");
15+
}
16+
1217
if (_instance == null)
13-
_instance = new Client(appConfiguration, new HttpClient());
18+
{
19+
_instance = new Client(_clientConfiguration, new HttpClient());
20+
}
1421

1522
return _instance;
1623
}
1724

25+
public static void Configure(string token, long applicationId)
26+
{
27+
if (_clientConfiguration == null)
28+
_clientConfiguration = new ClientConfiguration(token, applicationId);
29+
}
30+
1831
}
1932
}

src/Disc.NET.Client.SDK/Disc.NET.Client.SDK.csproj

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44
<TargetFramework>net8.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7+
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);IncludeSharedProjectOutput</TargetsForTfmSpecificBuildOutput>
78
</PropertyGroup>
89

910
<ItemGroup>
10-
<ProjectReference Include="..\Disc.NET.Shared\Disc.NET.Shared.csproj" />
11+
<ProjectReference Include="..\Disc.NET.Shared\Disc.NET.Shared.csproj" PrivateAssets="all" />
1112
</ItemGroup>
1213

14+
<Target Name="IncludeSharedProjectOutput" DependsOnTargets="ResolveReferences">
15+
<ItemGroup>
16+
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths)"
17+
Condition="'%(ReferenceCopyLocalPaths.ReferenceSourceTarget)' == 'ProjectReference' and '%(ReferenceCopyLocalPaths.Filename)' == 'Disc.NET.Shared'" />
18+
</ItemGroup>
19+
</Target>
20+
1321
</Project>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
namespace Disc.NET.Shared.Enums
1+
namespace Disc.NET.Client.SDK.Enums
22
{
33
[Flags]
44
public enum MessageFlag
55
{
66
Ephemeral = 1 << 6, // 64
77
}
8-
}
8+
}

0 commit comments

Comments
 (0)