Project restructure, Added .Net Core support, Fixed some bugs
This commit is contained in:
32
src/Discord.Net.Commands/CommandBuilder.cs
Normal file
32
src/Discord.Net.Commands/CommandBuilder.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class CommandBuilder
|
||||
{
|
||||
private readonly DiscordBotClient _client;
|
||||
private readonly string _prefix;
|
||||
private readonly bool _useWhitelist;
|
||||
|
||||
public CommandBuilder(DiscordBotClient client, string prefix, bool useWhitelist = false)
|
||||
{
|
||||
_client = client;
|
||||
_prefix = prefix;
|
||||
_useWhitelist = useWhitelist;
|
||||
}
|
||||
|
||||
public void AddCommandGroup(string cmd, Action<CommandBuilder> config, bool useWhitelist = false)
|
||||
{
|
||||
config(new CommandBuilder(_client, _prefix + ' ' + cmd, useWhitelist));
|
||||
}
|
||||
public void AddCommand(string cmd, int minArgs, int maxArgs, Action<DiscordBotClient.CommandEventArgs> handler, bool? useWhitelist = null)
|
||||
{
|
||||
AddCommand(cmd, minArgs, maxArgs, e => { handler(e); return null; }, useWhitelist);
|
||||
}
|
||||
public void AddCommand(string cmd, int minArgs, int maxArgs, Func<DiscordBotClient.CommandEventArgs, Task> handler, bool? useWhitelist = null)
|
||||
{
|
||||
_client.AddCommand(cmd != "" ? _prefix + ' ' + cmd : _prefix, minArgs, maxArgs, handler, useWhitelist ?? _useWhitelist);
|
||||
}
|
||||
}
|
||||
}
|
||||
154
src/Discord.Net.Commands/CommandParser.cs
Normal file
154
src/Discord.Net.Commands/CommandParser.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public static class CommandParser
|
||||
{
|
||||
private enum CommandParserPart
|
||||
{
|
||||
None,
|
||||
CommandName,
|
||||
Parameter,
|
||||
QuotedParameter,
|
||||
DoubleQuotedParameter
|
||||
}
|
||||
|
||||
public static bool Parse(string input, out string command, out string[] args)
|
||||
{
|
||||
return Parse(input, out command, out args, true);
|
||||
}
|
||||
public static bool ParseArgs(string input, out string[] args)
|
||||
{
|
||||
string ignored;
|
||||
return Parse(input, out ignored, out args, false);
|
||||
}
|
||||
|
||||
private static bool Parse(string input, out string command, out string[] args, bool parseCommand)
|
||||
{
|
||||
CommandParserPart currentPart = parseCommand ? CommandParserPart.CommandName : CommandParserPart.None;
|
||||
int startPosition = 0;
|
||||
int endPosition = 0;
|
||||
int inputLength = input.Length;
|
||||
bool isEscaped = false;
|
||||
List<string> argList = new List<string>();
|
||||
|
||||
command = null;
|
||||
args = null;
|
||||
|
||||
if (input == "")
|
||||
return false;
|
||||
|
||||
while (endPosition < inputLength)
|
||||
{
|
||||
char currentChar = input[endPosition++];
|
||||
if (isEscaped)
|
||||
isEscaped = false;
|
||||
else if (currentChar == '\\')
|
||||
isEscaped = true;
|
||||
|
||||
switch (currentPart)
|
||||
{
|
||||
case CommandParserPart.CommandName:
|
||||
if ((!isEscaped && currentChar == ' ') || endPosition >= inputLength)
|
||||
{
|
||||
int length = (currentChar == ' ' ? endPosition - 1 : endPosition) - startPosition;
|
||||
string temp = input.Substring(startPosition, length);
|
||||
if (temp == "")
|
||||
startPosition = endPosition;
|
||||
else
|
||||
{
|
||||
currentPart = CommandParserPart.None;
|
||||
command = temp;
|
||||
startPosition = endPosition;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CommandParserPart.None:
|
||||
if ((!isEscaped && currentChar == '\"'))
|
||||
{
|
||||
currentPart = CommandParserPart.DoubleQuotedParameter;
|
||||
startPosition = endPosition;
|
||||
}
|
||||
else if ((!isEscaped && currentChar == '\''))
|
||||
{
|
||||
currentPart = CommandParserPart.QuotedParameter;
|
||||
startPosition = endPosition;
|
||||
}
|
||||
else if ((!isEscaped && currentChar == ' ') || endPosition >= inputLength)
|
||||
{
|
||||
int length = (currentChar == ' ' ? endPosition - 1 : endPosition) - startPosition;
|
||||
string temp = input.Substring(startPosition, length);
|
||||
if (temp == "")
|
||||
startPosition = endPosition;
|
||||
else
|
||||
{
|
||||
currentPart = CommandParserPart.None;
|
||||
argList.Add(temp);
|
||||
startPosition = endPosition;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CommandParserPart.QuotedParameter:
|
||||
if ((!isEscaped && currentChar == '\''))
|
||||
{
|
||||
string temp = input.Substring(startPosition, endPosition - startPosition - 1);
|
||||
currentPart = CommandParserPart.None;
|
||||
argList.Add(temp);
|
||||
startPosition = endPosition;
|
||||
}
|
||||
else if (endPosition >= inputLength)
|
||||
return false;
|
||||
break;
|
||||
case CommandParserPart.DoubleQuotedParameter:
|
||||
if ((!isEscaped && currentChar == '\"'))
|
||||
{
|
||||
string temp = input.Substring(startPosition, endPosition - startPosition - 1);
|
||||
currentPart = CommandParserPart.None;
|
||||
argList.Add(temp);
|
||||
startPosition = endPosition;
|
||||
}
|
||||
else if (endPosition >= inputLength)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parseCommand && (command == null || command == ""))
|
||||
return false;
|
||||
|
||||
args = argList.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ArgsEqual(string[] args, int expected)
|
||||
{
|
||||
return args.Length == expected;
|
||||
}
|
||||
public static bool ArgsAtLeast(string[] args, int expected)
|
||||
{
|
||||
return args.Length >= expected;
|
||||
}
|
||||
public static bool ArgsAtMost(string[] args, int expected)
|
||||
{
|
||||
return args.Length <= expected;
|
||||
}
|
||||
public static bool ArgsIn(string[] args, params int[] expected)
|
||||
{
|
||||
int count = args.Length;
|
||||
for (int i = 0; i < expected.Length; i++)
|
||||
{
|
||||
if (count == expected[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static bool ArgsBetween(string[] args, int min, int max)
|
||||
{
|
||||
return args.Length >= min && args.Length <= max;
|
||||
}
|
||||
public static bool NoArgs(string[] args, params int[] expected)
|
||||
{
|
||||
return args.Length == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/Discord.Net.Commands/Discord.Net.Commands.xproj
Normal file
21
src/Discord.Net.Commands/Discord.Net.Commands.xproj
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>19793545-ef89-48f4-8100-3ebaad0a9141</ProjectGuid>
|
||||
<RootNamespace>Discord.Net.Commands</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
50
src/Discord.Net.Commands/DiscordBotClient.Events.cs
Normal file
50
src/Discord.Net.Commands/DiscordBotClient.Events.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public partial class DiscordBotClient : DiscordClient
|
||||
{
|
||||
public class CommandEventArgs
|
||||
{
|
||||
public readonly Message Message;
|
||||
public readonly Command Command;
|
||||
public readonly string[] Args;
|
||||
|
||||
public User User => Message.User;
|
||||
public string UserId => Message.UserId;
|
||||
public Channel Channel => Message.Channel;
|
||||
public string ChannelId => Message.ChannelId;
|
||||
public Server Server => Message.Channel.Server;
|
||||
public string ServerId => Message.Channel.ServerId;
|
||||
|
||||
public CommandEventArgs(Message message, Command command, string[] args)
|
||||
{
|
||||
Message = message;
|
||||
Command = command;
|
||||
Args = args;
|
||||
}
|
||||
}
|
||||
public class CommandErrorEventArgs : CommandEventArgs
|
||||
{
|
||||
public readonly Exception Exception;
|
||||
public CommandErrorEventArgs(Message message, Command command, string[] args, Exception ex)
|
||||
: base(message, command, args)
|
||||
{
|
||||
Exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<CommandEventArgs> RanCommand;
|
||||
private void RaiseRanCommand(CommandEventArgs args)
|
||||
{
|
||||
if (RanCommand != null)
|
||||
RanCommand(this, args);
|
||||
}
|
||||
public event EventHandler<CommandErrorEventArgs> CommandError;
|
||||
private void RaiseCommandError(Message msg, Command command, string[] args, Exception ex)
|
||||
{
|
||||
if (CommandError != null)
|
||||
CommandError(this, new CommandErrorEventArgs(msg, command, args, ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
151
src/Discord.Net.Commands/DiscordBotClient.cs
Normal file
151
src/Discord.Net.Commands/DiscordBotClient.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
public sealed class Command
|
||||
{
|
||||
public readonly string[] Text;
|
||||
public readonly int MinArgs, MaxArgs;
|
||||
public readonly bool UseWhitelist;
|
||||
internal readonly Func<DiscordBotClient.CommandEventArgs, Task> Handler;
|
||||
|
||||
public Command(string[] text, int minArgs, int maxArgs, bool useWhitelist, Func<DiscordBotClient.CommandEventArgs, Task> handler)
|
||||
{
|
||||
Text = text;
|
||||
MinArgs = minArgs;
|
||||
MaxArgs = maxArgs;
|
||||
UseWhitelist = useWhitelist;
|
||||
Handler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Discord.Net client with extensions for handling common bot operations like text commands.
|
||||
/// </summary>
|
||||
public partial class DiscordBotClient : DiscordClient
|
||||
{
|
||||
private List<Command> _commands;
|
||||
private List<string> _whitelist;
|
||||
|
||||
public IEnumerable<Command> Commands => _commands;
|
||||
|
||||
public char CommandChar { get; set; }
|
||||
public bool UseCommandChar { get; set; }
|
||||
public bool RequireCommandCharInPublic { get; set; }
|
||||
public bool RequireCommandCharInPrivate { get; set; }
|
||||
public bool AlwaysUseWhitelist { get; set; }
|
||||
|
||||
public DiscordBotClient()
|
||||
{
|
||||
_commands = new List<Command>();
|
||||
_whitelist = new List<string>();
|
||||
|
||||
CommandChar = '~';
|
||||
RequireCommandCharInPublic = true;
|
||||
RequireCommandCharInPrivate = true;
|
||||
AlwaysUseWhitelist = false;
|
||||
|
||||
MessageCreated += async (s, e) =>
|
||||
{
|
||||
//Ignore messages from ourselves
|
||||
if (e.Message.UserId == UserId)
|
||||
return;
|
||||
|
||||
//Check the global whitelist
|
||||
if (AlwaysUseWhitelist && !_whitelist.Contains(e.Message.UserId))
|
||||
return;
|
||||
|
||||
//Check for the command character
|
||||
string msg = e.Message.Text;
|
||||
if (UseCommandChar)
|
||||
{
|
||||
if (msg.Length == 0)
|
||||
return;
|
||||
bool isPrivate = e.Message.Channel.IsPrivate;
|
||||
bool hasCommandChar = msg[0] == CommandChar;
|
||||
if (hasCommandChar)
|
||||
msg = msg.Substring(1);
|
||||
if (!isPrivate && RequireCommandCharInPublic && !hasCommandChar)
|
||||
return;
|
||||
if (isPrivate && RequireCommandCharInPrivate && !hasCommandChar)
|
||||
return;
|
||||
}
|
||||
|
||||
string[] args;
|
||||
if (!CommandParser.ParseArgs(msg, out args))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _commands.Count; i++)
|
||||
{
|
||||
Command cmd = _commands[i];
|
||||
|
||||
//Check Command Parts
|
||||
if (args.Length < cmd.Text.Length)
|
||||
continue;
|
||||
|
||||
bool isValid = true;
|
||||
for (int j = 0; j < cmd.Text.Length; j++)
|
||||
{
|
||||
if (!string.Equals(args[j], cmd.Text[j], StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isValid)
|
||||
continue;
|
||||
|
||||
//Check Whitelist
|
||||
if (cmd.UseWhitelist && !_whitelist.Contains(e.Message.UserId))
|
||||
continue;
|
||||
|
||||
//Check Arg Count
|
||||
int argCount = args.Length - cmd.Text.Length;
|
||||
if (argCount < cmd.MinArgs || argCount > cmd.MaxArgs)
|
||||
continue;
|
||||
|
||||
//Run Command
|
||||
string[] newArgs = new string[argCount];
|
||||
for (int j = 0; j < newArgs.Length; j++)
|
||||
newArgs[j] = args[j + cmd.Text.Length];
|
||||
|
||||
var eventArgs = new CommandEventArgs(e.Message, cmd, newArgs);
|
||||
RaiseRanCommand(eventArgs);
|
||||
try
|
||||
{
|
||||
var task = cmd.Handler(eventArgs);
|
||||
if (task != null)
|
||||
await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
RaiseCommandError(e.Message, cmd, newArgs, ex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void AddCommandGroup(string cmd, Action<CommandBuilder> config, bool useWhitelist = false)
|
||||
{
|
||||
config(new CommandBuilder(this, cmd, useWhitelist));
|
||||
}
|
||||
public void AddCommand(string cmd, int minArgs, int maxArgs, Action<CommandEventArgs> handler, bool useWhitelist = false)
|
||||
{
|
||||
AddCommand(cmd, minArgs, maxArgs, e => { handler(e); return null; }, useWhitelist);
|
||||
}
|
||||
public void AddCommand(string cmd, int minArgs, int maxArgs, Func<CommandEventArgs, Task> handler, bool useWhitelist = false)
|
||||
{
|
||||
_commands.Add(new Command(cmd.Split(' '), minArgs, maxArgs, useWhitelist, handler));
|
||||
}
|
||||
|
||||
public void AddWhitelist(User user)
|
||||
=> AddWhitelist(user.Id);
|
||||
public void AddWhitelist(string userId)
|
||||
{
|
||||
_whitelist.Add(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/Discord.Net.Commands/project.json
Normal file
26
src/Discord.Net.Commands/project.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": "0.2.0-*",
|
||||
"description": "A small Discord.Net extension to make bot creation easier.",
|
||||
"authors": [ "RogueException" ],
|
||||
"tags": [ "discord", "discordapp" ],
|
||||
"projectUrl": "https://github.com/RogueException/Discord.Net",
|
||||
"licenseUrl": "http://opensource.org/licenses/MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/RogueException/Discord.Net"
|
||||
},
|
||||
"compilationOptions": {
|
||||
"warningsAsErrors": true
|
||||
},
|
||||
"dependencies": {
|
||||
"Discord.Net": ""
|
||||
},
|
||||
"frameworks": {
|
||||
"dotnet": {
|
||||
"dependencies": {
|
||||
"System.Runtime": "4.0.20",
|
||||
"Microsoft.CSharp": "4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user