910 lines
31 KiB
C#
910 lines
31 KiB
C#
using Maki.Gateway;
|
|
using Maki.Rest;
|
|
using Maki.Structures.Channels;
|
|
using Maki.Structures.Gateway;
|
|
using Maki.Structures.Guilds;
|
|
using Maki.Structures.Messages;
|
|
using Maki.Structures.Presences;
|
|
using Maki.Structures.Roles;
|
|
using Maki.Structures.Users;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
namespace Maki
|
|
{
|
|
/// <summary>
|
|
/// Discord Client
|
|
/// </summary>
|
|
public class Discord : IDisposable
|
|
{
|
|
/// <summary>
|
|
/// Discord Gateway/API version we're targeting
|
|
/// </summary>
|
|
internal const int GATEWAY_VERSION = 6;
|
|
|
|
/// <summary>
|
|
/// Whether this client is a bot
|
|
/// </summary>
|
|
public bool IsBot => TokenType == DiscordTokenType.Bot;
|
|
|
|
/// <summary>
|
|
/// Token type, bot and user connections are handled differently
|
|
/// </summary>
|
|
public DiscordTokenType TokenType = DiscordTokenType.Bot;
|
|
|
|
#region Token
|
|
|
|
/// <summary>
|
|
/// Discord token
|
|
/// </summary>
|
|
public string Token {
|
|
get => token;
|
|
|
|
set
|
|
{
|
|
token = value;
|
|
WebRequest.Authorisation = (IsBot ? "Bot " : string.Empty) + token;
|
|
}
|
|
}
|
|
|
|
private string token = string.Empty;
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Gateway Url
|
|
/// </summary>
|
|
internal string Gateway { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Gateway shard client
|
|
/// </summary>
|
|
internal readonly GatewayShardClient ShardClient;
|
|
|
|
#region Containers
|
|
|
|
internal readonly ServerManager ServerManager;
|
|
|
|
/// <summary>
|
|
/// Servers we're in
|
|
/// </summary>
|
|
public IEnumerable<DiscordServer> Servers => ServerManager.Items;
|
|
|
|
internal readonly UserManager UserManager;
|
|
|
|
/// <summary>
|
|
/// Users we are affiliated with
|
|
/// </summary>
|
|
public IEnumerable<DiscordUser> Users => UserManager.Items;
|
|
|
|
internal readonly MemberManager MemberManager;
|
|
|
|
/// <summary>
|
|
/// Members of servers, different from users!
|
|
/// </summary>
|
|
public IEnumerable<DiscordMember> Members => MemberManager.Items;
|
|
|
|
internal readonly RoleManager RoleManager;
|
|
|
|
/// <summary>
|
|
/// Roles in servers
|
|
/// </summary>
|
|
public IEnumerable<DiscordRole> Roles => RoleManager.Items;
|
|
|
|
internal readonly ChannelManager ChannelManager;
|
|
|
|
/// <summary>
|
|
/// Channels
|
|
/// </summary>
|
|
public IEnumerable<DiscordChannel> Channels => ChannelManager.Items;
|
|
|
|
internal readonly MessageManager MessageManager;
|
|
|
|
/// <summary>
|
|
/// Messages
|
|
/// </summary>
|
|
public IEnumerable<DiscordMessage> Messages => MessageManager.Items;
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
/// <summary>
|
|
/// A channel has been created
|
|
/// </summary>
|
|
public event Action<DiscordChannel> OnChannelCreate;
|
|
|
|
/// <summary>
|
|
/// A channel has been updated
|
|
/// </summary>
|
|
public event Action<DiscordChannel> OnChannelUpdate;
|
|
|
|
/// <summary>
|
|
/// A channel has been deleted
|
|
/// </summary>
|
|
public event Action<DiscordChannel> OnChannelDelete;
|
|
|
|
/// <summary>
|
|
/// Someone has been banned from a server
|
|
/// </summary>
|
|
public event Action<DiscordUser, DiscordServer> OnBanAdd;
|
|
|
|
/// <summary>
|
|
/// Someone's ban has been lifted from a server
|
|
/// </summary>
|
|
public event Action<DiscordUser, DiscordServer> OnBanRemove;
|
|
|
|
/// <summary>
|
|
/// A server has been created
|
|
/// </summary>
|
|
public event Action<DiscordServer> OnServerCreate;
|
|
|
|
/// <summary>
|
|
/// A server has been updated
|
|
/// </summary>
|
|
public event Action<DiscordServer> OnServerUpdate;
|
|
|
|
/// <summary>
|
|
/// A server has been deleted
|
|
/// </summary>
|
|
public event Action<DiscordServer> OnServerDelete;
|
|
|
|
/// <summary>
|
|
/// A server's emoji set has been updated
|
|
/// </summary>
|
|
public event Action<DiscordServer> OnEmojisUpdate;
|
|
|
|
/// <summary>
|
|
/// Someone joined a server
|
|
/// </summary>
|
|
public event Action<DiscordMember> OnMemberAdd;
|
|
|
|
/// <summary>
|
|
/// A server member has been updated
|
|
/// </summary>
|
|
public event Action<DiscordMember> OnMemberUpdate;
|
|
|
|
/// <summary>
|
|
/// Someone left a server
|
|
/// </summary>
|
|
public event Action<DiscordMember> OnMemberRemove;
|
|
|
|
/// <summary>
|
|
/// A user role has been created in a server
|
|
/// </summary>
|
|
public event Action<DiscordRole> OnRoleCreate;
|
|
|
|
/// <summary>
|
|
/// A user role has been updated in a server
|
|
/// </summary>
|
|
public event Action<DiscordRole> OnRoleUpdate;
|
|
|
|
/// <summary>
|
|
/// A user role has been deleted in a server
|
|
/// </summary>
|
|
public event Action<DiscordRole> OnRoleDelete;
|
|
|
|
/// <summary>
|
|
/// A message has been received
|
|
/// </summary>
|
|
public event Action<DiscordMessage> OnMessageCreate;
|
|
|
|
/// <summary>
|
|
/// A message has been edited
|
|
/// </summary>
|
|
public event Action<DiscordMessage> OnMessageUpdate;
|
|
|
|
/// <summary>
|
|
/// A message has been deleted
|
|
/// </summary>
|
|
public event Action<DiscordMessage> OnMessageDelete;
|
|
|
|
/// <summary>
|
|
/// Someone started typing, fired again after 8 seconds
|
|
/// </summary>
|
|
public event Action<DiscordUser, DiscordChannel> OnTypingStart;
|
|
|
|
/// <summary>
|
|
/// Someone's status and/or game updated
|
|
/// </summary>
|
|
public event Action<DiscordMember> OnPresenceUpdate;
|
|
|
|
/// <summary>
|
|
/// Fired when the connection was successful and we're currently logged into Discord
|
|
/// </summary>
|
|
public event Action<DiscordUser> OnReady;
|
|
|
|
/// <summary>
|
|
/// Someone updated their account
|
|
/// </summary>
|
|
public event Action<DiscordUser> OnUserUpdate;
|
|
#endregion
|
|
|
|
public DiscordUser Me => UserManager.Items.FirstOrDefault();
|
|
|
|
private DiscordParams Params;
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
public Discord(DiscordParams parameters = null)
|
|
{
|
|
Params = parameters ?? new DiscordParams();
|
|
ShardClient = new GatewayShardClient(this);
|
|
|
|
UserManager = new UserManager();
|
|
RoleManager = new RoleManager();
|
|
MemberManager = new MemberManager();
|
|
ChannelManager = new ChannelManager();
|
|
MessageManager = new MessageManager();
|
|
ServerManager = new ServerManager();
|
|
|
|
#region Assigning event handlers
|
|
ShardClient.OnChannelCreate += ShardManager_OnChannelCreate;
|
|
ShardClient.OnChannelUpdate += ShardManager_OnChannelUpdate;
|
|
ShardClient.OnChannelDelete += ShardManager_OnChannelDelete;
|
|
|
|
ShardClient.OnGuildBanAdd += ShardManager_OnGuildBanAdd;
|
|
ShardClient.OnGuildBanRemove += ShardManager_OnGuildBanRemove;
|
|
ShardClient.OnGuildCreate += ShardManager_OnGuildCreate;
|
|
ShardClient.OnGuildDelete += ShardManager_OnGuildDelete;
|
|
ShardClient.OnGuildUpdate += ShardManager_OnGuildUpdate;
|
|
ShardClient.OnGuildEmojisUpdate += ShardManager_OnGuildEmojisUpdate;
|
|
ShardClient.OnGuildMemberAdd += ShardManager_OnGuildMemberAdd;
|
|
ShardClient.OnGuildMemberUpdate += ShardManager_OnGuildMemberUpdate;
|
|
ShardClient.OnGuildMemberRemove += ShardManager_OnGuildMemberRemove;
|
|
ShardClient.OnGuildRoleCreate += ShardManager_OnGuildRoleCreate;
|
|
ShardClient.OnGuildRoleDelete += ShardManager_OnGuildRoleDelete;
|
|
ShardClient.OnGuildRoleUpdate += ShardManager_OnGuildRoleUpdate;
|
|
ShardClient.OnGuildIntegrationsUpdate += ShardManager_OnGuildIntegrationsUpdate;
|
|
ShardClient.OnGuildMembersChunk += ShardManager_OnGuildMembersChunk;
|
|
|
|
ShardClient.OnMessageCreate += ShardManager_OnMessageCreate;
|
|
ShardClient.OnMessageDelete += ShardManager_OnMessageDelete;
|
|
ShardClient.OnMessageUpdate += ShardManager_OnMessageUpdate;
|
|
|
|
ShardClient.OnTypingStart += ShardManager_OnTypingStart;
|
|
ShardClient.OnPresenceUpdate += ShardManager_OnPresenceUpdate;
|
|
|
|
ShardClient.OnReady += ShardManager_OnReady;
|
|
ShardClient.OnResumed += ShardManager_OnResumed;
|
|
|
|
ShardClient.OnUserUpdate += ShardManager_OnUserUpdate;
|
|
|
|
ShardClient.OnSocketOpen += ShardManager_OnSocketOpen;
|
|
ShardClient.OnSocketClose += ShardManager_OnSocketClose;
|
|
ShardClient.OnSocketError += ShardManager_OnSocketError;
|
|
ShardClient.OnSocketMessage += ShardManager_OnSocketMessage;
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connects to Discord using the token assigned to Token
|
|
/// </summary>
|
|
public void Connect()
|
|
{
|
|
int shards = 1;
|
|
|
|
using (WebRequest wr = new WebRequest(HttpMethod.GET, IsBot ? RestEndpoints.GatewayBot : RestEndpoints.Gateway))
|
|
{
|
|
wr.Perform();
|
|
|
|
if (wr.Status != 200)
|
|
throw new DiscordException("Failed to retrieve gateway url, is your token valid?");
|
|
|
|
GatewayInfo gi = wr.ResponseJson<GatewayInfo>();
|
|
|
|
Gateway = gi.Url;
|
|
|
|
if (gi.Shards.HasValue)
|
|
shards = gi.Shards.Value;
|
|
}
|
|
|
|
if (Gateway.Contains("?"))
|
|
Gateway = Gateway.Substring(0, Gateway.IndexOf('?'));
|
|
|
|
if (!Gateway.EndsWith("/"))
|
|
Gateway += "/";
|
|
|
|
for (int i = 0; i < shards; i++)
|
|
ShardClient.Create(i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connects to Discord using a provided token and token type
|
|
/// </summary>
|
|
/// <param name="token"></param>
|
|
/// <param name="type"></param>
|
|
public void Connect(string token, DiscordTokenType type = DiscordTokenType.Bot)
|
|
{
|
|
TokenType = type;
|
|
Token = token;
|
|
Connect();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disconnects from Discord, use Dispose instead if you're not restarting the connection
|
|
/// </summary>
|
|
public void Disconnect()
|
|
{
|
|
ShardClient.Disconnect();
|
|
Token = string.Empty;
|
|
Gateway = string.Empty;
|
|
}
|
|
|
|
#region Event Handlers
|
|
|
|
private void ShardManager_OnChannelCreate(GatewayShard shard, Channel channel)
|
|
{
|
|
Debug.Assert(channel.GuildId.HasValue, "Guild ID does not have a value?");
|
|
DiscordServer server = ServerManager.Id(channel.GuildId ?? 0);
|
|
Debug.Assert(server != null, "Target guild/server was not present in ServerManager");
|
|
|
|
DiscordChannel chan = new DiscordChannel(this, channel, server);
|
|
ChannelManager.Add(chan);
|
|
OnChannelCreate?.Invoke(chan);
|
|
}
|
|
|
|
private void ShardManager_OnChannelUpdate(GatewayShard shard, Channel channel)
|
|
{
|
|
DiscordChannel chan = ChannelManager.Id(channel.Id);
|
|
Debug.Assert(chan != null, "channel is null");
|
|
|
|
chan.Name = channel.Name;
|
|
|
|
if (chan.Type == DiscordChannelType.Private)
|
|
{
|
|
// update recipients
|
|
} else
|
|
{
|
|
// update permissions too
|
|
chan.Position = channel.Position ?? 0;
|
|
}
|
|
|
|
if (chan.Type == DiscordChannelType.Voice)
|
|
{
|
|
chan.Bitrate = channel.Bitrate ?? 0;
|
|
chan.UserLimit = channel.UserLimit ?? 0;
|
|
} else
|
|
{
|
|
chan.Topic = channel.Topic;
|
|
chan.LastMessageId = channel.LastMessageId ?? 0;
|
|
}
|
|
|
|
OnChannelUpdate?.Invoke(chan);
|
|
}
|
|
|
|
private void ShardManager_OnChannelDelete(GatewayShard shard, Channel channel)
|
|
{
|
|
DiscordChannel chan = ChannelManager.Id(channel.Id);
|
|
Debug.Assert(chan != null, "channel is null");
|
|
|
|
ChannelManager.Remove(chan);
|
|
OnChannelDelete?.Invoke(chan);
|
|
}
|
|
|
|
private void ShardManager_OnGuildBanAdd(GatewayShard shard, User sUser)
|
|
{
|
|
/*$"Id: {user.Id}",
|
|
$"Guild Id: {user.GuildId}",
|
|
$"Username: {user.Username}",
|
|
$"Tag: {user.Tag:0000}",
|
|
$"Avatar Hash: {user.AvatarHash}",
|
|
$"Is bot?: {user.IsBot}",
|
|
$"Has MFA?: {user.HasMFA}",
|
|
$"Is verified?: {user.IsVerified}",
|
|
$"E-mail: {user.EMail}"*/
|
|
|
|
DiscordUser user = UserManager.Id(sUser.Id);
|
|
Debug.Assert(user != null, "user is null");
|
|
|
|
Debug.Assert(sUser.GuildId.HasValue, "Guild ID does not have a value.");
|
|
DiscordServer server = ServerManager.Id(sUser.GuildId ?? 0);
|
|
|
|
Debug.Assert(user != null, "user is null");
|
|
Debug.Assert(server != null, "server is null");
|
|
|
|
OnBanAdd?.Invoke(user, server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildBanRemove(GatewayShard shard, User sUser)
|
|
{
|
|
/*$"Id: {user.Id}",
|
|
$"Guild Id: {user.GuildId}",
|
|
$"Username: {user.Username}",
|
|
$"Tag: {user.Tag:0000}",
|
|
$"Avatar Hash: {user.AvatarHash}",
|
|
$"Is bot?: {user.IsBot}",
|
|
$"Has MFA?: {user.HasMFA}",
|
|
$"Is verified?: {user.IsVerified}",
|
|
$"E-mail: {user.EMail}"*/
|
|
|
|
DiscordUser user = UserManager.Id(sUser.Id);
|
|
Debug.Assert(user != null, "user is null");
|
|
|
|
DiscordServer server = ServerManager.Id(sUser.GuildId ?? 0);
|
|
|
|
Debug.Assert(user != null, "user is null");
|
|
Debug.Assert(server != null, "server is null");
|
|
|
|
OnBanRemove?.Invoke(user, server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildCreate(GatewayShard shard, Guild guild)
|
|
{
|
|
DiscordServer server = ServerManager.Id(guild.Id);
|
|
|
|
if (server == null)
|
|
{
|
|
server = new DiscordServer(this, guild);
|
|
ServerManager.Add(server);
|
|
}
|
|
else
|
|
{
|
|
server.Name = guild.Name;
|
|
server.OwnerId = guild.OwnerId ?? 0;
|
|
server.IconHash = guild.IconHash;
|
|
server.Created = guild.Created ?? DateTime.MinValue;
|
|
}
|
|
|
|
if (guild.Channels != null)
|
|
for (int i = 0; i < guild.Channels.Length; i++)
|
|
{
|
|
Channel channel = guild.Channels[i];
|
|
channel.GuildId = server.Id;
|
|
|
|
if (ChannelManager.Exists(channel.Id))
|
|
ShardManager_OnChannelUpdate(shard, channel);
|
|
else
|
|
ShardManager_OnChannelCreate(shard, channel);
|
|
}
|
|
|
|
if (guild.Roles != null)
|
|
foreach (Role role in guild.Roles) {
|
|
GuildRole gRole = new GuildRole
|
|
{
|
|
Guild = server.Id,
|
|
Role = role,
|
|
RoleId = role.Id
|
|
};
|
|
|
|
if (RoleManager.Exists(role.Id))
|
|
ShardManager_OnGuildRoleUpdate(shard, gRole);
|
|
else
|
|
ShardManager_OnGuildRoleCreate(shard, gRole);
|
|
}
|
|
|
|
shard.Send(GatewayOPCode.RequestGuildMembers, new GatewayRequestGuildMembers {
|
|
Guild = server.Id,
|
|
Query = string.Empty,
|
|
Limit = 0,
|
|
});
|
|
|
|
OnServerCreate?.Invoke(server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildDelete(GatewayShard shard, Guild guild)
|
|
{
|
|
DiscordServer server = ServerManager.Id(guild.Id);
|
|
Debug.Assert(server != null, "server is null");
|
|
|
|
server.Name = guild.Name;
|
|
server.OwnerId = guild.OwnerId ?? 0;
|
|
/*server.VoiceRegionId = guild.VoiceRegionId;
|
|
server.AfkChannelId = guild.AfkChannelId;
|
|
server.AfkTimeout = guild.AfkTimeout;
|
|
server.EmbedEnabled = guild.EmbedEnabled;
|
|
server.EmbedChannelId = guild.EmbedChannelId;
|
|
server.VerificationLevel = guild.VerificationLevel;
|
|
server.MessageNotificationLevel = guild.MessageNotificationLevel;
|
|
server.Roles = guild.Roles;
|
|
server.Emojis = guild.Emojis;
|
|
server.Features = guild.Features;
|
|
server.MultiFactorAuthLevel = guild.MultiFactorAuthLevel;*/
|
|
|
|
OnServerDelete?.Invoke(server);
|
|
|
|
// NOT THREAD SAFE AT ALL
|
|
MessageManager .Server(server).ToList().ForEach(x => MessageManager.Remove(x));
|
|
ChannelManager .Server(server).ToList().ForEach(x => ChannelManager.Remove(x));
|
|
MemberManager .Server(server).ToList().ForEach(x => MemberManager.Remove(x));
|
|
RoleManager .Server(server).ToList().ForEach(x => RoleManager.Remove(x));
|
|
|
|
ServerManager.Remove(server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildUpdate(GatewayShard shard, Guild guild)
|
|
{
|
|
DiscordServer server = ServerManager.Id(guild.Id);
|
|
Debug.Assert(server != null, "server is null");
|
|
|
|
server.Name = guild.Name;
|
|
server.OwnerId = guild.OwnerId ?? 0;
|
|
server.IconHash = guild.IconHash;
|
|
/*server.VoiceRegionId = guild.VoiceRegionId;
|
|
server.AfkChannelId = guild.AfkChannelId;
|
|
server.AfkTimeout = guild.AfkTimeout;
|
|
server.EmbedEnabled = guild.EmbedEnabled;
|
|
server.EmbedChannelId = guild.EmbedChannelId;
|
|
server.VerificationLevel = guild.VerificationLevel;
|
|
server.MessageNotificationLevel = guild.MessageNotificationLevel;
|
|
server.Roles = guild.Roles;
|
|
server.Emojis = guild.Emojis;
|
|
server.Features = guild.Features;
|
|
server.MultiFactorAuthLevel = guild.MultiFactorAuthLevel;*/
|
|
|
|
OnServerUpdate?.Invoke(server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildEmojisUpdate(GatewayShard shard, Guild guild)
|
|
{
|
|
DiscordServer server = ServerManager.Id(guild.Id);
|
|
Debug.Assert(server != null, "server is null");
|
|
|
|
//servers.Emojis = guild.Emojis;
|
|
OnEmojisUpdate?.Invoke(server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildMemberAdd(GatewayShard shard, GuildMember sMember)
|
|
{
|
|
Debug.Assert(sMember.GuildId.HasValue, "GuildId has no value");
|
|
|
|
DiscordServer server = ServerManager.Id(sMember.GuildId ?? 0);
|
|
Debug.Assert(server != null, "server is null");
|
|
|
|
DiscordUser user = UserManager.Id(sMember.User.Id);
|
|
//Debug.Assert(user != null, "user is null");
|
|
|
|
if (user == null)
|
|
{
|
|
user = new DiscordUser(this, sMember.User);
|
|
UserManager.Add(user);
|
|
}
|
|
|
|
DiscordMember member = new DiscordMember(this, sMember, user, server);
|
|
MemberManager.Add(member);
|
|
OnMemberAdd?.Invoke(member);
|
|
}
|
|
|
|
private void ShardManager_OnGuildMemberUpdate(GatewayShard shard, GuildMember sMember)
|
|
{
|
|
Debug.Assert(sMember.GuildId.HasValue, "GuildId has no value");
|
|
|
|
DiscordMember member = MemberManager.Id(sMember.GuildId ?? 0, sMember.User.Id);
|
|
Debug.Assert(member != null, "member is null");
|
|
|
|
member.Nickname = sMember.Nickname;
|
|
member.IsDeaf = sMember.IsDeafened == true;
|
|
member.IsMute = sMember.IsMuted == true;
|
|
member.roles = new List<ulong>(sMember.Roles);
|
|
|
|
OnMemberUpdate?.Invoke(member);
|
|
}
|
|
|
|
private void ShardManager_OnGuildMemberRemove(GatewayShard shard, GuildMember sMember)
|
|
{
|
|
Debug.Assert(sMember.GuildId.HasValue, "GuildId has no value");
|
|
|
|
DiscordMember member = MemberManager.Id(sMember.GuildId ?? 0, sMember.User.Id);
|
|
Debug.Assert(member != null, "member is null");
|
|
|
|
MemberManager.Remove(member);
|
|
OnMemberRemove?.Invoke(member);
|
|
}
|
|
|
|
private void ShardManager_OnGuildRoleCreate(GatewayShard shard, GuildRole sRole)
|
|
{
|
|
/*$"Guild Id: {role.Guild}",
|
|
$"Id: {role.Role.Value.Id}",
|
|
$"Name: {role.Role.Value.Name}",
|
|
$"Colour: {role.Role.Value.Colour}",
|
|
$"Is Hoisted?: {role.Role.Value.IsHoisted}",
|
|
$"Position: {role.Role.Value.Position}",
|
|
$"Is Managed?: {role.Role.Value.IsManaged}",
|
|
$"Is Mentionable?: {role.Role.Value.IsMentionable}",
|
|
$"Permissions: {role.Role.Value.Permissions}"*/
|
|
|
|
DiscordServer server = ServerManager.Id(sRole.Guild);
|
|
Debug.Assert(server != null, "server is null");
|
|
|
|
DiscordRole role = RoleManager.Id(sRole.Role.Value.Id);
|
|
//Debug.Assert(role != null, "role is null");
|
|
|
|
// fixme
|
|
if (server == null)
|
|
return;
|
|
|
|
if (role == null)
|
|
{
|
|
role = new DiscordRole(this, sRole.Role.Value, server);
|
|
RoleManager.Add(role);
|
|
} else
|
|
{
|
|
role.Colour.Raw = sRole.Role.Value.Colour.Value;
|
|
}
|
|
|
|
OnRoleCreate?.Invoke(role);
|
|
}
|
|
|
|
private void ShardManager_OnGuildRoleDelete(GatewayShard shard, GuildRole sRole)
|
|
{
|
|
Debug.Assert(sRole.RoleId.HasValue, "RoleId has no value");
|
|
DiscordRole role = RoleManager.Id(sRole.RoleId ?? 0);
|
|
Debug.Assert(role != null, "role is null");
|
|
|
|
// NOT THREAD SAFE
|
|
MemberManager.Role(role).ToList().ForEach(x => x.roles.RemoveAll(y => y == role.Id));
|
|
RoleManager.Remove(role);
|
|
|
|
OnRoleDelete?.Invoke(role);
|
|
}
|
|
|
|
private void ShardManager_OnGuildRoleUpdate(GatewayShard shard, GuildRole sRole)
|
|
{
|
|
Debug.Assert(sRole.Role.HasValue, "Role has no value");
|
|
Role dRole = sRole.Role.Value;
|
|
|
|
DiscordRole role = RoleManager.Id(dRole.Id);
|
|
Debug.Assert(role != null, "role is null");
|
|
|
|
role.Name = dRole.Name;
|
|
role.IsHoisted = dRole.IsHoisted == true;
|
|
role.IsMentionable = dRole.IsMentionable == true;
|
|
role.Perms = (DiscordPermission)dRole.Permissions;
|
|
role.Position = dRole.Position ?? 0;
|
|
role.Colour.Raw = dRole.Colour ?? 0;
|
|
|
|
OnRoleUpdate?.Invoke(role);
|
|
}
|
|
|
|
private void ShardManager_OnGuildIntegrationsUpdate(GatewayShard shard, GuildIntegration integration)
|
|
{
|
|
/*$"Id: {integration.Id}",
|
|
$"Name: {integration.Name}",
|
|
$"Type: {integration.Type}",
|
|
$"Is Enabled?: {integration.IsEnabled}",
|
|
$"Is Syncing?: {integration.IsSyncing}",
|
|
$"Role Id: {integration.RoleId}",
|
|
$"Expire Behaviour: {integration.ExpireBehaviour}",
|
|
$"Expire Grace Period: {integration.ExpireGracePeriod}",
|
|
$"User: {integration.User.Id}",
|
|
$"Account: {integration.Account.Id}, {integration.Account.Name}",
|
|
$"Last Sync: {integration.LastSync}"*/
|
|
}
|
|
|
|
private void ShardManager_OnGuildMembersChunk(GatewayShard shard, GuildMembersChunk membersChunk)
|
|
{
|
|
for (int i = 0; i < membersChunk.Members.Length; i++)
|
|
{
|
|
GuildMember member = membersChunk.Members[i];
|
|
member.GuildId = membersChunk.Guild;
|
|
ShardManager_OnGuildMemberAdd(shard, member);
|
|
}
|
|
}
|
|
|
|
private void ShardManager_OnMessageCreate(GatewayShard shard, Message message)
|
|
{
|
|
DiscordChannel channel = ChannelManager.Id(message.ChannelId);
|
|
Debug.Assert(channel != null, "channel is null");
|
|
|
|
DiscordMember member = MemberManager.Id(channel.Server, message.User.Id);
|
|
Debug.Assert(member != null, "member is null");
|
|
|
|
DiscordMessage msg = new DiscordMessage(this, message, channel, member);
|
|
MessageManager.Add(msg);
|
|
OnMessageCreate?.Invoke(msg);
|
|
}
|
|
|
|
private void ShardManager_OnMessageDelete(GatewayShard shard, Message message)
|
|
{
|
|
DiscordMessage msg = MessageManager.Id(message.Id);
|
|
Debug.Assert(msg != null, "message is null");
|
|
|
|
MessageManager.Remove(msg);
|
|
OnMessageDelete?.Invoke(msg);
|
|
}
|
|
|
|
private void ShardManager_OnMessageUpdate(GatewayShard shard, Message message)
|
|
{
|
|
DiscordMessage msg = MessageManager.Id(message.Id);
|
|
//Debug.Assert(msg != null, "message is null");
|
|
|
|
// basically what should happen
|
|
if (msg == null)
|
|
{
|
|
using (WebRequest wr = new WebRequest(HttpMethod.GET, RestEndpoints.ChannelMessage(message.ChannelId, message.Id)))
|
|
{
|
|
wr.Perform();
|
|
|
|
if (wr.Status != 200 || wr.Response.Length < 1)
|
|
throw new DiscordException("Failed to load message from API");
|
|
|
|
message = wr.ResponseJson<Message>();
|
|
}
|
|
|
|
DiscordChannel channel = ChannelManager.Id(message.ChannelId);
|
|
Debug.Assert(channel != null, "channel is null");
|
|
|
|
DiscordMember member = MemberManager.Id(channel.Server, message.User.Id) ?? MemberManager.Id(message.User.Id);
|
|
Debug.Assert(member != null, "member is null");
|
|
|
|
msg = new DiscordMessage(this, message, channel, member);
|
|
MessageManager.Add(msg);
|
|
} else
|
|
{
|
|
if (!string.IsNullOrEmpty(message.Content))
|
|
msg.Text = message.Content;
|
|
|
|
msg.IsPinned = message.IsPinned == true;
|
|
}
|
|
|
|
msg.Edited = DateTime.Now;
|
|
|
|
OnMessageUpdate?.Invoke(msg);
|
|
}
|
|
|
|
private void ShardManager_OnTypingStart(GatewayShard shard, TypingStart typing)
|
|
{
|
|
DiscordChannel channel = ChannelManager.Id(typing.Channel);
|
|
Debug.Assert(channel != null, "channel is null");
|
|
|
|
DiscordUser user = UserManager.Id(typing.User);
|
|
Debug.Assert(user != null, "user is null");
|
|
|
|
OnTypingStart?.Invoke(user, channel);
|
|
}
|
|
|
|
private void ShardManager_OnPresenceUpdate(GatewayShard shard, Presence presence)
|
|
{
|
|
DiscordMember member = MemberManager.Id(presence.Guild, presence.User.Id);
|
|
Debug.Assert(member != null, "member is null");
|
|
|
|
if (!string.IsNullOrEmpty(presence.User.Username))
|
|
member.User.Username = presence.User.Username;
|
|
|
|
if (presence.User.Tag.HasValue)
|
|
member.User.Tag = presence.User.Tag.Value;
|
|
|
|
if (!string.IsNullOrEmpty(presence.User.AvatarHash))
|
|
member.User.avatarHash = presence.User.AvatarHash;
|
|
|
|
member.User.Game = presence.Game.HasValue ? new DiscordGame(presence.Game.Value) : null;
|
|
|
|
if (presence.Roles != null)
|
|
member.roles = new List<ulong>(presence.Roles);
|
|
|
|
switch (presence.Status.ToLower())
|
|
{
|
|
case "online":
|
|
member.User.Status = DiscordUserStatus.Online;
|
|
break;
|
|
|
|
case "away":
|
|
case "idle":
|
|
member.User.Status = DiscordUserStatus.Idle;
|
|
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 (ChannelManager.Exists(channel.Id))
|
|
continue;
|
|
|
|
ChannelManager.Add(channel);
|
|
}
|
|
|
|
foreach (Guild guild in ready.UnavailableGuilds)
|
|
{
|
|
DiscordServer server = new DiscordServer(this, guild);
|
|
|
|
if (ServerManager.Exists(server.Id))
|
|
continue;
|
|
|
|
ServerManager.Add(server);
|
|
}
|
|
|
|
DiscordUser user = new DiscordUser(this, ready.User);
|
|
UserManager.Add(user);
|
|
OnReady?.Invoke(user);
|
|
}
|
|
|
|
private void ShardManager_OnResumed(GatewayShard shard)
|
|
{
|
|
OnReady?.Invoke(Me);
|
|
}
|
|
|
|
private void ShardManager_OnUserUpdate(GatewayShard shard, User sUser)
|
|
{
|
|
/*$"Id: {user.Id}",
|
|
$"Guild Id: {user.GuildId}",
|
|
$"Username: {user.Username}",
|
|
$"Tag: {user.Tag:0000}",
|
|
$"Avatar Hash: {user.AvatarHash}",
|
|
$"Is bot?: {user.IsBot}",
|
|
$"Has MFA?: {user.HasMFA}",
|
|
$"Is verified?: {user.IsVerified}",
|
|
$"E-mail: {user.EMail}"*/
|
|
|
|
DiscordUser user = UserManager.Id(sUser.Id);
|
|
//Debug.Assert(user != null, "user is null");
|
|
|
|
if (user == null)
|
|
{
|
|
user = new DiscordUser(this, sUser);
|
|
UserManager.Add(user);
|
|
}
|
|
|
|
OnUserUpdate?.Invoke(user);
|
|
}
|
|
|
|
private void ShardManager_OnSocketOpen(GatewayShard shard)
|
|
{
|
|
}
|
|
|
|
private void ShardManager_OnSocketClose(GatewayShard shard, bool wasClean, ushort code, string reason)
|
|
{
|
|
}
|
|
|
|
private void ShardManager_OnSocketError(GatewayShard shard, Exception ex)
|
|
{
|
|
}
|
|
|
|
private void ShardManager_OnSocketMessage(GatewayShard shard, string text)
|
|
{
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IDisposable
|
|
|
|
private bool IsDisposed = false;
|
|
|
|
/// <summary>
|
|
/// Disconnects and releases all unmanaged objects
|
|
/// </summary>
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
IsDisposed = true;
|
|
Disconnect();
|
|
|
|
ServerManager.Dispose();
|
|
MessageManager.Dispose();
|
|
ChannelManager.Dispose();
|
|
MemberManager.Dispose();
|
|
RoleManager.Dispose();
|
|
UserManager.Dispose();
|
|
|
|
if (disposing)
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
~Discord()
|
|
=> Dispose(false);
|
|
|
|
public void Dispose()
|
|
=> Dispose(true);
|
|
|
|
#endregion
|
|
}
|
|
}
|