Merge branch 'concrete2' into dev

This commit is contained in:
RogueException
2016-10-12 02:07:54 -03:00
522 changed files with 11661 additions and 7895 deletions

View File

@@ -3,7 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 14
VisualStudioVersion = 14.0.25420.1 VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7F3E124-93C7-4846-AE87-9CE12BD82859}"
ProjectSection(SolutionItems) = preProject
global.json = global.json
README.md = README.md
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net", "src\Discord.Net\Discord.Net.xproj", "{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.xproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Impls", "Impls", "{288C363D-A636-4EAE-9AC1-4698B641B26E}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Rest", "src\Discord.Net.Rest\Discord.Net.Rest.xproj", "{BFC6DC28-0351-4573-926A-D4124244C04F}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.xproj", "{22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Rpc", "src\Discord.Net.Rpc\Discord.Net.Rpc.xproj", "{5688A353-121E-40A1-8BFA-B17B91FB48FB}"
EndProject EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}"
EndProject EndProject
@@ -13,10 +29,26 @@ Global
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.Build.0 = Debug|Any CPU
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Release|Any CPU
{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Release|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU
{BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.Build.0 = Release|Any CPU
{22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D}.Release|Any CPU.Build.0 = Release|Any CPU
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.Build.0 = Release|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -25,4 +57,9 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
{22AB6C66-536C-4AC2-BBDB-A8BC4EB6B14D} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
{5688A353-121E-40A1-8BFA-B17B91FB48FB} = {288C363D-A636-4EAE-9AC1-4698B641B26E}
EndGlobalSection
EndGlobal EndGlobal

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Discord.Net.Tests")]

View File

@@ -6,6 +6,7 @@ namespace Discord.Commands
public class CommandAttribute : Attribute public class CommandAttribute : Attribute
{ {
public string Text { get; } public string Text { get; }
public RunMode RunMode { get; set; } = RunMode.Sync;
public CommandAttribute() public CommandAttribute()
{ {

View File

@@ -0,0 +1,9 @@
using System;
namespace Discord.Commands
{
[AttributeUsage(AttributeTargets.Class)]
public class DontAutoLoadAttribute : Attribute
{
}
}

View File

@@ -1,22 +0,0 @@
using System;
namespace Discord.Commands
{
[AttributeUsage(AttributeTargets.Class)]
public class ModuleAttribute : Attribute
{
public string Prefix { get; }
public bool AutoLoad { get; set; }
public ModuleAttribute()
{
Prefix = null;
AutoLoad = true;
}
public ModuleAttribute(string prefix)
{
Prefix = prefix;
AutoLoad = true;
}
}
}

View File

@@ -6,6 +6,6 @@ namespace Discord.Commands
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public abstract class PreconditionAttribute : Attribute public abstract class PreconditionAttribute : Attribute
{ {
public abstract Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance); public abstract Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map);
} }
} }

View File

@@ -21,7 +21,7 @@ namespace Discord.Commands
Contexts = contexts; Contexts = contexts;
} }
public override Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance) public override Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map)
{ {
bool isValid = false; bool isValid = false;

View File

@@ -20,9 +20,9 @@ namespace Discord.Commands
GuildPermission = null; GuildPermission = null;
} }
public override Task<PreconditionResult> CheckPermissions(IUserMessage context, Command executingCommand, object moduleInstance) public override Task<PreconditionResult> CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map)
{ {
var guildUser = context.Author as IGuildUser; var guildUser = context.User as IGuildUser;
if (GuildPermission.HasValue) if (GuildPermission.HasValue)
{ {

View File

@@ -0,0 +1,18 @@
namespace Discord.Commands
{
public struct CommandContext
{
public IGuild Guild { get; }
public IMessageChannel Channel { get; }
public IUser User { get; }
public IUserMessage Message { get; }
public CommandContext(IGuild guild, IMessageChannel channel, IUser user, IUserMessage msg)
{
Guild = guild;
Channel = channel;
User = user;
Message = msg;
}
}
}

View File

@@ -10,38 +10,38 @@ using System.Threading.Tasks;
namespace Discord.Commands namespace Discord.Commands
{ {
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class Command public class CommandInfo
{ {
private static readonly MethodInfo _convertParamsMethod = typeof(Command).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList)); private static readonly MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList));
private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> _arrayConverters = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>(); private static readonly ConcurrentDictionary<Type, Func<IEnumerable<object>, object>> _arrayConverters = new ConcurrentDictionary<Type, Func<IEnumerable<object>, object>>();
private readonly object _instance; private readonly Func<CommandContext, object[], Task> _action;
private readonly Func<IUserMessage, IReadOnlyList<object>, Task> _action;
public MethodInfo Source { get; } public MethodInfo Source { get; }
public Module Module { get; } public ModuleInfo Module { get; }
public string Name { get; } public string Name { get; }
public string Summary { get; } public string Summary { get; }
public string Remarks { get; } public string Remarks { get; }
public string Text { get; } public string Text { get; }
public int Priority { get; } public int Priority { get; }
public bool HasVarArgs { get; } public bool HasVarArgs { get; }
public RunMode RunMode { get; }
public IReadOnlyList<string> Aliases { get; } public IReadOnlyList<string> Aliases { get; }
public IReadOnlyList<CommandParameter> Parameters { get; } public IReadOnlyList<CommandParameter> Parameters { get; }
public IReadOnlyList<PreconditionAttribute> Preconditions { get; } public IReadOnlyList<PreconditionAttribute> Preconditions { get; }
internal Command(MethodInfo source, Module module, object instance, CommandAttribute attribute, string groupPrefix) internal CommandInfo(MethodInfo source, ModuleInfo module, CommandAttribute attribute, string groupPrefix)
{ {
try try
{ {
Source = source; Source = source;
Module = module; Module = module;
_instance = instance;
Name = source.Name; Name = source.Name;
if (attribute.Text == null) if (attribute.Text == null)
Text = groupPrefix; Text = groupPrefix;
RunMode = attribute.RunMode;
if (groupPrefix != "") if (groupPrefix != "")
groupPrefix += " "; groupPrefix += " ";
@@ -85,18 +85,18 @@ namespace Discord.Commands
} }
} }
public async Task<PreconditionResult> CheckPreconditions(IUserMessage context) public async Task<PreconditionResult> CheckPreconditions(CommandContext context, IDependencyMap map = null)
{ {
foreach (PreconditionAttribute precondition in Module.Preconditions) foreach (PreconditionAttribute precondition in Module.Preconditions)
{ {
var result = await precondition.CheckPermissions(context, this, Module.Instance).ConfigureAwait(false); var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
return result; return result;
} }
foreach (PreconditionAttribute precondition in Preconditions) foreach (PreconditionAttribute precondition in Preconditions)
{ {
var result = await precondition.CheckPermissions(context, this, Module.Instance).ConfigureAwait(false); var result = await precondition.CheckPermissions(context, this, map).ConfigureAwait(false);
if (!result.IsSuccess) if (!result.IsSuccess)
return result; return result;
} }
@@ -104,7 +104,7 @@ namespace Discord.Commands
return PreconditionResult.FromSuccess(); return PreconditionResult.FromSuccess();
} }
public async Task<ParseResult> Parse(IUserMessage context, SearchResult searchResult, PreconditionResult? preconditionResult = null) public async Task<ParseResult> Parse(CommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null)
{ {
if (!searchResult.IsSuccess) if (!searchResult.IsSuccess)
return ParseResult.FromError(searchResult); return ParseResult.FromError(searchResult);
@@ -125,7 +125,7 @@ namespace Discord.Commands
return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false); return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false);
} }
public Task<ExecuteResult> Execute(IUserMessage context, ParseResult parseResult) public Task<ExecuteResult> Execute(CommandContext context, ParseResult parseResult)
{ {
if (!parseResult.IsSuccess) if (!parseResult.IsSuccess)
return Task.FromResult(ExecuteResult.FromError(parseResult)); return Task.FromResult(ExecuteResult.FromError(parseResult));
@@ -148,11 +148,23 @@ namespace Discord.Commands
return Execute(context, argList, paramList); return Execute(context, argList, paramList);
} }
public async Task<ExecuteResult> Execute(IUserMessage context, IEnumerable<object> argList, IEnumerable<object> paramList) public async Task<ExecuteResult> Execute(CommandContext context, IEnumerable<object> argList, IEnumerable<object> paramList)
{ {
try try
{ {
await _action.Invoke(context, GenerateArgs(argList, paramList)).ConfigureAwait(false);//Note: This code may need context var args = GenerateArgs(argList, paramList);
switch (RunMode)
{
case RunMode.Sync: //Always sync
await _action(context, args).ConfigureAwait(false);
break;
case RunMode.Mixed: //Sync until first await statement
var t1 = _action(context, args);
break;
case RunMode.Async: //Always async
var t2 = Task.Run(() => _action(context, args));
break;
}
return ExecuteResult.FromSuccess(); return ExecuteResult.FromSuccess();
} }
catch (Exception ex) catch (Exception ex)
@@ -169,11 +181,9 @@ namespace Discord.Commands
private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo) private IReadOnlyList<CommandParameter> BuildParameters(MethodInfo methodInfo)
{ {
var parameters = methodInfo.GetParameters(); var parameters = methodInfo.GetParameters();
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(IUserMessage))
throw new InvalidOperationException($"The first parameter of a command must be {nameof(IUserMessage)}.");
var paramBuilder = ImmutableArray.CreateBuilder<CommandParameter>(parameters.Length - 1); var paramBuilder = ImmutableArray.CreateBuilder<CommandParameter>(parameters.Length);
for (int i = 1; i < parameters.Length; i++) for (int i = 0; i < parameters.Length; i++)
{ {
var parameter = parameters[i]; var parameter = parameters[i];
var type = parameter.ParameterType; var type = parameter.ParameterType;
@@ -209,19 +219,23 @@ namespace Discord.Commands
} }
return paramBuilder.ToImmutable(); return paramBuilder.ToImmutable();
} }
private Func<IUserMessage, IReadOnlyList<object>, Task> BuildAction(MethodInfo methodInfo) private Func<CommandContext, object[], Task> BuildAction(MethodInfo methodInfo)
{ {
if (methodInfo.ReturnType != typeof(Task)) if (methodInfo.ReturnType != typeof(Task))
throw new InvalidOperationException("Commands must return a non-generic Task."); throw new InvalidOperationException("Commands must return a non-generic Task.");
return (msg, args) => return (context, args) =>
{ {
object[] newArgs = new object[args.Count + 1]; var instance = Module.CreateInstance();
newArgs[0] = msg; instance.Context = context;
for (int i = 0; i < args.Count; i++) try
newArgs[i + 1] = args[i]; {
var result = methodInfo.Invoke(_instance, newArgs); return methodInfo.Invoke(instance, args) as Task ?? Task.CompletedTask;
return result as Task ?? Task.CompletedTask; }
finally
{
(instance as IDisposable)?.Dispose();
}
}; };
} }

View File

@@ -32,7 +32,7 @@ namespace Discord.Commands
DefaultValue = defaultValue; DefaultValue = defaultValue;
} }
public async Task<TypeReaderResult> Parse(IUserMessage context, string input) public async Task<TypeReaderResult> Parse(CommandContext context, string input)
{ {
return await _reader.Read(context, input).ConfigureAwait(false); return await _reader.Read(context, input).ConfigureAwait(false);
} }

View File

@@ -13,7 +13,7 @@ namespace Discord.Commands
QuotedParameter QuotedParameter
} }
public static async Task<ParseResult> ParseArgs(Command command, IUserMessage context, string input, int startPos) public static async Task<ParseResult> ParseArgs(CommandInfo command, CommandContext context, string input, int startPos)
{ {
CommandParameter curParam = null; CommandParameter curParam = null;
StringBuilder argBuilder = new StringBuilder(input.Length); StringBuilder argBuilder = new StringBuilder(input.Length);

View File

@@ -11,21 +11,25 @@ namespace Discord.Commands
{ {
public class CommandService public class CommandService
{ {
private static readonly TypeInfo _moduleTypeInfo = typeof(ModuleBase).GetTypeInfo();
private readonly SemaphoreSlim _moduleLock; private readonly SemaphoreSlim _moduleLock;
private readonly ConcurrentDictionary<Type, Module> _modules; private readonly ConcurrentDictionary<Type, ModuleInfo> _moduleDefs;
private readonly ConcurrentDictionary<Type, TypeReader> _typeReaders; private readonly ConcurrentDictionary<Type, TypeReader> _typeReaders;
private readonly CommandMap _map; private readonly CommandMap _map;
public IEnumerable<Module> Modules => _modules.Select(x => x.Value); public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x.Value);
public IEnumerable<Command> Commands => _modules.SelectMany(x => x.Value.Commands); public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Value.Commands);
public CommandService() public CommandService()
{ {
_moduleLock = new SemaphoreSlim(1, 1); _moduleLock = new SemaphoreSlim(1, 1);
_modules = new ConcurrentDictionary<Type, Module>(); _moduleDefs = new ConcurrentDictionary<Type, ModuleInfo>();
_map = new CommandMap(); _map = new CommandMap();
_typeReaders = new ConcurrentDictionary<Type, TypeReader> _typeReaders = new ConcurrentDictionary<Type, TypeReader>
{ {
[typeof(bool)] = new SimpleTypeReader<bool>(),
[typeof(char)] = new SimpleTypeReader<char>(),
[typeof(string)] = new SimpleTypeReader<string>(), [typeof(string)] = new SimpleTypeReader<string>(),
[typeof(byte)] = new SimpleTypeReader<byte>(), [typeof(byte)] = new SimpleTypeReader<byte>(),
[typeof(sbyte)] = new SimpleTypeReader<sbyte>(), [typeof(sbyte)] = new SimpleTypeReader<sbyte>(),
@@ -43,7 +47,6 @@ namespace Discord.Commands
[typeof(IMessage)] = new MessageTypeReader<IMessage>(), [typeof(IMessage)] = new MessageTypeReader<IMessage>(),
[typeof(IUserMessage)] = new MessageTypeReader<IUserMessage>(), [typeof(IUserMessage)] = new MessageTypeReader<IUserMessage>(),
//[typeof(ISystemMessage)] = new MessageTypeReader<ISystemMessage>(),
[typeof(IChannel)] = new ChannelTypeReader<IChannel>(), [typeof(IChannel)] = new ChannelTypeReader<IChannel>(),
[typeof(IDMChannel)] = new ChannelTypeReader<IDMChannel>(), [typeof(IDMChannel)] = new ChannelTypeReader<IDMChannel>(),
[typeof(IGroupChannel)] = new ChannelTypeReader<IGroupChannel>(), [typeof(IGroupChannel)] = new ChannelTypeReader<IGroupChannel>(),
@@ -53,19 +56,109 @@ namespace Discord.Commands
[typeof(ITextChannel)] = new ChannelTypeReader<ITextChannel>(), [typeof(ITextChannel)] = new ChannelTypeReader<ITextChannel>(),
[typeof(IVoiceChannel)] = new ChannelTypeReader<IVoiceChannel>(), [typeof(IVoiceChannel)] = new ChannelTypeReader<IVoiceChannel>(),
//[typeof(IGuild)] = new GuildTypeReader<IGuild>(),
[typeof(IRole)] = new RoleTypeReader<IRole>(), [typeof(IRole)] = new RoleTypeReader<IRole>(),
//[typeof(IInvite)] = new InviteTypeReader<IInvite>(),
//[typeof(IInviteMetadata)] = new InviteTypeReader<IInviteMetadata>(),
[typeof(IUser)] = new UserTypeReader<IUser>(), [typeof(IUser)] = new UserTypeReader<IUser>(),
[typeof(IGroupUser)] = new UserTypeReader<IGroupUser>(), [typeof(IGroupUser)] = new UserTypeReader<IGroupUser>(),
[typeof(IGuildUser)] = new UserTypeReader<IGuildUser>(), [typeof(IGuildUser)] = new UserTypeReader<IGuildUser>(),
}; };
} }
//Modules
public async Task<ModuleInfo> AddModule<T>(IDependencyMap dependencyMap = null)
{
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
if (_moduleDefs.ContainsKey(typeof(T)))
throw new ArgumentException($"This module has already been added.");
var typeInfo = typeof(T).GetTypeInfo();
if (!_moduleTypeInfo.IsAssignableFrom(typeInfo))
throw new ArgumentException($"Modules must inherit ModuleBase.");
return AddModuleInternal(typeInfo, dependencyMap);
}
finally
{
_moduleLock.Release();
}
}
public async Task<IEnumerable<ModuleInfo>> AddModules(Assembly assembly, IDependencyMap dependencyMap = null)
{
var moduleDefs = ImmutableArray.CreateBuilder<ModuleInfo>();
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
foreach (var type in assembly.ExportedTypes)
{
if (!_moduleDefs.ContainsKey(type))
{
var typeInfo = type.GetTypeInfo();
if (_moduleTypeInfo.IsAssignableFrom(typeInfo))
{
var dontAutoLoad = typeInfo.GetCustomAttribute<DontAutoLoadAttribute>();
if (dontAutoLoad == null)
moduleDefs.Add(AddModuleInternal(typeInfo, dependencyMap));
}
}
}
return moduleDefs.ToImmutable();
}
finally
{
_moduleLock.Release();
}
}
private ModuleInfo AddModuleInternal(TypeInfo typeInfo, IDependencyMap dependencyMap)
{
var moduleDef = new ModuleInfo(typeInfo, this, dependencyMap);
_moduleDefs[typeInfo.BaseType] = moduleDef;
foreach (var cmd in moduleDef.Commands)
_map.AddCommand(cmd);
return moduleDef;
}
public async Task<bool> RemoveModule(ModuleInfo module)
{
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
return RemoveModuleInternal(module.Source.BaseType);
}
finally
{
_moduleLock.Release();
}
}
public async Task<bool> RemoveModule<T>()
{
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
return RemoveModuleInternal(typeof(T));
}
finally
{
_moduleLock.Release();
}
}
private bool RemoveModuleInternal(Type type)
{
ModuleInfo unloadedModule;
if (_moduleDefs.TryRemove(type, out unloadedModule))
{
foreach (var cmd in unloadedModule.Commands)
_map.RemoveCommand(cmd);
return true;
}
else
return false;
}
//Type Readers
public void AddTypeReader<T>(TypeReader reader) public void AddTypeReader<T>(TypeReader reader)
{ {
_typeReaders[typeof(T)] = reader; _typeReaders[typeof(T)] = reader;
@@ -82,102 +175,9 @@ namespace Discord.Commands
return null; return null;
} }
public async Task<Module> Load(object moduleInstance) //Execution
{ public SearchResult Search(CommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos));
await _moduleLock.WaitAsync().ConfigureAwait(false); public SearchResult Search(CommandContext context, string input)
try
{
if (_modules.ContainsKey(moduleInstance.GetType()))
throw new ArgumentException($"This module has already been loaded.");
var typeInfo = moduleInstance.GetType().GetTypeInfo();
var moduleAttr = typeInfo.GetCustomAttribute<ModuleAttribute>();
if (moduleAttr == null)
throw new ArgumentException($"Modules must be marked with ModuleAttribute.");
return LoadInternal(moduleInstance, moduleAttr, typeInfo, null);
}
finally
{
_moduleLock.Release();
}
}
private Module LoadInternal(object moduleInstance, ModuleAttribute moduleAttr, TypeInfo typeInfo, IDependencyMap dependencyMap)
{
if (_modules.ContainsKey(moduleInstance.GetType()))
return _modules[moduleInstance.GetType()];
var loadedModule = new Module(typeInfo, this, moduleInstance, moduleAttr, dependencyMap);
_modules[moduleInstance.GetType()] = loadedModule;
foreach (var cmd in loadedModule.Commands)
_map.AddCommand(cmd);
return loadedModule;
}
public async Task<IEnumerable<Module>> LoadAssembly(Assembly assembly, IDependencyMap dependencyMap = null)
{
var modules = ImmutableArray.CreateBuilder<Module>();
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
foreach (var type in assembly.ExportedTypes)
{
var typeInfo = type.GetTypeInfo();
var moduleAttr = typeInfo.GetCustomAttribute<ModuleAttribute>();
if (moduleAttr != null && moduleAttr.AutoLoad)
{
var moduleInstance = ReflectionUtils.CreateObject(typeInfo, this, dependencyMap);
modules.Add(LoadInternal(moduleInstance, moduleAttr, typeInfo, dependencyMap));
}
}
return modules.ToImmutable();
}
finally
{
_moduleLock.Release();
}
}
public async Task<bool> Unload(Module module)
{
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
return UnloadInternal(module.Instance);
}
finally
{
_moduleLock.Release();
}
}
public async Task<bool> Unload(object moduleInstance)
{
await _moduleLock.WaitAsync().ConfigureAwait(false);
try
{
return UnloadInternal(moduleInstance);
}
finally
{
_moduleLock.Release();
}
}
private bool UnloadInternal(object module)
{
Module unloadedModule;
if (_modules.TryRemove(module.GetType(), out unloadedModule))
{
foreach (var cmd in unloadedModule.Commands)
_map.RemoveCommand(cmd);
return true;
}
else
return false;
}
public SearchResult Search(IUserMessage message, int argPos) => Search(message, message.Content.Substring(argPos));
public SearchResult Search(IUserMessage message, string input)
{ {
string lowerInput = input.ToLowerInvariant(); string lowerInput = input.ToLowerInvariant();
var matches = _map.GetCommands(input).OrderByDescending(x => x.Priority).ToImmutableArray(); var matches = _map.GetCommands(input).OrderByDescending(x => x.Priority).ToImmutableArray();
@@ -188,18 +188,18 @@ namespace Discord.Commands
return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command.");
} }
public Task<IResult> Execute(IUserMessage message, int argPos, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) public Task<IResult> Execute(CommandContext context, int argPos, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
=> Execute(message, message.Content.Substring(argPos), multiMatchHandling); => Execute(context, context.Message.Content.Substring(argPos), multiMatchHandling);
public async Task<IResult> Execute(IUserMessage message, string input, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) public async Task<IResult> Execute(CommandContext context, string input, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
{ {
var searchResult = Search(message, input); var searchResult = Search(context, input);
if (!searchResult.IsSuccess) if (!searchResult.IsSuccess)
return searchResult; return searchResult;
var commands = searchResult.Commands; var commands = searchResult.Commands;
for (int i = commands.Count - 1; i >= 0; i--) for (int i = commands.Count - 1; i >= 0; i--)
{ {
var preconditionResult = await commands[i].CheckPreconditions(message); var preconditionResult = await commands[i].CheckPreconditions(context).ConfigureAwait(false);
if (!preconditionResult.IsSuccess) if (!preconditionResult.IsSuccess)
{ {
if (commands.Count == 1) if (commands.Count == 1)
@@ -208,17 +208,17 @@ namespace Discord.Commands
continue; continue;
} }
var parseResult = await commands[i].Parse(message, searchResult, preconditionResult); var parseResult = await commands[i].Parse(context, searchResult, preconditionResult).ConfigureAwait(false);
if (!parseResult.IsSuccess) if (!parseResult.IsSuccess)
{ {
if (parseResult.Error == CommandError.MultipleMatches) if (parseResult.Error == CommandError.MultipleMatches)
{ {
TypeReaderValue[] argList, paramList; IReadOnlyList<TypeReaderValue> argList, paramList;
switch (multiMatchHandling) switch (multiMatchHandling)
{ {
case MultiMatchHandling.Best: case MultiMatchHandling.Best:
argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToArray(); argList = parseResult.ArgValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray();
paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToArray(); paramList = parseResult.ParamValues.Select(x => x.Values.OrderByDescending(y => y.Score).First()).ToImmutableArray();
parseResult = ParseResult.FromSuccess(argList, paramList); parseResult = ParseResult.FromSuccess(argList, paramList);
break; break;
} }
@@ -233,7 +233,7 @@ namespace Discord.Commands
} }
} }
return await commands[i].Execute(message, parseResult); return await commands[i].Execute(context, parseResult).ConfigureAwait(false);
} }
return SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload."); return SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.");

View File

@@ -32,7 +32,7 @@
if (text.Length < endPos + 2 || text[endPos + 1] != ' ') return false; //Must end in "> " if (text.Length < endPos + 2 || text[endPos + 1] != ' ') return false; //Must end in "> "
ulong userId; ulong userId;
if (!MentionUtils.TryParseUser(text.Substring(0, endPos + 2), out userId)) return false; if (!MentionUtils.TryParseUser(text.Substring(0, endPos + 1), out userId)) return false;
if (userId == user.Id) if (userId == user.Id)
{ {
argPos = endPos + 2; argPos = endPos + 2;

View File

@@ -16,7 +16,7 @@ namespace Discord.Commands
_nodes = new ConcurrentDictionary<string, CommandMapNode>(); _nodes = new ConcurrentDictionary<string, CommandMapNode>();
} }
public void AddCommand(Command command) public void AddCommand(CommandInfo command)
{ {
foreach (string text in command.Aliases) foreach (string text in command.Aliases)
{ {
@@ -35,7 +35,7 @@ namespace Discord.Commands
} }
} }
} }
public void RemoveCommand(Command command) public void RemoveCommand(CommandInfo command)
{ {
foreach (string text in command.Aliases) foreach (string text in command.Aliases)
{ {
@@ -60,7 +60,7 @@ namespace Discord.Commands
} }
} }
public IEnumerable<Command> GetCommands(string text) public IEnumerable<CommandInfo> GetCommands(string text)
{ {
int nextSpace = NextWhitespace(text); int nextSpace = NextWhitespace(text);
string name; string name;
@@ -76,7 +76,7 @@ namespace Discord.Commands
if (_nodes.TryGetValue(name, out nextNode)) if (_nodes.TryGetValue(name, out nextNode))
return nextNode.GetCommands(text, nextSpace + 1); return nextNode.GetCommands(text, nextSpace + 1);
else else
return Enumerable.Empty<Command>(); return Enumerable.Empty<CommandInfo>();
} }
} }

View File

@@ -9,7 +9,7 @@ namespace Discord.Commands
private readonly ConcurrentDictionary<string, CommandMapNode> _nodes; private readonly ConcurrentDictionary<string, CommandMapNode> _nodes;
private readonly string _name; private readonly string _name;
private readonly object _lockObj = new object(); private readonly object _lockObj = new object();
private ImmutableArray<Command> _commands; private ImmutableArray<CommandInfo> _commands;
public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0; public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0;
@@ -17,10 +17,10 @@ namespace Discord.Commands
{ {
_name = name; _name = name;
_nodes = new ConcurrentDictionary<string, CommandMapNode>(); _nodes = new ConcurrentDictionary<string, CommandMapNode>();
_commands = ImmutableArray.Create<Command>(); _commands = ImmutableArray.Create<CommandInfo>();
} }
public void AddCommand(string text, int index, Command command) public void AddCommand(string text, int index, CommandInfo command)
{ {
int nextSpace = text.IndexOf(' ', index); int nextSpace = text.IndexOf(' ', index);
string name; string name;
@@ -41,7 +41,7 @@ namespace Discord.Commands
} }
} }
} }
public void RemoveCommand(string text, int index, Command command) public void RemoveCommand(string text, int index, CommandInfo command)
{ {
int nextSpace = text.IndexOf(' ', index); int nextSpace = text.IndexOf(' ', index);
string name; string name;
@@ -68,7 +68,7 @@ namespace Discord.Commands
} }
} }
public IEnumerable<Command> GetCommands(string text, int index) public IEnumerable<CommandInfo> GetCommands(string text, int index)
{ {
int nextSpace = text.IndexOf(' ', index); int nextSpace = text.IndexOf(' ', index);
string name; string name;

View File

@@ -0,0 +1,15 @@
using System.Threading.Tasks;
namespace Discord.Commands
{
public abstract class ModuleBase
{
public IDiscordClient Client { get; internal set; }
public CommandContext Context { get; internal set; }
protected virtual async Task ReplyAsync(string message, bool isTTS = false, RequestOptions options = null)
{
await Context.Channel.SendMessageAsync(message, isTTS, options).ConfigureAwait(false);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
@@ -6,26 +7,31 @@ using System.Reflection;
namespace Discord.Commands namespace Discord.Commands
{ {
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class Module public class ModuleInfo
{ {
internal readonly Func<ModuleBase> _builder;
public TypeInfo Source { get; } public TypeInfo Source { get; }
public CommandService Service { get; } public CommandService Service { get; }
public string Name { get; } public string Name { get; }
public string Prefix { get; } public string Prefix { get; }
public string Summary { get; } public string Summary { get; }
public string Remarks { get; } public string Remarks { get; }
public IEnumerable<Command> Commands { get; } public IEnumerable<CommandInfo> Commands { get; }
internal object Instance { get; }
public IReadOnlyList<PreconditionAttribute> Preconditions { get; } public IReadOnlyList<PreconditionAttribute> Preconditions { get; }
internal Module(TypeInfo source, CommandService service, object instance, ModuleAttribute moduleAttr, IDependencyMap dependencyMap) internal ModuleInfo(TypeInfo source, CommandService service, IDependencyMap dependencyMap)
{ {
Source = source; Source = source;
Service = service; Service = service;
Name = source.Name; Name = source.Name;
Prefix = moduleAttr.Prefix ?? ""; _builder = ReflectionUtils.CreateBuilder<ModuleBase>(source, Service, dependencyMap);
Instance = instance;
var groupAttr = source.GetCustomAttribute<GroupAttribute>();
if (groupAttr != null)
Prefix = groupAttr.Prefix;
else
Prefix = "";
var nameAttr = source.GetCustomAttribute<NameAttribute>(); var nameAttr = source.GetCustomAttribute<NameAttribute>();
if (nameAttr != null) if (nameAttr != null)
@@ -39,20 +45,19 @@ namespace Discord.Commands
if (remarksAttr != null) if (remarksAttr != null)
Remarks = remarksAttr.Text; Remarks = remarksAttr.Text;
List<Command> commands = new List<Command>(); List<CommandInfo> commands = new List<CommandInfo>();
SearchClass(source, instance, commands, Prefix, dependencyMap); SearchClass(source, commands, Prefix, dependencyMap);
Commands = commands; Commands = commands;
Preconditions = BuildPreconditions(); Preconditions = Source.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray();
} }
private void SearchClass(TypeInfo parentType, List<CommandInfo> commands, string groupPrefix, IDependencyMap dependencyMap)
private void SearchClass(TypeInfo parentType, object instance, List<Command> commands, string groupPrefix, IDependencyMap dependencyMap)
{ {
foreach (var method in parentType.DeclaredMethods) foreach (var method in parentType.DeclaredMethods)
{ {
var cmdAttr = method.GetCustomAttribute<CommandAttribute>(); var cmdAttr = method.GetCustomAttribute<CommandAttribute>();
if (cmdAttr != null) if (cmdAttr != null)
commands.Add(new Command(method, this, instance, cmdAttr, groupPrefix)); commands.Add(new CommandInfo(method, this, cmdAttr, groupPrefix));
} }
foreach (var type in parentType.DeclaredNestedTypes) foreach (var type in parentType.DeclaredNestedTypes)
{ {
@@ -66,15 +71,13 @@ namespace Discord.Commands
else else
nextGroupPrefix = groupAttrib.Prefix ?? type.Name.ToLowerInvariant(); nextGroupPrefix = groupAttrib.Prefix ?? type.Name.ToLowerInvariant();
SearchClass(type, ReflectionUtils.CreateObject(type, Service, dependencyMap), commands, nextGroupPrefix, dependencyMap); SearchClass(type, commands, nextGroupPrefix, dependencyMap);
} }
} }
} }
private IReadOnlyList<PreconditionAttribute> BuildPreconditions() internal ModuleBase CreateInstance()
{ => _builder();
return Source.GetCustomAttributes<PreconditionAttribute>().ToImmutableArray();
}
public override string ToString() => Name; public override string ToString() => Name;
private string DebuggerDisplay => Name; private string DebuggerDisplay => Name;

View File

@@ -13,7 +13,7 @@ namespace Discord.Commands
static PrimitiveParsers() static PrimitiveParsers()
{ {
var parserBuilder = ImmutableDictionary.CreateBuilder<Type, Delegate>(); var parserBuilder = ImmutableDictionary.CreateBuilder<Type, Delegate>();
parserBuilder[typeof(string)] = (TryParseDelegate<string>)delegate(string str, out string value) { value = str; return true; }; parserBuilder[typeof(bool)] = (TryParseDelegate<bool>)bool.TryParse;
parserBuilder[typeof(sbyte)] = (TryParseDelegate<sbyte>)sbyte.TryParse; parserBuilder[typeof(sbyte)] = (TryParseDelegate<sbyte>)sbyte.TryParse;
parserBuilder[typeof(byte)] = (TryParseDelegate<byte>)byte.TryParse; parserBuilder[typeof(byte)] = (TryParseDelegate<byte>)byte.TryParse;
parserBuilder[typeof(short)] = (TryParseDelegate<short>)short.TryParse; parserBuilder[typeof(short)] = (TryParseDelegate<short>)short.TryParse;
@@ -27,6 +27,12 @@ namespace Discord.Commands
parserBuilder[typeof(decimal)] = (TryParseDelegate<decimal>)decimal.TryParse; parserBuilder[typeof(decimal)] = (TryParseDelegate<decimal>)decimal.TryParse;
parserBuilder[typeof(DateTime)] = (TryParseDelegate<DateTime>)DateTime.TryParse; parserBuilder[typeof(DateTime)] = (TryParseDelegate<DateTime>)DateTime.TryParse;
parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate<DateTimeOffset>)DateTimeOffset.TryParse; parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate<DateTimeOffset>)DateTimeOffset.TryParse;
parserBuilder[typeof(char)] = (TryParseDelegate<char>)char.TryParse;
parserBuilder[typeof(string)] = (TryParseDelegate<string>)delegate (string str, out string value)
{
value = str;
return true;
};
_parsers = parserBuilder.ToImmutable(); _parsers = parserBuilder.ToImmutable();
} }

View File

@@ -9,23 +9,21 @@ namespace Discord.Commands
internal class ChannelTypeReader<T> : TypeReader internal class ChannelTypeReader<T> : TypeReader
where T : class, IChannel where T : class, IChannel
{ {
public override async Task<TypeReaderResult> Read(IUserMessage context, string input) public override async Task<TypeReaderResult> Read(CommandContext context, string input)
{ {
var guild = (context.Channel as IGuildChannel)?.Guild; if (context.Guild != null)
if (guild != null)
{ {
var results = new Dictionary<ulong, TypeReaderValue>(); var results = new Dictionary<ulong, TypeReaderValue>();
var channels = await guild.GetChannelsAsync().ConfigureAwait(false); var channels = await context.Guild.GetChannelsAsync(CacheMode.CacheOnly).ConfigureAwait(false);
ulong id; ulong id;
//By Mention (1.0) //By Mention (1.0)
if (MentionUtils.TryParseChannel(input, out id)) if (MentionUtils.TryParseChannel(input, out id))
AddResult(results, await guild.GetChannelAsync(id).ConfigureAwait(false) as T, 1.00f); AddResult(results, await context.Guild.GetChannelAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
//By Id (0.9) //By Id (0.9)
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id)) if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id))
AddResult(results, await guild.GetChannelAsync(id).ConfigureAwait(false) as T, 0.90f); AddResult(results, await context.Guild.GetChannelAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 0.90f);
//By Name (0.7-0.8) //By Name (0.7-0.8)
foreach (var channel in channels.Where(x => string.Equals(input, x.Name, StringComparison.OrdinalIgnoreCase))) foreach (var channel in channels.Where(x => string.Equals(input, x.Name, StringComparison.OrdinalIgnoreCase)))

View File

@@ -42,7 +42,7 @@ namespace Discord.Commands
_enumsByValue = byValueBuilder.ToImmutable(); _enumsByValue = byValueBuilder.ToImmutable();
} }
public override Task<TypeReaderResult> Read(IUserMessage context, string input) public override Task<TypeReaderResult> Read(CommandContext context, string input)
{ {
T baseValue; T baseValue;
object enumValue; object enumValue;

View File

@@ -6,19 +6,19 @@ namespace Discord.Commands
internal class MessageTypeReader<T> : TypeReader internal class MessageTypeReader<T> : TypeReader
where T : class, IMessage where T : class, IMessage
{ {
public override Task<TypeReaderResult> Read(IUserMessage context, string input) public override async Task<TypeReaderResult> Read(CommandContext context, string input)
{ {
ulong id; ulong id;
//By Id (1.0) //By Id (1.0)
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id)) if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{ {
var msg = context.Channel.GetCachedMessage(id) as T; var msg = await context.Channel.GetMessageAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T;
if (msg != null) if (msg != null)
return Task.FromResult(TypeReaderResult.FromSuccess(msg)); return TypeReaderResult.FromSuccess(msg);
} }
return Task.FromResult(TypeReaderResult.FromError(CommandError.ObjectNotFound, "Message not found.")); return TypeReaderResult.FromError(CommandError.ObjectNotFound, "Message not found.");
} }
} }
} }

View File

@@ -9,23 +9,22 @@ namespace Discord.Commands
internal class RoleTypeReader<T> : TypeReader internal class RoleTypeReader<T> : TypeReader
where T : class, IRole where T : class, IRole
{ {
public override Task<TypeReaderResult> Read(IUserMessage context, string input) public override Task<TypeReaderResult> Read(CommandContext context, string input)
{ {
var guild = (context.Channel as IGuildChannel)?.Guild;
ulong id; ulong id;
if (guild != null) if (context.Guild != null)
{ {
var results = new Dictionary<ulong, TypeReaderValue>(); var results = new Dictionary<ulong, TypeReaderValue>();
var roles = guild.Roles; var roles = context.Guild.Roles;
//By Mention (1.0) //By Mention (1.0)
if (MentionUtils.TryParseRole(input, out id)) if (MentionUtils.TryParseRole(input, out id))
AddResult(results, guild.GetRole(id) as T, 1.00f); AddResult(results, context.Guild.GetRole(id) as T, 1.00f);
//By Id (0.9) //By Id (0.9)
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id)) if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id))
AddResult(results, guild.GetRole(id) as T, 0.90f); AddResult(results, context.Guild.GetRole(id) as T, 0.90f);
//By Name (0.7-0.8) //By Name (0.7-0.8)
foreach (var role in roles.Where(x => string.Equals(input, x.Name, StringComparison.OrdinalIgnoreCase))) foreach (var role in roles.Where(x => string.Equals(input, x.Name, StringComparison.OrdinalIgnoreCase)))

View File

@@ -11,7 +11,7 @@ namespace Discord.Commands
_tryParse = PrimitiveParsers.Get<T>(); _tryParse = PrimitiveParsers.Get<T>();
} }
public override Task<TypeReaderResult> Read(IUserMessage context, string input) public override Task<TypeReaderResult> Read(CommandContext context, string input)
{ {
T value; T value;
if (_tryParse(input, out value)) if (_tryParse(input, out value))

View File

@@ -4,6 +4,6 @@ namespace Discord.Commands
{ {
public abstract class TypeReader public abstract class TypeReader
{ {
public abstract Task<TypeReaderResult> Read(IUserMessage context, string input); public abstract Task<TypeReaderResult> Read(CommandContext context, string input);
} }
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -9,33 +10,32 @@ namespace Discord.Commands
internal class UserTypeReader<T> : TypeReader internal class UserTypeReader<T> : TypeReader
where T : class, IUser where T : class, IUser
{ {
public override async Task<TypeReaderResult> Read(IUserMessage context, string input) public override async Task<TypeReaderResult> Read(CommandContext context, string input)
{ {
var results = new Dictionary<ulong, TypeReaderValue>(); var results = new Dictionary<ulong, TypeReaderValue>();
var guild = (context.Channel as IGuildChannel)?.Guild; IReadOnlyCollection<IUser> channelUsers = (await context.Channel.GetUsersAsync(CacheMode.CacheOnly).Flatten().ConfigureAwait(false)).ToArray(); //TODO: must be a better way?
IReadOnlyCollection<IUser> channelUsers = await context.Channel.GetUsersAsync().ConfigureAwait(false);
IReadOnlyCollection<IGuildUser> guildUsers = null; IReadOnlyCollection<IGuildUser> guildUsers = null;
ulong id; ulong id;
if (guild != null) if (context.Guild != null)
guildUsers = await guild.GetUsersAsync().ConfigureAwait(false); guildUsers = await context.Guild.GetUsersAsync(CacheMode.CacheOnly).ConfigureAwait(false);
//By Mention (1.0) //By Mention (1.0)
if (MentionUtils.TryParseUser(input, out id)) if (MentionUtils.TryParseUser(input, out id))
{ {
if (guild != null) if (context.Guild != null)
AddResult(results, await guild.GetUserAsync(id).ConfigureAwait(false) as T, 1.00f); AddResult(results, await context.Guild.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
else else
AddResult(results, await context.Channel.GetUserAsync(id).ConfigureAwait(false) as T, 1.00f); AddResult(results, await context.Channel.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
} }
//By Id (0.9) //By Id (0.9)
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id)) if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id))
{ {
if (guild != null) if (context.Guild != null)
AddResult(results, await guild.GetUserAsync(id).ConfigureAwait(false) as T, 0.90f); AddResult(results, await context.Guild.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 0.90f);
else else
AddResult(results, await context.Channel.GetUserAsync(id).ConfigureAwait(false) as T, 0.90f); AddResult(results, await context.Channel.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 0.90f);
} }
//By Username + Discriminator (0.7-0.85) //By Username + Discriminator (0.7-0.85)
@@ -75,7 +75,7 @@ namespace Discord.Commands
} }
if (results.Count > 0) if (results.Count > 0)
return TypeReaderResult.FromSuccess(results.Values.ToArray()); return TypeReaderResult.FromSuccess(results.Values.ToImmutableArray());
return TypeReaderResult.FromError(CommandError.ObjectNotFound, "User not found."); return TypeReaderResult.FromError(CommandError.ObjectNotFound, "User not found.");
} }

View File

@@ -6,7 +6,10 @@ namespace Discord.Commands
{ {
internal class ReflectionUtils internal class ReflectionUtils
{ {
internal static object CreateObject(TypeInfo typeInfo, CommandService service, IDependencyMap map = null) internal static T CreateObject<T>(TypeInfo typeInfo, CommandService service, IDependencyMap map = null)
=> CreateBuilder<T>(typeInfo, service, map)();
internal static Func<T> CreateBuilder<T>(TypeInfo typeInfo, CommandService service, IDependencyMap map = null)
{ {
var constructors = typeInfo.DeclaredConstructors.Where(x => !x.IsStatic).ToArray(); var constructors = typeInfo.DeclaredConstructors.Where(x => !x.IsStatic).ToArray();
if (constructors.Length == 0) if (constructors.Length == 0)
@@ -14,7 +17,7 @@ namespace Discord.Commands
else if (constructors.Length > 1) else if (constructors.Length > 1)
throw new InvalidOperationException($"Multiple constructors found for \"{typeInfo.FullName}\""); throw new InvalidOperationException($"Multiple constructors found for \"{typeInfo.FullName}\"");
var constructor = constructors[0]; var constructor = constructors[0];
ParameterInfo[] parameters = constructor.GetParameters(); ParameterInfo[] parameters = constructor.GetParameters();
object[] args = new object[parameters.Length]; object[] args = new object[parameters.Length];
@@ -34,14 +37,17 @@ namespace Discord.Commands
args[i] = arg; args[i] = arg;
} }
try return () =>
{ {
return constructor.Invoke(args); try
} {
catch (Exception ex) return (T)constructor.Invoke(args);
{ }
throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex); catch (Exception ex)
} {
throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex);
}
};
} }
} }
} }

View File

@@ -7,14 +7,14 @@ namespace Discord.Commands
public struct SearchResult : IResult public struct SearchResult : IResult
{ {
public string Text { get; } public string Text { get; }
public IReadOnlyList<Command> Commands { get; } public IReadOnlyList<CommandInfo> Commands { get; }
public CommandError? Error { get; } public CommandError? Error { get; }
public string ErrorReason { get; } public string ErrorReason { get; }
public bool IsSuccess => !Error.HasValue; public bool IsSuccess => !Error.HasValue;
private SearchResult(string text, IReadOnlyList<Command> commands, CommandError? error, string errorReason) private SearchResult(string text, IReadOnlyList<CommandInfo> commands, CommandError? error, string errorReason)
{ {
Text = text; Text = text;
Commands = commands; Commands = commands;
@@ -22,7 +22,7 @@ namespace Discord.Commands
ErrorReason = errorReason; ErrorReason = errorReason;
} }
public static SearchResult FromSuccess(string text, IReadOnlyList<Command> commands) public static SearchResult FromSuccess(string text, IReadOnlyList<CommandInfo> commands)
=> new SearchResult(text, commands, null, null); => new SearchResult(text, commands, null, null);
public static SearchResult FromError(CommandError error, string reason) public static SearchResult FromError(CommandError error, string reason)
=> new SearchResult(null, null, error, reason); => new SearchResult(null, null, error, reason);

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
namespace Discord.Commands namespace Discord.Commands
{ {

View File

@@ -0,0 +1,9 @@
namespace Discord.Commands
{
public enum RunMode
{
Sync,
Mixed,
Async
}
}

View File

@@ -13,24 +13,22 @@
} }
}, },
"buildOptions": {
"allowUnsafe": true,
"warningsAsErrors": false,
"xmlDoc": true
},
"configurations": { "configurations": {
"Release": { "Release": {
"buildOptions": { "buildOptions": {
"define": [ "RELEASE" ], "define": [ "RELEASE" ],
"nowarn": [ "CS1573", "CS1591" ], "nowarn": [ "CS1573", "CS1591" ],
"optimize": true "optimize": true,
"warningsAsErrors": true,
"xmlDoc": true
} }
} }
}, },
"dependencies": { "dependencies": {
"Discord.Net": "1.0.0-*" "Discord.Net.Core": {
"target": "project"
}
}, },
"frameworks": { "frameworks": {

View File

@@ -1,6 +1,6 @@
namespace Discord.API namespace Discord.API
{ {
internal static class CDN public static class CDN
{ {
public static string GetApplicationIconUrl(ulong appId, string iconId) public static string GetApplicationIconUrl(ulong appId, string iconId)
=> iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null;
@@ -12,5 +12,7 @@
=> splashId != null ? $"{DiscordConfig.CDNUrl}splashes/{guildId}/{splashId}.jpg" : null; => splashId != null ? $"{DiscordConfig.CDNUrl}splashes/{guildId}/{splashId}.jpg" : null;
public static string GetChannelIconUrl(ulong channelId, string iconId) public static string GetChannelIconUrl(ulong channelId, string iconId)
=> iconId != null ? $"{DiscordConfig.CDNUrl}channel-icons/{channelId}/{iconId}.jpg" : null; => iconId != null ? $"{DiscordConfig.CDNUrl}channel-icons/{channelId}/{iconId}.jpg" : null;
public static string GetEmojiUrl(ulong emojiId)
=> $"{DiscordConfig.CDNUrl}emojis/{emojiId}.png";
} }
} }

View File

@@ -11,13 +11,14 @@ namespace Discord.API
public string[] RPCOrigins { get; set; } public string[] RPCOrigins { get; set; }
[JsonProperty("name")] [JsonProperty("name")]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty("flags"), Int53]
public ulong Flags { get; set; }
[JsonProperty("owner")]
public User Owner { get; set; }
[JsonProperty("id")] [JsonProperty("id")]
public ulong Id { get; set; } public ulong Id { get; set; }
[JsonProperty("icon")] [JsonProperty("icon")]
public string Icon { get; set; } public string Icon { get; set; }
[JsonProperty("flags"), Int53]
public Optional<ulong> Flags { get; set; }
[JsonProperty("owner")]
public Optional<User> Owner { get; set; }
} }
} }

View File

@@ -12,6 +12,8 @@ namespace Discord.API
public MessageType Type { get; set; } public MessageType Type { get; set; }
[JsonProperty("channel_id")] [JsonProperty("channel_id")]
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
[JsonProperty("webhook_id")]
public Optional<ulong> WebhookId { get; set; }
[JsonProperty("author")] [JsonProperty("author")]
public Optional<User> Author { get; set; } public Optional<User> Author { get; set; }
[JsonProperty("content")] [JsonProperty("content")]
@@ -25,7 +27,9 @@ namespace Discord.API
[JsonProperty("mention_everyone")] [JsonProperty("mention_everyone")]
public Optional<bool> MentionEveryone { get; set; } public Optional<bool> MentionEveryone { get; set; }
[JsonProperty("mentions")] [JsonProperty("mentions")]
public Optional<User[]> Mentions { get; set; } public Optional<ObjectOrId<User>[]> UserMentions { get; set; }
[JsonProperty("mention_roles")]
public Optional<ulong[]> RoleMentions { get; set; }
[JsonProperty("attachments")] [JsonProperty("attachments")]
public Optional<Attachment[]> Attachments { get; set; } public Optional<Attachment[]> Attachments { get; set; }
[JsonProperty("embeds")] [JsonProperty("embeds")]

View File

@@ -5,6 +5,7 @@ namespace Discord.API
{ {
Friend = 1, Friend = 1,
Blocked = 2, Blocked = 2,
Pending = 4 IncomingPending = 3,
OutgoingPending = 4
} }
} }

View File

@@ -13,6 +13,8 @@ namespace Discord.API
public uint Color { get; set; } public uint Color { get; set; }
[JsonProperty("hoist")] [JsonProperty("hoist")]
public bool Hoist { get; set; } public bool Hoist { get; set; }
[JsonProperty("mentionable")]
public bool Mentionable { get; set; }
[JsonProperty("position")] [JsonProperty("position")]
public int Position { get; set; } public int Position { get; set; }
[JsonProperty("permissions"), Int53] [JsonProperty("permissions"), Int53]

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
namespace Discord.API namespace Discord.API
{ {
internal struct Image public struct Image
{ {
public Stream Stream { get; } public Stream Stream { get; }
public string Hash { get; } public string Hash { get; }

View File

@@ -0,0 +1,19 @@
namespace Discord.API
{
public struct ObjectOrId<T>
{
public ulong Id { get; }
public T Object { get; }
public ObjectOrId(ulong id)
{
Id = id;
Object = default(T);
}
public ObjectOrId(T obj)
{
Id = 0;
Object = obj;
}
}
}

View File

@@ -0,0 +1,16 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class CreateChannelInviteParams
{
[JsonProperty("max_age")]
public Optional<int> MaxAge { get; set; }
[JsonProperty("max_uses")]
public Optional<int> MaxUses { get; set; }
[JsonProperty("temporary")]
public Optional<bool> IsTemporary { get; set; }
}
}

View File

@@ -7,8 +7,11 @@ namespace Discord.API.Rest
public class CreateDMChannelParams public class CreateDMChannelParams
{ {
[JsonProperty("recipient_id")] [JsonProperty("recipient_id")]
internal ulong _recipientId { get; set; } public ulong RecipientId { get; }
public ulong RecipientId { set { _recipientId = value; } }
public IUser Recipient { set { _recipientId = value.Id; } } public CreateDMChannelParams(ulong recipientId)
{
RecipientId = recipientId;
}
} }
} }

View File

@@ -0,0 +1,8 @@
#pragma warning disable CS1591
namespace Discord.API.Rest
{
public class CreateGuildBanParams
{
public Optional<int> DeleteMessageDays { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class CreateGuildChannelParams
{
[JsonProperty("name")]
public string Name { get; }
[JsonProperty("type")]
public ChannelType Type { get; }
[JsonProperty("bitrate")]
public Optional<int> Bitrate { get; set; }
public CreateGuildChannelParams(string name, ChannelType type)
{
Name = name;
Type = type;
}
}
}

View File

@@ -7,9 +7,14 @@ namespace Discord.API.Rest
public class CreateGuildIntegrationParams public class CreateGuildIntegrationParams
{ {
[JsonProperty("id")] [JsonProperty("id")]
public ulong Id { internal get; set; } public ulong Id { get; }
[JsonProperty("type")] [JsonProperty("type")]
public string Type { internal get; set; } public string Type { get; }
public CreateGuildIntegrationParams(ulong id, string type)
{
Id = id;
Type = type;
}
} }
} }

View File

@@ -1,6 +1,5 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;
using System.IO;
namespace Discord.API.Rest namespace Discord.API.Rest
{ {
@@ -8,13 +7,17 @@ namespace Discord.API.Rest
public class CreateGuildParams public class CreateGuildParams
{ {
[JsonProperty("name")] [JsonProperty("name")]
public string Name { internal get; set; } public string Name { get; }
[JsonProperty("region")] [JsonProperty("region")]
public string Region { internal get; set; } public string RegionId { get; }
[JsonProperty("icon")] [JsonProperty("icon")]
internal Optional<Image?> _icon { get; set; } public Optional<Image?> Icon { get; set; }
public Stream Icon { set { _icon = value != null ? new Image(value) : (Image?)null; } }
public CreateGuildParams(string name, string regionId)
{
Name = name;
RegionId = regionId;
}
} }
} }

View File

@@ -0,0 +1,22 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class CreateMessageParams
{
[JsonProperty("content")]
public string Content { get; }
[JsonProperty("nonce")]
public Optional<string> Nonce { get; set; }
[JsonProperty("tts")]
public Optional<bool> IsTTS { get; set; }
public CreateMessageParams(string content)
{
Content = content;
}
}
}

View File

@@ -0,0 +1,17 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class DeleteMessagesParams
{
[JsonProperty("messages")]
public ulong[] MessageIds { get; }
public DeleteMessagesParams(ulong[] messageIds)
{
MessageIds = messageIds;
}
}
}

View File

@@ -0,0 +1,10 @@
#pragma warning disable CS1591
namespace Discord.API.Rest
{
public class GetChannelMessagesParams
{
public Optional<int> Limit { get; set; }
public Optional<Direction> RelativeDirection { get; set; }
public Optional<ulong> RelativeMessageId { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
#pragma warning disable CS1591
namespace Discord.API.Rest
{
public class GetGuildMembersParams
{
public Optional<int> Limit { get; set; }
public Optional<ulong> AfterUserId { get; set; }
}
}

View File

@@ -7,6 +7,11 @@ namespace Discord.API.Rest
public class GuildPruneParams public class GuildPruneParams
{ {
[JsonProperty("days")] [JsonProperty("days")]
public int Days { internal get; set; } public int Days { get; }
public GuildPruneParams(int days)
{
Days = days;
}
} }
} }

View File

@@ -7,10 +7,17 @@ namespace Discord.API.Rest
public class ModifyChannelPermissionsParams public class ModifyChannelPermissionsParams
{ {
[JsonProperty("type")] [JsonProperty("type")]
public string Type { internal get; set; } public string Type { get; }
[JsonProperty("allow")] [JsonProperty("allow")]
public ulong Allow { internal get; set; } public ulong Allow { get; }
[JsonProperty("deny")] [JsonProperty("deny")]
public ulong Deny { internal get; set; } public ulong Deny { get; }
public ModifyChannelPermissionsParams(string type, ulong allow, ulong deny)
{
Type = type;
Allow = allow;
Deny = deny;
}
} }
} }

View File

@@ -7,6 +7,11 @@ namespace Discord.API.Rest
public class ModifyCurrentUserNickParams public class ModifyCurrentUserNickParams
{ {
[JsonProperty("nick")] [JsonProperty("nick")]
public string Nickname { internal get; set; } public string Nickname { get; }
public ModifyCurrentUserNickParams(string nickname)
{
Nickname = nickname;
}
} }
} }

View File

@@ -1,6 +1,5 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;
using System.IO;
namespace Discord.API.Rest namespace Discord.API.Rest
{ {
@@ -8,11 +7,8 @@ namespace Discord.API.Rest
public class ModifyCurrentUserParams public class ModifyCurrentUserParams
{ {
[JsonProperty("username")] [JsonProperty("username")]
internal Optional<string> _username { get; set; } public Optional<string> Username { get; set; }
public string Username { set { _username = value; } }
[JsonProperty("avatar")] [JsonProperty("avatar")]
internal Optional<Image> _avatar { get; set; } public Optional<Image> Avatar { get; set; }
public Stream Avatar { set { _avatar = new Image(value); } }
} }
} }

View File

@@ -7,11 +7,8 @@ namespace Discord.API.Rest
public class ModifyGuildChannelParams public class ModifyGuildChannelParams
{ {
[JsonProperty("name")] [JsonProperty("name")]
internal Optional<string> _name { get; set; } public Optional<string> Name { get; set; }
public string Name { set { _name = value; } }
[JsonProperty("position")] [JsonProperty("position")]
internal Optional<int> _position { get; set; } public Optional<int> Position { get; set; }
public int Position { set { _position = value; } }
} }
} }

View File

@@ -7,9 +7,14 @@ namespace Discord.API.Rest
public class ModifyGuildChannelsParams public class ModifyGuildChannelsParams
{ {
[JsonProperty("id")] [JsonProperty("id")]
public ulong Id { internal get; set; } public ulong Id { get; set; }
[JsonProperty("position")] [JsonProperty("position")]
public int Position { internal get; set; } public int Position { get; set; }
public ModifyGuildChannelsParams(ulong id, int position)
{
Id = id;
Position = position;
}
} }
} }

View File

@@ -0,0 +1,14 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ModifyGuildEmbedParams
{
[JsonProperty("enabled")]
public Optional<bool> Enabled { get; set; }
[JsonProperty("channel")]
public Optional<ulong?> ChannelId { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ModifyGuildIntegrationParams
{
[JsonProperty("expire_behavior")]
public Optional<int> ExpireBehavior { get; set; }
[JsonProperty("expire_grace_period")]
public Optional<int> ExpireGracePeriod { get; set; }
[JsonProperty("enable_emoticons")]
public Optional<bool> EnableEmoticons { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ModifyGuildMemberParams
{
[JsonProperty("mute")]
public Optional<bool> Mute { get; set; }
[JsonProperty("deaf")]
public Optional<bool> Deaf { get; set; }
[JsonProperty("nick")]
public Optional<string> Nickname { get; set; }
[JsonProperty("roles")]
public Optional<ulong[]> RoleIds { get; set; }
[JsonProperty("channel_id")]
public Optional<ulong> ChannelId { get; set; }
}
}

View File

@@ -0,0 +1,30 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ModifyGuildParams
{
[JsonProperty("username")]
public Optional<string> Username { get; set; }
[JsonProperty("name")]
public Optional<string> Name { get; set; }
[JsonProperty("region")]
public Optional<string> RegionId { get; set; }
[JsonProperty("verification_level")]
public Optional<VerificationLevel> VerificationLevel { get; set; }
[JsonProperty("default_message_notifications")]
public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; }
[JsonProperty("afk_timeout")]
public Optional<int> AfkTimeout { get; set; }
[JsonProperty("icon")]
public Optional<Image?> Icon { get; set; }
[JsonProperty("splash")]
public Optional<Image?> Splash { get; set; }
[JsonProperty("afk_channel_id")]
public Optional<ulong?> AfkChannelId { get; set; }
[JsonProperty("owner_id")]
public Optional<ulong> OwnerId { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
#pragma warning disable CS1591
using Newtonsoft.Json;
namespace Discord.API.Rest
{
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ModifyGuildRoleParams
{
[JsonProperty("name")]
public Optional<string> Name { get; set; }
[JsonProperty("permissions")]
public Optional<ulong> Permissions { get; set; }
[JsonProperty("position")]
public Optional<int> Position { get; set; }
[JsonProperty("color")]
public Optional<uint> Color { get; set; }
[JsonProperty("hoist")]
public Optional<bool> Hoist { get; set; }
}
}

View File

@@ -7,6 +7,11 @@ namespace Discord.API.Rest
public class ModifyGuildRolesParams : ModifyGuildRoleParams public class ModifyGuildRolesParams : ModifyGuildRoleParams
{ {
[JsonProperty("id")] [JsonProperty("id")]
public ulong Id { internal get; set; } public ulong Id { get; }
public ModifyGuildRolesParams(ulong id)
{
Id = id;
}
} }
} }

View File

@@ -7,7 +7,6 @@ namespace Discord.API.Rest
public class ModifyMessageParams public class ModifyMessageParams
{ {
[JsonProperty("content")] [JsonProperty("content")]
internal Optional<string> _content { get; set; } public Optional<string> Content { get; set; }
public string Content { set { _content = value; } }
} }
} }

View File

@@ -0,0 +1,11 @@
#pragma warning disable CS1591
using System;
namespace Discord.API.Rest
{
public class ModifyPresenceParams
{
public Optional<UserStatus> Status { get; set; }
public Optional<Game> Game { get; set; }
}
}

View File

@@ -7,7 +7,6 @@ namespace Discord.API.Rest
public class ModifyTextChannelParams : ModifyGuildChannelParams public class ModifyTextChannelParams : ModifyGuildChannelParams
{ {
[JsonProperty("topic")] [JsonProperty("topic")]
internal Optional<string> _topic { get; set; } public Optional<string> Topic { get; set; }
public string Topic { set { _topic = value; } }
} }
} }

View File

@@ -7,11 +7,8 @@ namespace Discord.API.Rest
public class ModifyVoiceChannelParams : ModifyGuildChannelParams public class ModifyVoiceChannelParams : ModifyGuildChannelParams
{ {
[JsonProperty("bitrate")] [JsonProperty("bitrate")]
internal Optional<int> _bitrate { get; set; } public Optional<int> Bitrate { get; set; }
public int Bitrate { set { _bitrate = value; } }
[JsonProperty("user_limit")] [JsonProperty("user_limit")]
internal Optional<int> _userLimit { get; set; } public Optional<int> UserLimit { get; set; }
public int UserLimit { set { _userLimit = value; } }
} }
} }

View File

@@ -0,0 +1,35 @@
#pragma warning disable CS1591
using Discord.Net.Rest;
using System.Collections.Generic;
using System.IO;
namespace Discord.API.Rest
{
public class UploadFileParams
{
public Stream File { get; }
public Optional<string> Filename { get; set; }
public Optional<string> Content { get; set; }
public Optional<string> Nonce { get; set; }
public Optional<bool> IsTTS { get; set; }
public UploadFileParams(Stream file)
{
File = file;
}
public IReadOnlyDictionary<string, object> ToDictionary()
{
var d = new Dictionary<string, object>();
d["file"] = new MultipartFile(File, Filename.GetValueOrDefault("unknown.dat"));
if (Content.IsSpecified)
d["content"] = Content.Value;
if (IsTTS.IsSpecified)
d["tts"] = IsTTS.Value.ToString();
if (Nonce.IsSpecified)
d["nonce"] = Nonce.Value;
return d;
}
}
}

View File

@@ -0,0 +1,7 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Discord.Net.Rest")]
[assembly: InternalsVisibleTo("Discord.Net.Rpc")]
[assembly: InternalsVisibleTo("Discord.Net.WebSocket")]
[assembly: InternalsVisibleTo("Discord.Net.Commands")]
[assembly: InternalsVisibleTo("Discord.Net.Tests")]

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Discord.Audio namespace Discord.Audio
@@ -8,8 +9,7 @@ namespace Discord.Audio
event Func<Task> Connected; event Func<Task> Connected;
event Func<Exception, Task> Disconnected; event Func<Exception, Task> Disconnected;
event Func<int, int, Task> LatencyUpdated; event Func<int, int, Task> LatencyUpdated;
DiscordVoiceAPIClient ApiClient { get; }
/// <summary> Gets the current connection state of this client. </summary> /// <summary> Gets the current connection state of this client. </summary>
ConnectionState ConnectionState { get; } ConnectionState ConnectionState { get; }
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
@@ -17,7 +17,7 @@ namespace Discord.Audio
Task DisconnectAsync(); Task DisconnectAsync();
RTPWriteStream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000);
OpusEncodeStream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000); Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000);
} }
} }

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>91e9e7bd-75c9-4e98-84aa-2c271922e5c2</ProjectGuid>
<RootNamespace>Discord</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Some files were not shown because too many files have changed in this diff Show More