Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Version 4.0.1 contains breaking changes.
- Removed obsolete methods.
- Methods are now asynchronous. (The return value changed from `void` to `Task` and gains `Async` suffix)
- Events are now asynchronous (return value changed from `void` to `Task`)
- `Add/RemoveChatCommandIdentifier` methods were removed, use `ChatCommandIdentifiers` property instead (same applies to whisper);
- `Add/RemoveChatCommandIdentifier` methods were removed, use `ChatCommandIdentifiers` property instead (same applies to whisper) and uses `string` instead of `char`
- `OnLog` event was removed (you can still use `ILoggerFactory` to get logs)
- removed builders classes (removed `TwitchLib.Client.Models.Builders namespace`)
- changed public fields to properties
Expand Down
2 changes: 1 addition & 1 deletion TwitchLib.Client.Example/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
var credentials = new ConnectionCredentials(); // anonymous user, add Username and OAuth token to get the ability to send messages
var client = new TwitchClient(loggerFactory: loggerFactory)
{
ChatCommandIdentifiers = { '!', '?' }, // you can customize the command identifiers, if not set, defaults to '!'
ChatCommandIdentifiers = { "!", "?" }, // you can customize the command identifiers, if not set, defaults to '!'
};

client.Initialize(credentials);
Expand Down
35 changes: 16 additions & 19 deletions TwitchLib.Client.Models/CommandInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace TwitchLib.Client.Models;
public class CommandInfo
{
/// <summary>Property representing the command identifier (ie command prefix).</summary>
public char Identifier { get; }
public string Identifier { get; }

/// <summary>Property representing the actual command (without the command prefix).</summary>
public string Name { get; }
Expand All @@ -20,13 +20,13 @@ public class CommandInfo
/// <summary>
/// Initializes a new instance of the <see cref="CommandInfo"/> class.
/// </summary>
public CommandInfo(char identifier, string name) : this(identifier, name, string.Empty, new())
public CommandInfo(string identifier, string name) : this(identifier, name, string.Empty, new())
{ }

/// <summary>
/// Initializes a new instance of the <see cref="CommandInfo"/> class.
/// </summary>
public CommandInfo(char identifier, string name, string argumentsAsString, List<string> argumentsAsList)
public CommandInfo(string identifier, string name, string argumentsAsString, List<string> argumentsAsList)
{
Identifier = identifier;
Name = name;
Expand All @@ -35,37 +35,34 @@ public CommandInfo(char identifier, string name, string argumentsAsString, List<
}

/// <summary>
/// Tries to parse a span of characters into a value.
/// Tries to parse a message with specified command identifier into a value.
/// </summary>
/// <param name="s">The span of characters to parse.</param>
/// <param name="result">When this method returns, contains the result of successfully parsing s, or an undefined value on failure.</param>
/// <returns>true if s was successfully parsed; otherwise, false.</returns>
#if NETSTANDARD2_0
public static bool TryParse(ReadOnlySpan<char> s, out CommandInfo result)
internal static bool TryParse(string commandIdentifier, ReadOnlySpan<char> message, out CommandInfo result)
#else
public static bool TryParse(ReadOnlySpan<char> s, [MaybeNullWhen(false)] out CommandInfo result)
internal static bool TryParse(string commandIdentifier, ReadOnlySpan<char> message, [MaybeNullWhen(false)] out CommandInfo result)
#endif
{
result = default!;
s = s.Trim();
if (s.IsEmpty)
if(!message.StartsWith(commandIdentifier.AsSpan()))
return false;
var commandIdentifier = s[0];
s = s.Slice(1);
if (s.IsEmpty || s[0] == ' ') // if string contains only the identifier or the first char after identifier is space, then it is invalid input

message = message.Slice(commandIdentifier.Length);
if (message.IsEmpty || message[0] == ' ') // if string contains only the identifier or the first char after identifier is space, then it is invalid input
return false;
var indexOfSpace = s.IndexOf(' ');
var indexOfSpace = message.IndexOf(' ');
if (indexOfSpace == -1)
{
var name = s.ToString();
var name = message.ToString();
result = new(commandIdentifier, name);
}
else
{
var name = s.Slice(0, indexOfSpace).ToString();
s = s.Slice(indexOfSpace + 1).TrimStart();
var argumentsAsString = s.ToString();
result = new(commandIdentifier, name, argumentsAsString, ParseArgumentsToList(s));
var name = message.Slice(0, indexOfSpace).ToString();
message = message.Slice(indexOfSpace + 1).TrimStart();
var argumentsAsString = message.ToString();
result = new(commandIdentifier, name, argumentsAsString, ParseArgumentsToList(message));
}
return true;

Expand Down
28 changes: 23 additions & 5 deletions TwitchLib.Client.Test/CommandInfoTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,30 @@ namespace TwitchLib.Client.Test;
public class CommandInfoTest
{
[Theory]
[InlineData("")]
[InlineData("!")]
[InlineData("! command")]
public void ParsingFailAndReturnNull(string s)
[InlineData("", "")]
[InlineData("!", "!")]
[InlineData("!", "! command")]
[InlineData("?", "!command")]
public void ParsingFailAndReturnNull(string commandIdentifier, string message)
{
Assert.False(CommandInfo.TryParse(s, out var commandInfo));
Assert.False(CommandInfo.TryParse(commandIdentifier, message, out var commandInfo));
Assert.Null(commandInfo);
}

[Theory]
[InlineData("!", "!command", 0)]
[InlineData("!", "!command arg1", 1)]
[InlineData("!", "!command arg1 arg2", 2)]
[InlineData("!", "!command arg1 arg2 arg3 arg4", 4)]
[InlineData("!", "!command \"arg1 with space\"", 1)]
[InlineData("!", "!command \"arg1 with space\" \"arg2 with space\"", 2)]
[InlineData("cmd!", "cmd!command", 0)]
[InlineData("cmd!", "cmd!command arg1", 1)]
[InlineData("cmd!", "cmd!command \"arg1 with space\" \"arg2 with space\"", 2)]
public void Parsing(string commandIdentifier, string message, int argCount)
{
Assert.True(CommandInfo.TryParse(commandIdentifier, message, out var commandInfo));
Assert.NotNull(commandInfo);
Assert.Equal(argCount, commandInfo.ArgumentsAsList.Count);
}
}
4 changes: 2 additions & 2 deletions TwitchLib.Client/Interfaces/ITwitchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ public interface ITwitchClient
/// <summary>
/// The chat command identifiers
/// </summary>
ICollection<char> ChatCommandIdentifiers { get; }
ICollection<string> ChatCommandIdentifiers { get; }
/// <summary>
/// The whisper command identifiers
/// </summary>
ICollection<char> WhisperCommandIdentifiers { get; }
ICollection<string> WhisperCommandIdentifiers { get; }

/// <summary>
/// Fires when an Announcement is received
Expand Down
46 changes: 28 additions & 18 deletions TwitchLib.Client/TwitchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ public class TwitchClient : ITwitchClient

#region Public Variables
/// <inheritdoc/>
public ICollection<char> ChatCommandIdentifiers { get; } = new HashSet<char>();
public ICollection<string> ChatCommandIdentifiers { get; } = new HashSet<string>();

/// <inheritdoc/>
public ICollection<char> WhisperCommandIdentifiers { get; } = new HashSet<char>();
public ICollection<string> WhisperCommandIdentifiers { get; } = new HashSet<string>();

/// <inheritdoc/>
#if NET
Expand Down Expand Up @@ -370,9 +370,9 @@ private void InitializationHelper(
ConnectionCredentials = credentials;

if (ChatCommandIdentifiers.Count == 0)
ChatCommandIdentifiers.Add('!');
ChatCommandIdentifiers.Add("!");
if (WhisperCommandIdentifiers.Count == 0)
WhisperCommandIdentifiers.Add('!');
WhisperCommandIdentifiers.Add("!");

for (var i = 0; i < channels.Count; i++)
{
Expand Down Expand Up @@ -898,13 +898,10 @@ private async Task HandlePrivMsg(IrcMessage ircMessage)
await OnUserIntro.Invoke(this, new(chatMessage));
}

if (OnChatCommandReceived is not null
&& !string.IsNullOrEmpty(chatMessage.Message)
&& ChatCommandIdentifiers.Contains(chatMessage.Message[0])
&& CommandInfo.TryParse(chatMessage.Message.AsSpan(), out var commandInfo)
)
if (OnChatCommandReceived is not null
&& CanInvokeCommand(ChatCommandIdentifiers, chatMessage.Message.AsSpan(), out var commandInfo))
{
await OnChatCommandReceived.Invoke(this, new(chatMessage, commandInfo));
await OnChatCommandReceived.Invoke(this, new(chatMessage, commandInfo!));
}

}
Expand Down Expand Up @@ -1101,16 +1098,29 @@ private async Task HandleWhisper(IrcMessage ircMessage)

await OnWhisperReceived.TryInvoke(this, new(whisperMessage));

if (OnWhisperCommandReceived is not null
&& !string.IsNullOrEmpty(whisperMessage.Message)
&& WhisperCommandIdentifiers.Contains(whisperMessage.Message[0])
&& CommandInfo.TryParse(whisperMessage.Message.AsSpan(), out var commandInfo)
)
if (OnWhisperCommandReceived is not null
&& CanInvokeCommand(WhisperCommandIdentifiers, whisperMessage.Message.AsSpan(), out var commandInfo))
{
await OnWhisperCommandReceived.Invoke(this, new(whisperMessage, commandInfo));
await OnWhisperCommandReceived.Invoke(this, new(whisperMessage, commandInfo!));
}
}

static bool CanInvokeCommand(ICollection<string> commandIdentifiers, ReadOnlySpan<char> message, out CommandInfo? commandInfo)
{
commandInfo = null;
if (message.IsEmpty)
return false;

foreach (var commandIdentifier in commandIdentifiers)
{
if (message.StartsWith(commandIdentifier.AsSpan()))
{
return CommandInfo.TryParse(commandIdentifier, message, out commandInfo);
}
}
return false;
}

/// <summary>
/// Handles the state of the room.
/// </summary>
Expand Down Expand Up @@ -1169,9 +1179,9 @@ private static Task HandleCap(IrcMessage _)
return Task.CompletedTask;
}

#endregion
#endregion

#endregion
#endregion

private Task UnaccountedFor(string ircString)
{
Expand Down
Loading