@@ -15,8 +15,8 @@ namespace Discord.API | |||
[JsonProperty("proxy_url")] | |||
public string ProxyUrl { get; set; } | |||
[JsonProperty("height")] | |||
public int? Height { get; set; } | |||
public Optional<int> Height { get; set; } | |||
[JsonProperty("width")] | |||
public int? Width { get; set; } | |||
public Optional<int> Width { get; set; } | |||
} | |||
} |
@@ -14,28 +14,28 @@ namespace Discord.API | |||
//GuildChannel | |||
[JsonProperty("guild_id")] | |||
public ulong? GuildId { get; set; } | |||
public Optional<ulong> GuildId { get; set; } | |||
[JsonProperty("name")] | |||
public string Name { get; set; } | |||
public Optional<string> Name { get; set; } | |||
[JsonProperty("type")] | |||
public ChannelType Type { get; set; } | |||
public Optional<ChannelType> Type { get; set; } | |||
[JsonProperty("position")] | |||
public int Position { get; set; } | |||
public Optional<int> Position { get; set; } | |||
[JsonProperty("permission_overwrites")] | |||
public Overwrite[] PermissionOverwrites { get; set; } | |||
public Optional<Overwrite[]> PermissionOverwrites { get; set; } | |||
//TextChannel | |||
[JsonProperty("topic")] | |||
public string Topic { get; set; } | |||
public Optional<string> Topic { get; set; } | |||
//VoiceChannel | |||
[JsonProperty("bitrate")] | |||
public int Bitrate { get; set; } | |||
public Optional<int> Bitrate { get; set; } | |||
[JsonProperty("user_limit")] | |||
public int UserLimit { get; set; } | |||
public Optional<int> UserLimit { get; set; } | |||
//DMChannel | |||
[JsonProperty("recipient")] | |||
public User Recipient { get; set; } | |||
public Optional<User> Recipient { get; set; } | |||
} | |||
} |
@@ -9,8 +9,8 @@ namespace Discord.API | |||
[JsonProperty("proxy_url")] | |||
public string ProxyUrl { get; set; } | |||
[JsonProperty("height")] | |||
public int? Height { get; set; } | |||
public int Height { get; set; } | |||
[JsonProperty("width")] | |||
public int? Width { get; set; } | |||
public int Width { get; set; } | |||
} | |||
} |
@@ -7,8 +7,8 @@ namespace Discord.API | |||
[JsonProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("url")] | |||
public string StreamUrl { get; set; } | |||
public Optional<string> StreamUrl { get; set; } | |||
[JsonProperty("type")] | |||
public StreamType? StreamType { get; set; } | |||
public Optional<StreamType?> StreamType { get; set; } | |||
} | |||
} |
@@ -7,6 +7,6 @@ namespace Discord.API | |||
[JsonProperty("enabled")] | |||
public bool Enabled { get; set; } | |||
[JsonProperty("channel_id")] | |||
public ulong? ChannelId { get; set; } | |||
public ulong ChannelId { get; set; } | |||
} | |||
} |
@@ -10,12 +10,12 @@ namespace Discord.API | |||
[JsonProperty("nick")] | |||
public Optional<string> Nick { get; set; } | |||
[JsonProperty("roles")] | |||
public Optional<ulong[]> Roles { get; set; } | |||
public ulong[] Roles { get; set; } | |||
[JsonProperty("joined_at")] | |||
public Optional<DateTime> JoinedAt { get; set; } | |||
public DateTime JoinedAt { get; set; } | |||
[JsonProperty("deaf")] | |||
public Optional<bool> Deaf { get; set; } | |||
public bool Deaf { get; set; } | |||
[JsonProperty("mute")] | |||
public Optional<bool> Mute { get; set; } | |||
public bool Mute { get; set; } | |||
} | |||
} |
@@ -7,7 +7,9 @@ namespace Discord.API | |||
[JsonProperty("user")] | |||
public User User { get; set; } | |||
[JsonProperty("guild_id")] | |||
public ulong? GuildId { get; set; } | |||
public Optional<ulong> GuildId { get; set; } | |||
[JsonProperty("roles")] | |||
public Optional<ulong[]> Roles { get; set; } | |||
[JsonProperty("status")] | |||
public UserStatus Status { get; set; } | |||
[JsonProperty("game")] | |||
@@ -9,6 +9,6 @@ namespace Discord.API | |||
[JsonProperty("mention_count")] | |||
public int MentionCount { get; set; } | |||
[JsonProperty("last_message_id")] | |||
public ulong? LastMessageId { get; set; } | |||
public Optional<ulong> LastMessageId { get; set; } | |||
} | |||
} |
@@ -9,14 +9,14 @@ namespace Discord.API | |||
[JsonProperty("name")] | |||
public string Name { get; set; } | |||
[JsonProperty("color")] | |||
public uint? Color { get; set; } | |||
public uint Color { get; set; } | |||
[JsonProperty("hoist")] | |||
public bool? Hoist { get; set; } | |||
public bool Hoist { get; set; } | |||
[JsonProperty("position")] | |||
public int? Position { get; set; } | |||
public int Position { get; set; } | |||
[JsonProperty("permissions"), Int53] | |||
public ulong? Permissions { get; set; } | |||
public ulong Permissions { get; set; } | |||
[JsonProperty("managed")] | |||
public bool? Managed { get; set; } | |||
public bool Managed { get; set; } | |||
} | |||
} |
@@ -391,7 +391,7 @@ namespace Discord.API | |||
try | |||
{ | |||
var model = await SendAsync<Channel>("GET", $"channels/{channelId}", options: options).ConfigureAwait(false); | |||
if (model.GuildId != guildId) | |||
if (!model.GuildId.IsSpecified || model.GuildId.Value != guildId) | |||
return null; | |||
return model; | |||
} | |||
@@ -133,7 +133,7 @@ namespace Discord | |||
var model = await ApiClient.GetChannelAsync(id).ConfigureAwait(false); | |||
if (model != null) | |||
{ | |||
if (model.GuildId != null) | |||
if (model.GuildId.IsSpecified) | |||
{ | |||
var guildModel = await ApiClient.GetGuildAsync(model.GuildId.Value).ConfigureAwait(false); | |||
if (guildModel != null) | |||
@@ -143,7 +143,7 @@ namespace Discord | |||
} | |||
} | |||
else | |||
return new DMChannel(this, new User(this, model.Recipient), model); | |||
return new DMChannel(this, new User(this, model.Recipient.Value), model); | |||
} | |||
return null; | |||
} | |||
@@ -151,7 +151,7 @@ namespace Discord | |||
public virtual async Task<IReadOnlyCollection<IDMChannel>> GetDMChannelsAsync() | |||
{ | |||
var models = await ApiClient.GetMyDMsAsync().ConfigureAwait(false); | |||
return models.Select(x => new DMChannel(this, new User(this, x.Recipient), x)).ToImmutableArray(); | |||
return models.Select(x => new DMChannel(this, new User(this, x.Recipient.Value), x)).ToImmutableArray(); | |||
} | |||
/// <inheritdoc /> | |||
@@ -239,7 +239,7 @@ namespace Discord | |||
} | |||
internal CachedDMChannel AddDMChannel(API.Channel model, DataStore dataStore) | |||
{ | |||
var recipient = GetOrAddUser(model.Recipient, dataStore); | |||
var recipient = GetOrAddUser(model.Recipient.Value, dataStore); | |||
var channel = recipient.AddDMChannel(model); | |||
dataStore.AddChannel(channel); | |||
return channel; | |||
@@ -457,7 +457,7 @@ namespace Discord | |||
var data = (payload as JToken).ToObject<API.Channel>(_serializer); | |||
ICachedChannel channel = null; | |||
if (data.GuildId != null) | |||
if (data.GuildId.IsSpecified) | |||
{ | |||
var guild = DataStore.GetGuild(data.GuildId.Value); | |||
if (guild != null) | |||
@@ -499,7 +499,7 @@ namespace Discord | |||
ICachedChannel channel = null; | |||
var data = (payload as JToken).ToObject<API.Channel>(_serializer); | |||
if (data.GuildId != null) | |||
if (data.GuildId.IsSpecified) | |||
{ | |||
var guild = DataStore.GetGuild(data.GuildId.Value); | |||
if (guild != null) | |||
@@ -812,13 +812,7 @@ namespace Discord | |||
await _gatewayLogger.DebugAsync("Received Dispatch (PRESENCE_UPDATE)").ConfigureAwait(false); | |||
var data = (payload as JToken).ToObject<API.Presence>(_serializer); | |||
if (data.GuildId == null) | |||
{ | |||
var user = DataStore.GetUser(data.User.Id); | |||
if (user == null) | |||
user.Update(data, UpdateSource.WebSocket); | |||
} | |||
else | |||
if (data.GuildId.IsSpecified) | |||
{ | |||
var guild = DataStore.GetGuild(data.GuildId.Value); | |||
if (guild == null) | |||
@@ -831,6 +825,12 @@ namespace Discord | |||
else | |||
guild.AddOrUpdatePresence(data); | |||
} | |||
else | |||
{ | |||
var user = DataStore.GetUser(data.User.Id); | |||
if (user == null) | |||
user.Update(data, UpdateSource.WebSocket); | |||
} | |||
} | |||
break; | |||
case "TYPING_START": | |||
@@ -30,7 +30,7 @@ namespace Discord | |||
{ | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
Recipient.Update(model.Recipient, UpdateSource.Rest); | |||
Recipient.Update(model.Recipient.Value, UpdateSource.Rest); | |||
} | |||
public async Task UpdateAsync() | |||
@@ -34,15 +34,13 @@ namespace Discord | |||
{ | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
Name = model.Name; | |||
Position = model.Position; | |||
Name = model.Name.Value; | |||
Position = model.Position.Value; | |||
var overwrites = model.PermissionOverwrites.Value; | |||
var newOverwrites = new ConcurrentDictionary<ulong, Overwrite>(); | |||
for (int i = 0; i < model.PermissionOverwrites.Length; i++) | |||
{ | |||
var overwrite = model.PermissionOverwrites[i]; | |||
newOverwrites[overwrite.TargetId] = new Overwrite(overwrite); | |||
} | |||
for (int i = 0; i < overwrites.Length; i++) | |||
newOverwrites[overwrites[i].TargetId] = new Overwrite(overwrites[i]); | |||
_overwrites = newOverwrites; | |||
} | |||
@@ -26,7 +26,7 @@ namespace Discord | |||
{ | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
Topic = model.Topic; | |||
Topic = model.Topic.Value; | |||
base.Update(model, source); | |||
} | |||
@@ -22,8 +22,8 @@ namespace Discord | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
base.Update(model, source); | |||
Bitrate = model.Bitrate; | |||
UserLimit = model.UserLimit; | |||
Bitrate = model.Bitrate.Value; | |||
UserLimit = model.UserLimit.Value; | |||
} | |||
public async Task ModifyAsync(Action<ModifyVoiceChannelParams> func) | |||
@@ -288,14 +288,14 @@ namespace Discord | |||
internal GuildChannel ToChannel(API.Channel model) | |||
{ | |||
switch (model.Type) | |||
switch (model.Type.Value) | |||
{ | |||
case ChannelType.Text: | |||
return new TextChannel(this, model); | |||
case ChannelType.Voice: | |||
return new VoiceChannel(this, model); | |||
default: | |||
throw new InvalidOperationException($"Unknown channel type: {model.Type}"); | |||
throw new InvalidOperationException($"Unknown channel type: {model.Type.Value}"); | |||
} | |||
} | |||
@@ -4,6 +4,8 @@ namespace Discord | |||
{ | |||
public interface IInviteMetadata : IInvite | |||
{ | |||
/// <summary> Gets the user that created this invite. </summary> | |||
IUser Inviter { get; } | |||
/// <summary> Returns true if this invite was revoked. </summary> | |||
bool IsRevoked { get; } | |||
/// <summary> Returns true if users accepting this invite will be removed from the guild when they log off. </summary> | |||
@@ -11,6 +11,7 @@ namespace Discord | |||
public int? MaxUses { get; private set; } | |||
public int Uses { get; private set; } | |||
public DateTime CreatedAt { get; private set; } | |||
public IUser Inviter { get; private set; } | |||
public InviteMetadata(DiscordClient client, Model model) | |||
: base(client, model) | |||
@@ -21,6 +22,7 @@ namespace Discord | |||
{ | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
Inviter = new User(Discord, model.Inviter); | |||
IsRevoked = model.Revoked; | |||
IsTemporary = model.Temporary; | |||
MaxAge = model.MaxAge != 0 ? model.MaxAge : (int?)null; | |||
@@ -37,11 +37,11 @@ namespace Discord | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
Name = model.Name; | |||
IsHoisted = model.Hoist.Value; | |||
IsManaged = model.Managed.Value; | |||
Position = model.Position.Value; | |||
Color = new Color(model.Color.Value); | |||
Permissions = new GuildPermissions(model.Permissions.Value); | |||
IsHoisted = model.Hoist; | |||
IsManaged = model.Managed; | |||
Position = model.Position; | |||
Color = new Color(model.Color); | |||
Permissions = new GuildPermissions(model.Permissions); | |||
} | |||
public async Task ModifyAsync(Action<ModifyGuildRoleParams> func) | |||
@@ -17,6 +17,6 @@ namespace Discord | |||
public Game(string name) | |||
: this(name, null, StreamType.NotStreaming) { } | |||
internal Game(Model model) | |||
: this(model.Name, model.StreamUrl, model.StreamType ?? StreamType.NotStreaming) { } | |||
: this(model.Name, model.StreamUrl.GetValueOrDefault(null), model.StreamType.GetValueOrDefault(null) ?? StreamType.NotStreaming) { } | |||
} | |||
} |
@@ -6,6 +6,7 @@ using System.Diagnostics; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Model = Discord.API.GuildMember; | |||
using PresenceModel = Discord.API.Presence; | |||
using VoiceStateModel = Discord.API.VoiceState; | |||
namespace Discord | |||
@@ -47,29 +48,24 @@ namespace Discord | |||
{ | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
if (model.Deaf.IsSpecified) | |||
IsDeaf = model.Deaf.Value; | |||
if (model.Mute.IsSpecified) | |||
IsMute = model.Mute.Value; | |||
if (model.JoinedAt.IsSpecified) | |||
JoinedAt = model.JoinedAt.Value; | |||
//if (model.Deaf.IsSpecified) | |||
IsDeaf = model.Deaf; | |||
//if (model.Mute.IsSpecified) | |||
IsMute = model.Mute; | |||
//if (model.JoinedAt.IsSpecified) | |||
JoinedAt = model.JoinedAt; | |||
if (model.Nick.IsSpecified) | |||
Nickname = model.Nick.Value; | |||
//if (model.Roles.IsSpecified) | |||
UpdateRoles(model.Roles); | |||
} | |||
public void Update(PresenceModel model, UpdateSource source) | |||
{ | |||
if (source == UpdateSource.Rest && IsAttached) return; | |||
if (model.Roles.IsSpecified) | |||
{ | |||
var value = model.Roles.Value; | |||
var roles = ImmutableArray.CreateBuilder<Role>(value.Length + 1); | |||
roles.Add(Guild.EveryoneRole); | |||
for (int i = 0; i < value.Length; i++) | |||
{ | |||
var role = Guild.GetRole(value[i]); | |||
if (role != null) | |||
roles.Add(role); | |||
} | |||
Roles = roles.ToImmutable(); | |||
GuildPermissions = new GuildPermissions(Permissions.ResolveGuild(this)); | |||
} | |||
UpdateRoles(model.Roles.Value); | |||
} | |||
public void Update(VoiceStateModel model, UpdateSource source) | |||
{ | |||
@@ -78,6 +74,19 @@ namespace Discord | |||
IsDeaf = model.Deaf; | |||
IsMute = model.Mute; | |||
} | |||
private void UpdateRoles(ulong[] roleIds) | |||
{ | |||
var roles = ImmutableArray.CreateBuilder<Role>(roleIds.Length + 1); | |||
roles.Add(Guild.EveryoneRole); | |||
for (int i = 0; i < roleIds.Length; i++) | |||
{ | |||
var role = Guild.GetRole(roleIds[i]); | |||
if (role != null) | |||
roles.Add(role); | |||
} | |||
Roles = roles.ToImmutable(); | |||
GuildPermissions = new GuildPermissions(Permissions.ResolveGuild(this)); | |||
} | |||
public async Task UpdateAsync() | |||
{ | |||
@@ -79,23 +79,22 @@ namespace Discord | |||
_channels = channels; | |||
var presences = new ConcurrentDictionary<ulong, Presence>(); | |||
var members = new ConcurrentDictionary<ulong, CachedGuildUser>(); | |||
if (model.Presences != null) | |||
{ | |||
for (int i = 0; i < model.Presences.Length; i++) | |||
AddOrUpdatePresence(model.Presences[i], presences); | |||
AddOrUpdatePresence(model.Presences[i], presences, members); | |||
} | |||
_presences = presences; | |||
var members = new ConcurrentDictionary<ulong, CachedGuildUser>(); | |||
if (model.Members != null) | |||
{ | |||
for (int i = 0; i < model.Members.Length; i++) | |||
AddUser(model.Members[i], dataStore, members); | |||
_downloaderPromise = new TaskCompletionSource<bool>(); | |||
DownloadedMemberCount = model.Members.Length; | |||
_downloaderPromise = new TaskCompletionSource<bool>(); | |||
if (!model.Large) | |||
_downloaderPromise.SetResult(true); | |||
} | |||
_presences = presences; | |||
_members = members; | |||
var voiceStates = new ConcurrentDictionary<ulong, VoiceState>(); | |||
@@ -125,7 +124,8 @@ namespace Discord | |||
return Discord.DataStore.RemoveChannel(id) as ICachedGuildChannel; | |||
} | |||
public Presence AddOrUpdatePresence(PresenceModel model, ConcurrentDictionary<ulong, Presence> presences = null) | |||
public Presence AddOrUpdatePresence(PresenceModel model, ConcurrentDictionary<ulong, Presence> presences = null, | |||
ConcurrentDictionary<ulong, CachedGuildUser> members = null) | |||
{ | |||
var game = model.Game != null ? new Game(model.Game) : (Game?)null; | |||
var presence = new Presence(model.Status, game); | |||
@@ -229,14 +229,14 @@ namespace Discord | |||
new internal ICachedGuildChannel ToChannel(ChannelModel model) | |||
{ | |||
switch (model.Type) | |||
switch (model.Type.Value) | |||
{ | |||
case ChannelType.Text: | |||
return new CachedTextChannel(this, model); | |||
case ChannelType.Voice: | |||
return new CachedVoiceChannel(this, model); | |||
default: | |||
throw new InvalidOperationException($"Unknown channel type: {model.Type}"); | |||
throw new InvalidOperationException($"Unknown channel type: {model.Type.Value}"); | |||
} | |||
} | |||
} | |||
@@ -15,7 +15,6 @@ | |||
"buildOptions": { | |||
"allowUnsafe": true, | |||
"define": [ "BENCHMARK" ], | |||
"warningsAsErrors": false | |||
}, | |||