mirror of
https://codeberg.org/Ikatono/TierMaker.git
synced 2025-10-28 20:45:35 -05:00
forgot to commit for a while
This commit is contained in:
129
TwitchChatWatcher.cs
Normal file
129
TwitchChatWatcher.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public partial class TwitchChatWatcher : Node
|
||||
{
|
||||
private readonly ClientWebSocket Socket = new();
|
||||
public readonly ConcurrentQueue<TwitchChatMessage> Queue = new();
|
||||
private readonly CancellationTokenSource TokenSource = new();
|
||||
public CancellationToken Token => TokenSource.Token;
|
||||
private CommandHandler CommandHandler { get; set; }
|
||||
|
||||
[Signal]
|
||||
public delegate void IncomingCommandEventHandler(Command command);
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
CommandHandler = GetNode<CommandHandler>("/root/CommandHandler");
|
||||
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
}
|
||||
public async Task ConnectAsync()
|
||||
{
|
||||
if (Socket.State == WebSocketState.Open)
|
||||
return;
|
||||
await Socket.ConnectAsync(new Uri("wss://irc-ws.chat.twitch.tv:443"), Token);
|
||||
_ = Task.Run(GetPacketsTask, Token);
|
||||
_ = Task.Run(HandleMessages, Token);
|
||||
|
||||
}
|
||||
public async Task Authenticate(string user = null, string pass = null)
|
||||
{
|
||||
user ??= $"justinfan{Random.Shared.NextInt64(10000):D4}";
|
||||
pass ??= "pass";
|
||||
await SendMessageAsync(TwitchChatMessageType.PASS, parameters: new string[] { pass });
|
||||
await SendMessageAsync(TwitchChatMessageType.NICK, parameters: new string[] { user });
|
||||
}
|
||||
public async Task JoinChannel(string channel)
|
||||
{
|
||||
channel = channel.TrimStart('#');
|
||||
await SendMessageAsync(TwitchChatMessageType.JOIN, parameters: new string[] {"#" + channel});
|
||||
}
|
||||
public async Task SendMessageAsync(string message)
|
||||
{
|
||||
await Socket.SendAsync(Encoding.UTF8.GetBytes(message),
|
||||
WebSocketMessageType.Text, true, Token);
|
||||
}
|
||||
public async Task SendMessageAsync(TwitchChatMessageType command, IEnumerable<string> parameters = null,
|
||||
IDictionary<string, string> tags = null, string prefix = null)
|
||||
{
|
||||
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(),
|
||||
}));
|
||||
}
|
||||
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())
|
||||
{
|
||||
message += string.Join(' ', parameters.SkipLast(1));
|
||||
message += " :" + parameters.Last();
|
||||
}
|
||||
await SendMessageAsync(message);
|
||||
}
|
||||
private async Task GetPacketsTask()
|
||||
{
|
||||
var buffer = ArraySegment<byte>.Empty;
|
||||
var stringData = "";
|
||||
while (!Token.IsCancellationRequested)
|
||||
{
|
||||
var res = await Socket.ReceiveAsync(buffer, Token);
|
||||
if (Token.IsCancellationRequested)
|
||||
return;
|
||||
stringData += Encoding.UTF8.GetString(buffer);
|
||||
var lines = stringData.Split("\r\n", StringSplitOptions.TrimEntries);
|
||||
stringData = lines.Last();
|
||||
foreach (var line in lines.SkipLast(1))
|
||||
MessageStrings.Enqueue(line);
|
||||
}
|
||||
}
|
||||
private readonly ConcurrentQueue<string> MessageStrings = new();
|
||||
private void HandleMessages()
|
||||
{
|
||||
while (MessageStrings.TryDequeue(out string message))
|
||||
{
|
||||
var tcm = TwitchChatMessage.Parse(message);
|
||||
if (tcm.MessageType == TwitchChatMessageType.PING)
|
||||
_ = SendPong(tcm);
|
||||
else if (tcm is Privmsg p)
|
||||
{
|
||||
EmitSignal(SignalName.IncomingCommand, new Command(p.DisplayName,
|
||||
false, p.Moderator, p.ChatMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
private async Task SendPong(TwitchChatMessage ping)
|
||||
{
|
||||
var pong = TwitchChatMessage.MakePong(ping);
|
||||
await SendMessageAsync(TwitchChatMessageType.PONG, ping.Parameters,
|
||||
ping.MessageTags, ping.Prefix);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user