Re-added model functions, and added legacy extensions
This commit is contained in:
@@ -3,6 +3,6 @@
|
|||||||
public static class CommandExtensions
|
public static class CommandExtensions
|
||||||
{
|
{
|
||||||
public static CommandService Commands(this DiscordClient client, bool required = true)
|
public static CommandService Commands(this DiscordClient client, bool required = true)
|
||||||
=> client.GetService<CommandService>(required);
|
=> client.Services.Get<CommandService>(required);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
@@ -13,8 +16,10 @@ namespace Discord
|
|||||||
=> ulong.Parse(value, NumberStyles.None, _format);
|
=> ulong.Parse(value, NumberStyles.None, _format);
|
||||||
public static ulong? ToNullableId(this string value)
|
public static ulong? ToNullableId(this string value)
|
||||||
=> value == null ? (ulong?)null : ulong.Parse(value, NumberStyles.None, _format);
|
=> value == null ? (ulong?)null : ulong.Parse(value, NumberStyles.None, _format);
|
||||||
|
public static bool TryToId(this string value, out ulong result)
|
||||||
public static string ToIdString(this ulong value)
|
=> ulong.TryParse(value, NumberStyles.None, _format, out result);
|
||||||
|
|
||||||
|
public static string ToIdString(this ulong value)
|
||||||
=> value.ToString(_format);
|
=> value.ToString(_format);
|
||||||
public static string ToIdString(this ulong? value)
|
public static string ToIdString(this ulong? value)
|
||||||
=> value?.ToString(_format);
|
=> value?.ToString(_format);
|
||||||
@@ -33,5 +38,100 @@ namespace Discord
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Channel> Find(this IEnumerable<Channel> channels, string name, ChannelType type = null, bool exactMatch = false)
|
||||||
|
{
|
||||||
|
//Search by name
|
||||||
|
var query = channels
|
||||||
|
.Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (!exactMatch)
|
||||||
|
{
|
||||||
|
if (name.Length >= 2 && name[0] == '<' && name[1] == '#' && name[name.Length - 1] == '>') //Search by raw mention
|
||||||
|
{
|
||||||
|
ulong id;
|
||||||
|
if (name.Substring(2, name.Length - 3).TryToId(out id))
|
||||||
|
{
|
||||||
|
var channel = channels.Where(x => x.Id == id).FirstOrDefault();
|
||||||
|
if (channel != null)
|
||||||
|
query = query.Concat(new Channel[] { channel });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name.Length >= 1 && name[0] == '#' && (type == null || type == ChannelType.Text)) //Search by clean mention
|
||||||
|
{
|
||||||
|
string name2 = name.Substring(1);
|
||||||
|
query = query.Concat(channels
|
||||||
|
.Where(x => x.Type == ChannelType.Text && string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != null)
|
||||||
|
query = query.Where(x => x.Type == type);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> Find(this IEnumerable<User> users,
|
||||||
|
string name, ushort? discriminator = null, bool exactMatch = false)
|
||||||
|
{
|
||||||
|
//Search by name
|
||||||
|
var query = users
|
||||||
|
.Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (!exactMatch)
|
||||||
|
{
|
||||||
|
if (name.Length >= 2 && name[0] == '<' && name[1] == '@' && name[name.Length - 1] == '>') //Search by raw mention
|
||||||
|
{
|
||||||
|
ulong id;
|
||||||
|
if (name.Substring(2, name.Length - 3).TryToId(out id))
|
||||||
|
{
|
||||||
|
var user = users.Where(x => x.Id == id).FirstOrDefault();
|
||||||
|
if (user != null)
|
||||||
|
query = query.Concat(new User[] { user });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name.Length >= 1 && name[0] == '@') //Search by clean mention
|
||||||
|
{
|
||||||
|
string name2 = name.Substring(1);
|
||||||
|
query = query.Concat(users
|
||||||
|
.Where(x => string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discriminator != null)
|
||||||
|
query = query.Where(x => x.Discriminator == discriminator.Value);
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Role> Find(this IEnumerable<Role> roles, string name, bool exactMatch = false)
|
||||||
|
{
|
||||||
|
// if (name.StartsWith("@"))
|
||||||
|
// {
|
||||||
|
// string name2 = name.Substring(1);
|
||||||
|
// return _roles.Where(x => x.Server.Id == server.Id &&
|
||||||
|
// string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
// string.Equals(x.Name, name2, StringComparison.OrdinalIgnoreCase));
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
return roles.Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Server> Find(this IEnumerable<Server> servers, string name, bool exactMatch = false)
|
||||||
|
=> servers.Where(x => string.Equals(x.Name, name, exactMatch ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
public static string Base64(this Stream stream, ImageType type, string existingId)
|
||||||
|
{
|
||||||
|
if (type == ImageType.None)
|
||||||
|
return null;
|
||||||
|
else if (stream != null)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[stream.Length - stream.Position];
|
||||||
|
stream.Read(bytes, 0, bytes.Length);
|
||||||
|
|
||||||
|
string base64 = Convert.ToBase64String(bytes);
|
||||||
|
string imageType = type == ImageType.Jpeg ? "image/jpeg;base64" : "image/png;base64";
|
||||||
|
return $"data:{imageType},{base64}";
|
||||||
|
}
|
||||||
|
return existingId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/Discord.Net/IService.cs
Normal file
7
src/Discord.Net/IService.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public interface IService
|
||||||
|
{
|
||||||
|
void Install(DiscordClient client);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace Discord
|
|
||||||
{
|
|
||||||
public static class Mention
|
|
||||||
{
|
|
||||||
private static readonly Regex _userRegex = new Regex(@"<@([0-9]+)>", RegexOptions.Compiled);
|
|
||||||
private static readonly Regex _channelRegex = new Regex(@"<#([0-9]+)>", RegexOptions.Compiled);
|
|
||||||
private static readonly Regex _roleRegex = new Regex(@"@everyone", RegexOptions.Compiled);
|
|
||||||
|
|
||||||
/// <summary> Returns the string used to create a user mention. </summary>
|
|
||||||
[Obsolete("Use User.Mention instead")]
|
|
||||||
public static string User(User user)
|
|
||||||
=> $"<@{user.Id}>";
|
|
||||||
/// <summary> Returns the string used to create a channel mention. </summary>
|
|
||||||
[Obsolete("Use Channel.Mention instead")]
|
|
||||||
public static string Channel(Channel channel)
|
|
||||||
=> $"<#{channel.Id}>";
|
|
||||||
/// <summary> Returns the string used to create a mention to everyone in a channel. </summary>
|
|
||||||
[Obsolete("Use Server.EveryoneRole.Mention instead")]
|
|
||||||
public static string Everyone()
|
|
||||||
=> $"@everyone";
|
|
||||||
|
|
||||||
internal static string CleanUserMentions(DiscordClient client, Channel channel, string text, List<User> users = null)
|
|
||||||
{
|
|
||||||
return _userRegex.Replace(text, new MatchEvaluator(e =>
|
|
||||||
{
|
|
||||||
var id = e.Value.Substring(2, e.Value.Length - 3).ToId();
|
|
||||||
var user = channel.GetUser(id);
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
if (users != null)
|
|
||||||
users.Add(user);
|
|
||||||
return '@' + user.Name;
|
|
||||||
}
|
|
||||||
else //User not found
|
|
||||||
return '@' + e.Value;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
internal static string CleanChannelMentions(DiscordClient client, Channel channel, string text, List<Channel> channels = null)
|
|
||||||
{
|
|
||||||
var server = channel.Server;
|
|
||||||
if (server == null) return text;
|
|
||||||
|
|
||||||
return _channelRegex.Replace(text, new MatchEvaluator(e =>
|
|
||||||
{
|
|
||||||
var id = e.Value.Substring(2, e.Value.Length - 3).ToId();
|
|
||||||
var mentionedChannel = server.GetChannel(id);
|
|
||||||
if (mentionedChannel != null && mentionedChannel.Server.Id == server.Id)
|
|
||||||
{
|
|
||||||
if (channels != null)
|
|
||||||
channels.Add(mentionedChannel);
|
|
||||||
return '#' + mentionedChannel.Name;
|
|
||||||
}
|
|
||||||
else //Channel not found
|
|
||||||
return '#' + e.Value;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/*internal static string CleanRoleMentions(DiscordClient client, User user, Channel channel, string text, List<Role> roles = null)
|
|
||||||
{
|
|
||||||
var server = channel.Server;
|
|
||||||
if (server == null) return text;
|
|
||||||
|
|
||||||
return _roleRegex.Replace(text, new MatchEvaluator(e =>
|
|
||||||
{
|
|
||||||
if (roles != null && user.GetPermissions(channel).MentionEveryone)
|
|
||||||
roles.Add(server.EveryoneRole);
|
|
||||||
return e.Value;
|
|
||||||
}));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/// <summary>Resolves all mentions in a provided string to those users, channels or roles' names.</summary>
|
|
||||||
public static string Resolve(Message source, string text)
|
|
||||||
{
|
|
||||||
if (source == null) throw new ArgumentNullException(nameof(source));
|
|
||||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
|
||||||
|
|
||||||
return Resolve(source.Channel, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Resolves all mentions in a provided string to those users, channels or roles' names.</summary>
|
|
||||||
public static string Resolve(Channel channel, string text)
|
|
||||||
{
|
|
||||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
|
||||||
|
|
||||||
var client = channel.Client;
|
|
||||||
text = CleanUserMentions(client, channel, text);
|
|
||||||
text = CleanChannelMentions(client, channel, text);
|
|
||||||
//text = CleanRoleMentions(_client, channel, text);
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
using Discord.API.Client;
|
using Discord.API.Client;
|
||||||
|
using Discord.API.Client.Rest;
|
||||||
|
using Discord.Net;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using APIChannel = Discord.API.Client.Channel;
|
using APIChannel = Discord.API.Client.Channel;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
@@ -38,9 +43,9 @@ namespace Discord
|
|||||||
private readonly ConcurrentDictionary<ulong, Member> _users;
|
private readonly ConcurrentDictionary<ulong, Member> _users;
|
||||||
private readonly ConcurrentDictionary<ulong, Message> _messages;
|
private readonly ConcurrentDictionary<ulong, Message> _messages;
|
||||||
private Dictionary<ulong, PermissionOverwrite> _permissionOverwrites;
|
private Dictionary<ulong, PermissionOverwrite> _permissionOverwrites;
|
||||||
|
|
||||||
/// <summary> Gets the client that generated this channel object. </summary>
|
|
||||||
internal DiscordClient Client { get; }
|
internal DiscordClient Client { get; }
|
||||||
|
|
||||||
/// <summary> Gets the unique identifier for this channel. </summary>
|
/// <summary> Gets the unique identifier for this channel. </summary>
|
||||||
public ulong Id { get; }
|
public ulong Id { get; }
|
||||||
/// <summary> Gets the server owning this channel, if this is a public chat. </summary>
|
/// <summary> Gets the server owning this channel, if this is a public chat. </summary>
|
||||||
@@ -149,32 +154,85 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Members
|
/// <summary> Edits this channel, changing only non-null attributes. </summary>
|
||||||
internal void AddUser(User user)
|
public async Task Edit(string name = null, string topic = null, int? position = null)
|
||||||
{
|
{
|
||||||
if (!Client.Config.UsePermissionsCache)
|
if (name != null || topic != null)
|
||||||
return;
|
{
|
||||||
|
var request = new UpdateChannelRequest(Id)
|
||||||
|
{
|
||||||
|
Name = name ?? Name,
|
||||||
|
Topic = topic ?? Topic,
|
||||||
|
Position = Position
|
||||||
|
};
|
||||||
|
await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
var member = new Member(user);
|
if (position != null)
|
||||||
if (_users.TryAdd(user.Id, member))
|
{
|
||||||
UpdatePermissions(user, member.Permissions);
|
Channel[] channels = Server.Channels.Where(x => x.Type == Type).OrderBy(x => x.Position).ToArray();
|
||||||
}
|
int oldPos = Array.IndexOf(channels, this);
|
||||||
internal void RemoveUser(ulong id)
|
var newPosChannel = channels.Where(x => x.Position > position).FirstOrDefault();
|
||||||
{
|
int newPos = (newPosChannel != null ? Array.IndexOf(channels, newPosChannel) : channels.Length) - 1;
|
||||||
if (!Client.Config.UsePermissionsCache)
|
if (newPos < 0)
|
||||||
return;
|
newPos = 0;
|
||||||
|
int minPos;
|
||||||
|
|
||||||
Member ignored;
|
if (oldPos < newPos) //Moving Down
|
||||||
_users.TryRemove(id, out ignored);
|
{
|
||||||
|
minPos = oldPos;
|
||||||
|
for (int i = oldPos; i < newPos; i++)
|
||||||
|
channels[i] = channels[i + 1];
|
||||||
|
channels[newPos] = this;
|
||||||
|
}
|
||||||
|
else //(oldPos > newPos) Moving Up
|
||||||
|
{
|
||||||
|
minPos = newPos;
|
||||||
|
for (int i = oldPos; i > newPos; i--)
|
||||||
|
channels[i] = channels[i - 1];
|
||||||
|
channels[newPos] = this;
|
||||||
|
}
|
||||||
|
Channel after = minPos > 0 ? channels.Skip(minPos - 1).FirstOrDefault() : null;
|
||||||
|
await Server.ReorderChannels(channels.Skip(minPos), after).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public User GetUser(ulong id)
|
|
||||||
|
public async Task Delete()
|
||||||
{
|
{
|
||||||
Member result;
|
try { await Client.ClientAPI.Send(new DeleteChannelRequest(Id)).ConfigureAwait(false); }
|
||||||
_users.TryGetValue(id, out result);
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
return result.User;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Messages
|
#region Invites
|
||||||
|
/// <summary> Gets all active (non-expired) invites to this server. </summary>
|
||||||
|
public async Task<IEnumerable<Invite>> GetInvites()
|
||||||
|
=> (await Server.GetInvites()).Where(x => x.Channel.Id == Id);
|
||||||
|
|
||||||
|
/// <summary> Creates a new invite to this channel. </summary>
|
||||||
|
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param>
|
||||||
|
/// <param name="maxUses"> The max amount of times this invite may be used. Set to null to have unlimited uses. </param>
|
||||||
|
/// <param name="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||||
|
/// <param name="withXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to null. </param>
|
||||||
|
public async Task<Invite> CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false)
|
||||||
|
{
|
||||||
|
if (maxAge < 0) throw new ArgumentOutOfRangeException(nameof(maxAge));
|
||||||
|
if (maxUses < 0) throw new ArgumentOutOfRangeException(nameof(maxUses));
|
||||||
|
|
||||||
|
var request = new CreateInviteRequest(Id)
|
||||||
|
{
|
||||||
|
MaxAge = maxAge ?? 0,
|
||||||
|
MaxUses = maxUses ?? 0,
|
||||||
|
IsTemporary = tempMembership,
|
||||||
|
WithXkcdPass = withXkcd
|
||||||
|
};
|
||||||
|
|
||||||
|
var response = await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
var invite = new Invite(Client, response.Code, response.XkcdPass);
|
||||||
|
return invite;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Messages
|
||||||
internal Message AddMessage(ulong id, ulong userId, DateTime timestamp)
|
internal Message AddMessage(ulong id, ulong userId, DateTime timestamp)
|
||||||
{
|
{
|
||||||
Message message = new Message(id, this, userId);
|
Message message = new Message(id, this, userId);
|
||||||
@@ -202,14 +260,117 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message GetMessage(ulong id)
|
public Message GetMessage(ulong id)
|
||||||
{
|
{
|
||||||
Message result;
|
Message result;
|
||||||
_messages.TryGetValue(id, out result);
|
_messages.TryGetValue(id, out result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
public async Task<Message[]> DownloadMessages(int limit = 100, ulong? relativeMessageId = null,
|
||||||
|
RelativeDirection relativeDir = RelativeDirection.Before, bool useCache = true)
|
||||||
|
{
|
||||||
|
if (limit < 0) throw new ArgumentOutOfRangeException(nameof(limit));
|
||||||
|
if (limit == 0 || Type != ChannelType.Text) return new Message[0];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new GetMessagesRequest(Id)
|
||||||
|
{
|
||||||
|
Limit = limit,
|
||||||
|
RelativeDir = relativeMessageId.HasValue ? relativeDir == RelativeDirection.Before ? "before" : "after" : null,
|
||||||
|
RelativeId = relativeMessageId ?? 0
|
||||||
|
};
|
||||||
|
var msgs = await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
return msgs.Select(x =>
|
||||||
|
{
|
||||||
|
Message msg = null;
|
||||||
|
if (useCache)
|
||||||
|
{
|
||||||
|
msg = AddMessage(x.Id, x.Author.Id, x.Timestamp.Value);
|
||||||
|
var user = msg.User;
|
||||||
|
if (user != null)
|
||||||
|
user.UpdateActivity(msg.EditedTimestamp ?? msg.Timestamp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
msg = new Message(x.Id, this, x.Author.Id);
|
||||||
|
msg.Update(x);
|
||||||
|
return msg;
|
||||||
|
})
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
return new Message[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Permissions
|
/// <summary> Returns all members of this channel with the specified name. </summary>
|
||||||
|
/// <remarks> Name formats supported: Name, @Name and <@Id>. Search is case-insensitive if exactMatch is false.</remarks>
|
||||||
|
public IEnumerable<User> FindUsers(string name, bool exactMatch = false)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
|
||||||
|
return _users.Select(x => x.Value.User).Find(name, exactMatch: exactMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Message> SendMessage(string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
return SendMessageInternal(text, false);
|
||||||
|
}
|
||||||
|
public Task<Message> SendTTSMessage(string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
return SendMessageInternal(text, true);
|
||||||
|
}
|
||||||
|
private async Task<Message> SendMessageInternal(string text, bool isTTS)
|
||||||
|
{
|
||||||
|
Message msg = null;
|
||||||
|
var mentionedUsers = new List<User>();
|
||||||
|
text = Message.CleanUserMentions(this, text, mentionedUsers);
|
||||||
|
if (text.Length > DiscordConfig.MaxMessageSize)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less.");
|
||||||
|
|
||||||
|
if (Client.Config.UseMessageQueue)
|
||||||
|
Client.MessageQueue.QueueSend(Id, text, mentionedUsers.Select(x => x.Id).ToArray(), isTTS);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var request = new SendMessageRequest(Id)
|
||||||
|
{
|
||||||
|
Content = text,
|
||||||
|
MentionedUserIds = mentionedUsers.Select(x => x.Id).ToArray(),
|
||||||
|
Nonce = null,
|
||||||
|
IsTTS = isTTS
|
||||||
|
};
|
||||||
|
var model = await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
msg = AddMessage(model.Id, model.Author.Id, model.Timestamp.Value);
|
||||||
|
msg.Update(model);
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Message> SendFile(string filePath)
|
||||||
|
=> SendFile(Path.GetFileName(filePath), File.OpenRead(filePath));
|
||||||
|
public async Task<Message> SendFile(string filename, Stream stream)
|
||||||
|
{
|
||||||
|
if (filename == null) throw new ArgumentNullException(nameof(filename));
|
||||||
|
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||||
|
|
||||||
|
var request = new SendFileRequest(Id)
|
||||||
|
{
|
||||||
|
Filename = filename,
|
||||||
|
Stream = stream
|
||||||
|
};
|
||||||
|
var model = await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var msg = AddMessage(model.Id, model.Author.Id, model.Timestamp.Value);
|
||||||
|
msg.Update(model);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Permissions
|
||||||
internal void UpdatePermissions()
|
internal void UpdatePermissions()
|
||||||
{
|
{
|
||||||
if (!Client.Config.UsePermissionsCache)
|
if (!Client.Config.UsePermissionsCache)
|
||||||
@@ -291,6 +452,116 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use Channel.GetPermissions")]
|
||||||
|
public DualChannelPermissions GetPermissionsRule(User user)
|
||||||
|
{
|
||||||
|
if (user == null) throw new ArgumentNullException(nameof(user));
|
||||||
|
|
||||||
|
return PermissionOverwrites
|
||||||
|
.Where(x => x.TargetType == PermissionTarget.User && x.TargetId == user.Id)
|
||||||
|
.Select(x => x.Permissions)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
[Obsolete("Use Channel.GetPermissions")]
|
||||||
|
public DualChannelPermissions GetPermissionsRule(Role role)
|
||||||
|
{
|
||||||
|
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||||
|
|
||||||
|
return PermissionOverwrites
|
||||||
|
.Where(x => x.TargetType == PermissionTarget.Role && x.TargetId == role.Id)
|
||||||
|
.Select(x => x.Permissions)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use Channel.SetPermissions")]
|
||||||
|
public Task AddPermissionsRule(User user, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||||
|
{
|
||||||
|
if (user == null) throw new ArgumentNullException(nameof(user));
|
||||||
|
|
||||||
|
return AddPermissionsRule(user.Id, PermissionTarget.User, allow, deny);
|
||||||
|
}
|
||||||
|
[Obsolete("Use Channel.SetPermissions")]
|
||||||
|
public Task AddPermissionsRule(User user, DualChannelPermissions permissions = null)
|
||||||
|
{
|
||||||
|
if (user == null) throw new ArgumentNullException(nameof(user));
|
||||||
|
|
||||||
|
return AddPermissionsRule(user.Id, PermissionTarget.User, permissions?.Allow, permissions?.Deny);
|
||||||
|
}
|
||||||
|
[Obsolete("Use Channel.SetPermissions")]
|
||||||
|
public Task AddPermissionsRule(Role role, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||||
|
{
|
||||||
|
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||||
|
|
||||||
|
return AddPermissionsRule(role.Id, PermissionTarget.Role, allow, deny);
|
||||||
|
}
|
||||||
|
[Obsolete("Use Channel.SetPermissions")]
|
||||||
|
public Task AddPermissionsRule(Role role, DualChannelPermissions permissions = null)
|
||||||
|
{
|
||||||
|
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||||
|
|
||||||
|
return AddPermissionsRule(role.Id, PermissionTarget.Role, permissions?.Allow, permissions?.Deny);
|
||||||
|
}
|
||||||
|
private Task AddPermissionsRule(ulong targetId, PermissionTarget targetType, ChannelPermissions allow = null, ChannelPermissions deny = null)
|
||||||
|
{
|
||||||
|
var request = new AddChannelPermissionsRequest(Id)
|
||||||
|
{
|
||||||
|
TargetId = targetId,
|
||||||
|
TargetType = targetType.Value,
|
||||||
|
Allow = allow?.RawValue ?? 0,
|
||||||
|
Deny = deny?.RawValue ?? 0
|
||||||
|
};
|
||||||
|
return Client.ClientAPI.Send(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Obsolete("Use Channel.RemovePermissions")]
|
||||||
|
public Task RemovePermissionsRule(User user)
|
||||||
|
{
|
||||||
|
if (user == null) throw new ArgumentNullException(nameof(user));
|
||||||
|
return RemovePermissionsRule(user.Id, PermissionTarget.User);
|
||||||
|
}
|
||||||
|
[Obsolete("Use Channel.RemovePermissions")]
|
||||||
|
public Task RemovePermissionsRule(Role role)
|
||||||
|
{
|
||||||
|
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||||
|
return RemovePermissionsRule(role.Id, PermissionTarget.Role);
|
||||||
|
}
|
||||||
|
private async Task RemovePermissionsRule(ulong userOrRoleId, PermissionTarget targetType)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var perms = PermissionOverwrites.Where(x => x.TargetType != targetType || x.TargetId != userOrRoleId).FirstOrDefault();
|
||||||
|
await Client.ClientAPI.Send(new RemoveChannelPermissionsRequest(Id, userOrRoleId)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Users
|
||||||
|
internal void AddUser(User user)
|
||||||
|
{
|
||||||
|
if (!Client.Config.UsePermissionsCache)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var member = new Member(user);
|
||||||
|
if (_users.TryAdd(user.Id, member))
|
||||||
|
UpdatePermissions(user, member.Permissions);
|
||||||
|
}
|
||||||
|
internal void RemoveUser(ulong id)
|
||||||
|
{
|
||||||
|
if (!Client.Config.UsePermissionsCache)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Member ignored;
|
||||||
|
_users.TryRemove(id, out ignored);
|
||||||
|
}
|
||||||
|
public User GetUser(ulong id)
|
||||||
|
{
|
||||||
|
Member result;
|
||||||
|
_users.TryGetValue(id, out result);
|
||||||
|
return result.User;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id;
|
public override bool Equals(object obj) => obj is Channel && (obj as Channel).Id == Id;
|
||||||
public override int GetHashCode() => unchecked(Id.GetHashCode() + 5658);
|
public override int GetHashCode() => unchecked(Id.GetHashCode() + 5658);
|
||||||
public override string ToString() => Name ?? Id.ToIdString();
|
public override string ToString() => Name ?? Id.ToIdString();
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
using Discord.API.Client;
|
using Discord.API.Client;
|
||||||
|
using Discord.API.Client.Rest;
|
||||||
|
using Discord.Net;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using APIInvite = Discord.API.Client.Invite;
|
using APIInvite = Discord.API.Client.Invite;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
@@ -55,6 +59,10 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ulong _serverId, _channelId;
|
||||||
|
|
||||||
|
internal DiscordClient Client { get; }
|
||||||
|
|
||||||
/// <summary> Gets the unique code for this invite. </summary>
|
/// <summary> Gets the unique code for this invite. </summary>
|
||||||
public string Code { get; }
|
public string Code { get; }
|
||||||
/// <summary> Gets, if enabled, an alternative human-readable invite code. </summary>
|
/// <summary> Gets, if enabled, an alternative human-readable invite code. </summary>
|
||||||
@@ -63,9 +71,9 @@ namespace Discord
|
|||||||
/// <summary> Gets information about the server this invite is attached to. </summary>
|
/// <summary> Gets information about the server this invite is attached to. </summary>
|
||||||
public ServerInfo Server { get; private set; }
|
public ServerInfo Server { get; private set; }
|
||||||
/// <summary> Gets information about the channel this invite is attached to. </summary>
|
/// <summary> Gets information about the channel this invite is attached to. </summary>
|
||||||
public ChannelInfo Channel { get; private set; }
|
public ChannelInfo Channel { get; private set; }
|
||||||
/// <summary> Gets the time (in seconds) until the invite expires. </summary>
|
/// <summary> Gets the time (in seconds) until the invite expires. </summary>
|
||||||
public int? MaxAge { get; private set; }
|
public int? MaxAge { get; private set; }
|
||||||
/// <summary> Gets the amount of times this invite has been used. </summary>
|
/// <summary> Gets the amount of times this invite has been used. </summary>
|
||||||
public int Uses { get; private set; }
|
public int Uses { get; private set; }
|
||||||
/// <summary> Gets the max amount of times this invite may be used. </summary>
|
/// <summary> Gets the max amount of times this invite may be used. </summary>
|
||||||
@@ -80,8 +88,9 @@ namespace Discord
|
|||||||
/// <summary> Returns a URL for this invite using XkcdCode if available or Id if not. </summary>
|
/// <summary> Returns a URL for this invite using XkcdCode if available or Id if not. </summary>
|
||||||
public string Url => $"{DiscordConfig.InviteUrl}/{Code}";
|
public string Url => $"{DiscordConfig.InviteUrl}/{Code}";
|
||||||
|
|
||||||
internal Invite(string code, string xkcdPass)
|
internal Invite(DiscordClient client, string code, string xkcdPass)
|
||||||
{
|
{
|
||||||
|
Client = client;
|
||||||
Code = code;
|
Code = code;
|
||||||
XkcdCode = xkcdPass;
|
XkcdCode = xkcdPass;
|
||||||
}
|
}
|
||||||
@@ -110,8 +119,16 @@ namespace Discord
|
|||||||
if (model.CreatedAt != null)
|
if (model.CreatedAt != null)
|
||||||
CreatedAt = model.CreatedAt.Value;
|
CreatedAt = model.CreatedAt.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Delete()
|
||||||
|
{
|
||||||
|
try { await Client.ClientAPI.Send(new DeleteInviteRequest(Code)).ConfigureAwait(false); }
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
|
}
|
||||||
|
public Task Accept()
|
||||||
|
=> Client.ClientAPI.Send(new AcceptInviteRequest(Code));
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is Invite && (obj as Invite).Code == Code;
|
public override bool Equals(object obj) => obj is Invite && (obj as Invite).Code == Code;
|
||||||
public override int GetHashCode() => unchecked(Code.GetHashCode() + 9980);
|
public override int GetHashCode() => unchecked(Code.GetHashCode() + 9980);
|
||||||
public override string ToString() => XkcdCode ?? Code;
|
public override string ToString() => XkcdCode ?? Code;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
using Newtonsoft.Json;
|
using Discord.API.Client.Rest;
|
||||||
|
using Discord.Net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using APIMessage = Discord.API.Client.Message;
|
using APIMessage = Discord.API.Client.Message;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
@@ -16,8 +21,73 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
|
|
||||||
public sealed class Message
|
public sealed class Message
|
||||||
{
|
{
|
||||||
/*internal class ImportResolver : DefaultContractResolver
|
private static readonly Regex _userRegex = new Regex(@"<@([0-9]+)>", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex _channelRegex = new Regex(@"<#([0-9]+)>", RegexOptions.Compiled);
|
||||||
|
private static readonly Regex _roleRegex = new Regex(@"@everyone", RegexOptions.Compiled);
|
||||||
|
private static readonly Attachment[] _initialAttachments = new Attachment[0];
|
||||||
|
private static readonly Embed[] _initialEmbeds = new Embed[0];
|
||||||
|
|
||||||
|
internal static string CleanUserMentions(Channel channel, string text, List<User> users = null)
|
||||||
|
{
|
||||||
|
return _userRegex.Replace(text, new MatchEvaluator(e =>
|
||||||
|
{
|
||||||
|
var id = e.Value.Substring(2, e.Value.Length - 3).ToId();
|
||||||
|
var user = channel.GetUser(id);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
if (users != null)
|
||||||
|
users.Add(user);
|
||||||
|
return '@' + user.Name;
|
||||||
|
}
|
||||||
|
else //User not found
|
||||||
|
return '@' + e.Value;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
internal static string CleanChannelMentions(Channel channel, string text, List<Channel> channels = null)
|
||||||
|
{
|
||||||
|
var server = channel.Server;
|
||||||
|
if (server == null) return text;
|
||||||
|
|
||||||
|
return _channelRegex.Replace(text, new MatchEvaluator(e =>
|
||||||
|
{
|
||||||
|
var id = e.Value.Substring(2, e.Value.Length - 3).ToId();
|
||||||
|
var mentionedChannel = server.GetChannel(id);
|
||||||
|
if (mentionedChannel != null && mentionedChannel.Server.Id == server.Id)
|
||||||
|
{
|
||||||
|
if (channels != null)
|
||||||
|
channels.Add(mentionedChannel);
|
||||||
|
return '#' + mentionedChannel.Name;
|
||||||
|
}
|
||||||
|
else //Channel not found
|
||||||
|
return '#' + e.Value;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
/*internal static string CleanRoleMentions(User user, Channel channel, string text, List<Role> roles = null)
|
||||||
|
{
|
||||||
|
var server = channel.Server;
|
||||||
|
if (server == null) return text;
|
||||||
|
|
||||||
|
return _roleRegex.Replace(text, new MatchEvaluator(e =>
|
||||||
|
{
|
||||||
|
if (roles != null && user.GetPermissions(channel).MentionEveryone)
|
||||||
|
roles.Add(server.EveryoneRole);
|
||||||
|
return e.Value;
|
||||||
|
}));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private static string Resolve(Channel channel, string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
|
||||||
|
var client = channel.Client;
|
||||||
|
text = CleanUserMentions(channel, text);
|
||||||
|
text = CleanChannelMentions(channel, text);
|
||||||
|
//text = CleanRoleMentions(Channel, text);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*internal class ImportResolver : DefaultContractResolver
|
||||||
{
|
{
|
||||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||||
{
|
{
|
||||||
@@ -33,7 +103,7 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
public sealed class Attachment : File
|
public sealed class Attachment : File
|
||||||
{
|
{
|
||||||
/// <summary> Unique identifier for this file. </summary>
|
/// <summary> Unique identifier for this file. </summary>
|
||||||
public string Id { get; internal set; }
|
public string Id { get; internal set; }
|
||||||
@@ -89,11 +159,10 @@ namespace Discord
|
|||||||
internal File() { }
|
internal File() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Attachment[] _initialAttachments = new Attachment[0];
|
|
||||||
private static readonly Embed[] _initialEmbeds = new Embed[0];
|
|
||||||
|
|
||||||
private readonly ulong _userId;
|
private readonly ulong _userId;
|
||||||
|
|
||||||
|
internal DiscordClient Client => Channel.Client;
|
||||||
|
|
||||||
/// <summary> Returns the unique identifier for this message. </summary>
|
/// <summary> Returns the unique identifier for this message. </summary>
|
||||||
public ulong Id { get; }
|
public ulong Id { get; }
|
||||||
/// <summary> Returns the channel this message was sent to. </summary>
|
/// <summary> Returns the channel this message was sent to. </summary>
|
||||||
@@ -117,7 +186,7 @@ namespace Discord
|
|||||||
public Attachment[] Attachments { get; private set; }
|
public Attachment[] Attachments { get; private set; }
|
||||||
/// <summary> Returns a collection of all embeded content in this message. </summary>
|
/// <summary> Returns a collection of all embeded content in this message. </summary>
|
||||||
public Embed[] Embeds { get; private set; }
|
public Embed[] Embeds { get; private set; }
|
||||||
|
|
||||||
/// <summary> Returns a collection of all users mentioned in this message. </summary>
|
/// <summary> Returns a collection of all users mentioned in this message. </summary>
|
||||||
public IEnumerable<User> MentionedUsers { get; internal set; }
|
public IEnumerable<User> MentionedUsers { get; internal set; }
|
||||||
/// <summary> Returns a collection of all channels mentioned in this message. </summary>
|
/// <summary> Returns a collection of all channels mentioned in this message. </summary>
|
||||||
@@ -210,11 +279,11 @@ namespace Discord
|
|||||||
//var mentionedUsers = new List<User>();
|
//var mentionedUsers = new List<User>();
|
||||||
var mentionedChannels = new List<Channel>();
|
var mentionedChannels = new List<Channel>();
|
||||||
//var mentionedRoles = new List<Role>();
|
//var mentionedRoles = new List<Role>();
|
||||||
text = Mention.CleanUserMentions(Channel.Client, channel, text/*, mentionedUsers*/);
|
text = CleanUserMentions(Channel, text/*, mentionedUsers*/);
|
||||||
if (server != null)
|
if (server != null)
|
||||||
{
|
{
|
||||||
text = Mention.CleanChannelMentions(Channel.Client, channel, text, mentionedChannels);
|
text = CleanChannelMentions(Channel, text, mentionedChannels);
|
||||||
//text = Mention.CleanRoleMentions(_client, User, channel, text, mentionedRoles);
|
//text = CleanRoleMentions(_client, User, channel, text, mentionedRoles);
|
||||||
}
|
}
|
||||||
Text = text;
|
Text = text;
|
||||||
|
|
||||||
@@ -236,7 +305,54 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is Message && (obj as Message).Id == Id;
|
public async Task Edit(string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
|
||||||
|
var channel = Channel;
|
||||||
|
var mentionedUsers = new List<User>();
|
||||||
|
if (!channel.IsPrivate)
|
||||||
|
text = CleanUserMentions(channel, text, mentionedUsers);
|
||||||
|
|
||||||
|
if (text.Length > DiscordConfig.MaxMessageSize)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(text), $"Message must be {DiscordConfig.MaxMessageSize} characters or less.");
|
||||||
|
|
||||||
|
if (Client.Config.UseMessageQueue)
|
||||||
|
Client.MessageQueue.QueueEdit(channel.Id, Id, text, mentionedUsers.Select(x => x.Id).ToArray());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var request = new UpdateMessageRequest(Channel.Id, Id)
|
||||||
|
{
|
||||||
|
Content = text,
|
||||||
|
MentionedUserIds = mentionedUsers.Select(x => x.Id).ToArray()
|
||||||
|
};
|
||||||
|
await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete()
|
||||||
|
{
|
||||||
|
var request = new DeleteMessageRequest(Channel.Id, Id);
|
||||||
|
try { await Client.ClientAPI.Send(request).ConfigureAwait(false); }
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Acknowledge()
|
||||||
|
{
|
||||||
|
if (_userId != Client.CurrentUser.Id)
|
||||||
|
return Client.ClientAPI.Send(new AckMessageRequest(Channel.Id, Id));
|
||||||
|
else
|
||||||
|
return TaskHelper.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Resolves all mentions in a provided string to those users, channels or roles' names.</summary>
|
||||||
|
public string Resolve(string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
return Resolve(Channel, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is Message && (obj as Message).Id == Id;
|
||||||
public override int GetHashCode() => unchecked(Id.GetHashCode() + 9979);
|
public override int GetHashCode() => unchecked(Id.GetHashCode() + 9979);
|
||||||
public override string ToString() => $"{User}: {RawText}";
|
public override string ToString() => $"{User}: {RawText}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
//General
|
//General
|
||||||
CreateInstantInvite = 0,
|
CreateInstantInvite = 0,
|
||||||
BanMembers = 1,
|
KickMembers = 1,
|
||||||
KickMembers = 2,
|
BanMembers = 2,
|
||||||
ManageRolesOrPermissions = 3,
|
ManageRolesOrPermissions = 3,
|
||||||
ManageChannel = 4,
|
ManageChannel = 4,
|
||||||
ManageServer = 5,
|
ManageServer = 5,
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
using Newtonsoft.Json;
|
using Discord.API.Client.Rest;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using APIUser = Discord.API.Client.User;
|
using APIUser = Discord.API.Client.User;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public sealed class Profile
|
public sealed class Profile
|
||||||
{
|
{
|
||||||
|
internal DiscordClient Client { get; }
|
||||||
|
|
||||||
/// <summary> Gets the unique identifier for this user. </summary>
|
/// <summary> Gets the unique identifier for this user. </summary>
|
||||||
public ulong Id { get; private set; }
|
public ulong Id { get; private set; }
|
||||||
/// <summary> Gets the email for this user. </summary>
|
/// <summary> Gets the email for this user. </summary>
|
||||||
@@ -12,7 +17,10 @@ namespace Discord
|
|||||||
/// <summary> Gets if the email for this user has been verified. </summary>
|
/// <summary> Gets if the email for this user has been verified. </summary>
|
||||||
public bool? IsVerified { get; private set; }
|
public bool? IsVerified { get; private set; }
|
||||||
|
|
||||||
internal Profile() { }
|
internal Profile(DiscordClient client)
|
||||||
|
{
|
||||||
|
Client = client;
|
||||||
|
}
|
||||||
|
|
||||||
internal void Update(APIUser model)
|
internal void Update(APIUser model)
|
||||||
{
|
{
|
||||||
@@ -21,6 +29,36 @@ namespace Discord
|
|||||||
IsVerified = model.IsVerified;
|
IsVerified = model.IsVerified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Edit(string currentPassword = "",
|
||||||
|
string username = null, string email = null, string password = null,
|
||||||
|
Stream avatar = null, ImageType avatarType = ImageType.Png)
|
||||||
|
{
|
||||||
|
if (currentPassword == null) throw new ArgumentNullException(nameof(currentPassword));
|
||||||
|
|
||||||
|
var request = new UpdateProfileRequest()
|
||||||
|
{
|
||||||
|
CurrentPassword = currentPassword,
|
||||||
|
Email = email ?? Email,
|
||||||
|
Password = password,
|
||||||
|
Username = username ?? Client.PrivateUser.Name,
|
||||||
|
AvatarBase64 = avatar.Base64(avatarType, Client.PrivateUser.AvatarId)
|
||||||
|
};
|
||||||
|
|
||||||
|
await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (password != null)
|
||||||
|
{
|
||||||
|
var loginRequest = new LoginRequest()
|
||||||
|
{
|
||||||
|
Email = Email,
|
||||||
|
Password = password
|
||||||
|
};
|
||||||
|
var loginResponse = await Client.ClientAPI.Send(loginRequest).ConfigureAwait(false);
|
||||||
|
Client.ClientAPI.Token = loginResponse.Token;
|
||||||
|
Client.GatewaySocket.Token = loginResponse.Token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
=> (obj is Profile && (obj as Profile).Id == Id) || (obj is User && (obj as User).Id == Id);
|
=> (obj is Profile && (obj as Profile).Id == Id) || (obj is User && (obj as User).Id == Id);
|
||||||
public override int GetHashCode() => unchecked(Id.GetHashCode() + 2061);
|
public override int GetHashCode() => unchecked(Id.GetHashCode() + 2061);
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
using System;
|
using Discord.API.Client.Rest;
|
||||||
|
using Discord.Net;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using APIRole = Discord.API.Client.Role;
|
using APIRole = Discord.API.Client.Role;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public sealed class Role
|
public sealed class Role
|
||||||
{
|
{
|
||||||
private readonly DiscordClient _client;
|
internal DiscordClient Client => Server.Client;
|
||||||
|
|
||||||
/// <summary> Gets the unique identifier for this role. </summary>
|
/// <summary> Gets the unique identifier for this role. </summary>
|
||||||
public ulong Id { get; }
|
public ulong Id { get; }
|
||||||
@@ -72,8 +76,57 @@ namespace Discord
|
|||||||
foreach (var member in Members)
|
foreach (var member in Members)
|
||||||
Server.UpdatePermissions(member);
|
Server.UpdatePermissions(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Edit(string name = null, ServerPermissions permissions = null, Color color = null, bool? isHoisted = null, int? position = null)
|
||||||
|
{
|
||||||
|
var updateRequest = new UpdateRoleRequest(Server.Id, Id)
|
||||||
|
{
|
||||||
|
Name = name ?? Name,
|
||||||
|
Permissions = (permissions ?? Permissions).RawValue,
|
||||||
|
Color = (color ?? Color).RawValue,
|
||||||
|
IsHoisted = isHoisted ?? IsHoisted
|
||||||
|
};
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is Role && (obj as Role).Id == Id;
|
var updateResponse = await Client.ClientAPI.Send(updateRequest).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (position != null)
|
||||||
|
{
|
||||||
|
int oldPos = Position;
|
||||||
|
int newPos = position.Value;
|
||||||
|
int minPos;
|
||||||
|
Role[] roles = Server.Roles.OrderBy(x => x.Position).ToArray();
|
||||||
|
|
||||||
|
if (oldPos < newPos) //Moving Down
|
||||||
|
{
|
||||||
|
minPos = oldPos;
|
||||||
|
for (int i = oldPos; i < newPos; i++)
|
||||||
|
roles[i] = roles[i + 1];
|
||||||
|
roles[newPos] = this;
|
||||||
|
}
|
||||||
|
else //(oldPos > newPos) Moving Up
|
||||||
|
{
|
||||||
|
minPos = newPos;
|
||||||
|
for (int i = oldPos; i > newPos; i--)
|
||||||
|
roles[i] = roles[i - 1];
|
||||||
|
roles[newPos] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reorderRequest = new ReorderRolesRequest(Server.Id)
|
||||||
|
{
|
||||||
|
RoleIds = roles.Skip(minPos).Select(x => x.Id).ToArray(),
|
||||||
|
StartPos = minPos
|
||||||
|
};
|
||||||
|
await Client.ClientAPI.Send(reorderRequest).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete()
|
||||||
|
{
|
||||||
|
try { await Client.ClientAPI.Send(new DeleteRoleRequest(Server.Id, Id)).ConfigureAwait(false); }
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is Role && (obj as Role).Id == Id;
|
||||||
public override int GetHashCode() => unchecked(Id.GetHashCode() + 6653);
|
public override int GetHashCode() => unchecked(Id.GetHashCode() + 6653);
|
||||||
public override string ToString() => Name ?? Id.ToIdString();
|
public override string ToString() => Name ?? Id.ToIdString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
using Discord.API.Client;
|
using Discord.API.Client;
|
||||||
|
using Discord.API.Client.Rest;
|
||||||
|
using Discord.Net;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
/// <summary> Represents a Discord server (also known as a guild). </summary>
|
/// <summary> Represents a Discord server (also known as a guild). </summary>
|
||||||
public sealed class Server
|
public sealed class Server
|
||||||
{
|
{
|
||||||
|
internal static string GetIconUrl(ulong serverId, string iconId)
|
||||||
|
=> iconId != null ? $"{DiscordConfig.CDNUrl}/icons/{serverId}/{iconId}.jpg" : null;
|
||||||
|
|
||||||
private struct Member
|
private struct Member
|
||||||
{
|
{
|
||||||
public readonly User User;
|
public readonly User User;
|
||||||
@@ -19,7 +27,7 @@ namespace Discord
|
|||||||
Permissions = new ServerPermissions();
|
Permissions = new ServerPermissions();
|
||||||
Permissions.Lock();
|
Permissions.Lock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<ulong, Role> _roles;
|
private readonly ConcurrentDictionary<ulong, Role> _roles;
|
||||||
private readonly ConcurrentDictionary<ulong, Member> _users;
|
private readonly ConcurrentDictionary<ulong, Member> _users;
|
||||||
@@ -27,9 +35,9 @@ namespace Discord
|
|||||||
private readonly ConcurrentDictionary<ulong, bool> _bans;
|
private readonly ConcurrentDictionary<ulong, bool> _bans;
|
||||||
private ulong _ownerId;
|
private ulong _ownerId;
|
||||||
private ulong? _afkChannelId;
|
private ulong? _afkChannelId;
|
||||||
|
|
||||||
/// <summary> Gets the client that generated this server object. </summary>
|
|
||||||
internal DiscordClient Client { get; }
|
internal DiscordClient Client { get; }
|
||||||
|
|
||||||
/// <summary> Gets the unique identifier for this server. </summary>
|
/// <summary> Gets the unique identifier for this server. </summary>
|
||||||
public ulong Id { get; }
|
public ulong Id { get; }
|
||||||
/// <summary> Gets the default channel for this server. </summary>
|
/// <summary> Gets the default channel for this server. </summary>
|
||||||
@@ -39,19 +47,14 @@ namespace Discord
|
|||||||
|
|
||||||
/// <summary> Gets the name of this server. </summary>
|
/// <summary> Gets the name of this server. </summary>
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
/// <summary> Gets the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK channel, if one is set. </summary>
|
|
||||||
public int AFKTimeout { get; private set; }
|
|
||||||
/// <summary> Gets the date and time you joined this server. </summary>
|
|
||||||
public DateTime JoinedAt { get; private set; }
|
|
||||||
/// <summary> Gets the voice region for this server. </summary>
|
/// <summary> Gets the voice region for this server. </summary>
|
||||||
public Region Region { get; private set; }
|
public Region Region { get; private set; }
|
||||||
/// <summary> Gets the unique identifier for this user's current avatar. </summary>
|
/// <summary> Gets the unique identifier for this user's current avatar. </summary>
|
||||||
public string IconId { get; private set; }
|
public string IconId { get; private set; }
|
||||||
/// <summary> Gets the URL to this user's current avatar. </summary>
|
/// <summary> Gets the amount of time (in seconds) a user must be inactive for until they are automatically moved to the AFK voice channel, if one is set. </summary>
|
||||||
public string IconUrl => GetIconUrl(Id, IconId);
|
public int AFKTimeout { get; private set; }
|
||||||
internal static string GetIconUrl(ulong serverId, string iconId)
|
/// <summary> Gets the date and time you joined this server. </summary>
|
||||||
=> iconId != null ? $"{DiscordConfig.CDNUrl}/icons/{serverId}/{iconId}.jpg" : null;
|
public DateTime JoinedAt { get; private set; }
|
||||||
|
|
||||||
/// <summary> Gets the user that created this server. </summary>
|
/// <summary> Gets the user that created this server. </summary>
|
||||||
public User Owner => GetUser(_ownerId);
|
public User Owner => GetUser(_ownerId);
|
||||||
@@ -59,16 +62,18 @@ namespace Discord
|
|||||||
public Channel AFKChannel => _afkChannelId != null ? GetChannel(_afkChannelId.Value) : null;
|
public Channel AFKChannel => _afkChannelId != null ? GetChannel(_afkChannelId.Value) : null;
|
||||||
/// <summary> Gets the current user in this server. </summary>
|
/// <summary> Gets the current user in this server. </summary>
|
||||||
public User CurrentUser => GetUser(Client.CurrentUser.Id);
|
public User CurrentUser => GetUser(Client.CurrentUser.Id);
|
||||||
|
/// <summary> Gets the URL to this user's current avatar. </summary>
|
||||||
|
public string IconUrl => GetIconUrl(Id, IconId);
|
||||||
|
|
||||||
/// <summary> Gets a collection of the ids of all users banned on this server. </summary>
|
/// <summary> Gets a collection of the ids of all users banned on this server. </summary>
|
||||||
public IEnumerable<ulong> BannedUserIds => _bans.Select(x => x.Key);
|
public IEnumerable<ulong> BannedUserIds => _bans.Select(x => x.Key);
|
||||||
/// <summary> Gets a collection of all channels within this server. </summary>
|
/// <summary> Gets a collection of all channels in this server. </summary>
|
||||||
public IEnumerable<Channel> Channels => _channels.Select(x => x.Value);
|
public IEnumerable<Channel> Channels => _channels.Select(x => x.Value);
|
||||||
/// <summary> Gets a collection of all users within this server with their server-specific data. </summary>
|
/// <summary> Gets a collection of all members in this server. </summary>
|
||||||
public IEnumerable<User> Users => _users.Select(x => x.Value.User);
|
public IEnumerable<User> Users => _users.Select(x => x.Value.User);
|
||||||
/// <summary> Gets a collection of all roles within this server. </summary>
|
/// <summary> Gets a collection of all roles in this server. </summary>
|
||||||
public IEnumerable<Role> Roles => _roles.Select(x => x.Value);
|
public IEnumerable<Role> Roles => _roles.Select(x => x.Value);
|
||||||
|
|
||||||
internal Server(DiscordClient client, ulong id)
|
internal Server(DiscordClient client, ulong id)
|
||||||
{
|
{
|
||||||
Client = client;
|
Client = client;
|
||||||
@@ -80,7 +85,7 @@ namespace Discord
|
|||||||
DefaultChannel = AddChannel(id);
|
DefaultChannel = AddChannel(id);
|
||||||
EveryoneRole = AddRole(id);
|
EveryoneRole = AddRole(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update(GuildReference model)
|
internal void Update(GuildReference model)
|
||||||
{
|
{
|
||||||
if (model.Name != null)
|
if (model.Name != null)
|
||||||
@@ -120,7 +125,7 @@ namespace Discord
|
|||||||
if (model.Members != null)
|
if (model.Members != null)
|
||||||
{
|
{
|
||||||
foreach (var subModel in model.Members)
|
foreach (var subModel in model.Members)
|
||||||
AddMember(subModel.User.Id).Update(subModel);
|
AddUser(subModel.User.Id).Update(subModel);
|
||||||
}
|
}
|
||||||
if (model.VoiceStates != null)
|
if (model.VoiceStates != null)
|
||||||
{
|
{
|
||||||
@@ -133,8 +138,39 @@ namespace Discord
|
|||||||
GetUser(subModel.User.Id)?.Update(subModel);
|
GetUser(subModel.User.Id)?.Update(subModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Edits this server, changing only non-null attributes. </summary>
|
||||||
|
public Task Edit(string name = null, string region = null, Stream icon = null, ImageType iconType = ImageType.Png)
|
||||||
|
{
|
||||||
|
var request = new UpdateGuildRequest(Id)
|
||||||
|
{
|
||||||
|
Name = name ?? Name,
|
||||||
|
Region = region ?? Region.Id,
|
||||||
|
IconBase64 = icon.Base64(iconType, IconId),
|
||||||
|
AFKChannelId = AFKChannel?.Id,
|
||||||
|
AFKTimeout = AFKTimeout
|
||||||
|
};
|
||||||
|
return Client.ClientAPI.Send(request);
|
||||||
|
}
|
||||||
|
|
||||||
//Bans
|
/// <summary> Leaves this server. This function will fail if you're the owner - use Delete instead. </summary>
|
||||||
|
public async Task Leave()
|
||||||
|
{
|
||||||
|
if (_ownerId == CurrentUser.Id)
|
||||||
|
throw new InvalidOperationException("Unable to leave a server you own, use Server.Delete instead");
|
||||||
|
try { await Client.ClientAPI.Send(new LeaveGuildRequest(Id)).ConfigureAwait(false); }
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
|
}
|
||||||
|
/// <summary> Deletes this server. This function will fail if you're not the owner - use Leave instead. </summary>
|
||||||
|
public async Task Delete()
|
||||||
|
{
|
||||||
|
if (_ownerId != CurrentUser.Id)
|
||||||
|
throw new InvalidOperationException("Unable to delete a server you don't own, use Server.Leave instead");
|
||||||
|
try { await Client.ClientAPI.Send(new LeaveGuildRequest(Id)).ConfigureAwait(false); }
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Bans
|
||||||
internal void AddBan(ulong banId)
|
internal void AddBan(ulong banId)
|
||||||
=> _bans.TryAdd(banId, true);
|
=> _bans.TryAdd(banId, true);
|
||||||
internal bool RemoveBan(ulong banId)
|
internal bool RemoveBan(ulong banId)
|
||||||
@@ -143,7 +179,22 @@ namespace Discord
|
|||||||
return _bans.TryRemove(banId, out ignored);
|
return _bans.TryRemove(banId, out ignored);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Channels
|
public Task Ban(User user, int pruneDays = 0)
|
||||||
|
{
|
||||||
|
var request = new AddGuildBanRequest(user.Server.Id, user.Id);
|
||||||
|
request.PruneDays = pruneDays;
|
||||||
|
return Client.ClientAPI.Send(request);
|
||||||
|
}
|
||||||
|
public Task Unban(User user, int pruneDays = 0)
|
||||||
|
=> Unban(user.Id);
|
||||||
|
public async Task Unban(ulong userId)
|
||||||
|
{
|
||||||
|
try { await Client.ClientAPI.Send(new RemoveGuildBanRequest(Id, userId)).ConfigureAwait(false); }
|
||||||
|
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Channels
|
||||||
internal Channel AddChannel(ulong id)
|
internal Channel AddChannel(ulong id)
|
||||||
=> _channels.GetOrAdd(id, x => new Channel(Client, x, this));
|
=> _channels.GetOrAdd(id, x => new Channel(Client, x, this));
|
||||||
internal Channel RemoveChannel(ulong id)
|
internal Channel RemoveChannel(ulong id)
|
||||||
@@ -152,6 +203,8 @@ namespace Discord
|
|||||||
_channels.TryRemove(id, out channel);
|
_channels.TryRemove(id, out channel);
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Gets the channel with the provided id and owned by this server, or null if not found. </summary>
|
||||||
public Channel GetChannel(ulong id)
|
public Channel GetChannel(ulong id)
|
||||||
{
|
{
|
||||||
Channel result;
|
Channel result;
|
||||||
@@ -159,36 +212,67 @@ namespace Discord
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Members
|
/// <summary> Returns all channels with the specified server and name. </summary>
|
||||||
internal User AddMember(ulong id)
|
/// <remarks> Name formats supported: Name, #Name and <#Id>. Search is case-insensitive if exactMatch is false.</remarks>
|
||||||
|
public IEnumerable<Channel> FindChannels(string name, ChannelType type = null, bool exactMatch = false)
|
||||||
{
|
{
|
||||||
User newUser = null;
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
var user = _users.GetOrAdd(id, x => new Member(new User(id, this)));
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
if (user.User == newUser)
|
|
||||||
{
|
return _channels.Select(x => x.Value).Find(name, type, exactMatch);
|
||||||
foreach (var channel in Channels)
|
|
||||||
channel.AddUser(newUser);
|
|
||||||
}
|
|
||||||
return user.User;
|
|
||||||
}
|
|
||||||
internal User RemoveMember(ulong id)
|
|
||||||
{
|
|
||||||
Member member;
|
|
||||||
if (_users.TryRemove(id, out member))
|
|
||||||
{
|
|
||||||
foreach (var channel in Channels)
|
|
||||||
channel.RemoveUser(id);
|
|
||||||
}
|
|
||||||
return member.User;
|
|
||||||
}
|
|
||||||
public User GetUser(ulong id)
|
|
||||||
{
|
|
||||||
Member result;
|
|
||||||
_users.TryGetValue(id, out result);
|
|
||||||
return result.User;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Roles
|
/// <summary> Creates a new channel. </summary>
|
||||||
|
public async Task<Channel> CreateChannel(string name, ChannelType type)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
if (type == null) throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
|
var request = new CreateChannelRequest(Id) { Name = name, Type = type.Value };
|
||||||
|
var response = await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var channel = AddChannel(response.Id);
|
||||||
|
channel.Update(response);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reorders the provided channels and optionally places them after a certain channel. </summary>
|
||||||
|
public Task ReorderChannels(IEnumerable<Channel> channels, Channel after = null)
|
||||||
|
{
|
||||||
|
if (channels == null) throw new ArgumentNullException(nameof(channels));
|
||||||
|
|
||||||
|
var request = new ReorderChannelsRequest(Id)
|
||||||
|
{
|
||||||
|
ChannelIds = channels.Select(x => x.Id).ToArray(),
|
||||||
|
StartPos = after != null ? after.Position + 1 : channels.Min(x => x.Position)
|
||||||
|
};
|
||||||
|
return Client.ClientAPI.Send(request);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Invites
|
||||||
|
/// <summary> Gets all active (non-expired) invites to this server. </summary>
|
||||||
|
public async Task<IEnumerable<Invite>> GetInvites()
|
||||||
|
{
|
||||||
|
var response = await Client.ClientAPI.Send(new GetInvitesRequest(Id)).ConfigureAwait(false);
|
||||||
|
return response.Select(x =>
|
||||||
|
{
|
||||||
|
var invite = new Invite(Client, x.Code, x.XkcdPass);
|
||||||
|
invite.Update(x);
|
||||||
|
return invite;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Creates a new invite to the default channel of this server. </summary>
|
||||||
|
/// <param name="maxAge"> Time (in seconds) until the invite expires. Set to null to never expire. </param>
|
||||||
|
/// <param name="maxUses"> The max amount of times this invite may be used. Set to null to have unlimited uses. </param>
|
||||||
|
/// <param name="tempMembership"> If true, a user accepting this invite will be kicked from the server after closing their client. </param>
|
||||||
|
/// <param name="withXkcd"> If true, creates a human-readable link. Not supported if maxAge is set to null. </param>
|
||||||
|
public Task<Invite> CreateInvite(int? maxAge = 1800, int? maxUses = null, bool tempMembership = false, bool withXkcd = false)
|
||||||
|
=> DefaultChannel.CreateInvite(maxAge, maxUses, tempMembership, withXkcd);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Roles
|
||||||
internal Role AddRole(ulong id)
|
internal Role AddRole(ulong id)
|
||||||
=> _roles.GetOrAdd(id, x => new Role(x, this));
|
=> _roles.GetOrAdd(id, x => new Role(x, this));
|
||||||
internal Role RemoveRole(ulong id)
|
internal Role RemoveRole(ulong id)
|
||||||
@@ -197,14 +281,59 @@ namespace Discord
|
|||||||
_roles.TryRemove(id, out role);
|
_roles.TryRemove(id, out role);
|
||||||
return role;
|
return role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Gets the role with the provided id and owned by this server, or null if not found. </summary>
|
||||||
public Role GetRole(ulong id)
|
public Role GetRole(ulong id)
|
||||||
{
|
{
|
||||||
Role result;
|
Role result;
|
||||||
_roles.TryGetValue(id, out result);
|
_roles.TryGetValue(id, out result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
/// <summary> Returns all roles with the specified server and name. </summary>
|
||||||
|
/// <remarks> Search is case-insensitive if exactMatch is false.</remarks>
|
||||||
|
public IEnumerable<Role> FindRoles(string name, bool exactMatch = false)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
return _roles.Select(x => x.Value).Find(name, exactMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Creates a new role. </summary>
|
||||||
|
public async Task<Role> CreateRole(string name, ServerPermissions permissions = null, Color color = null, bool isHoisted = false)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
|
||||||
//Permissions
|
var createRequest = new CreateRoleRequest(Id);
|
||||||
|
var createResponse = await Client.ClientAPI.Send(createRequest).ConfigureAwait(false);
|
||||||
|
var role = AddRole(createResponse.Id);
|
||||||
|
role.Update(createResponse);
|
||||||
|
|
||||||
|
var editRequest = new UpdateRoleRequest(role.Server.Id, role.Id)
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Permissions = (permissions ?? role.Permissions).RawValue,
|
||||||
|
Color = (color ?? Color.Default).RawValue,
|
||||||
|
IsHoisted = isHoisted
|
||||||
|
};
|
||||||
|
var editResponse = await Client.ClientAPI.Send(editRequest).ConfigureAwait(false);
|
||||||
|
role.Update(editResponse);
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Reorders the provided roles and optionally places them after a certain role. </summary>
|
||||||
|
public Task ReorderRoles(IEnumerable<Role> roles, Role after = null)
|
||||||
|
{
|
||||||
|
if (roles == null) throw new ArgumentNullException(nameof(roles));
|
||||||
|
|
||||||
|
return Client.ClientAPI.Send(new ReorderRolesRequest(Id)
|
||||||
|
{
|
||||||
|
RoleIds = roles.Select(x => x.Id).ToArray(),
|
||||||
|
StartPos = after != null ? after.Position + 1 : roles.Min(x => x.Position)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Permissions
|
||||||
internal ServerPermissions GetPermissions(User user)
|
internal ServerPermissions GetPermissions(User user)
|
||||||
{
|
{
|
||||||
Member member;
|
Member member;
|
||||||
@@ -213,12 +342,14 @@ namespace Discord
|
|||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void UpdatePermissions(User user)
|
internal void UpdatePermissions(User user)
|
||||||
{
|
{
|
||||||
Member member;
|
Member member;
|
||||||
if (_users.TryGetValue(user.Id, out member))
|
if (_users.TryGetValue(user.Id, out member))
|
||||||
UpdatePermissions(member.User, member.Permissions);
|
UpdatePermissions(member.User, member.Permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePermissions(User user, ServerPermissions permissions)
|
private void UpdatePermissions(User user, ServerPermissions permissions)
|
||||||
{
|
{
|
||||||
uint newPermissions = 0;
|
uint newPermissions = 0;
|
||||||
@@ -241,8 +372,75 @@ namespace Discord
|
|||||||
channel.Value.UpdatePermissions(user);
|
channel.Value.UpdatePermissions(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is Server && (obj as Server).Id == Id;
|
#region Users
|
||||||
|
internal User AddUser(ulong id)
|
||||||
|
{
|
||||||
|
User newUser = null;
|
||||||
|
var user = _users.GetOrAdd(id, x => new Member(new User(Client, id, this)));
|
||||||
|
if (user.User == newUser)
|
||||||
|
{
|
||||||
|
foreach (var channel in Channels)
|
||||||
|
channel.AddUser(newUser);
|
||||||
|
}
|
||||||
|
return user.User;
|
||||||
|
}
|
||||||
|
internal User RemoveUser(ulong id)
|
||||||
|
{
|
||||||
|
Member member;
|
||||||
|
if (_users.TryRemove(id, out member))
|
||||||
|
{
|
||||||
|
foreach (var channel in Channels)
|
||||||
|
channel.RemoveUser(id);
|
||||||
|
}
|
||||||
|
return member.User;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Gets the user with the provided id and is a member of this server, or null if not found. </summary>
|
||||||
|
public User GetUser(ulong id)
|
||||||
|
{
|
||||||
|
Member result;
|
||||||
|
_users.TryGetValue(id, out result);
|
||||||
|
return result.User;
|
||||||
|
}
|
||||||
|
/// <summary> Gets the user with the provided username and discriminator, that is a member of this server, or null if not found. </summary>
|
||||||
|
public User GetUser(string name, ushort discriminator)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
|
||||||
|
return _users.Select(x => x.Value.User).Find(name, discriminator: discriminator, exactMatch: false).FirstOrDefault();
|
||||||
|
}
|
||||||
|
/// <summary> Returns all members of this server with the specified name. </summary>
|
||||||
|
/// <remarks> Name formats supported: Name, @Name and <@Id>. Search is case-insensitive if exactMatch is false.</remarks>
|
||||||
|
public IEnumerable<User> FindUsers(string name, bool exactMatch = false)
|
||||||
|
{
|
||||||
|
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||||
|
|
||||||
|
return _users.Select(x => x.Value.User).Find(name, exactMatch: exactMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Kicks all users with an inactivity greater or equal to the provided number of days. </summary>
|
||||||
|
/// <param name="simulate">If true, no pruning will actually be done but instead return the number of users that would be pruned. </param>
|
||||||
|
public async Task<int> PruneUsers(int days = 30, bool simulate = false)
|
||||||
|
{
|
||||||
|
if (days <= 0) throw new ArgumentOutOfRangeException(nameof(days));
|
||||||
|
|
||||||
|
var request = new PruneMembersRequest(Id)
|
||||||
|
{
|
||||||
|
Days = days,
|
||||||
|
IsSimulation = simulate
|
||||||
|
};
|
||||||
|
var response = await Client.ClientAPI.Send(request).ConfigureAwait(false);
|
||||||
|
return response.Pruned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>When Config.UseLargeThreshold is enabled, running this command will request the Discord server to provide you with all offline users for this server.</summary>
|
||||||
|
public void RequestOfflineUsers()
|
||||||
|
=> Client.GatewaySocket.SendRequestMembers(Id, "", 0);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is Server && (obj as Server).Id == Id;
|
||||||
public override int GetHashCode() => unchecked(Id.GetHashCode() + 5175);
|
public override int GetHashCode() => unchecked(Id.GetHashCode() + 5175);
|
||||||
public override string ToString() => Name ?? Id.ToIdString();
|
public override string ToString() => Name ?? Id.ToIdString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
using Discord.API.Client;
|
using Discord.API.Client;
|
||||||
|
using Discord.API.Client.Rest;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using APIMember = Discord.API.Client.Member;
|
using APIMember = Discord.API.Client.Member;
|
||||||
|
|
||||||
namespace Discord
|
namespace Discord
|
||||||
{
|
{
|
||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
|
internal static string GetAvatarUrl(ulong userId, string avatarId)
|
||||||
|
=> avatarId != null ? $"{DiscordConfig.CDNUrl}/avatars/{userId}/{avatarId}.jpg" : null;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
private enum VoiceState : byte
|
private enum VoiceState : byte
|
||||||
{
|
{
|
||||||
@@ -34,15 +40,13 @@ namespace Discord
|
|||||||
=> unchecked(ServerId.GetHashCode() + UserId.GetHashCode() + 23);
|
=> unchecked(ServerId.GetHashCode() + UserId.GetHashCode() + 23);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string GetAvatarUrl(ulong userId, string avatarId) => avatarId != null ? $"{DiscordConfig.CDNUrl}/avatars/{userId}/{avatarId}.jpg" : null;
|
|
||||||
|
|
||||||
private VoiceState _voiceState;
|
private VoiceState _voiceState;
|
||||||
private DateTime? _lastOnline;
|
private DateTime? _lastOnline;
|
||||||
private ulong? _voiceChannelId;
|
private ulong? _voiceChannelId;
|
||||||
private Dictionary<ulong, Role> _roles;
|
private Dictionary<ulong, Role> _roles;
|
||||||
|
|
||||||
/// <summary> Gets the client that generated this user object. </summary>
|
|
||||||
internal DiscordClient Client { get; }
|
internal DiscordClient Client { get; }
|
||||||
|
|
||||||
/// <summary> Gets the unique identifier for this user. </summary>
|
/// <summary> Gets the unique identifier for this user. </summary>
|
||||||
public ulong Id { get; }
|
public ulong Id { get; }
|
||||||
/// <summary> Gets the server this user is a member of. </summary>
|
/// <summary> Gets the server this user is a member of. </summary>
|
||||||
@@ -129,9 +133,9 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal User(DiscordClient client, ulong id, Server server)
|
||||||
internal User(ulong id, Server server)
|
|
||||||
{
|
{
|
||||||
|
Client = client;
|
||||||
Server = server;
|
Server = server;
|
||||||
_roles = new Dictionary<ulong, Role>();
|
_roles = new Dictionary<ulong, Role>();
|
||||||
|
|
||||||
@@ -220,51 +224,109 @@ namespace Discord
|
|||||||
|
|
||||||
_voiceChannelId = model.ChannelId; //Allows null
|
_voiceChannelId = model.ChannelId; //Allows null
|
||||||
}
|
}
|
||||||
private void UpdateRoles(IEnumerable<Role> roles)
|
|
||||||
{
|
|
||||||
var newRoles = new Dictionary<ulong, Role>();
|
|
||||||
if (roles != null)
|
|
||||||
{
|
|
||||||
foreach (var r in roles)
|
|
||||||
{
|
|
||||||
if (r != null)
|
|
||||||
newRoles[r.Id] = r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Server != null)
|
|
||||||
{
|
|
||||||
var everyone = Server.EveryoneRole;
|
|
||||||
newRoles[everyone.Id] = everyone;
|
|
||||||
}
|
|
||||||
_roles = newRoles;
|
|
||||||
|
|
||||||
if (Server != null)
|
|
||||||
Server.UpdatePermissions(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateActivity(DateTime? activity = null)
|
internal void UpdateActivity(DateTime? activity = null)
|
||||||
{
|
{
|
||||||
if (LastActivityAt == null || activity > LastActivityAt.Value)
|
if (LastActivityAt == null || activity > LastActivityAt.Value)
|
||||||
LastActivityAt = activity ?? DateTime.UtcNow;
|
LastActivityAt = activity ?? DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerPermissions ServerPermissions => Server.GetPermissions(this);
|
public Task Edit(bool? isMuted = null, bool? isDeafened = null, Channel voiceChannel = null, IEnumerable<Role> roles = null)
|
||||||
|
{
|
||||||
|
if (Server == null) throw new InvalidOperationException("Unable to edit users in a private channel");
|
||||||
|
|
||||||
|
//Modify the roles collection and filter out the everyone role
|
||||||
|
var roleIds = roles == null ? null : Roles.Where(x => !x.IsEveryone).Select(x => x.Id);
|
||||||
|
|
||||||
|
var request = new UpdateMemberRequest(Server.Id, Id)
|
||||||
|
{
|
||||||
|
IsMuted = isMuted ?? IsServerMuted,
|
||||||
|
IsDeafened = isDeafened ?? IsServerDeafened,
|
||||||
|
VoiceChannelId = voiceChannel?.Id,
|
||||||
|
RoleIds = roleIds.ToArray()
|
||||||
|
};
|
||||||
|
return Client.ClientAPI.Send(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Kick()
|
||||||
|
{
|
||||||
|
if (Server == null) throw new InvalidOperationException("Unable to kick users from a private channel");
|
||||||
|
|
||||||
|
var request = new KickMemberRequest(Server.Id, Id);
|
||||||
|
return Client.ClientAPI.Send(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Permissions
|
||||||
|
public ServerPermissions ServerPermissions => Server.GetPermissions(this);
|
||||||
public ChannelPermissions GetPermissions(Channel channel)
|
public ChannelPermissions GetPermissions(Channel channel)
|
||||||
{
|
{
|
||||||
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
if (channel == null) throw new ArgumentNullException(nameof(channel));
|
||||||
|
|
||||||
return channel.GetPermissions(this);
|
return channel.GetPermissions(this);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public bool HasRole(Role role)
|
#region Channels
|
||||||
{
|
public Task<Channel> CreateChannel()
|
||||||
if (role == null) throw new ArgumentNullException(nameof(role));
|
=> Client.CreatePrivateChannel(this);
|
||||||
|
#endregion
|
||||||
return _roles.ContainsKey(role.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj) => obj is User && (obj as User).Id == Id;
|
#region Messages
|
||||||
|
public async Task<Message> SendMessage(string text)
|
||||||
|
{
|
||||||
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
|
|
||||||
|
var channel = await CreateChannel().ConfigureAwait(false);
|
||||||
|
return await channel.SendMessage(text).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
public async Task<Message> SendFile(string filePath)
|
||||||
|
{
|
||||||
|
if (filePath == null) throw new ArgumentNullException(nameof(filePath));
|
||||||
|
|
||||||
|
var channel = await CreateChannel().ConfigureAwait(false);
|
||||||
|
return await channel.SendFile(filePath).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
public async Task<Message> SendFile(string filename, Stream stream)
|
||||||
|
{
|
||||||
|
if (filename == null) throw new ArgumentNullException(nameof(filename));
|
||||||
|
if (stream == null) throw new ArgumentNullException(nameof(stream));
|
||||||
|
|
||||||
|
var channel = await CreateChannel().ConfigureAwait(false);
|
||||||
|
return await channel.SendFile(filename, stream).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Roles
|
||||||
|
private void UpdateRoles(IEnumerable<Role> roles)
|
||||||
|
{
|
||||||
|
var newRoles = new Dictionary<ulong, Role>();
|
||||||
|
if (roles != null)
|
||||||
|
{
|
||||||
|
foreach (var r in roles)
|
||||||
|
{
|
||||||
|
if (r != null)
|
||||||
|
newRoles[r.Id] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Server != null)
|
||||||
|
{
|
||||||
|
var everyone = Server.EveryoneRole;
|
||||||
|
newRoles[everyone.Id] = everyone;
|
||||||
|
}
|
||||||
|
_roles = newRoles;
|
||||||
|
|
||||||
|
if (Server != null)
|
||||||
|
Server.UpdatePermissions(this);
|
||||||
|
}
|
||||||
|
public bool HasRole(Role role)
|
||||||
|
{
|
||||||
|
if (role == null) throw new ArgumentNullException(nameof(role));
|
||||||
|
|
||||||
|
return _roles.ContainsKey(role.Id);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => obj is User && (obj as User).Id == Id;
|
||||||
public override int GetHashCode() => unchecked(Id.GetHashCode() + 7230);
|
public override int GetHashCode() => unchecked(Id.GetHashCode() + 7230);
|
||||||
public override string ToString() => Name != null ? $"{Name}#{Discriminator}" : Id.ToIdString();
|
public override string ToString() => Name != null ? $"{Name}#{Discriminator}" : Id.ToIdString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Discord.Net.WebSockets
|
|||||||
private int _lastSequence;
|
private int _lastSequence;
|
||||||
private string _sessionId;
|
private string _sessionId;
|
||||||
|
|
||||||
public string Token { get; private set; }
|
public string Token { get; internal set; }
|
||||||
|
|
||||||
public GatewaySocket(DiscordClient client, JsonSerializer serializer, Logger logger)
|
public GatewaySocket(DiscordClient client, JsonSerializer serializer, Logger logger)
|
||||||
: base(client, serializer, logger)
|
: base(client, serializer, logger)
|
||||||
@@ -26,11 +26,10 @@ namespace Discord.Net.WebSockets
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Connect(string token)
|
public async Task Connect()
|
||||||
{
|
{
|
||||||
Token = token;
|
|
||||||
await BeginConnect().ConfigureAwait(false);
|
await BeginConnect().ConfigureAwait(false);
|
||||||
SendIdentify(token);
|
SendIdentify(Token);
|
||||||
}
|
}
|
||||||
private async Task Redirect()
|
private async Task Redirect()
|
||||||
{
|
{
|
||||||
@@ -47,7 +46,7 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Connect(Token).ConfigureAwait(false);
|
await Connect().ConfigureAwait(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) { throw; }
|
catch (OperationCanceledException) { throw; }
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Discord
|
|
||||||
{
|
|
||||||
/*public struct Optional<T>
|
|
||||||
{
|
|
||||||
public bool HasValue { get; }
|
|
||||||
public T Value { get; }
|
|
||||||
|
|
||||||
public Optional(T value)
|
|
||||||
{
|
|
||||||
HasValue = true;
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static implicit operator Optional<T>(T value) => new Optional<T>(value);
|
|
||||||
public static bool operator ==(Optional<T> a, Optional<T> b) =>
|
|
||||||
a.HasValue == b.HasValue && EqualityComparer<T>.Default.Equals(a.Value, b.Value);
|
|
||||||
public static bool operator !=(Optional<T> a, Optional<T> b) =>
|
|
||||||
a.HasValue != b.HasValue || EqualityComparer<T>.Default.Equals(a.Value, b.Value);
|
|
||||||
public override bool Equals(object obj) =>
|
|
||||||
this == ((Optional<T>)obj);
|
|
||||||
public override int GetHashCode() =>
|
|
||||||
unchecked(HasValue.GetHashCode() + Value?.GetHashCode() ?? 0);
|
|
||||||
|
|
||||||
public override string ToString() => Value?.ToString() ?? "null";
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
12
src/Discord.Net/RelativeDirection.cs
Normal file
12
src/Discord.Net/RelativeDirection.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public enum RelativeDirection
|
||||||
|
{
|
||||||
|
Before, After
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Discord.Net/ServiceManager.cs
Normal file
39
src/Discord.Net/ServiceManager.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Discord
|
||||||
|
{
|
||||||
|
public class ServiceManager
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Type, IService> _services;
|
||||||
|
|
||||||
|
internal DiscordClient Client { get; }
|
||||||
|
|
||||||
|
internal ServiceManager(DiscordClient client)
|
||||||
|
{
|
||||||
|
Client = client;
|
||||||
|
_services = new Dictionary<Type, IService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add<T>(T service)
|
||||||
|
where T : class, IService
|
||||||
|
{
|
||||||
|
_services.Add(typeof(T), service);
|
||||||
|
service.Install(Client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Get<T>(bool isRequired = true)
|
||||||
|
where T : class, IService
|
||||||
|
{
|
||||||
|
IService service;
|
||||||
|
T singletonT = null;
|
||||||
|
|
||||||
|
if (_services.TryGetValue(typeof(T), out service))
|
||||||
|
singletonT = service as T;
|
||||||
|
|
||||||
|
if (singletonT == null && isRequired)
|
||||||
|
throw new InvalidOperationException($"This operation requires {typeof(T).Name} to be added to {nameof(DiscordClient)}.");
|
||||||
|
return singletonT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user