diff --git a/Maki/Discord.cs b/Maki/Discord.cs index 66e2407..877f0ae 100644 --- a/Maki/Discord.cs +++ b/Maki/Discord.cs @@ -182,7 +182,7 @@ namespace Maki /// /// Someone started typing, fired again after 8 seconds /// - public event Action OnTypingStart; + public event Action OnTypingStart; /// /// Someone's status and/or game updated @@ -383,24 +383,25 @@ namespace Maki DiscordChannel chan = channels.Find(x => x.Id == channel.Id); chan.Name = channel.Name; - /*existing.Type = channel.Type; - existing.LastMessageId = channel.LastMessageId; - if (existing.Type == ChannelType.Private) - existing.Recipients = channel.Recipients; - else + if (chan.Type == DiscordChannelType.Private) { - existing.GuildId = channel.GuildId; - existing.Topic = channel.Topic; - existing.PermissionOverwrites = channel.PermissionOverwrites; - existing.Position = channel.Position; + // update recipients + } else + { + // update permissions too + chan.Position = channel.Position ?? 0; } - if (existing.Type == ChannelType.Voice) + if (chan.Type == DiscordChannelType.Voice) { - existing.Bitrate = channel.Bitrate; - existing.UserLimit = channel.UserLimit; - }*/ + chan.Bitrate = channel.Bitrate ?? 0; + chan.UserLimit = channel.UserLimit ?? 0; + } else + { + chan.Topic = channel.Topic; + chan.LastMessageId = channel.LastMessageId ?? 0; + } OnChannelUpdate?.Invoke(chan); } @@ -408,27 +409,6 @@ namespace Maki private void ShardManager_OnChannelDelete(GatewayShard shard, Channel channel) { DiscordChannel chan = channels.Find(x => x.Id == channel.Id); - - chan.Name = channel.Name; - /*chan.Type = channel.Type; - chan.LastMessageId = channel.LastMessageId; - - if (chan.Type == ChannelType.Private) - chan.Recipients = channel.Recipients; - else - { - chan.GuildId = channel.GuildId; - chan.Topic = channel.Topic; - chan.PermissionOverwrites = channel.PermissionOverwrites; - chan.Position = channel.Position; - } - - if (chan.Type == ChannelType.Voice) - { - chan.Bitrate = channel.Bitrate; - chan.UserLimit = channel.UserLimit; - }*/ - channels.Remove(chan); OnChannelDelete?.Invoke(chan); } @@ -480,7 +460,6 @@ namespace Maki } else { - // should this explode instead if the guild isn't unavailable? server = servers.Find(x => x.Id == guild.Id); server.Name = guild.Name; } @@ -594,12 +573,11 @@ namespace Maki private void ShardManager_OnGuildMemberUpdate(GatewayShard shard, GuildMember sMember) { DiscordMember member = members.Find(x => x.User.Id == sMember.User.Id && x.Server.Id == sMember.GuildId); - - /*existing.IsDeafened = member.IsDeafened; - existing.IsMuted = member.IsMuted; - existing.JoinedAt = member.JoinedAt; - existing.Roles = member.Roles;*/ + member.Nickname = sMember.Nickname; + member.IsDeaf = sMember.IsDeafened == true; + member.IsMute = sMember.IsMuted == true; + member.roles = new List(sMember.Roles); OnMemberUpdate?.Invoke(member); } @@ -624,14 +602,24 @@ namespace Maki $"Permissions: {role.Role.Value.Permissions}"*/ DiscordServer server = servers.Find(x => x.Id == sRole.Guild); - DiscordRole role = new DiscordRole(this, sRole.Role.Value, server); - roles.Add(role); + DiscordRole role = roles.Where(x => x.Id == sRole.Role.Value.Id).FirstOrDefault(); + + if (role == default(DiscordRole)) + { + role = new DiscordRole(this, sRole.Role.Value, server); + roles.Add(role); + } else + { + role.Colour.Raw = sRole.Role.Value.Colour.Value; + } + OnRoleCreate?.Invoke(role); } private void ShardManager_OnGuildRoleDelete(GatewayShard shard, GuildRole sRole) { DiscordRole role = roles.Find(x => x.Id == sRole.RoleId && x.Server.Id == sRole.Guild); + members.Where(x => x.roles.Contains(role.Id)).ToList().ForEach(x => x.roles.RemoveAll(y => y == role.Id)); roles.Remove(role); OnRoleDelete?.Invoke(role); } @@ -663,9 +651,6 @@ namespace Maki private void ShardManager_OnGuildMembersChunk(GatewayShard shard, GuildMembersChunk membersChunk) { - /*$"Guild Id: {membersChunk.Guild}", - $"Members {string.Join(", ", membersChunk.Members.Select(x => x.User.Id).ToArray())}"*/ - for (int i = 0; i < membersChunk.Members.Length; i++) { GuildMember member = membersChunk.Members[i]; @@ -692,59 +677,88 @@ namespace Maki private void ShardManager_OnMessageUpdate(GatewayShard shard, Message message) { - DiscordMessage msg = messages.Find(x => x.Id == message.Id); + DiscordMessage msg = messages.Where(x => x.Id == message.Id).FirstOrDefault(); + + if (msg == null) + { + RestResponse getMsg = RestClient.Request(RestRequestMethod.GET, RestEndpoints.ChannelMessage(message.ChannelId, message.Id)); + DiscordChannel channel = channels.Where(x => x.Id == getMsg.Response.ChannelId).FirstOrDefault(); + DiscordMember member = members.Where(x => x.User.Id == getMsg.Response.User.Id && (channel.Server == null || channel.Server == x.Server)).FirstOrDefault(); + msg = new DiscordMessage(this, getMsg.Response, member, channel); + messages.Add(msg); + } msg.Edited = DateTime.Now; if (!string.IsNullOrEmpty(message.Content)) msg.Text = message.Content; - /*existing.IsTTS = message.IsTTS == true; - existing.IsPinned = message.IsPinned == true;*/ + msg.IsPinned = message.IsPinned == true; OnMessageUpdate?.Invoke(msg); } - // TODO: account for DMs private void ShardManager_OnTypingStart(GatewayShard shard, TypingStart typing) { DiscordChannel channel = channels.Find(x => x.Id == typing.Channel); - DiscordMember member = members.Find(x => x.User.Id == typing.User && x.Server.Id == channel.Server.Id); - OnTypingStart?.Invoke(member, channel); + DiscordUser user = users.Find(x => x.Id == typing.User); + OnTypingStart?.Invoke(user, channel); } private void ShardManager_OnPresenceUpdate(GatewayShard shard, Presence presence) { - /*$"User Id: {presence.User.Id}", - $"Roles: " + (presence.Roles != null ? string.Join(", ", presence.Roles) : string.Empty), - "Game: " + (presence.Game.HasValue ? presence.Game.Value.Name : string.Empty), - $"Guild Id: {presence.Guild}", - $"Status: {presence.Status}"*/ - DiscordMember member = members.Find(x => x.User.Id == presence.User.Id && x.Server.Id == presence.Guild); - // update user presence + member.User.Game = presence.Game.HasValue ? new DiscordGame(presence.Game.Value) : null; + + if (presence.Roles != null) + member.roles = new List(presence.Roles); + + switch (presence.Status.ToLower()) + { + case @"online": + member.User.Status = DiscordUserStatus.Online; + break; + + case @"away": + member.User.Status = DiscordUserStatus.Away; + break; + + case @"dnd": + member.User.Status = DiscordUserStatus.DoNotDisturb; + break; + + case @"offline": + default: + member.User.Status = DiscordUserStatus.Offline; + break; + } OnPresenceUpdate?.Invoke(member); } private void ShardManager_OnReady(GatewayShard shard, GatewayReady ready) { + foreach (Channel chan in ready.PrivateChannels) + { + DiscordChannel channel = new DiscordChannel(this, chan); + + // this shouldn't ever happen but just in case + if (channels.Where(x => x.Id == channel.Id).Count() > 0) + continue; + + channels.Add(channel); + } + foreach (Guild guild in ready.UnavailableGuilds) - // should this call an event handler? - ShardManager_OnGuildCreate(shard, guild); + { + DiscordServer server = new DiscordServer(this, guild); - /* not ready for these yet - foreach (Channel channel in ready.PrivateChannels) - ShardManager_OnChannelCreate(shard, channel);*/ + if (servers.Where(x => x.Id == server.Id).Count() > 0) + continue; - // keep track of self - - /*$"Version: {ready.Version}", - $"User: {ready.User.Id}, {ready.User.Username}, {ready.User.EMail}", - $"Private Channels: {string.Join(", ", ready.PrivateChannels.Select(x => x.Id).ToArray())}", - $"Unavailable Guilds: {string.Join(", ", ready.UnavailableGuilds.Select(x => x.Id).ToArray())}", - $"Session: {ready.Session}"*/ + servers.Add(server); + } DiscordUser user = new DiscordUser(this, ready.User); users.Add(user); @@ -810,12 +824,12 @@ namespace Maki #region IDisposable - private bool IsDisposed = false; + private bool isDisposed = false; private void Dispose(bool disposing) { - if (!IsDisposed) { - IsDisposed = true; + if (!isDisposed) { + isDisposed = true; Disconnect(); } } diff --git a/Maki/DiscordChannel.cs b/Maki/DiscordChannel.cs index ef589cf..2f2c8b3 100644 --- a/Maki/DiscordChannel.cs +++ b/Maki/DiscordChannel.cs @@ -14,12 +14,27 @@ namespace Maki public string Name { get; internal set; } public DiscordChannelType Type { get; internal set; } - internal DiscordChannel(Discord discord, Channel channel, DiscordServer server) + public int Position { get; internal set; } + + public string Topic { get; internal set; } + public ulong LastMessageId { get; internal set; } + + public int Bitrate { get; internal set; } + public int UserLimit { get; internal set; } + + public override string ToString() => $@"<#{Id}>"; + + internal DiscordChannel(Discord discord, Channel channel, DiscordServer server = null) { client = discord; Id = channel.Id; Name = channel.Name; Type = (DiscordChannelType)channel.Type; + Position = channel.Position ?? 0; + Topic = channel.Topic; + LastMessageId = channel.LastMessageId ?? 0; + Bitrate = channel.Bitrate ?? 0; + UserLimit = channel.UserLimit ?? 0; Server = server; } diff --git a/Maki/DiscordGame.cs b/Maki/DiscordGame.cs new file mode 100644 index 0000000..9deae01 --- /dev/null +++ b/Maki/DiscordGame.cs @@ -0,0 +1,21 @@ +using Maki.Structures.Presences; +using System; + +namespace Maki +{ + public class DiscordGame + { + public string Name { get; internal set; } + public bool IsStreaming { get; internal set; } + public Uri Url { get; internal set; } + + internal DiscordGame(Game game) + { + Name = game.Name; + IsStreaming = game.Type == GameType.Streaming; + + if (!string.IsNullOrEmpty(game.Url)) + Url = new Uri(game.Url); + } + } +} diff --git a/Maki/DiscordMember.cs b/Maki/DiscordMember.cs index 5c3f561..79684a4 100644 --- a/Maki/DiscordMember.cs +++ b/Maki/DiscordMember.cs @@ -16,11 +16,15 @@ namespace Maki public DateTime Joined { get; internal set; } public string Nickname { get; internal set; } + public bool IsDeaf { get; internal set; } + public bool IsMute { get; internal set; } + public string Name => string.IsNullOrEmpty(Nickname) ? User.Username : Nickname; public string NameWithTag => $"{Name}#{User.Tag:0000}"; - public override string ToString() => User.ToString(); - private List roles = new List(); + public override string ToString() => $@"<@!{User.Id}>"; + + internal List roles = new List(); internal DiscordMember(Discord discord, GuildMember member, DiscordUser user, DiscordServer server) { @@ -29,6 +33,8 @@ namespace Maki Server = server; Nickname = member.Nickname; Joined = member.JoinedAt ?? DateTime.MinValue; + IsDeaf = member.IsDeafened == true; + IsMute = member.IsMuted == true; roles.AddRange(member.Roles); } diff --git a/Maki/DiscordMessage.cs b/Maki/DiscordMessage.cs index 91f6bb5..7b17646 100644 --- a/Maki/DiscordMessage.cs +++ b/Maki/DiscordMessage.cs @@ -17,10 +17,11 @@ namespace Maki public DiscordMember[] MentionsUsers { get; internal set; } public DiscordRole[] MentionsRoles { get; internal set; } public bool MentionsEveryone { get; internal set; } + public bool IsPinned { get; internal set; } public bool MentionsMe(bool everyone = false, bool roles = true) => (everyone && MentionsEveryone) - || (roles && client.members.Where(x => x.User == client.Me && x.Server == Channel.Server).First().Roles.Where(x => MentionsRoles.Contains(x)).Count() > 0) + || (Channel.Type != DiscordChannelType.Private && roles && client.members.Where(x => x.User == client.Me && x.Server == Channel.Server).First().Roles.Where(x => MentionsRoles.Contains(x)).Count() > 0) || MentionsUsers.Select(x => x.User).Contains(client.Me); public bool IsMe => client.Me == Sender.User; @@ -36,6 +37,7 @@ namespace Maki MentionsUsers = client.members.Where(x => x.Server == channel.Server && msg.Mentions.Select(y => y.Id).Contains(x.User.Id)).ToArray(); MentionsRoles = client.roles.Where(x => x.Server == channel.Server && msg.MentionsRoles.Contains(x.Id)).ToArray(); MentionsEveryone = msg.MentioningEveryone; + IsPinned = msg.IsPinned == true; } public DiscordMessage Edit(string text) diff --git a/Maki/DiscordRole.cs b/Maki/DiscordRole.cs index 671fa2a..a33826d 100644 --- a/Maki/DiscordRole.cs +++ b/Maki/DiscordRole.cs @@ -17,6 +17,8 @@ namespace Maki public bool IsHoisted { get; internal set; } public bool IsMentionable { get; internal set; } + public override string ToString() => $@"<@&{Id}>"; + internal DiscordRole(Discord discord, Role role, DiscordServer server) { client = discord; diff --git a/Maki/DiscordServer.cs b/Maki/DiscordServer.cs index 12a66e6..f3f06b1 100644 --- a/Maki/DiscordServer.cs +++ b/Maki/DiscordServer.cs @@ -41,8 +41,14 @@ namespace Maki if (roleResp.ErrorCode != RestErrorCode.Ok) throw new Exception($"{roleResp.ErrorCode}: {roleResp.ErrorMessage}"); - DiscordRole role = new DiscordRole(client, roleResp.Response, this); - client.roles.Add(role); + DiscordRole role = client.roles.Where(x => x.Id == roleResp.Response.Id).FirstOrDefault(); + + if (role == default(DiscordRole)) + { + role = new DiscordRole(client, roleResp.Response, this); + client.roles.Add(role); + } + return role; } } diff --git a/Maki/DiscordUser.cs b/Maki/DiscordUser.cs index c4722f6..7e52869 100644 --- a/Maki/DiscordUser.cs +++ b/Maki/DiscordUser.cs @@ -17,10 +17,11 @@ namespace Maki public bool HasMFA { get; internal set; } public bool IsVerified { get; internal set; } public string EMail { get; internal set; } + public DiscordUserStatus Status { get; internal set; } + public DiscordGame Game { get; internal set; } public string NameWithTag => $"{Username}#{Tag:0000}"; - public string At => $"<@{Id}>"; - public override string ToString() => At; + public override string ToString() => $"<@{Id}>"; private string avatarHash; diff --git a/Maki/DiscordUserStatus.cs b/Maki/DiscordUserStatus.cs new file mode 100644 index 0000000..11ac896 --- /dev/null +++ b/Maki/DiscordUserStatus.cs @@ -0,0 +1,10 @@ +namespace Maki +{ + public enum DiscordUserStatus + { + Online, + Offline, + Away, + DoNotDisturb, + } +} diff --git a/Maki/Maki.csproj b/Maki/Maki.csproj index f91521d..396304e 100644 --- a/Maki/Maki.csproj +++ b/Maki/Maki.csproj @@ -59,10 +59,12 @@ + + diff --git a/Maki/Rest/RestClient.cs b/Maki/Rest/RestClient.cs index 84658b6..00227f9 100644 --- a/Maki/Rest/RestClient.cs +++ b/Maki/Rest/RestClient.cs @@ -17,7 +17,7 @@ namespace Maki.Rest /// /// User agent that is send alongside requests /// - private const string USER_AGENT = @"DiscordBot (https://github.com/flashwave/nicobot)"; + private const string USER_AGENT = @"DiscordBot (https://github.com/flashwave/maki, 1.0.0.0)"; /// /// Container for parent DiscordClient instance