838 lines
30 KiB
C#
838 lines
30 KiB
C#
using Maki.Gateway;
|
|
using Maki.Rest;
|
|
using Maki.Structures.Auth;
|
|
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 Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
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;
|
|
|
|
/// <summary>
|
|
/// Discord token
|
|
/// </summary>
|
|
public string Token = string.Empty;
|
|
|
|
/// <summary>
|
|
/// Gateway Url
|
|
/// </summary>
|
|
internal string Gateway { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Rest API request client
|
|
/// </summary>
|
|
internal readonly RestClient RestClient;
|
|
|
|
/// <summary>
|
|
/// Gateway shard client
|
|
/// </summary>
|
|
internal readonly GatewayShardClient ShardClient;
|
|
|
|
#region Containers
|
|
/// <summary>
|
|
/// Servers we're in
|
|
/// </summary>
|
|
internal readonly List<DiscordServer> servers = new List<DiscordServer>();
|
|
|
|
/// <summary>
|
|
/// Users we are affiliated with
|
|
/// </summary>
|
|
internal readonly List<DiscordUser> users = new List<DiscordUser>();
|
|
|
|
/// <summary>
|
|
/// Members of servers, different from users!
|
|
/// </summary>
|
|
internal readonly List<DiscordMember> members = new List<DiscordMember>();
|
|
|
|
/// <summary>
|
|
/// Roles in servers
|
|
/// </summary>
|
|
internal readonly List<DiscordRole> roles = new List<DiscordRole>();
|
|
|
|
/// <summary>
|
|
/// Channels
|
|
/// </summary>
|
|
internal readonly List<DiscordChannel> channels = new List<DiscordChannel>();
|
|
|
|
/// <summary>
|
|
/// Messages
|
|
/// </summary>
|
|
internal readonly List<DiscordMessage> messages = new List<DiscordMessage>();
|
|
#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<DiscordMember, 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 DiscordServer[] Servers => servers.ToArray();
|
|
public DiscordUser Me => users.FirstOrDefault();
|
|
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
public Discord()
|
|
{
|
|
RestClient = new RestClient(this);
|
|
ShardClient = new GatewayShardClient(this);
|
|
|
|
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;
|
|
}
|
|
|
|
private void ClearContainers()
|
|
{
|
|
servers.Clear();
|
|
users.Clear();
|
|
roles.Clear();
|
|
channels.Clear();
|
|
messages.Clear();
|
|
members.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connects to Discord using the token assigned to Token
|
|
/// </summary>
|
|
public void Connect()
|
|
{
|
|
ClearContainers();
|
|
|
|
RestResponse<GatewayInfo> gateway = RestClient.Request<GatewayInfo>(
|
|
RestRequestMethod.GET,
|
|
IsBot ? RestEndpoints.GatewayBot : RestEndpoints.Gateway
|
|
);
|
|
|
|
if (gateway.ErrorCode != RestErrorCode.Ok)
|
|
throw new Exception($"{gateway.ErrorCode}: {gateway.ErrorMessage}");
|
|
|
|
Gateway = gateway.Response.Url;
|
|
|
|
if (Gateway.Contains("?"))
|
|
Gateway = Gateway.Substring(0, Gateway.IndexOf('?'));
|
|
|
|
if (!Gateway.EndsWith("/"))
|
|
Gateway += "/";
|
|
|
|
int shards = gateway.Response.Shards ?? 1;
|
|
|
|
for (int i = 0; i < shards; i++)
|
|
ShardClient.Create(i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connects to Discord with the provided email, password and, optionally, mfa token
|
|
/// </summary>
|
|
/// <param name="email">Discord account email</param>
|
|
/// <param name="password">Discord account password</param>
|
|
/// <param name="code">Multi factor authentication token</param>
|
|
public void Connect(string email, string password, string code = null)
|
|
{
|
|
TokenType = DiscordTokenType.User;
|
|
|
|
RestResponse<LoginResponse> login = RestClient.Request<LoginResponse>(
|
|
RestRequestMethod.POST,
|
|
RestEndpoints.AuthLogin,
|
|
new LoginRequest
|
|
{
|
|
Email = email,
|
|
Password = password,
|
|
}
|
|
);
|
|
|
|
if (login.ErrorCode != RestErrorCode.Ok)
|
|
throw new Exception($"{login.ErrorCode}: {login.ErrorMessage}");
|
|
|
|
if (login.Response.UsernameError?.Length > 0)
|
|
throw new Exception(login.Response.UsernameError.FirstOrDefault());
|
|
|
|
if (login.Response.PasswordError?.Length > 0)
|
|
throw new Exception(login.Response.PasswordError.FirstOrDefault());
|
|
|
|
Token = login.Response.Token;
|
|
|
|
if (string.IsNullOrEmpty(Token))
|
|
{
|
|
if (login.Response.MFA == true && !string.IsNullOrEmpty(login.Response.Ticket))
|
|
{
|
|
RestResponse<LoginResponse> totp = RestClient.Request<LoginResponse>(
|
|
RestRequestMethod.POST,
|
|
RestEndpoints.AuthMfaTotp,
|
|
new LoginMultiFactorAuth
|
|
{
|
|
Code = code,
|
|
Ticket = login.Response.Ticket,
|
|
}
|
|
);
|
|
|
|
if (totp.ErrorCode != RestErrorCode.Ok)
|
|
throw new Exception($"{totp.ErrorCode}: {totp.ErrorMessage}");
|
|
|
|
Token = totp.Response.Token;
|
|
}
|
|
else
|
|
throw new Exception("Token was null but MFA is false and/or ticket is empty?");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(Token))
|
|
throw new Exception("Authentication failed!");
|
|
|
|
Connect();
|
|
}
|
|
|
|
/// <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();
|
|
ClearContainers();
|
|
Token = string.Empty;
|
|
Gateway = string.Empty;
|
|
}
|
|
|
|
private void ShardManager_OnChannelCreate(GatewayShard shard, Channel channel)
|
|
{
|
|
DiscordServer server = servers.Find(x => x.Id == channel.GuildId);
|
|
DiscordChannel chan = new DiscordChannel(this, channel, server);
|
|
channels.Add(chan);
|
|
OnChannelCreate?.Invoke(chan);
|
|
}
|
|
|
|
private void ShardManager_OnChannelUpdate(GatewayShard shard, Channel channel)
|
|
{
|
|
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
|
|
{
|
|
existing.GuildId = channel.GuildId;
|
|
existing.Topic = channel.Topic;
|
|
existing.PermissionOverwrites = channel.PermissionOverwrites;
|
|
existing.Position = channel.Position;
|
|
}
|
|
|
|
if (existing.Type == ChannelType.Voice)
|
|
{
|
|
existing.Bitrate = channel.Bitrate;
|
|
existing.UserLimit = channel.UserLimit;
|
|
}*/
|
|
|
|
OnChannelUpdate?.Invoke(chan);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 = users.Find(x => x.Id == sUser.Id);
|
|
DiscordServer server = servers.Find(x => x.Id == sUser.GuildId);
|
|
|
|
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 = users.Find(x => x.Id == sUser.Id);
|
|
DiscordServer server = servers.Find(x => x.Id == sUser.GuildId);
|
|
|
|
OnBanRemove?.Invoke(user, server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildCreate(GatewayShard shard, Guild guild)
|
|
{
|
|
DiscordServer server = servers.Where(x => x.Id == guild.Id).FirstOrDefault();
|
|
|
|
if (server == default(DiscordServer))
|
|
{
|
|
server = new DiscordServer(this, guild);
|
|
servers.Add(server);
|
|
}
|
|
else
|
|
{
|
|
// should this explode instead if the guild isn't unavailable?
|
|
server = servers.Find(x => x.Id == guild.Id);
|
|
server.Name = guild.Name;
|
|
}
|
|
|
|
if (guild.Channels != null)
|
|
for (int i = 0; i < guild.Channels.Length; i++)
|
|
{
|
|
Channel channel = guild.Channels[i];
|
|
channel.GuildId = server.Id;
|
|
|
|
if (channels.Where(x => x.Id == channel.Id).Count() > 0)
|
|
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 (roles.Where(x => x.Id == role.Id).Count() > 0)
|
|
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 = servers.Find(x => x.Id == guild.Id);
|
|
|
|
server.Name = guild.Name;
|
|
/*server.OwnerId = guild.OwnerId;
|
|
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);
|
|
messages.Where(x => x.Channel.Server == server).ToList().ForEach(x => messages.Remove(x));
|
|
channels.Where(x => x.Server == server).ToList().ForEach(x => channels.Remove(x));
|
|
members.Where(x => x.Server == server).ToList().ForEach(x => members.Remove(x));
|
|
roles.Where(x => x.Server == server).ToList().ForEach(x => roles.Remove(x));
|
|
servers.Remove(server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildUpdate(GatewayShard shard, Guild guild)
|
|
{
|
|
DiscordServer server = servers.Find(x => x.Id == guild.Id);
|
|
|
|
server.Name = guild.Name;
|
|
/*server.OwnerId = guild.OwnerId;
|
|
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 = servers.Find(x => x.Id == guild.Id);
|
|
//servers.Emojis = guild.Emojis;
|
|
OnEmojisUpdate?.Invoke(server);
|
|
}
|
|
|
|
private void ShardManager_OnGuildMemberAdd(GatewayShard shard, GuildMember sMember)
|
|
{
|
|
DiscordServer server = servers.Find(x => x.Id == sMember.GuildId);
|
|
DiscordUser user = users.Where(x => x.Id == sMember.User.Id).FirstOrDefault();
|
|
|
|
if (user == default(DiscordUser))
|
|
{
|
|
user = new DiscordUser(this, sMember.User);
|
|
users.Add(user);
|
|
}
|
|
|
|
DiscordMember member = new DiscordMember(this, sMember, user, server);
|
|
members.Add(member);
|
|
OnMemberAdd?.Invoke(member);
|
|
}
|
|
|
|
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;
|
|
|
|
OnMemberUpdate?.Invoke(member);
|
|
}
|
|
|
|
private void ShardManager_OnGuildMemberRemove(GatewayShard shard, GuildMember sMember)
|
|
{
|
|
DiscordMember member = members.Find(x => x.User.Id == sMember.User.Id && x.Server.Id == sMember.GuildId);
|
|
members.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 = servers.Find(x => x.Id == sRole.Guild);
|
|
DiscordRole role = new DiscordRole(this, sRole.Role.Value, server);
|
|
roles.Add(role);
|
|
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);
|
|
roles.Remove(role);
|
|
OnRoleDelete?.Invoke(role);
|
|
}
|
|
|
|
private void ShardManager_OnGuildRoleUpdate(GatewayShard shard, GuildRole sRole)
|
|
{
|
|
Role dRole = sRole.Role.Value;
|
|
DiscordRole role = roles.Find(x => x.Id == dRole.Id && x.Server.Id == sRole.Guild);
|
|
|
|
role.Name = dRole.Name;
|
|
|
|
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)
|
|
{
|
|
/*$"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];
|
|
member.GuildId = membersChunk.Guild;
|
|
ShardManager_OnGuildMemberAdd(shard, member);
|
|
}
|
|
}
|
|
|
|
private void ShardManager_OnMessageCreate(GatewayShard shard, Message message)
|
|
{
|
|
DiscordChannel channel = channels.Find(x => x.Id == message.ChannelId);
|
|
DiscordMember member = members.Find(x => x.User.Id == message.User.Id);
|
|
DiscordMessage msg = new DiscordMessage(this, message, member, channel);
|
|
messages.Add(msg);
|
|
OnMessageCreate?.Invoke(msg);
|
|
}
|
|
|
|
private void ShardManager_OnMessageDelete(GatewayShard shard, Message message)
|
|
{
|
|
DiscordMessage msg = messages.Find(x => x.Id == message.Id);
|
|
messages.Remove(msg);
|
|
OnMessageDelete?.Invoke(msg);
|
|
}
|
|
|
|
private void ShardManager_OnMessageUpdate(GatewayShard shard, Message message)
|
|
{
|
|
DiscordMessage msg = messages.Find(x => x.Id == message.Id);
|
|
|
|
msg.Edited = DateTime.Now;
|
|
|
|
if (!string.IsNullOrEmpty(message.Content))
|
|
msg.Text = message.Content;
|
|
|
|
/*existing.IsTTS = message.IsTTS == true;
|
|
existing.IsPinned = message.IsPinned == true;*/
|
|
|
|
OnMessageUpdate?.Invoke(msg);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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
|
|
|
|
OnPresenceUpdate?.Invoke(member);
|
|
}
|
|
|
|
private void ShardManager_OnReady(GatewayShard shard, GatewayReady ready)
|
|
{
|
|
foreach (Guild guild in ready.UnavailableGuilds)
|
|
// should this call an event handler?
|
|
ShardManager_OnGuildCreate(shard, guild);
|
|
|
|
/* not ready for these yet
|
|
foreach (Channel channel in ready.PrivateChannels)
|
|
ShardManager_OnChannelCreate(shard, channel);*/
|
|
|
|
// 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}"*/
|
|
|
|
DiscordUser user = new DiscordUser(this, ready.User);
|
|
users.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 = users.Where(x => x.Id == sUser.Id).FirstOrDefault();
|
|
|
|
if (user == default(DiscordUser))
|
|
{
|
|
user = new DiscordUser(this, sUser);
|
|
users.Add(user);
|
|
}
|
|
|
|
OnUserUpdate?.Invoke(user);
|
|
}
|
|
|
|
private void ShardManager_OnSocketOpen(GatewayShard shard)
|
|
{
|
|
//MultiLineWrite($"Connection opened on shard {shard.Id}");
|
|
}
|
|
|
|
private void ShardManager_OnSocketClose(GatewayShard shard, bool wasClean, ushort code, string reason)
|
|
{
|
|
//MultiLineWrite($"Connection closed on shard {shard.Id} ({wasClean}/{code}/{reason})");
|
|
}
|
|
|
|
private void ShardManager_OnSocketError(GatewayShard shard, Exception ex)
|
|
{
|
|
//MultiLineWrite($"Socket error on shard {shard.Id}", ex.Message);
|
|
}
|
|
|
|
private void ShardManager_OnSocketMessage(GatewayShard shard, string text)
|
|
{
|
|
if (!Directory.Exists("Json"))
|
|
Directory.CreateDirectory("Json");
|
|
|
|
File.WriteAllText(
|
|
$"Json/{DateTime.Now:yyyy-MM-dd HH-mm-ss.fffffff}.json",
|
|
JsonConvert.SerializeObject(
|
|
JsonConvert.DeserializeObject(text),
|
|
Formatting.Indented
|
|
)
|
|
);
|
|
}
|
|
|
|
#region IDisposable
|
|
|
|
private bool IsDisposed = false;
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (!IsDisposed) {
|
|
IsDisposed = true;
|
|
Disconnect();
|
|
}
|
|
}
|
|
|
|
~Discord()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disconnects and releases all unmanaged objects
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(true);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|