mirror of
https://github.com/Ikatono/TwitchIrcClient.git
synced 2025-10-29 04:56:12 -05:00
Compare commits
2 Commits
81651a0e59
...
4806e50736
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4806e50736 | ||
|
|
1bf8afc68b |
@@ -139,19 +139,68 @@ namespace TwitchIrcClient.IRC
|
||||
var bytes = Encoding.UTF8.GetBytes(line + ENDL);
|
||||
_Stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
//TODO make this unit testable?
|
||||
/// <summary>
|
||||
/// Construct an IRC message from parts and sends it. Does little to no validation on inputs.
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <param name="tags"></param>
|
||||
/// <param name="prefix"></param>
|
||||
public void SendMessage(IrcMessageType command, IEnumerable<string>? parameters = null,
|
||||
Dictionary<string, string?>? tags = null, string? prefix = null)
|
||||
{
|
||||
var message = "";
|
||||
if (tags is not null && tags.Count != 0)
|
||||
{
|
||||
message = "@" + string.Join(';',
|
||||
tags.OrderBy(p => p.Key).Select(p => $"{p.Key}={EscapeTagValue(p.Value)}"))
|
||||
+ " ";
|
||||
}
|
||||
if (prefix is not null && !string.IsNullOrWhiteSpace(prefix))
|
||||
message += ":" + prefix + " ";
|
||||
message += command.ToCommand() + " ";
|
||||
if (parameters is not null && parameters.Any())
|
||||
{
|
||||
//if ((command == IrcMessageType.NICK || command == IrcMessageType.PASS)
|
||||
// && parameters.Count() == 1)
|
||||
if (false)
|
||||
{
|
||||
message += " " + parameters.Single();
|
||||
}
|
||||
else
|
||||
{
|
||||
message += string.Join(' ', parameters.SkipLast(1));
|
||||
message += " :" + parameters.Last();
|
||||
}
|
||||
}
|
||||
SendLine(message);
|
||||
}
|
||||
private static string EscapeTagValue(string? s)
|
||||
{
|
||||
if (s is null)
|
||||
return "";
|
||||
return string.Join("", s.Select(c => c switch
|
||||
{
|
||||
';' => @"\:",
|
||||
' ' => @"\s",
|
||||
'\\' => @"\\",
|
||||
'\r' => @"\r",
|
||||
'\n' => @"\n",
|
||||
char ch => ch.ToString(),
|
||||
}));
|
||||
}
|
||||
public void Authenticate(string? user, string? pass)
|
||||
{
|
||||
if (user == null)
|
||||
user = $"justinfan{Random.Shared.NextInt64(10000):D4}";
|
||||
if (pass == null)
|
||||
pass = "pass";
|
||||
SendLine($"NICK {user}");
|
||||
SendLine($"PASS {pass}");
|
||||
user ??= $"justinfan{Random.Shared.NextInt64(10000):D4}";
|
||||
pass ??= "pass";
|
||||
SendMessage(IrcMessageType.PASS, parameters: [pass]);
|
||||
SendMessage(IrcMessageType.NICK, parameters: [user]);
|
||||
}
|
||||
public void JoinChannel(string channel)
|
||||
{
|
||||
channel = channel.TrimStart('#');
|
||||
SendLine($"JOIN #{channel}");
|
||||
SendMessage(IrcMessageType.JOIN, ["#" + channel]);
|
||||
}
|
||||
private async void ListenForInput()
|
||||
{
|
||||
@@ -268,21 +317,25 @@ namespace TwitchIrcClient.IRC
|
||||
public void AddCallback(MessageCallbackItem callbackItem)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(disposedValue, this);
|
||||
lock (UserCallbacks)
|
||||
UserCallbacks.Add(callbackItem);
|
||||
}
|
||||
public bool RemoveCallback(MessageCallbackItem callbackItem)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(disposedValue, this);
|
||||
lock (UserCallbacks)
|
||||
return UserCallbacks.Remove(callbackItem);
|
||||
}
|
||||
protected void AddSystemCallback(MessageCallbackItem callbackItem)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(disposedValue, this);
|
||||
lock (SystemCallbacks)
|
||||
SystemCallbacks.Add(callbackItem);
|
||||
}
|
||||
protected bool RemoveSystemCallback(MessageCallbackItem callbackItem)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(disposedValue, this);
|
||||
lock (SystemCallbacks)
|
||||
return SystemCallbacks.Remove(callbackItem);
|
||||
}
|
||||
private void RunCallbacks(ReceivedMessage message)
|
||||
@@ -290,7 +343,9 @@ namespace TwitchIrcClient.IRC
|
||||
ArgumentNullException.ThrowIfNull(message, nameof(message));
|
||||
if (disposedValue)
|
||||
return;
|
||||
lock (SystemCallbacks)
|
||||
SystemCallbacks.ForEach(c => c.TryCall(this, message));
|
||||
lock (UserCallbacks)
|
||||
UserCallbacks.ForEach(c => c.TryCall(this, message));
|
||||
}
|
||||
|
||||
@@ -306,6 +361,7 @@ namespace TwitchIrcClient.IRC
|
||||
TokenSource.Dispose();
|
||||
Client?.Dispose();
|
||||
_HeartbeatTimer?.Dispose();
|
||||
_Stream?.Dispose();
|
||||
}
|
||||
disposedValue = true;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace TwitchIrcClient.IRC
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the "command" of an IRC message.
|
||||
/// </summary>
|
||||
public enum IrcMessageType
|
||||
{
|
||||
//twitch standard messages
|
||||
@@ -174,12 +177,26 @@ namespace TwitchIrcClient.IRC
|
||||
}
|
||||
public static class IrcMessageTypeHelper
|
||||
{
|
||||
//parses a string that is either a numeric code or the command name
|
||||
/// <summary>
|
||||
/// Parses a string that is either a numeric code or the command name.
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// The value range 000-999 is reserved for numeric commands, and will
|
||||
/// be converted to a numeric string when forming a message.
|
||||
/// </remarks>
|
||||
public static IrcMessageType Parse(string s)
|
||||
{
|
||||
if (int.TryParse(s, out int result))
|
||||
return (IrcMessageType)result;
|
||||
return Enum.Parse<IrcMessageType>(s);
|
||||
}
|
||||
public static string ToCommand(this IrcMessageType type)
|
||||
{
|
||||
if ((int)type >= 0 && (int)type < 1000)
|
||||
return $"{(int)type,3}";
|
||||
return type.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,9 @@ RateLimiter limiter = new(20, 30);
|
||||
bool ssl = true;
|
||||
async Task<IrcConnection> CreateConnection(string channel)
|
||||
{
|
||||
IrcConnection connection;
|
||||
if (ssl)
|
||||
connection = new IrcConnection("irc.chat.twitch.tv", 6697, limiter, true, true);
|
||||
else
|
||||
connection = new IrcConnection("irc.chat.twitch.tv", 6667, limiter, true, false);
|
||||
IrcConnection connection = ssl
|
||||
? connection = new IrcConnection("irc.chat.twitch.tv", 6697, limiter, true, true)
|
||||
: connection = new IrcConnection("irc.chat.twitch.tv", 6667, limiter, true, false);
|
||||
connection.AddCallback(new MessageCallbackItem(
|
||||
(o, m) =>
|
||||
{
|
||||
@@ -51,7 +49,7 @@ async Task<IrcConnection> CreateConnection(string channel)
|
||||
}
|
||||
Console.Write("Channel: ");
|
||||
var channelName = Console.ReadLine();
|
||||
ArgumentNullException.ThrowIfNull(channelName, nameof(Channel));
|
||||
ArgumentNullException.ThrowIfNullOrWhiteSpace(channelName, nameof(channelName));
|
||||
var connection = await CreateConnection(channelName);
|
||||
while (true)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user