IRC parsing, tokenization, and state handling in C#
1using System;
2using System.Net.Sockets;
3using System.Threading;
4using IRCStates;
5using IRCTokens;
6
7namespace States;
8
9internal class Client(string host, int port, string nick)
10{
11 private readonly byte[] _bytes = new byte[1024];
12 private readonly StatefulEncoder _encoder = new();
13 private readonly Server _server = new("test");
14 private readonly Socket _socket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
15
16 private void Send(string raw)
17 {
18 _encoder.Push(new(raw));
19 }
20
21 public void Start()
22 {
23 _socket.Connect(host, port);
24 while (!_socket.Connected) Thread.Sleep(1000);
25
26 Send("USER test 0 * test");
27 Send($"NICK {nick}");
28
29 while (true)
30 {
31 while (_encoder.PendingBytes.Length != 0)
32 {
33 foreach (var line in _encoder.Pop(_socket.Send(_encoder.PendingBytes)))
34 Console.WriteLine($"> {line.Format()}");
35 }
36
37 var bytesReceived = _socket.Receive(_bytes);
38 if (bytesReceived == 0)
39 {
40 Console.WriteLine("! disconnected");
41 _socket.Shutdown(SocketShutdown.Both);
42 _socket.Close();
43 break;
44 }
45
46 var receivedLines = _server.Receive(_bytes, bytesReceived);
47 foreach (var (line, _) in receivedLines)
48 {
49 Console.WriteLine($"< {line.Format()}");
50
51 switch (line.Command)
52 {
53 case Commands.Privmsg:
54 if (line.Params[1].Contains(_server.NickName))
55 Send($"PRIVMSG {line.Params[0]} :hi {line.Hostmask.NickName}!");
56 break;
57 case "PING":
58 Send($"PONG :{line.Params[0]}");
59 break;
60 case Numeric.RPL_WELCOME:
61 if (!_server.HasChannel("#irctokens")) Send("JOIN #irctokens");
62 break;
63 case "INVITE":
64 var c = line.Params[1];
65 if (!_server.HasChannel(c)) Send($"JOIN {c}");
66 break;
67 }
68 }
69 }
70 }
71}