Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NetCord.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<File Path="Tests/Directory.Build.targets" />
<Project Path="Tests/ColorTest/ColorTest.csproj" />
<Project Path="Tests/MentionTest/MentionTest.csproj" />
<Project Path="Tests/NetCord.Rest.Tests/NetCord.Rest.Tests.csproj" />
<Project Path="Tests/NetCord.Test.Hosting.AspNetCore/NetCord.Test.Hosting.AspNetCore.csproj" />
<Project Path="Tests/NetCord.Test.Hosting/NetCord.Test.Hosting.csproj" />
<Project Path="Tests/NetCord.Test.Sharded.Hosting/NetCord.Test.Sharded.Hosting.csproj" />
Expand Down
4 changes: 4 additions & 0 deletions NetCord/Gateway/Invite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public Invite(JsonModels.JsonInvite jsonModel, RestClient client)

public int Uses => _jsonModel.Uses;

public DateTimeOffset? ExpiresAt => _jsonModel.ExpiresAt;

public IReadOnlyList<ulong>? RoleIds => _jsonModel.RoleIds;

ulong? IInvite.ChannelId => ChannelId;

int? IInvite.MaxAge => MaxAge;
Expand Down
6 changes: 6 additions & 0 deletions NetCord/Gateway/JsonModels/JsonInvite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,10 @@ public class JsonInvite

[JsonPropertyName("uses")]
public int Uses { get; set; }

[JsonPropertyName("expires_at")]
public DateTimeOffset? ExpiresAt { get; set; }

[JsonPropertyName("role_ids")]
public ulong[]? RoleIds { get; set; }
}
1 change: 1 addition & 0 deletions NetCord/IInvite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public interface IInvite
public bool? Temporary { get; }
public int? Uses { get; }
public DateTimeOffset? CreatedAt { get; }
Comment thread
KubaZ2 marked this conversation as resolved.
public DateTimeOffset? ExpiresAt { get; }
}
7 changes: 7 additions & 0 deletions NetCord/InviteFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace NetCord;

[Flags]
public enum InviteFlags
{
IsGuestInvite = 1 << 0,
}
Comment thread
KubaZ2 marked this conversation as resolved.
24 changes: 23 additions & 1 deletion NetCord/Rest/InviteProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace NetCord.Rest;

[GenerateMethodsForProperties]
public partial class InviteProperties
public partial class InviteProperties : IHttpSerializable
{
[JsonPropertyName("max_age")]
public int? MaxAge { get; set; }
Expand All @@ -25,4 +25,26 @@ public partial class InviteProperties

[JsonPropertyName("target_application_id")]
public ulong? TargetApplicationId { get; set; }

[JsonIgnore]
public InviteTargetUsersProperties? TargetUsers { get; set; }

[JsonPropertyName("role_ids")]
public IEnumerable<ulong>? RoleIds { get; set; }

HttpContent IHttpSerializable.Serialize() => Serialize();

internal HttpContent Serialize()
Comment thread
KubaZ2 marked this conversation as resolved.
{
JsonContent<InviteProperties> inviteContent = new(this, Serialization.Default.InviteProperties);

if (TargetUsers is not { } targetUsers)
return inviteContent;

return new MultipartFormDataContent()
{
{ inviteContent, "payload_json" },
{ targetUsers.Serialize(), "target_users_file", "target_users_file" }
};
}
}
28 changes: 28 additions & 0 deletions NetCord/Rest/InviteTargetUsersJobStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using NetCord.Rest.JsonModels;

namespace NetCord.Rest;

public class InviteTargetUsersJobStatus(JsonInviteTargetUsersJobStatus jsonModel) : IJsonModel<JsonInviteTargetUsersJobStatus>
{
JsonInviteTargetUsersJobStatus IJsonModel<JsonInviteTargetUsersJobStatus>.JsonModel => jsonModel;

public InviteTargetUsersJobStatusCode Status => jsonModel.Status;

public int TotalUsers => jsonModel.TotalUsers;

public int ProcessedUsers => jsonModel.ProcessedUsers;

public DateTimeOffset CreatedAt => jsonModel.CreatedAt;

public DateTimeOffset? CompletedAt => jsonModel.CompletedAt;

public string? ErrorMessage => jsonModel.ErrorMessage;
}

public enum InviteTargetUsersJobStatusCode
{
Unspecified = 0,
Processing = 1,
Completed = 2,
Failed = 3,
}
140 changes: 140 additions & 0 deletions NetCord/Rest/InviteTargetUsersProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System.Buffers.Text;
using System.Diagnostics.CodeAnalysis;

namespace NetCord.Rest;

[GenerateMethodsForProperties]
public partial class InviteTargetUsersProperties : IHttpSerializable
{
private readonly Stream _stream;
private byte _read;

private InviteTargetUsersProperties(Stream stream)
{
_stream = stream;
}

public static InviteTargetUsersProperties FromStream(Stream stream) => new(stream);

public static InviteTargetUsersProperties FromEnumerable(IEnumerable<ulong> userIds) => new(new UserIdsStream(userIds));

HttpContent IHttpSerializable.Serialize() => Serialize();

internal HttpContent Serialize()
{
if (Interlocked.Exchange(ref _read, 1) is 1)
ThrowAlreadySent();

return new StreamContent(_stream);
}

[DoesNotReturn]
private static void ThrowAlreadySent()
{
throw new InvalidOperationException("The invite target users have already been sent.");
}

Comment thread
KubaZ2 marked this conversation as resolved.
private sealed class UserIdsStream(IEnumerable<ulong> userIds) : Stream
{
private readonly IEnumerator<ulong> _enumerator = userIds.GetEnumerator();
private readonly byte[] _buffer = new byte[22];
private int _startPosition;
private int _endPosition;

public override bool CanRead => true;

public override bool CanSeek => false;

public override bool CanWrite => false;

public override long Length => throw new NotSupportedException();

public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }

public override void Flush() => throw new NotSupportedException();

public override int Read(byte[] buffer, int offset, int count) => Read(new Span<byte>(buffer, offset, count));

public override int Read(Span<byte> buffer)
{
int totalWritten = 0;

if (_startPosition != _endPosition)
{
var bufferedLength = _endPosition - _startPosition;

var length = Math.Min(bufferedLength, buffer.Length);

_buffer.AsSpan(_startPosition, length).CopyTo(buffer);
_startPosition += length;

if (length != bufferedLength)
return length;

totalWritten += length;
}

var newLine = "\r\n"u8;

while (_enumerator.MoveNext())
{
var userId = _enumerator.Current;
if (!Utf8Formatter.TryFormat(userId, buffer[totalWritten..], out var written))
{
_ = Utf8Formatter.TryFormat(userId, _buffer, out var writtenToBuffer);

_buffer.AsSpan(0, _startPosition = written = buffer.Length - totalWritten).CopyTo(buffer[totalWritten..]);

totalWritten += written;

newLine.CopyTo(_buffer.AsSpan(writtenToBuffer));
_endPosition = writtenToBuffer + newLine.Length;
break;
}

totalWritten += written;

if (!newLine.TryCopyTo(buffer[totalWritten..]))
{
var remaining = buffer.Length - totalWritten;
if (remaining > 0)
{
newLine[..remaining].CopyTo(buffer[totalWritten..]);
totalWritten += remaining;
}

newLine[remaining..].CopyTo(_buffer);
_startPosition = 0;
_endPosition = newLine.Length - remaining;

break;
}

totalWritten += newLine.Length;
}

return totalWritten;
}

public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
}

public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
Comment thread
KubaZ2 marked this conversation as resolved.
Comment thread
KubaZ2 marked this conversation as resolved.
return new(Read(buffer.Span));
}

public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();

protected override void Dispose(bool disposing)
{
if (disposing)
_enumerator.Dispose();
}
}
}

24 changes: 24 additions & 0 deletions NetCord/Rest/JsonModels/JsonInviteTargetUsersJobStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;

namespace NetCord.Rest.JsonModels;

public class JsonInviteTargetUsersJobStatus
{
[JsonPropertyName("status")]
public InviteTargetUsersJobStatusCode Status { get; set; }

[JsonPropertyName("total_users")]
public int TotalUsers { get; set; }

[JsonPropertyName("processed_users")]
public int ProcessedUsers { get; set; }

[JsonPropertyName("created_at")]
public DateTimeOffset CreatedAt { get; set; }

[JsonPropertyName("completed_at")]
public DateTimeOffset? CompletedAt { get; set; }

[JsonPropertyName("error_message")]
public string? ErrorMessage { get; set; }
}
9 changes: 6 additions & 3 deletions NetCord/Rest/JsonModels/JsonRestInvite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,15 @@ public class JsonRestInvite
[JsonPropertyName("expires_at")]
public DateTimeOffset? ExpiresAt { get; set; }

[JsonPropertyName("stage_instance")]
public JsonStageInstance? StageInstance { get; set; }

[JsonPropertyName("guild_scheduled_event")]
public JsonGuildScheduledEvent? GuildScheduledEvent { get; set; }

[JsonPropertyName("flags")]
public InviteFlags? Flags { get; set; }

[JsonPropertyName("roles")]
public JsonRole[]? Roles { get; set; }
Comment thread
KubaZ2 marked this conversation as resolved.

[JsonPropertyName("uses")]
public int? Uses { get; set; }

Expand Down
5 changes: 4 additions & 1 deletion NetCord/Rest/RestClient.Channel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,10 @@ public async Task<IEnumerable<RestInvite>> GetGuildChannelInvitesAsync(ulong cha
[GenerateAlias([typeof(IGuildChannel)], nameof(IGuildChannel.Id))]
public async Task<RestInvite> CreateGuildChannelInviteAsync(ulong channelId, InviteProperties? inviteProperties = null, RestRequestProperties? properties = null, CancellationToken cancellationToken = default)
{
using (HttpContent content = new JsonContent<InviteProperties?>(inviteProperties, Serialization.Default.InviteProperties))
if (inviteProperties is null)
return new(await (await SendRequestAsync(HttpMethod.Post, $"/channels/{channelId}/invites", null, new(channelId), properties, cancellationToken: cancellationToken).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInvite).ConfigureAwait(false), this);

using (HttpContent content = inviteProperties.Serialize())
return new(await (await SendRequestAsync(HttpMethod.Post, content, $"/channels/{channelId}/invites", null, new(channelId), properties, cancellationToken: cancellationToken).ConfigureAwait(false)).ToObjectAsync(Serialization.Default.JsonRestInvite).ConfigureAwait(false), this);
}

Expand Down
Loading