Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Data;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Net.Security;
- using System.Net.Sockets;
- using System.Text;
- using System.Threading;
- using LunaBotConnection.EventArgs;
- using LunaBotGlobals;
- namespace LunaBotConnection
- {
- public class Connection
- {
- private static readonly string[] MessageSeparators = {"\r\n"};
- private readonly List<string> _channelList = new List<string>();
- private readonly string _user;
- private readonly string _password;
- private readonly string _nick;
- private readonly string _url;
- private readonly int _port;
- private readonly TwitchCaps[] _caps;
- private readonly int _rateLimit;
- private readonly string _name;
- private readonly ConcurrentQueue<string> _prioritySendQueue = new ConcurrentQueue<string>();
- private readonly ConcurrentQueue<string> _sendQueue = new ConcurrentQueue<string>();
- private TcpClient _client;
- private Thread _readerThread;
- private Thread _senderThread;
- private SslStream _sslstream;
- private NetworkStream _stream;
- private bool _work;
- private ConnectionState _connectionState;
- internal Connection(string user, string password, string nick, string url, int port,
- TwitchCaps[] caps, int rateLimit, string name)
- {
- _connectionState = ConnectionState.Connecting;
- _name = name;
- _user = user;
- _password = password;
- _nick = nick;
- _url = url;
- _port = port;
- _caps = caps;
- _rateLimit = rateLimit;
- ConnectionLogger.Debug(GetType(), $"{_name}: Connection created.");
- }
- public event EventHandler<MessageEventArgs> RaiseMessageEvent;
- public event EventHandler<System.EventArgs> ConnectionEstablishedEvent;
- public event EventHandler<ConnectionUnexpectedCloseEventArgs> UnexpectedCloseEvent;
- public IEnumerable<string> GetChannels()
- {
- return new List<string>(_channelList);
- }
- public bool Connect()
- {
- ConnectionLogger.Debug(GetType(), $"{_name}: Try to connect.");
- if (_client == null)
- {
- try
- {
- _client = new TcpClient();
- var result = _client.BeginConnect(_url, _port, null, null);
- var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
- if (!success)
- {
- throw new Exception("Connection Timeout");
- }
- _client.EndConnect(result);
- }
- catch (Exception ex)
- {
- ConnectionLogger.Error(GetType(), ex);
- UnexpectedClose("TCP-Client Error");
- return false;
- }
- _sslstream = null;
- _stream = null;
- }
- if (!_client.Connected)
- return false;
- ConnectionLogger.Debug(GetType(), $"{_name}: TCP-Client connected.");
- if (_stream == null)
- _stream = _client.GetStream();
- if (_sslstream == null)
- {
- try
- {
- var sslStream = new SslStream(
- _client.GetStream(),
- false,
- ConnectionManager.ValidateServerCertificate,
- null
- );
- sslStream.AuthenticateAsClient(_url);
- _sslstream = sslStream;
- }
- catch (Exception ex)
- {
- ConnectionLogger.Error(GetType(), ex);
- UnexpectedClose("SSL Error!");
- return false;
- }
- }
- ConnectionLogger.Debug(GetType(), $"{_name}: SSL check okay.");
- _work = true;
- _readerThread = new Thread(Reader);
- _readerThread.Start();
- _senderThread = new Thread(Sender);
- _senderThread.Start();
- EnqueueMessage(@"USER " + _user, true);
- EnqueueMessage(@"PASS " + _password, true);
- EnqueueMessage(@"NICK " + _nick, true);
- if (_caps != null)
- {
- foreach (var cap in _caps)
- {
- EnqueueMessage(@"CAP REQ :twitch.tv/" + cap.ToString().ToLower(), true);
- }
- }
- if (_channelList.Count != 0)
- {
- _channelList.ForEach(Join);
- }
- ConnectionLogger.Debug(GetType(), $"{_name}: Connection complete.");
- _connectionState = ConnectionState.Fetching;
- return true;
- }
- public void Join(string channel)
- {
- EnqueueMessage(@"JOIN #" + channel, true);
- if (!_channelList.Contains(channel))
- _channelList.Add(channel);
- }
- private void UnexpectedClose(string reason)
- {
- try
- {
- _work = false;
- _client?.Close();
- _stream?.Close();
- _sslstream?.Close();
- _client = null;
- _stream = null;
- _sslstream = null;
- }
- catch (Exception e)
- {
- ConnectionLogger.Error(GetType(), e);
- }
- ConnectionLogger.Debug(GetType(), $"{_name}: Closed unexpected. Reason: {reason}");
- OnUnexpectedCloseEvent(new ConnectionUnexpectedCloseEventArgs(reason, _name));
- }
- public bool Close()
- {
- try
- {
- _work = false;
- _client?.Close();
- _stream?.Close();
- _sslstream?.Close();
- _client = null;
- _stream = null;
- _sslstream = null;
- ConnectionLogger.Debug(GetType(), $"{_name}: Connection closed.");
- return true;
- }
- catch (Exception e)
- {
- ConnectionLogger.Error(
- GetType(),
- $"{_name}: Error while closing. See below for further information:"
- );
- ConnectionLogger.Error(GetType(), e);
- return false;
- }
- }
- private void EnqueueMessage(string message, bool hasPriority = false)
- {
- (hasPriority ? _prioritySendQueue : _sendQueue).Enqueue(message);
- }
- private void Sender()
- {
- while (_client != null && _client.Connected)
- {
- if (!_work)
- return;
- string message;
- if (!_prioritySendQueue.IsEmpty && _prioritySendQueue.TryDequeue(out message))
- {
- Write(message);
- ConnectionLogger.Debug(GetType(), $"{_name}: Sent priority message: " + message);
- }
- else if (!_sendQueue.IsEmpty && _sendQueue.TryDequeue(out message))
- {
- Write(message);
- ConnectionLogger.Debug(GetType(), $"{_name}: Sent message: " + message);
- Thread.Sleep(_rateLimit);
- }
- Thread.Sleep(1);
- }
- }
- private void Reader()
- {
- var buffer = new byte[1024];
- var msg = new StringBuilder();
- var badMessagesReceived = 0;
- Stream stream = _sslstream;
- while (_client != null && _client.Connected)
- {
- Thread.Sleep(1);
- if (!_work)
- return;
- if (!_stream.DataAvailable)
- continue;
- Array.Clear(buffer, 0, buffer.Length);
- stream.Read(buffer, 0, buffer.Length);
- var data = Encoding.UTF8.GetString(buffer).TrimEnd('\0');
- msg.Append(data);
- var msgstr = msg.ToString();
- if (!msgstr.EndsWith("\r\n"))
- {
- var all0 = buffer.All(b => b == 0);
- if (all0)
- {
- if (++badMessagesReceived <= 2)
- continue;
- UnexpectedClose("Received to many bad messages!");
- break;
- }
- var idx = msgstr.LastIndexOf("\r\n", StringComparison.Ordinal);
- if (idx != -1)
- {
- idx += 2;
- msg.Remove(0, idx);
- msgstr = msgstr.Substring(0, idx);
- }
- else
- continue;
- }
- else
- msg.Clear();
- var messages = msgstr.Split(MessageSeparators, StringSplitOptions.RemoveEmptyEntries);
- foreach (var message in messages)
- {
- if (_connectionState == ConnectionState.Fetching)
- {
- if (!message.StartsWith(":")) continue;
- var end = message.IndexOf(" ", StringComparison.Ordinal);
- var sub = message.Substring(end).Trim();
- end = sub.IndexOf(" ", StringComparison.Ordinal);
- var code = sub.Substring(0, end);
- if (code != "004") continue;
- _connectionState = ConnectionState.Open;
- OnConnectionEstablishedEvent(new System.EventArgs());
- }
- else
- {
- if (message.StartsWith("PING"))
- {
- EnqueueMessage(message.Replace("PING", "PONG"), true);
- ConnectionLogger.Debug(GetType(), $"{_name}: PING received -> PONG sent!");
- }
- else
- {
- ConnectionLogger.Debug(GetType(), $"{_name}: Received message: " + message);
- }
- OnRaiseMessageEvent(new MessageEventArgs(message));
- }
- }
- }
- }
- private void Write(string message)
- {
- message = message.Replace("\r\n", " ");
- var buffer = Encoding.UTF8.GetBytes(message + "\r\n");
- try
- {
- _sslstream.Write(buffer, 0, buffer.Length);
- }
- catch (ObjectDisposedException)
- {
- UnexpectedClose("Lost Connection.");
- }
- catch (IOException)
- {
- UnexpectedClose("Lost Connection.");
- }
- }
- protected virtual void OnRaiseMessageEvent(MessageEventArgs e)
- => RaiseMessageEvent?.Invoke(this, e);
- protected virtual void OnConnectionEstablishedEvent(System.EventArgs e)
- => ConnectionEstablishedEvent?.Invoke(this, e);
- protected virtual void OnUnexpectedCloseEvent(ConnectionUnexpectedCloseEventArgs e)
- => UnexpectedCloseEvent?.Invoke(this, e);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement