Decompiled source of TwitchCustomers v0.3.1

TwitchCustomers-0.3.1.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using HarmonyLib;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Economy;
using Il2CppScheduleOne.Messaging;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.Persistence;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Logging;
using TwitchCustomers;
using TwitchCustomers.HarmonyPatches;
using TwitchCustomers.HarmonyPatches.Customer;
using TwitchCustomers.HarmonyPatches.LoadManager;
using TwitchCustomers.HarmonyPatches.MSGConversation;
using TwitchCustomers.NPC;
using TwitchCustomers.TwitchIntegration;
using TwitchLib.Client;
using TwitchLib.Client.Enums;
using TwitchLib.Client.Events;
using TwitchLib.Client.Models;
using TwitchLib.Communication.Interfaces;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: HarmonyDontPatchAll]
[assembly: MelonInfo(typeof(Mod), "TwitchCustomers", "0.3.1", "ReservedKeyword", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: MelonColor(1, 160, 32, 240)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("TwitchCustomers")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.3.1")]
[assembly: AssemblyInformationalVersion("1.0.0+40b1757cc4e8b973e747407a8f3748c238e42c9e")]
[assembly: AssemblyProduct("TwitchCustomers")]
[assembly: AssemblyTitle("TwitchCustomers")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TwitchCustomers
{
	public class Constants
	{
		public const string ENTRY_TRANSFORM_FIND_NAME = "Name";

		public const string ENTRY_TRANSFORM_FIND_CATEGORY = "Category";

		public const float ENTRY_TRANSFORM_CATEGORY_MARGIN_RIGHT = 225f;

		public static string ToHarmonyID()
		{
			return "TwitchCustomers-MelonLoader.0.3.1";
		}
	}
	public class Mod : MelonMod
	{
		private Harmony harmony;

		public static Mod Instance { get; private set; }

		public ModConfig ModConfig { get; private set; }

		public ChatterManager ChatterManager { get; private set; }

		public CachedNPCManager CachedNPCManger { get; private set; }

		public override void OnInitializeMelon()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			Instance = this;
			ModConfig = new ModConfig();
			if (!ModConfig.IsEnabled)
			{
				((MelonBase)this).LoggerInstance.Msg("Plugin disabled in configuration file, won't proceed...");
				return;
			}
			ChatterManager = new ChatterManager(this, ModConfig);
			ChatterManager.Connect();
			CachedNPCManger = new CachedNPCManager(((MelonBase)this).LoggerInstance);
			harmony = new Harmony(Constants.ToHarmonyID());
			List<IPatchModule> list = new List<IPatchModule>(3)
			{
				new CustomerPatchModule(),
				new LoadManagerPatchModule(),
				new MSGConversationPatchModule()
			};
			foreach (IPatchModule item in list)
			{
				item.Setup(this);
			}
			harmony.PatchAll();
			((MelonBase)this).LoggerInstance.Msg("Mod has finished initialization process!");
		}
	}
	public class ModConfig
	{
		public bool IsEnabled { get; private set; }

		public bool PreserveOriginalNPCName { get; private set; }

		public string ChannelName { get; private set; }

		public List<string> BlocklistedChatters { get; private set; }

		public string MessageCommand { get; private set; }

		public double SubscriberWeight { get; private set; }

		public int QueueSize { get; private set; }

		public ModConfig()
		{
			MelonPreferences_Category val = MelonPreferences.CreateCategory("TwitchCustomers_General", "Twitch Customers - General");
			IsEnabled = val.CreateEntry<bool>("Enable Mod", true, (string)null, "If true, plugin will initialize when loaded.", false, false, (ValueValidator)null, (string)null).Value;
			PreserveOriginalNPCName = val.CreateEntry<bool>("Preserve Original NPC Name", true, (string)null, "If true, NPC names are reset once a deal has finished.", false, false, (ValueValidator)null, (string)null).Value;
			MelonPreferences_Category val2 = MelonPreferences.CreateCategory("TwitchCustomers_Integration", "Twitch Customers - Twitch Integration");
			ChannelName = val2.CreateEntry<string>("Channel Name", "reservedkeyword", (string)null, "Twitch channel to listen to messages in (no authentication needed).", false, false, (ValueValidator)null, (string)null).Value;
			BlocklistedChatters = val2.CreateEntry<string>("Blocklisted Chatters", "", (string)null, "Comma-separated list of chatter usernames to not process messages from.", false, false, (ValueValidator)null, (string)null).Value.Split(",").ToList();
			MessageCommand = val2.CreateEntry<string>("Message Command", "!iwannabeacustomer", (string)null, "Unique command that registers a Twitch chatter's intent to be a non-playable, in-game character.", false, false, (ValueValidator)null, (string)null).Value;
			SubscriberWeight = val2.CreateEntry<double>("Subscriber Weight", 1.2, (string)null, "Weight value that makes subscribers more likely to get picked. (For example, 1.2 means subscribers are 20% luckier.)", false, false, (ValueValidator)null, (string)null).Value;
			QueueSize = val2.CreateEntry<int>("Queue Size", 100, (string)null, "Limit of **unique** chatters, subscribers and non-subscribers combined, to keep in queue. Mod will refuse above limit.", false, false, (ValueValidator)null, (string)null).Value;
		}
	}
}
namespace TwitchCustomers.TwitchIntegration
{
	public class ChatterManager
	{
		private enum TargetGroup
		{
			Subscriber,
			NonSubscriber,
			None
		}

		private const double NON_SUBSCRIBER_WEIGHT = 1.0;

		private readonly string channelName = modConfig.ChannelName;

		private readonly List<string> blocklistedChatters = modConfig.BlocklistedChatters;

		private readonly string messageCommand = modConfig.MessageCommand;

		private readonly double subscriberWeight = modConfig.SubscriberWeight;

		private readonly int queueSize = modConfig.QueueSize;

		private TwitchClient client;

		private readonly Instance log = ((MelonBase)mod).LoggerInstance;

		private readonly object chattersLock = new object();

		private readonly HashSet<string> subscriberParticipants = new HashSet<string>();

		private readonly HashSet<string> nonSubscriberParticipants = new HashSet<string>();

		private readonly Random random = new Random();

		public ChatterManager(Mod mod, ModConfig modConfig)
		{
		}

		public void Connect()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			ConnectionCredentials val = new ConnectionCredentials("justinfan1234567", "", "wss://irc-ws.chat.twitch.tv:443", false, (Capabilities)null);
			client = new TwitchClient((IClient)null, (ClientProtocol)1, (ILogger<TwitchClient>)null);
			client.Initialize(val, channelName, '!', '!', true);
			client.OnConnected += Client_OnConnected;
			client.OnConnectionError += Client_OnConnectionError;
			client.OnJoinedChannel += Client_OnJoinedChannel;
			client.OnMessageReceived += Client_OnMessageReceived;
			client.Connect();
			log.Msg("Attempting to connect to Twitch IRC client...");
		}

		private void Client_OnConnected(object sender, OnConnectedArgs e)
		{
			log.Msg("Connected to Twitch IRC client.");
			client.JoinChannel(channelName, false);
			log.Msg("Attempting to join channel " + channelName + " as anonymous user...");
		}

		private void Client_OnConnectionError(object sender, OnConnectionErrorArgs e)
		{
			log.Error("Failed to connect to Twitch IRC client!");
			log.Error(e.Error.Message);
		}

		private void Client_OnJoinedChannel(object sender, OnJoinedChannelArgs e)
		{
			log.Msg("Joined " + channelName + "'s Twitch channel as anonymous user.");
		}

		private void Client_OnMessageReceived(object sender, OnMessageReceivedArgs e)
		{
			string displayName = ((TwitchLibMessage)e.ChatMessage).DisplayName;
			bool isSubscriber = e.ChatMessage.IsSubscriber;
			if (blocklistedChatters.Contains<string>(displayName, StringComparer.CurrentCultureIgnoreCase))
			{
				log.Msg("Detected blocklisted chatter " + displayName + ", will not add to queue.");
			}
			else
			{
				if (!e.ChatMessage.Message.Contains(messageCommand))
				{
					return;
				}
				lock (chattersLock)
				{
					if (GetTotalParticipants() < queueSize)
					{
						if (isSubscriber)
						{
							subscriberParticipants.Add(displayName);
							log.Msg("Twitch chatter " + displayName + " (subscriber) added as participant.");
						}
						else
						{
							nonSubscriberParticipants.Add(displayName);
							log.Msg("Twitch chatter " + displayName + " (non-subscriber) added as participant.");
						}
					}
				}
			}
		}

		private (double totalWeight, double subscriberWeightTotal) CalculateWeights()
		{
			double num = (double)subscriberParticipants.Count * subscriberWeight;
			double num2 = (double)nonSubscriberParticipants.Count * 1.0;
			return (num + num2, num);
		}

		private TargetGroup DetermineTargetGroup(double randomPick, double subscriberWeightTotal)
		{
			bool flag = randomPick < subscriberWeightTotal;
			int count = subscriberParticipants.Count;
			int count2 = nonSubscriberParticipants.Count;
			if (flag && count > 0)
			{
				return TargetGroup.Subscriber;
			}
			if (count2 > 0)
			{
				return TargetGroup.NonSubscriber;
			}
			if (count > 0)
			{
				return TargetGroup.Subscriber;
			}
			return TargetGroup.None;
		}

		public string GetRandomChatter()
		{
			lock (chattersLock)
			{
				if (GetTotalParticipants() == 0)
				{
					log.Warning("No chatters found, nothing to return.");
					return null;
				}
				var (num, subscriberWeightTotal) = CalculateWeights();
				if (num <= 0.0)
				{
					log.Error("Total weight is zero or negative. Cannot perform weighted pick.");
					return null;
				}
				double randomPick = random.NextDouble() * num;
				TargetGroup targetGroup = DetermineTargetGroup(randomPick, subscriberWeightTotal);
				string text = null;
				bool flag = false;
				switch (targetGroup)
				{
				case TargetGroup.Subscriber:
				{
					int index2 = random.Next(subscriberParticipants.Count);
					text = subscriberParticipants.ElementAt(index2);
					subscriberParticipants.Remove(text);
					log.Msg("Selected winner (subscriber): " + text);
					flag = true;
					break;
				}
				case TargetGroup.NonSubscriber:
				{
					int index = random.Next(nonSubscriberParticipants.Count);
					text = nonSubscriberParticipants.ElementAt(index);
					nonSubscriberParticipants.Remove(text);
					log.Msg("Selected winner (non-subscriber): " + text);
					flag = true;
					break;
				}
				default:
					log.Warning("No target group determined for selection.");
					flag = false;
					break;
				}
				if (!flag && GetTotalParticipants() > 0)
				{
					log.Error($"Failed to select winner event thought participants exist (Target Group: {targetGroup}).");
					return null;
				}
				log.Msg($"Returning winner: {text}. Remaining participants: {GetTotalParticipants()}.");
				return text;
			}
		}

		private int GetTotalParticipants()
		{
			return subscriberParticipants.Count + nonSubscriberParticipants.Count;
		}
	}
}
namespace TwitchCustomers.NPC
{
	public class CachedNPC
	{
		[CompilerGenerated]
		private sealed class <UpdateConversationDisplayName_Coroutine>d__22 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public string newDisplayName;

			public CachedNPC <>4__this;

			private Text <nameTextComponent>5__1;

			private Exception <ex>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <UpdateConversationDisplayName_Coroutine>d__22(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<nameTextComponent>5__1 = null;
				<ex>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				if ((Object)(object)<>4__this.UnityRectEntry == (Object)null)
				{
					log.Warning("Can't update conversation display name, UnityRectEntry was null.");
					return false;
				}
				try
				{
					<nameTextComponent>5__1 = <>4__this.GetConversationTextComponent();
					if ((Object)(object)<nameTextComponent>5__1 == (Object)null)
					{
						log.Warning("Can't update conversation display name, nameTextComponent was null");
						return false;
					}
					<nameTextComponent>5__1.text = newDisplayName;
					<>4__this.UpdateConversationCategoryPadding();
					log.Msg($"NPC {<>4__this.NPCGUID} text component updated successfully!");
					<nameTextComponent>5__1 = null;
				}
				catch (Exception ex)
				{
					<ex>5__2 = ex;
					log.Error($"Exception while updating conversation display name: {<ex>5__2}.");
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly Mod mod = Mod.Instance;

		private static readonly Instance log = ((MelonBase)mod).LoggerInstance;

		public Guid NPCGUID { get; private set; }

		public RectTransform UnityRectEntry { get; set; }

		public OriginalNPCName OriginalNPCName { get; private set; }

		public CachedNPC(NPC gameNpc)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			NPCGUID = gameNpc.GUID;
			OriginalNPCName = new OriginalNPCName(gameNpc.FirstName, gameNpc.LastName);
		}

		public CachedNPC(NPC gameNpc, RectTransform unityRectEntry)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			NPCGUID = gameNpc.GUID;
			UnityRectEntry = unityRectEntry;
			OriginalNPCName = new OriginalNPCName(gameNpc.FirstName, gameNpc.LastName);
		}

		private Text GetConversationTextComponent()
		{
			if ((Object)(object)UnityRectEntry == (Object)null)
			{
				return null;
			}
			Transform val = ((Transform)UnityRectEntry).Find("Name");
			if ((Object)(object)val == (Object)null)
			{
				log.Warning("Could not find 'Name' child transform on " + ((Object)UnityRectEntry).name + ".");
				return null;
			}
			Text component = ((Component)val).GetComponent<Text>();
			if ((Object)(object)component == (Object)null)
			{
				log.Warning("'Name' transform on " + ((Object)UnityRectEntry).name + " is missing Text component.");
				return null;
			}
			return component;
		}

		private NPC GetGameNPC()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (NPCManager.NPCRegistry == null)
			{
				log.Error("Cannot fetch Game NPC, NPCRegistry was found null.");
				return null;
			}
			Enumerator<NPC> enumerator = NPCManager.NPCRegistry.GetEnumerator();
			while (enumerator.MoveNext())
			{
				NPC current = enumerator.Current;
				if ((Object)(object)current != (Object)null && current.GUID == NPCGUID)
				{
					return current;
				}
			}
			return null;
		}

		public void ResetCharacterName()
		{
			UpdateCharacterName(OriginalNPCName.FirstName, OriginalNPCName.LastName);
		}

		public void ResetConversationDisplayName()
		{
			UpdateConversationDisplayName(OriginalNPCName.FirstName + " " + OriginalNPCName.LastName);
		}

		public void UpdateCharacterName(string firstName, string lastName = "")
		{
			NPC gameNPC = GetGameNPC();
			if ((Object)(object)gameNPC == (Object)null)
			{
				log.Error("Failed to update character name, NPC not found in game registry.");
				return;
			}
			gameNPC.FirstName = firstName;
			gameNPC.LastName = lastName;
		}

		public void UpdateConversationDisplayName(string contactName)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)NetworkSingleton<MessagingManager>.instance == (Object)null)
			{
				log.Error("MessagingManager.instance is null!");
				return;
			}
			NPC gameNPC = GetGameNPC();
			if ((Object)(object)gameNPC == (Object)null)
			{
				log.Error("Failed to update conversation display name, NPC not found in game registry.");
				return;
			}
			MSGConversation conversation = NetworkSingleton<MessagingManager>.instance.GetConversation(gameNPC);
			if (conversation == null)
			{
				log.Warning($"Could not get MSGConversation for {((gameNPC != null) ? new Guid?(gameNPC.GUID) : null)}. Cannot update contactName.");
			}
			else
			{
				conversation.contactName = contactName;
				MelonCoroutines.Start(UpdateConversationDisplayName_Coroutine(contactName));
			}
		}

		[IteratorStateMachine(typeof(<UpdateConversationDisplayName_Coroutine>d__22))]
		private IEnumerator UpdateConversationDisplayName_Coroutine(string newDisplayName)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <UpdateConversationDisplayName_Coroutine>d__22(0)
			{
				<>4__this = this,
				newDisplayName = newDisplayName
			};
		}

		private void UpdateConversationCategoryPadding()
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)UnityRectEntry == (Object)null)
			{
				log.Warning("Can't update conversation display name, UnityRectEntry was null.");
				return;
			}
			Text conversationTextComponent = GetConversationTextComponent();
			if ((Object)(object)conversationTextComponent == (Object)null)
			{
				log.Warning("Can't update conversation display name, nameTextComponent was null");
				return;
			}
			float preferredWidth = conversationTextComponent.preferredWidth;
			Transform obj = ((Transform)UnityRectEntry).Find("Category");
			RectTransform val = ((obj != null) ? ((Component)obj).GetComponent<RectTransform>() : null);
			if ((Object)(object)val == (Object)null)
			{
				log.Warning("Could not find 'Category' child transform (or RectTransform) on " + ((Object)UnityRectEntry).name + ".");
				return;
			}
			Vector2 anchoredPosition = val.anchoredPosition;
			anchoredPosition.x = preferredWidth + 225f;
			val.anchoredPosition = anchoredPosition;
		}
	}
	public class CachedNPCManager
	{
		private readonly Instance log;

		private readonly Dictionary<Guid, CachedNPC> cachedNpcsByGuid;

		public CachedNPCManager(Instance log)
		{
			this.log = log;
			cachedNpcsByGuid = new Dictionary<Guid, CachedNPC>();
			base..ctor();
		}

		public bool AddCachedNPC(CachedNPC npc)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (npc != null)
			{
				_ = npc.NPCGUID;
				if (0 == 0)
				{
					Guid nPCGUID = npc.NPCGUID;
					return cachedNpcsByGuid.TryAdd(nPCGUID, npc);
				}
			}
			log.Warning("Attempted to add a null CachedNPC or one with a null GameNPC.");
			return false;
		}

		public void Clear()
		{
			cachedNpcsByGuid.Clear();
		}

		public CachedNPC GetFromGameNPC(NPC gameNPC)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)gameNPC == (Object)null)
			{
				return null;
			}
			Guid gUID = gameNPC.GUID;
			if (cachedNpcsByGuid.TryGetValue(gUID, out var value))
			{
				return value;
			}
			return null;
		}
	}
	public class OriginalNPCName
	{
		public string FirstName { get; private set; }

		public string LastName { get; private set; }

		public OriginalNPCName(string firstName, string lastName)
		{
			FirstName = firstName;
			LastName = lastName;
			base..ctor();
		}
	}
}
namespace TwitchCustomers.HarmonyPatches
{
	public interface IPatchModule
	{
		Type StaticWrapperType { get; }

		void Setup(Mod mod);
	}
	public abstract class PatchModule<TLogic> : IPatchModule where TLogic : class
	{
		public abstract Type StaticWrapperType { get; }

		protected TLogic LogicInstance { get; private set; }

		public abstract TLogic CreateLogicInstance(Mod mod);

		public abstract void InitializeStaticWrapper(Instance log);

		public virtual void Setup(Mod mod)
		{
			Instance loggerInstance = ((MelonBase)mod).LoggerInstance;
			if (LogicInstance != null)
			{
				loggerInstance.Warning("PatchModule for " + StaticWrapperType.Name + " already initialized. Skipping...");
				return;
			}
			LogicInstance = CreateLogicInstance(mod);
			if (LogicInstance == null)
			{
				throw new InvalidOperationException("CreateLogicInstance for " + GetType().Name + " did not set the LogicInstance property.");
			}
			InitializeStaticWrapper(((MelonBase)mod).LoggerInstance);
		}
	}
}
namespace TwitchCustomers.HarmonyPatches.MSGConversation
{
	public class MSGConversationPatchLogic
	{
		private readonly CachedNPCManager cachedNpcManager = mod.CachedNPCManger;

		private readonly Instance log = ((MelonBase)mod).LoggerInstance;

		public MSGConversationPatchLogic(Mod mod)
		{
		}

		public void CreateUI_Postfix(MSGConversation __instance)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			NPC sender = __instance.sender;
			RectTransform entry = __instance.entry;
			CachedNPC fromGameNPC = cachedNpcManager.GetFromGameNPC(sender);
			if (fromGameNPC != null)
			{
				fromGameNPC.UnityRectEntry = entry;
				log.Msg($"NPC {sender.GUID} already found cached, updated Unity container.");
			}
			else
			{
				cachedNpcManager.AddCachedNPC(new CachedNPC(sender, entry));
				log.Msg($"NPC {sender.GUID} not found cached, cached for future use.");
			}
		}
	}
	public class MSGConversationPatchModule : PatchModule<MSGConversationPatchLogic>
	{
		public override Type StaticWrapperType => typeof(MSGConversationPatchWrapper);

		public override MSGConversationPatchLogic CreateLogicInstance(Mod mod)
		{
			((MelonBase)mod).LoggerInstance.Msg("LoadManagerPatchModule created & bound LoadManagerPatchLogic instance.");
			return new MSGConversationPatchLogic(mod);
		}

		public override void InitializeStaticWrapper(Instance log)
		{
			MSGConversationPatchLogic logicInstance = base.LogicInstance;
			if (logicInstance != null)
			{
				MSGConversationPatchWrapper.Initialize(logicInstance);
				return;
			}
			log.Error("LogicInstance is not type MSGConversationPatchLogic in MSGConversationPatchModule. Type is $" + (base.LogicInstance?.GetType().Name ?? "unknown") + ".");
			throw new InvalidOperationException("LogicInstance is not type MSGConversationPatchLogic for " + GetType().Name + ".");
		}
	}
	[HarmonyPatch(typeof(MSGConversation))]
	public static class MSGConversationPatchWrapper
	{
		private static MSGConversationPatchLogic patchLogic;

		public static void Initialize(MSGConversationPatchLogic patchLogic)
		{
			MSGConversationPatchWrapper.patchLogic = patchLogic;
		}

		[HarmonyPatch("CreateUI")]
		[HarmonyPostfix]
		public static void CreateUI_Postfix_Wrapper(MSGConversation __instance)
		{
			patchLogic?.CreateUI_Postfix(__instance);
		}
	}
}
namespace TwitchCustomers.HarmonyPatches.LoadManager
{
	public class LoadManagerPatchLogic
	{
		private readonly CachedNPCManager cachedNpcManager = mod.CachedNPCManger;

		private readonly Instance log = ((MelonBase)mod).LoggerInstance;

		public LoadManagerPatchLogic(Mod mod)
		{
		}

		public void ExitToMenu_Postfix()
		{
			log.Msg("Player exited to main menu, clearing NPC cache...");
			cachedNpcManager.Clear();
		}
	}
	public class LoadManagerPatchModule : PatchModule<LoadManagerPatchLogic>
	{
		public override Type StaticWrapperType => typeof(LoadManagerPatchWrapper);

		public override LoadManagerPatchLogic CreateLogicInstance(Mod mod)
		{
			((MelonBase)mod).LoggerInstance.Msg("LoadManagerPatchModule created & bound LoadManagerPatchLogic instance.");
			return new LoadManagerPatchLogic(mod);
		}

		public override void InitializeStaticWrapper(Instance log)
		{
			LoadManagerPatchLogic logicInstance = base.LogicInstance;
			if (logicInstance != null)
			{
				LoadManagerPatchWrapper.Initialize(logicInstance);
				return;
			}
			log.Error("LogicInstance is not type LoadManagerPatchLogic in LoadManagerPatchModule. Type is $" + (base.LogicInstance?.GetType().Name ?? "unknown") + ".");
			throw new InvalidOperationException("LogicInstance is not type LoadManagerPatchLogic for " + GetType().Name + ".");
		}
	}
	[HarmonyPatch(typeof(LoadManager))]
	public static class LoadManagerPatchWrapper
	{
		private static LoadManagerPatchLogic patchLogic;

		public static void Initialize(LoadManagerPatchLogic patchLogic)
		{
			LoadManagerPatchWrapper.patchLogic = patchLogic;
		}

		[HarmonyPatch("ExitToMenu")]
		[HarmonyPostfix]
		public static void ExitToMenu_Postfix_Wrapper()
		{
			patchLogic?.ExitToMenu_Postfix();
		}
	}
}
namespace TwitchCustomers.HarmonyPatches.Customer
{
	public class CustomerPatchLogic
	{
		private readonly CachedNPCManager cachedNpcManager = mod.CachedNPCManger;

		private readonly ChatterManager chatterManager = mod.ChatterManager;

		private readonly Instance log = ((MelonBase)mod).LoggerInstance;

		private readonly ModConfig modConfig = mod.ModConfig;

		public CustomerPatchLogic(Mod mod)
		{
		}

		public void ContractRejected_Postfix(Customer __instance)
		{
			ResetCustomerIfPreserveOriginalNPC(__instance);
		}

		public void CurrentContractEnded_Postfix(Customer __instance)
		{
			ResetCustomerIfPreserveOriginalNPC(__instance);
		}

		public void EvaluateCounterOffer_Postfix(Customer __instance, bool __result)
		{
			if (!__result)
			{
				ResetCustomerIfPreserveOriginalNPC(__instance);
			}
		}

		public void ExpireOffer_Prefix(Customer __instance)
		{
			ResetCustomerIfPreserveOriginalNPC(__instance);
		}

		public void NotifyPlayerOfContract_Prefix(Customer __instance)
		{
			string randomChatter = chatterManager.GetRandomChatter();
			if (string.IsNullOrEmpty(randomChatter))
			{
				log.Warning("No chatter found for contract notification, skipping name update.");
				return;
			}
			NPC val = ((__instance != null) ? __instance.NPC : null);
			CachedNPC cachedNPC = cachedNpcManager.GetFromGameNPC(val);
			if (cachedNPC == null)
			{
				cachedNPC = new CachedNPC(val);
				cachedNpcManager.AddCachedNPC(cachedNPC);
			}
			cachedNPC.UpdateCharacterName(randomChatter);
			cachedNPC.UpdateConversationDisplayName(randomChatter);
			log.Msg("Updated in-game NPC with Twitch chatter: " + randomChatter + ".");
		}

		private void ResetCustomerIfPreserveOriginalNPC(Customer customer)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (modConfig.PreserveOriginalNPCName)
			{
				NPC nPC = customer.NPC;
				CachedNPC fromGameNPC = cachedNpcManager.GetFromGameNPC(nPC);
				if (fromGameNPC == null)
				{
					log.Warning($"Failed to find cached NPC {nPC.GUID}. Unable to reset customer.");
				}
				else
				{
					fromGameNPC.ResetCharacterName();
					fromGameNPC.ResetConversationDisplayName();
				}
			}
		}
	}
	public class CustomerPatchModule : PatchModule<CustomerPatchLogic>
	{
		public override Type StaticWrapperType => typeof(CustomerPatchWrapper);

		public override CustomerPatchLogic CreateLogicInstance(Mod mod)
		{
			((MelonBase)mod).LoggerInstance.Msg("CustomerPatchModule created & bound CustomerPatchLogic instance.");
			return new CustomerPatchLogic(mod);
		}

		public override void InitializeStaticWrapper(Instance log)
		{
			CustomerPatchLogic logicInstance = base.LogicInstance;
			if (logicInstance != null)
			{
				CustomerPatchWrapper.Initialize(logicInstance);
				return;
			}
			log.Error("LogicInstance is not type CustomerPatchLogic in CustomerPatchModule. Type is $" + (base.LogicInstance?.GetType().Name ?? "unknown") + ".");
			throw new InvalidOperationException("LogicInstance is not type CustomerPatchLogic for " + GetType().Name + ".");
		}
	}
	[HarmonyPatch(typeof(Customer))]
	public static class CustomerPatchWrapper
	{
		private static CustomerPatchLogic patchLogic;

		public static void Initialize(CustomerPatchLogic patchLogic)
		{
			CustomerPatchWrapper.patchLogic = patchLogic;
		}

		[HarmonyPatch("ContractRejected")]
		[HarmonyPostfix]
		public static void ContractRejected_Postfix_Wrapper(Customer __instance)
		{
			patchLogic?.ContractRejected_Postfix(__instance);
		}

		[HarmonyPatch("CurrentContractEnded")]
		[HarmonyPostfix]
		public static void CurrentContractEnded_Postfix_Wrapper(Customer __instance)
		{
			patchLogic?.CurrentContractEnded_Postfix(__instance);
		}

		[HarmonyPatch("EvaluateCounteroffer")]
		[HarmonyPostfix]
		public static void EvaluateCounterOffer_Postfix_Wrapper(Customer __instance, bool __result)
		{
			patchLogic?.EvaluateCounterOffer_Postfix(__instance, __result);
		}

		[HarmonyPatch("ExpireOffer")]
		[HarmonyPrefix]
		public static void ExpireOffer_Prefix_Wrapper(Customer __instance)
		{
			patchLogic?.ExpireOffer_Prefix(__instance);
		}

		[HarmonyPatch("NotifyPlayerOfContract")]
		[HarmonyPrefix]
		public static void NotifyPlayerOfContract_Prefix_Wrapper(Customer __instance)
		{
			patchLogic?.NotifyPlayerOfContract_Prefix(__instance);
		}
	}
}

TwitchLib.Client.dll

Decompiled a month ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using Microsoft.Extensions.Logging;
using TwitchLib.Client.Enums;
using TwitchLib.Client.Enums.Internal;
using TwitchLib.Client.Events;
using TwitchLib.Client.Exceptions;
using TwitchLib.Client.Interfaces;
using TwitchLib.Client.Internal;
using TwitchLib.Client.Internal.Parsing;
using TwitchLib.Client.Manager;
using TwitchLib.Client.Models;
using TwitchLib.Client.Models.Internal;
using TwitchLib.Communication.Clients;
using TwitchLib.Communication.Events;
using TwitchLib.Communication.Interfaces;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright 2022")]
[assembly: AssemblyDescription("Client component of TwitchLib. This component allows you to access Twitch chat and whispers, as well as the events that are sent over this connection.")]
[assembly: AssemblyFileVersion("3.3.1")]
[assembly: AssemblyInformationalVersion("3.3.1")]
[assembly: AssemblyProduct("TwitchLib.Client")]
[assembly: AssemblyTitle("TwitchLib.Client")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TwitchLib/TwitchLib.Client")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("3.3.1.0")]
namespace TwitchLib.Client
{
	public class TwitchClient : ITwitchClient
	{
		private IClient _client;

		private MessageEmoteCollection _channelEmotes = new MessageEmoteCollection();

		private readonly ICollection<char> _chatCommandIdentifiers = new HashSet<char>();

		private readonly ICollection<char> _whisperCommandIdentifiers = new HashSet<char>();

		private readonly Queue<JoinedChannel> _joinChannelQueue = new Queue<JoinedChannel>();

		private readonly ILogger<TwitchClient> _logger;

		private readonly ClientProtocol _protocol;

		private bool _currentlyJoiningChannels;

		private Timer _joinTimer;

		private List<KeyValuePair<string, DateTime>> _awaitingJoins;

		private readonly IrcParser _ircParser;

		private readonly JoinedChannelManager _joinedChannelManager;

		private readonly List<string> _hasSeenJoinedChannels = new List<string>();

		private string _lastMessageSent;

		public Version Version => Assembly.GetEntryAssembly().GetName().Version;

		public bool IsInitialized => _client != null;

		public IReadOnlyList<JoinedChannel> JoinedChannels => _joinedChannelManager.GetJoinedChannels();

		public string TwitchUsername { get; private set; }

		public WhisperMessage PreviousWhisper { get; private set; }

		public bool IsConnected
		{
			get
			{
				if (!IsInitialized || _client == null)
				{
					return false;
				}
				return _client.IsConnected;
			}
		}

		public MessageEmoteCollection ChannelEmotes => _channelEmotes;

		public bool DisableAutoPong { get; set; }

		public bool WillReplaceEmotes { get; set; }

		public ConnectionCredentials ConnectionCredentials { get; private set; }

		public bool AutoReListenOnException { get; set; }

		public event EventHandler<OnAnnouncementArgs> OnAnnouncement;

		public event EventHandler<OnVIPsReceivedArgs> OnVIPsReceived;

		public event EventHandler<OnLogArgs> OnLog;

		public event EventHandler<OnConnectedArgs> OnConnected;

		public event EventHandler<OnJoinedChannelArgs> OnJoinedChannel;

		public event EventHandler<OnIncorrectLoginArgs> OnIncorrectLogin;

		public event EventHandler<OnChannelStateChangedArgs> OnChannelStateChanged;

		public event EventHandler<OnUserStateChangedArgs> OnUserStateChanged;

		public event EventHandler<OnMessageReceivedArgs> OnMessageReceived;

		public event EventHandler<OnWhisperReceivedArgs> OnWhisperReceived;

		public event EventHandler<OnMessageSentArgs> OnMessageSent;

		public event EventHandler<OnWhisperSentArgs> OnWhisperSent;

		public event EventHandler<OnChatCommandReceivedArgs> OnChatCommandReceived;

		public event EventHandler<OnWhisperCommandReceivedArgs> OnWhisperCommandReceived;

		public event EventHandler<OnUserJoinedArgs> OnUserJoined;

		public event EventHandler<OnModeratorJoinedArgs> OnModeratorJoined;

		public event EventHandler<OnModeratorLeftArgs> OnModeratorLeft;

		public event EventHandler<OnMessageClearedArgs> OnMessageCleared;

		public event EventHandler<OnNewSubscriberArgs> OnNewSubscriber;

		public event EventHandler<OnReSubscriberArgs> OnReSubscriber;

		public event EventHandler<OnPrimePaidSubscriberArgs> OnPrimePaidSubscriber;

		public event EventHandler<OnExistingUsersDetectedArgs> OnExistingUsersDetected;

		public event EventHandler<OnUserLeftArgs> OnUserLeft;

		public event EventHandler<OnDisconnectedEventArgs> OnDisconnected;

		public event EventHandler<OnConnectionErrorArgs> OnConnectionError;

		public event EventHandler<OnChatClearedArgs> OnChatCleared;

		public event EventHandler<OnUserTimedoutArgs> OnUserTimedout;

		public event EventHandler<OnLeftChannelArgs> OnLeftChannel;

		public event EventHandler<OnUserBannedArgs> OnUserBanned;

		public event EventHandler<OnModeratorsReceivedArgs> OnModeratorsReceived;

		public event EventHandler<OnChatColorChangedArgs> OnChatColorChanged;

		public event EventHandler<OnSendReceiveDataArgs> OnSendReceiveData;

		public event EventHandler<OnRaidNotificationArgs> OnRaidNotification;

		public event EventHandler<OnGiftedSubscriptionArgs> OnGiftedSubscription;

		public event EventHandler<OnCommunitySubscriptionArgs> OnCommunitySubscription;

		public event EventHandler<OnContinuedGiftedSubscriptionArgs> OnContinuedGiftedSubscription;

		public event EventHandler<OnMessageThrottledEventArgs> OnMessageThrottled;

		public event EventHandler<OnWhisperThrottledEventArgs> OnWhisperThrottled;

		public event EventHandler<OnErrorEventArgs> OnError;

		public event EventHandler<OnReconnectedEventArgs> OnReconnected;

		public event EventHandler<OnRequiresVerifiedEmailArgs> OnRequiresVerifiedEmail;

		public event EventHandler<OnRequiresVerifiedPhoneNumberArgs> OnRequiresVerifiedPhoneNumber;

		public event EventHandler<OnRateLimitArgs> OnRateLimit;

		public event EventHandler<OnDuplicateArgs> OnDuplicate;

		public event EventHandler<OnBannedEmailAliasArgs> OnBannedEmailAlias;

		public event EventHandler OnSelfRaidError;

		public event EventHandler OnNoPermissionError;

		public event EventHandler OnRaidedChannelIsMatureAudience;

		public event EventHandler<OnFailureToReceiveJoinConfirmationArgs> OnFailureToReceiveJoinConfirmation;

		public event EventHandler<OnFollowersOnlyArgs> OnFollowersOnly;

		public event EventHandler<OnSubsOnlyArgs> OnSubsOnly;

		public event EventHandler<OnEmoteOnlyArgs> OnEmoteOnly;

		public event EventHandler<OnSuspendedArgs> OnSuspended;

		public event EventHandler<OnBannedArgs> OnBanned;

		public event EventHandler<OnSlowModeArgs> OnSlowMode;

		public event EventHandler<OnR9kModeArgs> OnR9kMode;

		public event EventHandler<OnUserIntroArgs> OnUserIntro;

		public event EventHandler<OnUnaccountedForArgs> OnUnaccountedFor;

		public TwitchClient(IClient client = null, ClientProtocol protocol = 1, ILogger<TwitchClient> logger = null)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			_logger = logger;
			_client = client;
			_protocol = protocol;
			_joinedChannelManager = new JoinedChannelManager();
			_ircParser = new IrcParser();
		}

		public void Initialize(ConnectionCredentials credentials, string channel = null, char chatCommandIdentifier = '!', char whisperCommandIdentifier = '!', bool autoReListenOnExceptions = true)
		{
			if (channel != null && channel[0] == '#')
			{
				channel = channel.Substring(1);
			}
			initializeHelper(credentials, new List<string> { channel }, chatCommandIdentifier, whisperCommandIdentifier, autoReListenOnExceptions);
		}

		public void Initialize(ConnectionCredentials credentials, List<string> channels, char chatCommandIdentifier = '!', char whisperCommandIdentifier = '!', bool autoReListenOnExceptions = true)
		{
			channels = channels.Select((string x) => (x[0] != '#') ? x : x.Substring(1)).ToList();
			initializeHelper(credentials, channels, chatCommandIdentifier, whisperCommandIdentifier, autoReListenOnExceptions);
		}

		private void initializeHelper(ConnectionCredentials credentials, List<string> channels, char chatCommandIdentifier = '!', char whisperCommandIdentifier = '!', bool autoReListenOnExceptions = true)
		{
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Expected O, but got Unknown
			Log($"TwitchLib-TwitchClient initialized, assembly version: {Assembly.GetExecutingAssembly().GetName().Version}");
			ConnectionCredentials = credentials;
			TwitchUsername = ConnectionCredentials.TwitchUsername;
			if (chatCommandIdentifier != 0)
			{
				_chatCommandIdentifiers.Add(chatCommandIdentifier);
			}
			if (whisperCommandIdentifier != 0)
			{
				_whisperCommandIdentifiers.Add(whisperCommandIdentifier);
			}
			AutoReListenOnException = autoReListenOnExceptions;
			if (channels != null && channels.Count > 0)
			{
				int i;
				for (i = 0; i < channels.Count; i++)
				{
					if (!string.IsNullOrEmpty(channels[i]))
					{
						if (((IEnumerable<JoinedChannel>)JoinedChannels).FirstOrDefault((Func<JoinedChannel, bool>)((JoinedChannel x) => x.Channel.ToLower() == channels[i])) != null)
						{
							return;
						}
						_joinChannelQueue.Enqueue(new JoinedChannel(channels[i]));
					}
				}
			}
			InitializeClient();
		}

		private void InitializeClient()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			if (_client == null)
			{
				ClientProtocol protocol = _protocol;
				if ((int)protocol != 0)
				{
					if ((int)protocol == 1)
					{
						_client = (IClient)new WebSocketClient((IClientOptions)null);
					}
				}
				else
				{
					_client = (IClient)new TcpClient((IClientOptions)null);
				}
			}
			_client.OnConnected += _client_OnConnected;
			_client.OnMessage += _client_OnMessage;
			_client.OnDisconnected += _client_OnDisconnected;
			_client.OnFatality += _client_OnFatality;
			_client.OnMessageThrottled += _client_OnMessageThrottled;
			_client.OnWhisperThrottled += _client_OnWhisperThrottled;
			_client.OnReconnected += _client_OnReconnected;
		}

		internal void RaiseEvent(string eventName, object args = null)
		{
			Delegate[] invocationList = (GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this) as MulticastDelegate).GetInvocationList();
			foreach (Delegate @delegate in invocationList)
			{
				@delegate.Method.Invoke(@delegate.Target, (args == null) ? new object[2]
				{
					this,
					new EventArgs()
				} : new object[2] { this, args });
			}
		}

		public void SendRaw(string message)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			Log("Writing: " + message);
			_client.Send(message);
			this.OnSendReceiveData?.Invoke(this, new OnSendReceiveDataArgs
			{
				Direction = (SendReceiveDirection)0,
				Data = message
			});
		}

		private void SendTwitchMessage(JoinedChannel channel, string message, string replyToId = null, bool dryRun = false)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			if (channel == null || message == null || dryRun)
			{
				return;
			}
			if (message.Length > 500)
			{
				LogError("Message length has exceeded the maximum character count. (500)");
				return;
			}
			OutboundChatMessage val = new OutboundChatMessage
			{
				Channel = channel.Channel,
				Username = ConnectionCredentials.TwitchUsername,
				Message = message
			};
			if (replyToId != null)
			{
				val.ReplyToId = replyToId;
			}
			_lastMessageSent = message;
			_client.Send(((object)val).ToString());
		}

		public void SendMessage(JoinedChannel channel, string message, bool dryRun = false)
		{
			SendTwitchMessage(channel, message, null, dryRun);
		}

		public void SendMessage(string channel, string message, bool dryRun = false)
		{
			SendMessage(GetJoinedChannel(channel), message, dryRun);
		}

		public void SendReply(JoinedChannel channel, string replyToId, string message, bool dryRun = false)
		{
			SendTwitchMessage(channel, message, replyToId, dryRun);
		}

		public void SendReply(string channel, string replyToId, string message, bool dryRun = false)
		{
			SendReply(GetJoinedChannel(channel), replyToId, message, dryRun);
		}

		public void SendWhisper(string receiver, string message, bool dryRun = false)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			if (!dryRun)
			{
				OutboundWhisperMessage val = new OutboundWhisperMessage
				{
					Receiver = receiver,
					Username = ConnectionCredentials.TwitchUsername,
					Message = message
				};
				_client.SendWhisper(((object)val).ToString());
				this.OnWhisperSent?.Invoke(this, new OnWhisperSentArgs
				{
					Receiver = receiver,
					Message = message
				});
			}
		}

		public bool Connect()
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			Log("Connecting to: " + ConnectionCredentials.TwitchWebsocketURI);
			_joinedChannelManager.Clear();
			if (_client.Open())
			{
				Log("Should be connected!");
				return true;
			}
			return false;
		}

		public void Disconnect()
		{
			Log("Disconnect Twitch Chat Client...");
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			_client.Close(true);
			_joinedChannelManager.Clear();
			PreviousWhisper = null;
		}

		public void Reconnect()
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			Log("Reconnecting to Twitch");
			_client.Reconnect();
		}

		public void AddChatCommandIdentifier(char identifier)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			_chatCommandIdentifiers.Add(identifier);
		}

		public void RemoveChatCommandIdentifier(char identifier)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			_chatCommandIdentifiers.Remove(identifier);
		}

		public void AddWhisperCommandIdentifier(char identifier)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			_whisperCommandIdentifiers.Add(identifier);
		}

		public void RemoveWhisperCommandIdentifier(char identifier)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			_whisperCommandIdentifiers.Remove(identifier);
		}

		public void SetConnectionCredentials(ConnectionCredentials credentials)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			if (IsConnected)
			{
				throw new IllegalAssignmentException("While the client is connected, you are unable to change the connection credentials. Please disconnect first and then change them.");
			}
			ConnectionCredentials = credentials;
		}

		public void JoinChannel(string channel, bool overrideCheck = false)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			if (!IsConnected)
			{
				HandleNotConnected();
			}
			if (((IEnumerable<JoinedChannel>)JoinedChannels).FirstOrDefault((Func<JoinedChannel, bool>)((JoinedChannel x) => x.Channel.ToLower() == channel && !overrideCheck)) == null)
			{
				if (channel[0] == '#')
				{
					channel = channel.Substring(1);
				}
				_joinChannelQueue.Enqueue(new JoinedChannel(channel));
				if (!_currentlyJoiningChannels)
				{
					QueueingJoinCheck();
				}
			}
		}

		public JoinedChannel GetJoinedChannel(string channel)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			if (JoinedChannels.Count == 0)
			{
				throw new BadStateException("Must be connected to at least one channel.");
			}
			if (channel[0] == '#')
			{
				channel = channel.Substring(1);
			}
			return _joinedChannelManager.GetJoinedChannel(channel);
		}

		public void LeaveChannel(string channel)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			channel = channel.ToLower();
			if (channel[0] == '#')
			{
				channel = channel.Substring(1);
			}
			Log("Leaving channel: " + channel);
			if (_joinedChannelManager.GetJoinedChannel(channel) != null)
			{
				_client.Send(Rfc2812.Part("#" + channel));
			}
		}

		public void LeaveChannel(JoinedChannel channel)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			LeaveChannel(channel.Channel);
		}

		public void OnReadLineTest(string rawIrc)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			HandleIrcMessage(_ircParser.ParseIrcMessage(rawIrc));
		}

		private void _client_OnWhisperThrottled(object sender, OnWhisperThrottledEventArgs e)
		{
			this.OnWhisperThrottled?.Invoke(sender, e);
		}

		private void _client_OnMessageThrottled(object sender, OnMessageThrottledEventArgs e)
		{
			this.OnMessageThrottled?.Invoke(sender, e);
		}

		private void _client_OnFatality(object sender, OnFatalErrorEventArgs e)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			this.OnConnectionError?.Invoke(this, new OnConnectionErrorArgs
			{
				BotUsername = TwitchUsername,
				Error = new ErrorEvent
				{
					Message = e.Reason
				}
			});
		}

		private void _client_OnDisconnected(object sender, OnDisconnectedEventArgs e)
		{
			this.OnDisconnected?.Invoke(sender, e);
		}

		private void _client_OnReconnected(object sender, OnReconnectedEventArgs e)
		{
			foreach (JoinedChannel joinedChannel in _joinedChannelManager.GetJoinedChannels())
			{
				if (!string.Equals(joinedChannel.Channel, TwitchUsername, StringComparison.CurrentCultureIgnoreCase))
				{
					_joinChannelQueue.Enqueue(joinedChannel);
				}
			}
			_joinedChannelManager.Clear();
			this.OnReconnected?.Invoke(sender, e);
		}

		private void _client_OnMessage(object sender, OnMessageEventArgs e)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			string[] separator = new string[1] { "\r\n" };
			string[] array = e.Message.Split(separator, StringSplitOptions.None);
			foreach (string text in array)
			{
				if (text.Length > 1)
				{
					Log("Received: " + text);
					this.OnSendReceiveData?.Invoke(this, new OnSendReceiveDataArgs
					{
						Direction = (SendReceiveDirection)1,
						Data = text
					});
					HandleIrcMessage(_ircParser.ParseIrcMessage(text));
				}
			}
		}

		private void _client_OnConnected(object sender, object e)
		{
			_client.Send(Rfc2812.Pass(ConnectionCredentials.TwitchOAuth));
			_client.Send(Rfc2812.Nick(ConnectionCredentials.TwitchUsername));
			_client.Send(Rfc2812.User(ConnectionCredentials.TwitchUsername, 0, ConnectionCredentials.TwitchUsername));
			if (ConnectionCredentials.Capabilities.Membership)
			{
				_client.Send("CAP REQ twitch.tv/membership");
			}
			if (ConnectionCredentials.Capabilities.Commands)
			{
				_client.Send("CAP REQ twitch.tv/commands");
			}
			if (ConnectionCredentials.Capabilities.Tags)
			{
				_client.Send("CAP REQ twitch.tv/tags");
			}
			if (_joinChannelQueue != null && _joinChannelQueue.Count > 0)
			{
				QueueingJoinCheck();
			}
		}

		private void QueueingJoinCheck()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			if (_joinChannelQueue.Count > 0)
			{
				_currentlyJoiningChannels = true;
				JoinedChannel val = _joinChannelQueue.Dequeue();
				Log("Joining channel: " + val.Channel);
				_client.Send(Rfc2812.Join("#" + val.Channel.ToLower()));
				_joinedChannelManager.AddJoinedChannel(new JoinedChannel(val.Channel));
				StartJoinedChannelTimer(val.Channel);
			}
			else
			{
				Log("Finished channel joining queue.");
			}
		}

		private void StartJoinedChannelTimer(string channel)
		{
			if (_joinTimer == null)
			{
				_joinTimer = new Timer(1000.0);
				_joinTimer.Elapsed += JoinChannelTimeout;
				_awaitingJoins = new List<KeyValuePair<string, DateTime>>();
			}
			_awaitingJoins.Add(new KeyValuePair<string, DateTime>(channel.ToLower(), DateTime.Now));
			if (!_joinTimer.Enabled)
			{
				_joinTimer.Start();
			}
		}

		private void JoinChannelTimeout(object sender, ElapsedEventArgs e)
		{
			if (_awaitingJoins.Any())
			{
				List<KeyValuePair<string, DateTime>> list = _awaitingJoins.Where((KeyValuePair<string, DateTime> x) => (DateTime.Now - x.Value).TotalSeconds > 5.0).ToList();
				if (!list.Any())
				{
					return;
				}
				_awaitingJoins.RemoveAll((KeyValuePair<string, DateTime> x) => (DateTime.Now - x.Value).TotalSeconds > 5.0);
				{
					foreach (KeyValuePair<string, DateTime> item in list)
					{
						_joinedChannelManager.RemoveJoinedChannel(item.Key.ToLowerInvariant());
						this.OnFailureToReceiveJoinConfirmation?.Invoke(this, new OnFailureToReceiveJoinConfirmationArgs
						{
							Exception = new FailureToReceiveJoinConfirmationException(item.Key)
						});
					}
					return;
				}
			}
			_joinTimer.Stop();
			_currentlyJoiningChannels = false;
			QueueingJoinCheck();
		}

		private void HandleIrcMessage(IrcMessage ircMessage)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected I4, but got Unknown
			if (ircMessage.Message.Contains("Login authentication failed"))
			{
				this.OnIncorrectLogin?.Invoke(this, new OnIncorrectLoginArgs
				{
					Exception = new ErrorLoggingInException(ircMessage.ToString(), TwitchUsername)
				});
				return;
			}
			IrcCommand command = ircMessage.Command;
			switch ((int)command)
			{
			case 1:
				HandlePrivMsg(ircMessage);
				break;
			case 2:
				HandleNotice(ircMessage);
				break;
			case 3:
				if (!DisableAutoPong)
				{
					SendRaw("PONG");
				}
				break;
			case 4:
				break;
			case 5:
				HandleJoin(ircMessage);
				break;
			case 6:
				HandlePart(ircMessage);
				break;
			case 7:
				HandleClearChat(ircMessage);
				break;
			case 8:
				HandleClearMsg(ircMessage);
				break;
			case 9:
				HandleUserState(ircMessage);
				break;
			case 17:
				Handle004();
				break;
			case 18:
				Handle353(ircMessage);
				break;
			case 19:
				Handle366();
				break;
			case 23:
				HandleWhisper(ircMessage);
				break;
			case 24:
				HandleRoomState(ircMessage);
				break;
			case 25:
				Reconnect();
				break;
			case 27:
				HandleUserNotice(ircMessage);
				break;
			case 28:
				HandleMode(ircMessage);
				break;
			case 13:
				HandleCap(ircMessage);
				break;
			default:
				this.OnUnaccountedFor?.Invoke(this, new OnUnaccountedForArgs
				{
					BotUsername = TwitchUsername,
					Channel = null,
					Location = "HandleIrcMessage",
					RawIRC = ircMessage.ToString()
				});
				UnaccountedFor(ircMessage.ToString());
				break;
			case 10:
			case 14:
			case 15:
			case 16:
			case 20:
			case 21:
			case 22:
				break;
			}
		}

		private void HandlePrivMsg(IrcMessage ircMessage)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			ChatMessage val = new ChatMessage(TwitchUsername, ircMessage, ref _channelEmotes, WillReplaceEmotes);
			foreach (JoinedChannel item in JoinedChannels.Where((JoinedChannel x) => string.Equals(x.Channel, ircMessage.Channel, StringComparison.InvariantCultureIgnoreCase)))
			{
				item.HandleMessage(val);
			}
			this.OnMessageReceived?.Invoke(this, new OnMessageReceivedArgs
			{
				ChatMessage = val
			});
			if (ircMessage.Tags.TryGetValue("msg-id", out var value) && value == "user-intro")
			{
				this.OnUserIntro?.Invoke(this, new OnUserIntroArgs
				{
					ChatMessage = val
				});
			}
			if (_chatCommandIdentifiers != null && _chatCommandIdentifiers.Count != 0 && !string.IsNullOrEmpty(val.Message) && _chatCommandIdentifiers.Contains(val.Message[0]))
			{
				ChatCommand command = new ChatCommand(val);
				this.OnChatCommandReceived?.Invoke(this, new OnChatCommandReceivedArgs
				{
					Command = command
				});
			}
		}

		private void HandleNotice(IrcMessage ircMessage)
		{
			if (ircMessage.Message.Contains("Improperly formatted auth"))
			{
				this.OnIncorrectLogin?.Invoke(this, new OnIncorrectLoginArgs
				{
					Exception = new ErrorLoggingInException(ircMessage.ToString(), TwitchUsername)
				});
				return;
			}
			if (!ircMessage.Tags.TryGetValue("msg-id", out var value))
			{
				this.OnUnaccountedFor?.Invoke(this, new OnUnaccountedForArgs
				{
					BotUsername = TwitchUsername,
					Channel = ircMessage.Channel,
					Location = "NoticeHandling",
					RawIRC = ircMessage.ToString()
				});
				UnaccountedFor(ircMessage.ToString());
			}
			switch (value)
			{
			case "color_changed":
				this.OnChatColorChanged?.Invoke(this, new OnChatColorChangedArgs
				{
					Channel = ircMessage.Channel
				});
				break;
			case "room_mods":
				this.OnModeratorsReceived?.Invoke(this, new OnModeratorsReceivedArgs
				{
					Channel = ircMessage.Channel,
					Moderators = ircMessage.Message.Replace(" ", "").Split(new char[1] { ':' })[1].Split(new char[1] { ',' }).ToList()
				});
				break;
			case "no_mods":
				this.OnModeratorsReceived?.Invoke(this, new OnModeratorsReceivedArgs
				{
					Channel = ircMessage.Channel,
					Moderators = new List<string>()
				});
				break;
			case "no_permission":
				this.OnNoPermissionError?.Invoke(this, null);
				break;
			case "raid_error_self":
				this.OnSelfRaidError?.Invoke(this, null);
				break;
			case "raid_notice_mature":
				this.OnRaidedChannelIsMatureAudience?.Invoke(this, null);
				break;
			case "msg_banned_email_alias":
				this.OnBannedEmailAlias?.Invoke(this, new OnBannedEmailAliasArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_channel_suspended":
				_awaitingJoins.RemoveAll((KeyValuePair<string, DateTime> x) => x.Key.ToLower() == ircMessage.Channel);
				_joinedChannelManager.RemoveJoinedChannel(ircMessage.Channel);
				QueueingJoinCheck();
				this.OnFailureToReceiveJoinConfirmation?.Invoke(this, new OnFailureToReceiveJoinConfirmationArgs
				{
					Exception = new FailureToReceiveJoinConfirmationException(ircMessage.Channel, ircMessage.Message)
				});
				break;
			case "msg_requires_verified_phone_number":
				this.OnRequiresVerifiedPhoneNumber?.Invoke(this, new OnRequiresVerifiedPhoneNumberArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_verified_email":
				this.OnRequiresVerifiedEmail?.Invoke(this, new OnRequiresVerifiedEmailArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "no_vips":
				this.OnVIPsReceived?.Invoke(this, new OnVIPsReceivedArgs
				{
					Channel = ircMessage.Channel,
					VIPs = new List<string>()
				});
				break;
			case "vips_success":
				this.OnVIPsReceived?.Invoke(this, new OnVIPsReceivedArgs
				{
					Channel = ircMessage.Channel,
					VIPs = ircMessage.Message.Replace(" ", "").Replace(".", "").Split(new char[1] { ':' })[1].Split(new char[1] { ',' }).ToList()
				});
				break;
			case "msg_ratelimit":
				this.OnRateLimit?.Invoke(this, new OnRateLimitArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_duplicate":
				this.OnDuplicate?.Invoke(this, new OnDuplicateArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_followersonly":
				this.OnFollowersOnly?.Invoke(this, new OnFollowersOnlyArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_subsonly":
				this.OnSubsOnly?.Invoke(this, new OnSubsOnlyArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_emoteonly":
				this.OnEmoteOnly?.Invoke(this, new OnEmoteOnlyArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_suspended":
				this.OnSuspended?.Invoke(this, new OnSuspendedArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_banned":
				this.OnBanned?.Invoke(this, new OnBannedArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_slowmode":
				this.OnSlowMode?.Invoke(this, new OnSlowModeArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			case "msg_r9k":
				this.OnR9kMode?.Invoke(this, new OnR9kModeArgs
				{
					Channel = ircMessage.Channel,
					Message = ircMessage.Message
				});
				break;
			default:
				this.OnUnaccountedFor?.Invoke(this, new OnUnaccountedForArgs
				{
					BotUsername = TwitchUsername,
					Channel = ircMessage.Channel,
					Location = "NoticeHandling",
					RawIRC = ircMessage.ToString()
				});
				UnaccountedFor(ircMessage.ToString());
				break;
			}
		}

		private void HandleJoin(IrcMessage ircMessage)
		{
			this.OnUserJoined?.Invoke(this, new OnUserJoinedArgs
			{
				Channel = ircMessage.Channel,
				Username = ircMessage.User
			});
		}

		private void HandlePart(IrcMessage ircMessage)
		{
			if (string.Equals(TwitchUsername, ircMessage.User, StringComparison.InvariantCultureIgnoreCase))
			{
				_joinedChannelManager.RemoveJoinedChannel(ircMessage.Channel);
				_hasSeenJoinedChannels.Remove(ircMessage.Channel);
				this.OnLeftChannel?.Invoke(this, new OnLeftChannelArgs
				{
					BotUsername = TwitchUsername,
					Channel = ircMessage.Channel
				});
			}
			else
			{
				this.OnUserLeft?.Invoke(this, new OnUserLeftArgs
				{
					Channel = ircMessage.Channel,
					Username = ircMessage.User
				});
			}
		}

		private void HandleClearChat(IrcMessage ircMessage)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			string value;
			if (string.IsNullOrWhiteSpace(ircMessage.Message))
			{
				this.OnChatCleared?.Invoke(this, new OnChatClearedArgs
				{
					Channel = ircMessage.Channel
				});
			}
			else if (ircMessage.Tags.TryGetValue("ban-duration", out value))
			{
				UserTimeout userTimeout = new UserTimeout(ircMessage);
				this.OnUserTimedout?.Invoke(this, new OnUserTimedoutArgs
				{
					UserTimeout = userTimeout
				});
			}
			else
			{
				UserBan userBan = new UserBan(ircMessage);
				this.OnUserBanned?.Invoke(this, new OnUserBannedArgs
				{
					UserBan = userBan
				});
			}
		}

		private void HandleClearMsg(IrcMessage ircMessage)
		{
			this.OnMessageCleared?.Invoke(this, new OnMessageClearedArgs
			{
				Channel = ircMessage.Channel,
				Message = ircMessage.Message,
				TargetMessageId = ircMessage.ToString().Split(new char[1] { '=' })[3].Split(new char[1] { ';' })[0],
				TmiSentTs = ircMessage.ToString().Split(new char[1] { '=' })[4].Split(new char[1] { ' ' })[0]
			});
		}

		private void HandleUserState(IrcMessage ircMessage)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			UserState val = new UserState(ircMessage);
			if (!_hasSeenJoinedChannels.Contains(val.Channel.ToLowerInvariant()))
			{
				_hasSeenJoinedChannels.Add(val.Channel.ToLowerInvariant());
				this.OnUserStateChanged?.Invoke(this, new OnUserStateChangedArgs
				{
					UserState = val
				});
			}
			else
			{
				this.OnMessageSent?.Invoke(this, new OnMessageSentArgs
				{
					SentMessage = new SentMessage(val, _lastMessageSent)
				});
			}
		}

		private void Handle004()
		{
			this.OnConnected?.Invoke(this, new OnConnectedArgs
			{
				BotUsername = TwitchUsername
			});
		}

		private void Handle353(IrcMessage ircMessage)
		{
			this.OnExistingUsersDetected?.Invoke(this, new OnExistingUsersDetectedArgs
			{
				Channel = ircMessage.Channel,
				Users = ircMessage.Message.Split(new char[1] { ' ' }).ToList()
			});
		}

		private void Handle366()
		{
			_currentlyJoiningChannels = false;
			QueueingJoinCheck();
		}

		private void HandleWhisper(IrcMessage ircMessage)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			WhisperMessage val2 = (PreviousWhisper = new WhisperMessage(ircMessage, TwitchUsername));
			this.OnWhisperReceived?.Invoke(this, new OnWhisperReceivedArgs
			{
				WhisperMessage = val2
			});
			if (_whisperCommandIdentifiers != null && _whisperCommandIdentifiers.Count != 0 && !string.IsNullOrEmpty(val2.Message) && _whisperCommandIdentifiers.Contains(val2.Message[0]))
			{
				WhisperCommand command = new WhisperCommand(val2);
				this.OnWhisperCommandReceived?.Invoke(this, new OnWhisperCommandReceivedArgs
				{
					Command = command
				});
				return;
			}
			this.OnUnaccountedFor?.Invoke(this, new OnUnaccountedForArgs
			{
				BotUsername = TwitchUsername,
				Channel = ircMessage.Channel,
				Location = "WhispergHandling",
				RawIRC = ircMessage.ToString()
			});
			UnaccountedFor(ircMessage.ToString());
		}

		private void HandleRoomState(IrcMessage ircMessage)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			if (ircMessage.Tags.Count > 2)
			{
				KeyValuePair<string, DateTime> item = _awaitingJoins.FirstOrDefault((KeyValuePair<string, DateTime> x) => x.Key == ircMessage.Channel);
				_awaitingJoins.Remove(item);
				this.OnJoinedChannel?.Invoke(this, new OnJoinedChannelArgs
				{
					BotUsername = TwitchUsername,
					Channel = ircMessage.Channel
				});
			}
			this.OnChannelStateChanged?.Invoke(this, new OnChannelStateChangedArgs
			{
				ChannelState = new ChannelState(ircMessage),
				Channel = ircMessage.Channel
			});
		}

		private void HandleUserNotice(IrcMessage ircMessage)
		{
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Expected O, but got Unknown
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Expected O, but got Unknown
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Expected O, but got Unknown
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Expected O, but got Unknown
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Expected O, but got Unknown
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Expected O, but got Unknown
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Expected O, but got Unknown
			if (!ircMessage.Tags.TryGetValue("msg-id", out var value))
			{
				this.OnUnaccountedFor?.Invoke(this, new OnUnaccountedForArgs
				{
					BotUsername = TwitchUsername,
					Channel = ircMessage.Channel,
					Location = "UserNoticeHandling",
					RawIRC = ircMessage.ToString()
				});
				UnaccountedFor(ircMessage.ToString());
				return;
			}
			switch (value)
			{
			case "announcement":
			{
				Announcement announcement = new Announcement(ircMessage);
				this.OnAnnouncement?.Invoke(this, new OnAnnouncementArgs
				{
					Announcement = announcement,
					Channel = ircMessage.Channel
				});
				break;
			}
			case "raid":
			{
				RaidNotification raidNotification = new RaidNotification(ircMessage);
				this.OnRaidNotification?.Invoke(this, new OnRaidNotificationArgs
				{
					Channel = ircMessage.Channel,
					RaidNotification = raidNotification
				});
				break;
			}
			case "resub":
			{
				ReSubscriber reSubscriber = new ReSubscriber(ircMessage);
				this.OnReSubscriber?.Invoke(this, new OnReSubscriberArgs
				{
					ReSubscriber = reSubscriber,
					Channel = ircMessage.Channel
				});
				break;
			}
			case "subgift":
			{
				GiftedSubscription giftedSubscription2 = new GiftedSubscription(ircMessage);
				this.OnGiftedSubscription?.Invoke(this, new OnGiftedSubscriptionArgs
				{
					GiftedSubscription = giftedSubscription2,
					Channel = ircMessage.Channel
				});
				break;
			}
			case "submysterygift":
			{
				CommunitySubscription giftedSubscription = new CommunitySubscription(ircMessage);
				this.OnCommunitySubscription?.Invoke(this, new OnCommunitySubscriptionArgs
				{
					GiftedSubscription = giftedSubscription,
					Channel = ircMessage.Channel
				});
				break;
			}
			case "giftpaidupgrade":
			{
				ContinuedGiftedSubscription continuedGiftedSubscription = new ContinuedGiftedSubscription(ircMessage);
				this.OnContinuedGiftedSubscription?.Invoke(this, new OnContinuedGiftedSubscriptionArgs
				{
					ContinuedGiftedSubscription = continuedGiftedSubscription,
					Channel = ircMessage.Channel
				});
				break;
			}
			case "sub":
			{
				Subscriber subscriber = new Subscriber(ircMessage);
				this.OnNewSubscriber?.Invoke(this, new OnNewSubscriberArgs
				{
					Subscriber = subscriber,
					Channel = ircMessage.Channel
				});
				break;
			}
			case "primepaidupgrade":
			{
				PrimePaidSubscriber primePaidSubscriber = new PrimePaidSubscriber(ircMessage);
				this.OnPrimePaidSubscriber?.Invoke(this, new OnPrimePaidSubscriberArgs
				{
					PrimePaidSubscriber = primePaidSubscriber,
					Channel = ircMessage.Channel
				});
				break;
			}
			default:
				this.OnUnaccountedFor?.Invoke(this, new OnUnaccountedForArgs
				{
					BotUsername = TwitchUsername,
					Channel = ircMessage.Channel,
					Location = "UserNoticeHandling",
					RawIRC = ircMessage.ToString()
				});
				UnaccountedFor(ircMessage.ToString());
				break;
			}
		}

		private void HandleMode(IrcMessage ircMessage)
		{
			if (ircMessage.Message.StartsWith("+o"))
			{
				this.OnModeratorJoined?.Invoke(this, new OnModeratorJoinedArgs
				{
					Channel = ircMessage.Channel,
					Username = ircMessage.Message.Split(new char[1] { ' ' })[1]
				});
			}
			else if (ircMessage.Message.StartsWith("-o"))
			{
				this.OnModeratorLeft?.Invoke(this, new OnModeratorLeftArgs
				{
					Channel = ircMessage.Channel,
					Username = ircMessage.Message.Split(new char[1] { ' ' })[1]
				});
			}
		}

		private void HandleCap(IrcMessage ircMessage)
		{
		}

		private void UnaccountedFor(string ircString)
		{
			Log("Unaccounted for: " + ircString + " (please create a TwitchLib GitHub issue :P)");
		}

		private void Log(string message, bool includeDate = false, bool includeTime = false)
		{
			string arg = ((includeDate && includeTime) ? $"{DateTime.UtcNow}" : ((!includeDate) ? (DateTime.UtcNow.ToShortTimeString() ?? "") : (DateTime.UtcNow.ToShortDateString() ?? "")));
			if (includeDate || includeTime)
			{
				_logger?.LogInformation($"[TwitchLib, {Assembly.GetExecutingAssembly().GetName().Version} - {arg}] {message}");
			}
			else
			{
				_logger?.LogInformation($"[TwitchLib, {Assembly.GetExecutingAssembly().GetName().Version}] {message}");
			}
			EventHandler<OnLogArgs> onLog = this.OnLog;
			if (onLog != null)
			{
				OnLogArgs onLogArgs = new OnLogArgs();
				ConnectionCredentials connectionCredentials = ConnectionCredentials;
				onLogArgs.BotUsername = ((connectionCredentials != null) ? connectionCredentials.TwitchUsername : null);
				onLogArgs.Data = message;
				onLogArgs.DateTime = DateTime.UtcNow;
				onLog(this, onLogArgs);
			}
		}

		private void LogError(string message, bool includeDate = false, bool includeTime = false)
		{
			string arg = ((includeDate && includeTime) ? $"{DateTime.UtcNow}" : ((!includeDate) ? (DateTime.UtcNow.ToShortTimeString() ?? "") : (DateTime.UtcNow.ToShortDateString() ?? "")));
			if (includeDate || includeTime)
			{
				_logger?.LogError($"[TwitchLib, {Assembly.GetExecutingAssembly().GetName().Version} - {arg}] {message}");
			}
			else
			{
				_logger?.LogError($"[TwitchLib, {Assembly.GetExecutingAssembly().GetName().Version}] {message}");
			}
			EventHandler<OnLogArgs> onLog = this.OnLog;
			if (onLog != null)
			{
				OnLogArgs onLogArgs = new OnLogArgs();
				ConnectionCredentials connectionCredentials = ConnectionCredentials;
				onLogArgs.BotUsername = ((connectionCredentials != null) ? connectionCredentials.TwitchUsername : null);
				onLogArgs.Data = message;
				onLogArgs.DateTime = DateTime.UtcNow;
				onLog(this, onLogArgs);
			}
		}

		public void SendQueuedItem(string message)
		{
			if (!IsInitialized)
			{
				HandleNotInitialized();
			}
			_client.Send(message);
		}

		protected static void HandleNotInitialized()
		{
			throw new ClientNotInitializedException("The twitch client has not been initialized and cannot be used. Please call Initialize();");
		}

		protected static void HandleNotConnected()
		{
			throw new ClientNotConnectedException("In order to perform this action, the client must be connected to Twitch. To confirm connection, try performing this action in or after the OnConnected event has been fired.");
		}
	}
}
namespace TwitchLib.Client.Manager
{
	internal class JoinedChannelManager
	{
		private readonly ConcurrentDictionary<string, JoinedChannel> _joinedChannels;

		public JoinedChannelManager()
		{
			_joinedChannels = new ConcurrentDictionary<string, JoinedChannel>(StringComparer.OrdinalIgnoreCase);
		}

		public void AddJoinedChannel(JoinedChannel joinedChannel)
		{
			_joinedChannels.TryAdd(joinedChannel.Channel, joinedChannel);
		}

		public JoinedChannel GetJoinedChannel(string channel)
		{
			_joinedChannels.TryGetValue(channel, out var value);
			return value;
		}

		public IReadOnlyList<JoinedChannel> GetJoinedChannels()
		{
			return _joinedChannels.Values.ToList().AsReadOnly();
		}

		public void RemoveJoinedChannel(string channel)
		{
			_joinedChannels.TryRemove(channel, out var _);
		}

		public void Clear()
		{
			_joinedChannels.Clear();
		}
	}
}
namespace TwitchLib.Client.Internal
{
	public sealed class Rfc2812
	{
		private static readonly Regex NicknameRegex = new Regex("^[A-Za-z\\[\\]\\\\`_^{|}][A-Za-z0-9\\[\\]\\\\`_\\-^{|}]+$", RegexOptions.Compiled);

		private Rfc2812()
		{
		}

		public static bool IsValidNickname(string nickname)
		{
			if (!string.IsNullOrEmpty(nickname))
			{
				return NicknameRegex.Match(nickname).Success;
			}
			return false;
		}

		public static string Pass(string password)
		{
			return "PASS " + password;
		}

		public static string Nick(string nickname)
		{
			return "NICK " + nickname;
		}

		public static string User(string username, int usermode, string realname)
		{
			return $"USER {username} {usermode} * :{realname}";
		}

		public static string Oper(string name, string password)
		{
			return "OPER " + name + " " + password;
		}

		public static string Privmsg(string destination, string message)
		{
			return "PRIVMSG " + destination + " :" + message;
		}

		public static string Notice(string destination, string message)
		{
			return "NOTICE " + destination + " :" + message;
		}

		public static string Join(string channel)
		{
			return "JOIN " + channel;
		}

		public static string Join(string[] channels)
		{
			return "JOIN " + string.Join(",", channels);
		}

		public static string Join(string channel, string key)
		{
			return "JOIN " + channel + " " + key;
		}

		public static string Join(string[] channels, string[] keys)
		{
			return "JOIN " + string.Join(",", channels) + " " + string.Join(",", keys);
		}

		public static string Part(string channel)
		{
			return "PART " + channel;
		}

		public static string Part(string[] channels)
		{
			return "PART " + string.Join(",", channels);
		}

		public static string Part(string channel, string partmessage)
		{
			return "PART " + channel + " :" + partmessage;
		}

		public static string Part(string[] channels, string partmessage)
		{
			return "PART " + string.Join(",", channels) + " :" + partmessage;
		}

		public static string Kick(string channel, string nickname)
		{
			return "KICK " + channel + " " + nickname;
		}

		public static string Kick(string channel, string nickname, string comment)
		{
			return "KICK " + channel + " " + nickname + " :" + comment;
		}

		public static string Kick(string[] channels, string nickname)
		{
			return "KICK " + string.Join(",", channels) + " " + nickname;
		}

		public static string Kick(string[] channels, string nickname, string comment)
		{
			return "KICK " + string.Join(",", channels) + " " + nickname + " :" + comment;
		}

		public static string Kick(string channel, string[] nicknames)
		{
			return "KICK " + channel + " " + string.Join(",", nicknames);
		}

		public static string Kick(string channel, string[] nicknames, string comment)
		{
			return "KICK " + channel + " " + string.Join(",", nicknames) + " :" + comment;
		}

		public static string Kick(string[] channels, string[] nicknames)
		{
			return "KICK " + string.Join(",", channels) + " " + string.Join(",", nicknames);
		}

		public static string Kick(string[] channels, string[] nicknames, string comment)
		{
			return "KICK " + string.Join(",", channels) + " " + string.Join(",", nicknames) + " :" + comment;
		}

		public static string Motd()
		{
			return "MOTD";
		}

		public static string Motd(string target)
		{
			return "MOTD " + target;
		}

		public static string Lusers()
		{
			return "LUSERS";
		}

		public static string Lusers(string mask)
		{
			return "LUSER " + mask;
		}

		public static string Lusers(string mask, string target)
		{
			return "LUSER " + mask + " " + target;
		}

		public static string Version()
		{
			return "VERSION";
		}

		public static string Version(string target)
		{
			return "VERSION " + target;
		}

		public static string Stats()
		{
			return "STATS";
		}

		public static string Stats(string query)
		{
			return "STATS " + query;
		}

		public static string Stats(string query, string target)
		{
			return "STATS " + query + " " + target;
		}

		public static string Links()
		{
			return "LINKS";
		}

		public static string Links(string servermask)
		{
			return "LINKS " + servermask;
		}

		public static string Links(string remoteserver, string servermask)
		{
			return "LINKS " + remoteserver + " " + servermask;
		}

		public static string Time()
		{
			return "TIME";
		}

		public static string Time(string target)
		{
			return "TIME " + target;
		}

		public static string Connect(string targetserver, string port)
		{
			return "CONNECT " + targetserver + " " + port;
		}

		public static string Connect(string targetserver, string port, string remoteserver)
		{
			return "CONNECT " + targetserver + " " + port + " " + remoteserver;
		}

		public static string Trace()
		{
			return "TRACE";
		}

		public static string Trace(string target)
		{
			return "TRACE " + target;
		}

		public static string Admin()
		{
			return "ADMIN";
		}

		public static string Admin(string target)
		{
			return "ADMIN " + target;
		}

		public static string Info()
		{
			return "INFO";
		}

		public static string Info(string target)
		{
			return "INFO " + target;
		}

		public static string Servlist()
		{
			return "SERVLIST";
		}

		public static string Servlist(string mask)
		{
			return "SERVLIST " + mask;
		}

		public static string Servlist(string mask, string type)
		{
			return "SERVLIST " + mask + " " + type;
		}

		public static string Squery(string servicename, string servicetext)
		{
			return "SQUERY " + servicename + " :" + servicename;
		}

		public static string List()
		{
			return "LIST";
		}

		public static string List(string channel)
		{
			return "LIST " + channel;
		}

		public static string List(string[] channels)
		{
			return "LIST " + string.Join(",", channels);
		}

		public static string List(string channel, string target)
		{
			return "LIST " + channel + " " + target;
		}

		public static string List(string[] channels, string target)
		{
			return "LIST " + string.Join(",", channels) + " " + target;
		}

		public static string Names()
		{
			return "NAMES";
		}

		public static string Names(string channel)
		{
			return "NAMES " + channel;
		}

		public static string Names(string[] channels)
		{
			return "NAMES " + string.Join(",", channels);
		}

		public static string Names(string channel, string target)
		{
			return "NAMES " + channel + " " + target;
		}

		public static string Names(string[] channels, string target)
		{
			return "NAMES " + string.Join(",", channels) + " " + target;
		}

		public static string Topic(string channel)
		{
			return "TOPIC " + channel;
		}

		public static string Topic(string channel, string newtopic)
		{
			return "TOPIC " + channel + " :" + newtopic;
		}

		public static string Mode(string target)
		{
			return "MODE " + target;
		}

		public static string Mode(string target, string newmode)
		{
			return "MODE " + target + " " + newmode + target + " " + newmode;
		}

		public static string Mode(string target, string[] newModes, string[] newModeParameters)
		{
			if (newModes == null)
			{
				throw new ArgumentNullException("newModes");
			}
			if (newModeParameters == null)
			{
				throw new ArgumentNullException("newModeParameters");
			}
			if (newModes.Length != newModeParameters.Length)
			{
				throw new ArgumentException("newModes and newModeParameters must have the same size.");
			}
			StringBuilder stringBuilder = new StringBuilder(newModes.Length);
			StringBuilder stringBuilder2 = new StringBuilder();
			if (newModes.Length > 3)
			{
				throw new ArgumentOutOfRangeException("Length", newModes.Length, $"Mode change list is too large (> {3}).");
			}
			for (int i = 0; i <= newModes.Length; i += 3)
			{
				for (int j = 0; j < 3 && i + j < newModes.Length; j++)
				{
					stringBuilder.Append(newModes[i + j]);
				}
				for (int k = 0; k < 3 && i + k < newModeParameters.Length; k++)
				{
					stringBuilder2.Append(newModeParameters[i + k]);
					stringBuilder2.Append(" ");
				}
			}
			if (stringBuilder2.Length <= 0)
			{
				return Mode(target, stringBuilder.ToString());
			}
			stringBuilder2.Length--;
			stringBuilder.Append(" ");
			stringBuilder.Append((object?)stringBuilder2);
			return Mode(target, stringBuilder.ToString());
		}

		public static string Service(string nickname, string distribution, string info)
		{
			return "SERVICE " + nickname + " * " + distribution + " * * :" + info;
		}

		public static string Invite(string nickname, string channel)
		{
			return "INVITE " + nickname + " " + channel;
		}

		public static string Who()
		{
			return "WHO";
		}

		public static string Who(string mask)
		{
			return "WHO " + mask;
		}

		public static string Who(string mask, bool ircop)
		{
			if (!ircop)
			{
				return "WHO " + mask;
			}
			return "WHO " + mask + " o";
		}

		public static string Whois(string mask)
		{
			return "WHOIS " + mask;
		}

		public static string Whois(string[] masks)
		{
			return "WHOIS " + string.Join(",", masks);
		}

		public static string Whois(string target, string mask)
		{
			return "WHOIS " + target + " " + mask;
		}

		public static string Whois(string target, string[] masks)
		{
			return "WHOIS " + target + " " + string.Join(",", masks);
		}

		public static string Whowas(string nickname)
		{
			return "WHOWAS " + nickname;
		}

		public static string Whowas(string[] nicknames)
		{
			return "WHOWAS " + string.Join(",", nicknames);
		}

		public static string Whowas(string nickname, string count)
		{
			return "WHOWAS " + nickname + " " + count + " ";
		}

		public static string Whowas(string[] nicknames, string count)
		{
			return "WHOWAS " + string.Join(",", nicknames) + " " + count + " ";
		}

		public static string Whowas(string nickname, string count, string target)
		{
			return "WHOWAS " + nickname + " " + count + " " + target;
		}

		public static string Whowas(string[] nicknames, string count, string target)
		{
			return "WHOWAS " + string.Join(",", nicknames) + " " + count + " " + target;
		}

		public static string Kill(string nickname, string comment)
		{
			return "KILL " + nickname + " :" + comment;
		}

		public static string Ping(string server)
		{
			return "PING " + server;
		}

		public static string Ping(string server, string server2)
		{
			return "PING " + server + " " + server2;
		}

		public static string Pong(string server)
		{
			return "PONG " + server;
		}

		public static string Pong(string server, string server2)
		{
			return "PONG " + server + " " + server2;
		}

		public static string Error(string errormessage)
		{
			return "ERROR :" + errormessage;
		}

		public static string Away()
		{
			return "AWAY";
		}

		public static string Away(string awaytext)
		{
			return "AWAY :" + awaytext;
		}

		public static string Rehash()
		{
			return "REHASH";
		}

		public static string Die()
		{
			return "DIE";
		}

		public static string Restart()
		{
			return "RESTART";
		}

		public static string Summon(string user)
		{
			return "SUMMON " + user;
		}

		public static string Summon(string user, string target)
		{
			return "SUMMON " + user + " " + target + user + " " + target;
		}

		public static string Summon(string user, string target, string channel)
		{
			return "SUMMON " + user + " " + target + " " + channel;
		}

		public static string Users()
		{
			return "USERS";
		}

		public static string Users(string target)
		{
			return "USERS " + target;
		}

		public static string Wallops(string wallopstext)
		{
			return "WALLOPS :" + wallopstext;
		}

		public static string Userhost(string nickname)
		{
			return "USERHOST " + nickname;
		}

		public static string Userhost(string[] nicknames)
		{
			return "USERHOST " + string.Join(" ", nicknames);
		}

		public static string Ison(string nickname)
		{
			return "ISON " + nickname;
		}

		public static string Ison(string[] nicknames)
		{
			return "ISON " + string.Join(" ", nicknames);
		}

		public static string Quit()
		{
			return "QUIT";
		}

		public static string Quit(string quitmessage)
		{
			return "QUIT :" + quitmessage;
		}

		public static string Squit(string server, string comment)
		{
			return "SQUIT " + server + " :" + comment;
		}
	}
}
namespace TwitchLib.Client.Internal.Parsing
{
	internal class IrcParser
	{
		private enum ParserState
		{
			STATE_NONE,
			STATE_V3,
			STATE_PREFIX,
			STATE_COMMAND,
			STATE_PARAM,
			STATE_TRAILING
		}

		public IrcMessage ParseIrcMessage(string raw)
		{
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_068c: Unknown result type (might be due to invalid IL or missing references)
			//IL_064f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0691: Unknown result type (might be due to invalid IL or missing references)
			//IL_065f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0728: Unknown result type (might be due to invalid IL or missing references)
			//IL_073d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0743: Expected O, but got Unknown
			//IL_06ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_06cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0680: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_06de: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0696: Unknown result type (might be due to invalid IL or missing references)
			//IL_0667: Unknown result type (might be due to invalid IL or missing references)
			//IL_0657: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_06fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0677: Unknown result type (might be due to invalid IL or missing references)
			//IL_066f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0686: Unknown result type (might be due to invalid IL or missing references)
			//IL_069c: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			ParserState parserState = ParserState.STATE_NONE;
			int[] array = new int[6];
			int[] array2 = new int[6];
			for (int i = 0; i < raw.Length; i++)
			{
				array2[(int)parserState] = i - array[(int)parserState] - 1;
				if (parserState == ParserState.STATE_NONE && raw[i] == '@')
				{
					parserState = ParserState.STATE_V3;
					i = (array[(int)parserState] = i + 1);
					int num = i;
					string text = null;
					for (; i < raw.Length; i++)
					{
						if (raw[i] == '=')
						{
							text = raw.Substring(num, i - num);
							num = i + 1;
						}
						else if (raw[i] == ';')
						{
							if (text == null)
							{
								dictionary[raw.Substring(num, i - num)] = "1";
							}
							else
							{
								dictionary[text] = raw.Substring(num, i - num);
							}
							num = i + 1;
						}
						else if (raw[i] == ' ')
						{
							if (text == null)
							{
								dictionary[raw.Substring(num, i - num)] = "1";
							}
							else
							{
								dictionary[text] = raw.Substring(num, i - num);
							}
							break;
						}
					}
				}
				else if (parserState < ParserState.STATE_PREFIX && raw[i] == ':')
				{
					parserState = ParserState.STATE_PREFIX;
					i = (array[(int)parserState] = i + 1);
				}
				else if (parserState < ParserState.STATE_COMMAND)
				{
					parserState = ParserState.STATE_COMMAND;
					array[(int)parserState] = i;
				}
				else
				{
					if (parserState < ParserState.STATE_TRAILING && raw[i] == ':')
					{
						parserState = ParserState.STATE_TRAILING;
						i = (array[(int)parserState] = i + 1);
						break;
					}
					if ((parserState < ParserState.STATE_TRAILING && raw[i] == '+') || (parserState < ParserState.STATE_TRAILING && raw[i] == '-'))
					{
						parserState = ParserState.STATE_TRAILING;
						array[(int)parserState] = i;
						break;
					}
					if (parserState == ParserState.STATE_COMMAND)
					{
						parserState = ParserState.STATE_PARAM;
						array[(int)parserState] = i;
					}
				}
				for (; i < raw.Length && raw[i] != ' '; i++)
				{
				}
			}
			array2[(int)parserState] = raw.Length - array[(int)parserState];
			string text2 = raw.Substring(array[3], array2[3]);
			IrcCommand val = (IrcCommand)0;
			switch (text2)
			{
			case "PRIVMSG":
				val = (IrcCommand)1;
				break;
			case "NOTICE":
				val = (IrcCommand)2;
				break;
			case "PING":
				val = (IrcCommand)3;
				break;
			case "PONG":
				val = (IrcCommand)4;
				break;
			case "CLEARCHAT":
				val = (IrcCommand)7;
				break;
			case "CLEARMSG":
				val = (IrcCommand)8;
				break;
			case "USERSTATE":
				val = (IrcCommand)9;
				break;
			case "GLOBALUSERSTATE":
				val = (IrcCommand)10;
				break;
			case "NICK":
				val = (IrcCommand)11;
				break;
			case "JOIN":
				val = (IrcCommand)5;
				break;
			case "PART":
				val = (IrcCommand)6;
				break;
			case "PASS":
				val = (IrcCommand)12;
				break;
			case "CAP":
				val = (IrcCommand)13;
				break;
			case "001":
				val = (IrcCommand)14;
				break;
			case "002":
				val = (IrcCommand)15;
				break;
			case "003":
				val = (IrcCommand)16;
				break;
			case "004":
				val = (IrcCommand)17;
				break;
			case "353":
				val = (IrcCommand)18;
				break;
			case "366":
				val = (IrcCommand)19;
				break;
			case "372":
				val = (IrcCommand)20;
				break;
			case "375":
				val = (IrcCommand)21;
				break;
			case "376":
				val = (IrcCommand)22;
				break;
			case "WHISPER":
				val = (IrcCommand)23;
				break;
			case "SERVERCHANGE":
				val = (IrcCommand)26;
				break;
			case "RECONNECT":
				val = (IrcCommand)25;
				break;
			case "ROOMSTATE":
				val = (IrcCommand)24;
				break;
			case "USERNOTICE":
				val = (IrcCommand)27;
				break;
			case "MODE":
				val = (IrcCommand)28;
				break;
			}
			string text3 = raw.Substring(array[4], array2[4]);
			string text4 = raw.Substring(array[5], array2[5]);
			string text5 = raw.Substring(array[2], array2[2]);
			return new IrcMessage(val, new string[2] { text3, text4 }, text5, dictionary);
		}
	}
}
namespace TwitchLib.Client.Interfaces
{
	public interface ITwitchClient
	{
		bool AutoReListenOnException { get; set; }

		MessageEmoteCollection ChannelEmotes { get; }

		ConnectionCredentials ConnectionCredentials { get; }

		bool DisableAutoPong { get; set; }

		bool IsConnected { get; }

		bool IsInitialized { get; }

		IReadOnlyList<JoinedChannel> JoinedChannels { get; }

		WhisperMessage PreviousWhisper { get; }

		string TwitchUsername { get; }

		bool WillReplaceEmotes { get; set; }

		event EventHandler<OnChannelStateChangedArgs> OnChannelStateChanged;

		event EventHandler<OnChatClearedArgs> OnChatCleared;

		event EventHandler<OnChatColorChangedArgs> OnChatColorChanged;

		event EventHandler<OnChatCommandReceivedArgs> OnChatCommandReceived;

		event EventHandler<OnConnectedArgs> OnConnected;

		event EventHandler<OnConnectionErrorArgs> OnConnectionError;

		event EventHandler<OnDisconnectedEventArgs> OnDisconnected;

		event EventHandler<OnExistingUsersDetectedArgs> OnExistingUsersDetected;

		event EventHandler<OnGiftedSubscriptionArgs> OnGiftedSubscription;

		event EventHandler<OnIncorrectLoginArgs> OnIncorrectLogin;

		event EventHandler<OnJoinedChannelArgs> OnJoinedChannel;

		event EventHandler<OnLeftChannelArgs> OnLeftChannel;

		event EventHandler<OnLogArgs> OnLog;

		event EventHandler<OnMessageReceivedArgs> OnMessageReceived;

		event EventHandler<OnMessageSentArgs> OnMessageSent;

		event EventHandler<OnModeratorJoinedArgs> OnModeratorJoined;

		event EventHandler<OnModeratorLeftArgs> OnModeratorLeft;

		event EventHandler<OnModeratorsReceivedArgs> OnModeratorsReceived;

		event EventHandler<OnNewSubscriberArgs> OnNewSubscriber;

		event EventHandler<OnRaidNotificationArgs> OnRaidNotification;

		event EventHandler<OnReSubscriberArgs> OnReSubscriber;

		event EventHandler<OnSendReceiveDataArgs> OnSendReceiveData;

		event EventHandler<OnUserBannedArgs> OnUserBanned;

		event EventHandler<OnUserJoinedArgs> OnUserJoined;

		event EventHandler<OnUserLeftArgs> OnUserLeft;

		event EventHandler<OnUserStateChangedArgs> OnUserStateChanged;

		event EventHandler<OnUserTimedoutArgs> OnUserTimedout;

		event EventHandler<OnWhisperCommandReceivedArgs> OnWhisperCommandReceived;

		event EventHandler<OnWhisperReceivedArgs> OnWhisperReceived;

		event EventHandler<OnWhisperSentArgs> OnWhisperSent;

		event EventHandler<OnMessageThrottledEventArgs> OnMessageThrottled;

		event EventHandler<OnWhisperThrottledEventArgs> OnWhisperThrottled;

		event EventHandler<OnErrorEventArgs> OnError;

		event EventHandler<OnReconnectedEventArgs> OnReconnected;

		event EventHandler<OnVIPsReceivedArgs> OnVIPsReceived;

		event EventHandler<OnCommunitySubscriptionArgs> OnCommunitySubscription;

		event EventHandler<OnMessageClearedArgs> OnMessageCleared;

		event EventHandler<OnRequiresVerifiedEmailArgs> OnRequiresVerifiedEmail;

		event EventHandler<OnRequiresVerifiedPhoneNumberArgs> OnRequiresVerifiedPhoneNumber;

		event EventHandler<OnBannedEmailAliasArgs> OnBannedEmailAlias;

		event EventHandler<OnUserIntroArgs> OnUserIntro;

		event EventHandler<OnAnnouncementArgs> OnAnnouncement;

		void Initialize(ConnectionCredentials credentials, string channel = null, char chatCommandIdentifier = '!', char whisperCommandIdentifier = '!', bool autoReListenOnExceptions = true);

		void Initialize(ConnectionCredentials credentials, List<string> channels, char chatCommandIdentifier = '!', char whisperCommandIdentifier = '!', bool autoReListenOnExceptions = true);

		void SetConnectionCredentials(ConnectionCredentials credentials);

		void AddChatCommandIdentifier(char identifier);

		void AddWhisperCommandIdentifier(char identifier);

		void RemoveChatCommandIdentifier(char identifier);

		void RemoveWhisperCommandIdentifier(char identifier);

		bool Connect();

		void Disconnect();

		void Reconnect();

		JoinedChannel GetJoinedChannel(string channel);

		void JoinChannel(string channel, bool overrideCheck = false);

		void LeaveChannel(JoinedChannel channel);

		void LeaveChannel(string channel);

		void OnReadLineTest(string rawIrc);

		void SendMessage(JoinedChannel channel, string message, bool dryRun = false);

		void SendMessage(string channel, string message, bool dryRun = false);

		void SendReply(JoinedChannel channel, string replyToId, string message, bool dryRun = false);

		void SendReply(string channel, string replyToId, string message, bool dryRun = false);

		void SendQueuedItem(string message);

		void SendRaw(string message);

		void SendWhisper(string receiver, string message, bool dryRun = false);
	}
}
namespace TwitchLib.Client.Extensions
{
	public static class AnnoucementExt
	{
		public static void Announce(this ITwitchClient client, JoinedChannel channel, string message)
		{
			client.SendMessage(channel, ".announce " + message);
		}

		public static void Announce(this ITwitchClient client, string channel, string message)
		{
			client.SendMessage(channel, ".announce " + message);
		}
	}
	public static class BanUserExt
	{
		public static void BanUser(this ITwitchClient client, JoinedChannel channel, string viewer, string message = "", bool dryRun = false)
		{
			client.SendMessage(channel, ".ban " + viewer + " " + message);
		}

		public static void BanUser(this ITwitchClient client, string channel, string viewer, string message = "", bool dryRun = false)
		{
			JoinedChannel joinedChannel = client.GetJoinedChannel(channel);
			if (joinedChannel != null)
			{
				client.BanUser(joinedChannel, viewer, message, dryRun);
			}
		}
	}
	public static class ChangeChatColorExt
	{
		public static void ChangeChatColor(this ITwitchClient client, JoinedChannel channel, ChatColorPresets color)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			client.SendMessage(channel, $".color {color}");
		}

		public static void ChangeChatColor(this ITwitchClient client, string channel, ChatColorPresets color)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			client.SendMessage(channel, $".color {color}");
		}
	}
	public static class ClearChatExt
	{
		public static void ClearChat(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".clear");
		}

		public static void ClearChat(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".clear");
		}
	}
	public static class CommercialExt
	{
		public static void StartCommercial(this ITwitchClient client, JoinedChannel channel, CommercialLength length)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if ((int)length <= 90)
			{
				if ((int)length == 30)
				{
					client.SendMessage(channel, ".commercial 30");
					return;
				}
				if ((int)length == 60)
				{
					client.SendMessage(channel, ".commercial 60");
					return;
				}
				if ((int)length == 90)
				{
					client.SendMessage(channel, ".commercial 90");
					return;
				}
			}
			else
			{
				if ((int)length == 120)
				{
					client.SendMessage(channel, ".commercial 120");
					return;
				}
				if ((int)length == 150)
				{
					client.SendMessage(channel, ".commercial 150");
					return;
				}
				if ((int)length == 180)
				{
					client.SendMessage(channel, ".commercial 180");
					return;
				}
			}
			throw new ArgumentOutOfRangeException("length", length, null);
		}

		public static void StartCommercial(this ITwitchClient client, string channel, CommercialLength length)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if ((int)length <= 90)
			{
				if ((int)length == 30)
				{
					client.SendMessage(channel, ".commercial 30");
					return;
				}
				if ((int)length == 60)
				{
					client.SendMessage(channel, ".commercial 60");
					return;
				}
				if ((int)length == 90)
				{
					client.SendMessage(channel, ".commercial 90");
					return;
				}
			}
			else
			{
				if ((int)length == 120)
				{
					client.SendMessage(channel, ".commercial 120");
					return;
				}
				if ((int)length == 150)
				{
					client.SendMessage(channel, ".commercial 150");
					return;
				}
				if ((int)length == 180)
				{
					client.SendMessage(channel, ".commercial 180");
					return;
				}
			}
			throw new ArgumentOutOfRangeException("length", length, null);
		}
	}
	public static class DeleteMessageExt
	{
		public static void DeleteMessage(this ITwitchClient client, JoinedChannel channel, string messageId)
		{
			client.SendMessage(channel, ".delete " + messageId);
		}

		public static void DeleteMessage(this ITwitchClient client, string channel, string messageId)
		{
			client.SendMessage(channel, ".delete " + messageId);
		}

		public static void DeleteMessage(this ITwitchClient client, JoinedChannel channel, ChatMessage msg)
		{
			client.SendMessage(channel, ".delete " + msg.Id);
		}

		public static void DeleteMessage(this ITwitchClient client, string channel, ChatMessage msg)
		{
			client.SendMessage(channel, ".delete " + msg.Id);
		}
	}
	public static class EmoteOnlyExt
	{
		public static void EmoteOnlyOn(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".emoteonly");
		}

		public static void EmoteOnlyOn(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".emoteonly");
		}

		public static void EmoteOnlyOff(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".emoteonlyoff");
		}

		public static void EmoteOnlyOff(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".emoteonlyoff");
		}
	}
	public static class EventInvocationExt
	{
		public static void InvokeChannelStateChanged(this TwitchClient client, string channel, bool r9k, bool rituals, bool subOnly, int slowMode, bool emoteOnly, string broadcasterLanguage, TimeSpan followersOnly, bool mercury, string roomId)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			ChannelState channelState = new ChannelState(r9k, rituals, subOnly, slowMode, emoteOnly, broadcasterLanguage, channel, followersOnly, mercury, roomId);
			OnChannelStateChangedArgs args = new OnChannelStateChangedArgs
			{
				Channel = channel,
				ChannelState = channelState
			};
			client.RaiseEvent("OnChannelStateChanged", args);
		}

		public static void InvokeChatCleared(this TwitchClient client, string channel)
		{
			OnChatClearedArgs args = new OnChatClearedArgs
			{
				Channel = channel
			};
			client.RaiseEvent("OnChatCleared", args);
		}

		public static void InvokeChatCommandsReceived(this TwitchClient client, string botUsername, string userId, string userName, string displayName, string colorHex, Color color, EmoteSet emoteSet, string message, UserType userType, string channel, string id, bool isSubscriber, int subscribedMonthCount, string roomId, bool isTurbo, bool isModerator, bool isMe, bool isBroadcaster, bool isVip, bool isPartner, bool isStaff, Noisy noisy, string rawIrcMessage, string emoteReplacedMessage, List<KeyValuePair<string, string>> badges, CheerBadge cheerBadge, int bits, double bitsInDollars, string commandText, string argumentsAsString, List<string> argumentsAsList, char commandIdentifier)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			ChatMessage val = new ChatMessage(botUsername, userId, userName, displayName, colorHex, color, emoteSet, message, userType, channel, id, isSubscriber, subscribedMonthCount, roomId, isTurbo, isModerator, isMe, isBroadcaster, isVip, isPartner, isStaff, noisy, rawIrcMessage, emoteReplacedMessage, badges, cheerBadge, bits, bitsInDollars);
			OnChatCommandReceivedArgs args = new OnChatCommandReceivedArgs
			{
				Command = new ChatCommand(val, commandText, argumentsAsString, argumentsAsList, commandIdentifier)
			};
			client.RaiseEvent("OnChatCommandReceived", args);
		}

		public static void InvokeConnected(this TwitchClient client, string autoJoinChannel, string botUsername)
		{
			OnConnectedArgs args = new OnConnectedArgs
			{
				AutoJoinChannel = autoJoinChannel,
				BotUsername = botUsername
			};
			client.RaiseEvent("OnConnected", args);
		}

		public static void InvokeConnectionError(this TwitchClient client, string botUsername, ErrorEvent errorEvent)
		{
			OnConnectionErrorArgs args = new OnConnectionErrorArgs
			{
				BotUsername = botUsername,
				Error = errorEvent
			};
			client.RaiseEvent("OnConnectionError", args);
		}

		public static void InvokeDisconnected(this TwitchClient client, string botUsername)
		{
			OnDisconnectedArgs args = new OnDisconnectedArgs
			{
				BotUsername = botUsername
			};
			client.RaiseEvent("OnDisconnected", args);
		}

		public static void InvokeExistingUsersDetected(this TwitchClient client, string channel, List<string> users)
		{
			OnExistingUsersDetectedArgs args = new OnExistingUsersDetectedArgs
			{
				Channel = channel,
				Users = users
			};
			client.RaiseEvent("OnExistingUsersDetected", args);
		}

		public static void InvokeGiftedSubscription(this TwitchClient client, List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string color, string displayName, string emotes, string id, string login, bool isModerator, string msgId, string msgParamMonths, string msgParamRecipientDisplayName, string msgParamRecipientId, string msgParamRecipientUserName, string msgParamSubPlanName, string msgMultiMonthGiftDuration, SubscriptionPlan msgParamSubPlan, string roomId, bool isSubscriber, string systemMsg, string systemMsgParsed, string tmiSentTs, bool isTurbo, UserType userType, string userId)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			OnGiftedSubscriptionArgs args = new OnGiftedSubscriptionArgs
			{
				GiftedSubscription = new GiftedSubscription(badges, badgeInfo, color, displayName, emotes, id, login, isModerator, msgId, msgParamMonths, msgParamRecipientDisplayName, msgParamRecipientId, msgParamRecipientUserName, msgParamSubPlanName, msgMultiMonthGiftDuration, msgParamSubPlan, roomId, isSubscriber, systemMsg, systemMsgParsed, tmiSentTs, isTurbo, userType, userId)
			};
			client.RaiseEvent("OnGiftedSubscription", args);
		}

		public static void InvokeIncorrectLogin(this TwitchClient client, ErrorLoggingInException ex)
		{
			OnIncorrectLoginArgs args = new OnIncorrectLoginArgs
			{
				Exception = ex
			};
			client.RaiseEvent("OnIncorrectLogin", args);
		}

		public static void InvokeJoinedChannel(this TwitchClient client, string botUsername, string channel)
		{
			OnJoinedChannelArgs args = new OnJoinedChannelArgs
			{
				BotUsername = botUsername,
				Channel = channel
			};
			client.RaiseEvent("OnJoinedChannel", args);
		}

		public static void InvokeLeftChannel(this TwitchClient client, string botUsername, string channel)
		{
			OnLeftChannelArgs args = new OnLeftChannelArgs
			{
				BotUsername = botUsername,
				Channel = channel
			};
			client.RaiseEvent("OnLeftChannel", args);
		}

		public static void InvokeLog(this TwitchClient client, string botUsername, string data, DateTime dateTime)
		{
			OnLogArgs args = new OnLogArgs
			{
				BotUsername = botUsername,
				Data = data,
				DateTime = dateTime
			};
			client.RaiseEvent("OnLog", args);
		}

		public static void InvokeMessageReceived(this TwitchClient client, string botUsername, string userId, string userName, string displayName, string colorHex, Color color, EmoteSet emoteSet, string message, UserType userType, string channel, string id, bool isSubscriber, int subscribedMonthCount, string roomId, bool isTurbo, bool isModerator, bool isMe, bool isBroadcaster, bool isVip, bool isPartner, bool isStaff, Noisy noisy, string rawIrcMessage, string emoteReplacedMessage, List<KeyValuePair<string, string>> badges, CheerBadge cheerBadge, int bits, double bitsInDollars)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			OnMessageReceivedArgs args = new OnMessageReceivedArgs
			{
				ChatMessage = new ChatMessage(botUsername, userId, userName, displayName, colorHex, color, emoteSet, message, userType, channel, id, isSubscriber, subscribedMonthCount, roomId, isTurbo, isModerator, isMe, isBroadcaster, isVip, isPartner, isStaff, noisy, rawIrcMessage, emoteReplacedMessage, badges, cheerBadge, bits, bitsInDollars)
			};
			client.RaiseEvent("OnMessageReceived", args);
		}

		public static void InvokeMessageSent(this TwitchClient client, List<KeyValuePair<string, string>> badges, string channel, string colorHex, string displayName, string emoteSet, bool isModerator, bool isSubscriber, UserType userType, string message)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			OnMessageSentArgs args = new OnMessageSentArgs
			{
				SentMessage = new SentMessage(badges, channel, colorHex, displayName, emoteSet, isModerator, isSubscriber, userType, message)
			};
			client.RaiseEvent("OnMessageSent", args);
		}

		public static void InvokeModeratorJoined(this TwitchClient client, string channel, string username)
		{
			OnModeratorJoinedArgs args = new OnModeratorJoinedArgs
			{
				Channel = channel,
				Username = username
			};
			client.RaiseEvent("OnModeratorJoined", args);
		}

		public static void InvokeModeratorLeft(this TwitchClient client, string channel, string username)
		{
			OnModeratorLeftArgs args = new OnModeratorLeftArgs
			{
				Channel = channel,
				Username = username
			};
			client.RaiseEvent("OnModeratorLeft", args);
		}

		public static void InvokeModeratorsReceived(this TwitchClient client, string channel, List<string> moderators)
		{
			OnModeratorsReceivedArgs args = new OnModeratorsReceivedArgs
			{
				Channel = channel,
				Moderators = moderators
			};
			client.RaiseEvent("OnModeratorsReceived", args);
		}

		public static void InvokeNewSubscriber(this TwitchClient client, List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, Color color, string displayName, string emoteSet, string id, string login, string systemMessage, string msgId, string msgParamCumulativeMonths, string msgParamStreakMonths, bool msgParamShouldShareStreak, string systemMessageParsed, string resubMessage, SubscriptionPlan subscriptionPlan, string subscriptionPlanName, string roomId, string userId, bool isModerator, bool isTurbo, bool isSubscriber, bool isPartner, string tmiSentTs, UserType userType, string rawIrc, string channel)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			OnNewSubscriberArgs args = new OnNewSubscriberArgs
			{
				Subscriber = new Subscriber(badges, badgeInfo, colorHex, color, displayName, emoteSet, id, login, systemMessage, msgId, msgParamCumulativeMonths, msgParamStreakMonths, msgParamShouldShareStreak, systemMessageParsed, resubMessage, subscriptionPlan, subscriptionPlanName, roomId, userId, isModerator, isTurbo, isSubscriber, isPartner, tmiSentTs, userType, rawIrc, channel)
			};
			client.RaiseEvent("OnNewSubscriber", args);
		}

		public static void InvokeRaidNotification(this TwitchClient client, string channel, List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string color, string displayName, string emotes, string id, string login, bool moderator, string msgId, string msgParamDisplayName, string msgParamLogin, string msgParamViewerCount, string roomId, bool subscriber, string systemMsg, string systemMsgParsed, string tmiSentTs, bool turbo, UserType userType, string userId)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			OnRaidNotificationArgs args = new OnRaidNotificationArgs
			{
				Channel = channel,
				RaidNotification = new RaidNotification(badges, badgeInfo, color, displayName, emotes, id, login, moderator, msgId, msgParamDisplayName, msgParamLogin, msgParamViewerCount, roomId, subscriber, systemMsg, systemMsgParsed, tmiSentTs, turbo, userType, userId)
			};
			client.RaiseEvent("OnRaidNotification", args);
		}

		public static void InvokeReSubscriber(this TwitchClient client, List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, Color color, string displayName, string emoteSet, string id, string login, string systemMessage, string msgId, string msgParamCumulativeMonths, string msgParamStreakMonths, bool msgParamShouldShareStreak, string systemMessageParsed, string resubMessage, SubscriptionPlan subscriptionPlan, string subscriptionPlanName, string roomId, string userId, bool isModerator, bool isTurbo, bool isSubscriber, bool isPartner, string tmiSentTs, UserType userType, string rawIrc, string channel)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			OnReSubscriberArgs args = new OnReSubscriberArgs
			{
				ReSubscriber = new ReSubscriber(badges, badgeInfo, colorHex, color, displayName, emoteSet, id, login, systemMessage, msgId, msgParamCumulativeMonths, msgParamStreakMonths, msgParamShouldShareStreak, systemMessageParsed, resubMessage, subscriptionPlan, subscriptionPlanName, roomId, userId, isModerator, isTurbo, isSubscriber, isPartner, tmiSentTs, userType, rawIrc, channel, 0)
			};
			client.RaiseEvent("OnReSubscriber", args);
		}

		public static void InvokeSendReceiveData(this TwitchClient client, string data, SendReceiveDirection direction)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			OnSendReceiveDataArgs args = new OnSendReceiveDataArgs
			{
				Data = data,
				Direction = direction
			};
			client.RaiseEvent("OnSendReceiveData", args);
		}

		public static void InvokeUserBanned(this TwitchClient client, string channel, string username, string banReason, string roomId, string targetUserId)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			OnUserBannedArgs args = new OnUserBannedArgs
			{
				UserBan = new UserBan(channel, username, banReason, roomId, targetUserId)
			};
			client.RaiseEvent("OnUserBanned", args);
		}

		public static void InvokeUserJoined(this TwitchClient client, string channel, string username)
		{
			OnUserJoinedArgs args = new OnUserJoinedArgs
			{
				Channel = channel,
				Username = username
			};
			client.RaiseEvent("OnUserJoined", args);
		}

		public static void InvokeUserLeft(this TwitchClient client, string channel, string username)
		{
			OnUserLeftArgs args = new OnUserLeftArgs
			{
				Channel = channel,
				Username = username
			};
			client.RaiseEvent("OnUserLeft", args);
		}

		public static void InvokeUserStateChanged(this TwitchClient client, List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, string displayName, string emoteSet, string channel, string id, bool isSubscriber, bool isModerator, UserType userType)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			OnUserStateChangedArgs args = new OnUserStateChangedArgs
			{
				UserState = new UserState(badges, badgeInfo, colorHex, displayName, emoteSet, channel, id, isSubscriber, isModerator, userType)
			};
			client.RaiseEvent("OnUserStateChanged", args);
		}

		public static void InvokeUserTimedout(this TwitchClient client, string channel, string username, string targetUserId, int timeoutDuration, string timeoutReason)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			OnUserTimedoutArgs args = new OnUserTimedoutArgs
			{
				UserTimeout = new UserTimeout(channel, username, targetUserId, timeoutDuration, timeoutReason)
			};
			client.RaiseEvent("OnUserTimedout", args);
		}

		public static void InvokeWhisperCommandReceived(this TwitchClient client, List<KeyValuePair<string, string>> badges, string colorHex, Color color, string username, string displayName, EmoteSet emoteSet, string threadId, string messageId, string userId, bool isTurbo, string botUsername, string message, UserType userType, string commandText, string argumentsAsString, List<string> argumentsAsList, char commandIdentifier)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			WhisperMessage val = new WhisperMessage(badges, colorHex, color, username, displayName, emoteSet, threadId, messageId, userId, isTurbo, botUsername, message, userType);
			OnWhisperCommandReceivedArgs args = new OnWhisperCommandReceivedArgs
			{
				Command = new WhisperCommand(val, commandText, argumentsAsString, argumentsAsList, commandIdentifier)
			};
			client.RaiseEvent("OnWhisperCommandReceived", args);
		}

		public static void InvokeWhisperReceived(this TwitchClient client, List<KeyValuePair<string, string>> badges, string colorHex, Color color, string username, string displayName, EmoteSet emoteSet, string threadId, string messageId, string userId, bool isTurbo, string botUsername, string message, UserType userType)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			OnWhisperReceivedArgs args = new OnWhisperReceivedArgs
			{
				WhisperMessage = new WhisperMessage(badges, colorHex, color, username, displayName, emoteSet, threadId, messageId, userId, isTurbo, botUsername, message, userType)
			};
			client.RaiseEvent("OnWhisperReceived", args);
		}

		public static void InvokeWhisperSent(this TwitchClient client, string username, string receiver, string message)
		{
			OnWhisperSentArgs args = new OnWhisperSentArgs
			{
				Message = message,
				Receiver = receiver,
				Username = username
			};
			client.RaiseEvent("OnWhisperSent", args);
		}
	}
	public static class FollowersOnlyExt
	{
		private const int MaximumDurationAllowedDays = 90;

		public static void FollowersOnlyOn(this ITwitchClient client, JoinedChannel channel, TimeSpan requiredFollowTime)
		{
			if (requiredFollowTime > TimeSpan.FromDays(90.0))
			{
				throw new InvalidParameterException("The amount of time required to chat exceeded the maximum allowed by Twitch, which is 3 months.", client.TwitchUsername);
			}
			string text = $"{requiredFollowTime.Days}d {requiredFollowTime.Hours}h {requiredFollowTime.Minutes}m";
			client.SendMessage(channel, ".followers " + text);
		}

		public static void FollowersOnlyOn(this ITwitchClient client, string channel, TimeSpan requiredFollowTime)
		{
			if (requiredFollowTime > TimeSpan.FromDays(90.0))
			{
				throw new InvalidParameterException("The amount of time required to chat exceeded the maximum allowed by Twitch, which is 3 months.", client.TwitchUsername);
			}
			string text = $"{requiredFollowTime.Days}d {requiredFollowTime.Hours}h {requiredFollowTime.Minutes}m";
			client.SendMessage(channel, ".followers " + text);
		}

		public static void FollowersOnlyOff(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".followersoff");
		}

		public static void FollowersOnlyOff(this TwitchClient client, string channel)
		{
			client.SendMessage(channel, ".followersoff");
		}
	}
	public static class GetChannelModeratorsExt
	{
		public static void GetChannelModerators(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".mods");
		}

		public static void GetChannelModerators(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".mods");
		}
	}
	public static class MarkerExt
	{
		public static void Marker(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".marker");
		}

		public static void Marker(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".marker");
		}
	}
	public static class ModExt
	{
		public static void Mod(this ITwitchClient client, JoinedChannel channel, string viewerToMod)
		{
			client.SendMessage(channel, ".mod " + viewerToMod);
		}

		public static void Mod(this ITwitchClient client, string channel, string viewerToMod)
		{
			client.SendMessage(channel, ".mod " + viewerToMod);
		}

		public static void Unmod(this ITwitchClient client, JoinedChannel channel, string viewerToUnmod)
		{
			client.SendMessage(channel, ".unmod " + viewerToUnmod);
		}

		public static void Unmod(this ITwitchClient client, string channel, string viewerToUnmod)
		{
			client.SendMessage(channel, ".unmod " + viewerToUnmod);
		}
	}
	public static class RaidExt
	{
		public static void Raid(this ITwitchClient client, JoinedChannel channel, string channelToRaid)
		{
			client.SendMessage(channel, ".raid " + channelToRaid);
		}

		public static void Raid(this ITwitchClient client, string channel, string channelToRaid)
		{
			client.SendMessage(channel, ".raid " + channelToRaid);
		}
	}
	public static class ReplyWhisperExt
	{
		public static void ReplyToLastWhisper(this ITwitchClient client, string message = "", bool dryRun = false)
		{
			if (client.PreviousWhisper != null)
			{
				client.SendWhisper(((TwitchLibMessage)client.PreviousWhisper).Username, message, dryRun);
			}
		}
	}
	public static class SlowModeExt
	{
		public static void SlowModeOn(this ITwitchClient client, JoinedChannel channel, TimeSpan messageCooldown)
		{
			if (messageCooldown > TimeSpan.FromDays(1.0))
			{
				throw new InvalidParameterException("The message cooldown time supplied exceeded the maximum allowed by Twitch, which is 1 day.", client.TwitchUsername);
			}
			client.SendMessage(channel, $".slow {messageCooldown.TotalSeconds}");
		}

		public static void SlowModeOn(this ITwitchClient client, string channel, TimeSpan messageCooldown)
		{
			if (messageCooldown > TimeSpan.FromDays(1.0))
			{
				throw new InvalidParameterException("The message cooldown time supplied exceeded the maximum allowed by Twitch, which is 1 day.", client.TwitchUsername);
			}
			client.SendMessage(channel, $".slow {messageCooldown.TotalSeconds}");
		}

		public static void SlowModeOff(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".slowoff");
		}

		public static void SlowModeOff(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".slowoff");
		}
	}
	public static class SubscribersOnly
	{
		public static void SubscribersOnlyOn(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".subscribers");
		}

		public static void SubscribersOnlyOn(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".subscribers");
		}

		public static void SubscribersOnlyOff(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".subscribersoff");
		}

		public static void SubscribersOnlyOff(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".subscribersoff");
		}
	}
	public static class TimeoutUserExt
	{
		public static void TimeoutUser(this ITwitchClient client, JoinedChannel channel, string viewer, TimeSpan duration, string message = "", bool dryRun = false)
		{
			client.SendMessage(channel, $".timeout {viewer} {duration.TotalSeconds} {message}", dryRun);
		}

		public static void TimeoutUser(this ITwitchClient client, string channel, string viewer, TimeSpan duration, string message = "", bool dryRun = false)
		{
			JoinedChannel joinedChannel = client.GetJoinedChannel(channel);
			if (joinedChannel != null)
			{
				client.TimeoutUser(joinedChannel, viewer, duration, message, dryRun);
			}
		}
	}
	public static class UnbanUserExt
	{
		public static void UnbanUser(this ITwitchClient client, JoinedChannel channel, string viewer, bool dryRun = false)
		{
			client.SendMessage(channel, ".unban " + viewer, dryRun);
		}

		public static void UnbanUser(this ITwitchClient client, string channel, string viewer, bool dryRun = false)
		{
			JoinedChannel joinedChannel = client.GetJoinedChannel(channel);
			if (joinedChannel != null)
			{
				client.UnbanUser(joinedChannel, viewer, dryRun);
			}
		}
	}
	public static class VIPExt
	{
		public static void VIP(this ITwitchClient client, JoinedChannel channel, string viewerToVIP)
		{
			client.SendMessage(channel, ".vip " + viewerToVIP);
		}

		public static void VIP(this ITwitchClient client, string channel, string viewerToVIP)
		{
			client.SendMessage(channel, ".vip " + viewerToVIP);
		}

		public static void UnVIP(this ITwitchClient client, JoinedChannel channel, string viewerToUnVIP)
		{
			client.SendMessage(channel, ".unvip " + viewerToUnVIP);
		}

		public static void UnVIP(this ITwitchClient client, string channel, string viewerToUnVIP)
		{
			client.SendMessage(channel, ".unvip " + viewerToUnVIP);
		}

		public static void GetVIPs(this ITwitchClient client, JoinedChannel channel)
		{
			client.SendMessage(channel, ".vips");
		}

		public static void GetVIPs(this ITwitchClient client, string channel)
		{
			client.SendMessage(channel, ".vips");
		}
	}
}
namespace TwitchLib.Client.Exceptions
{
	public class BadListenException : Exception
	{
		public BadListenException(string eventName, string additionalDetails = "")
			: base(string.IsNullOrEmpty(additionalDetails) ? ("You are listening to event '" + eventName + "', which is not currently allowed. See details: " + additionalDetails) : ("You are listening to event '" + eventName + "', which is not currently allowed."))
		{
		}
	}
	public class BadStateException : Exception
	{
		public BadStateException(string details)
			: base(details)
		{
		}
	}
	public class ClientNotConnectedException : Exception
	{
		public ClientNotConnectedException(string description)
			: base(description)
		{
		}
	}
	public class ClientNotInitializedException : Exception
	{
		public ClientNotInitializedException(string description)
			: base(description)
		{
		}
	}
	public class ErrorLoggingInException : Exception
	{
		public string Username { get; protected set; }

		public ErrorLoggingInException(string ircData, string twitchUsername)
			: base(ircData)
		{
			Username = twitchUsername;
		}
	}
	public class EventNotHandled : Exception
	{
		public EventNotHandled(string eventName, string additionalDetails = "")
			: base(string.IsNullOrEmpty(additionalDetails) ? ("To use this call, you must handle/subscribe to event: " + eventName) : ("To use this call, you must handle/subscribe to event: " + eventName + ", additional details: " + additionalDetails))
		{
		}
	}
	public class FailureToReceiveJoinConfirmationException
	{
		public string Channel { get; protected set; }

		public string Details { get; protected set; }

		

TwitchLib.Client.Enums.dll

Decompiled a month ago
using System.Diagnostics;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright 2021")]
[assembly: AssemblyDescription("Project containing all of the enums used in TwitchLib.Client.")]
[assembly: AssemblyFileVersion("3.3.1")]
[assembly: AssemblyInformationalVersion("3.3.1")]
[assembly: AssemblyProduct("TwitchLib.Client.Enums")]
[assembly: AssemblyTitle("TwitchLib.Client.Enums")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TwitchLib/TwitchLib.Client")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("3.3.1.0")]
namespace TwitchLib.Client.Enums
{
	public enum BadgeColor
	{
		Red = 10000,
		Blue = 5000,
		Green = 1000,
		Purple = 100,
		Gray = 1
	}
	public enum ChatColorPresets
	{
		Blue,
		Coral,
		DodgerBlue,
		SpringGreen,
		YellowGreen,
		Green,
		OrangeRed,
		Red,
		GoldenRod,
		HotPink,
		CadetBlue,
		SeaGreen,
		Chocolate,
		BlueViolet,
		Firebrick
	}
	public enum ClientProtocol
	{
		TCP,
		WebSocket
	}
	public enum CommercialLength
	{
		Seconds30 = 30,
		Seconds60 = 60,
		Seconds90 = 90,
		Seconds120 = 120,
		Seconds150 = 150,
		Seconds180 = 180
	}
	public enum Noisy
	{
		NotSet,
		True,
		False
	}
	public enum SendReceiveDirection
	{
		Sent,
		Received
	}
	public abstract class StringEnum
	{
		public string Value { get; }

		protected StringEnum(string value)
		{
			Value = value;
		}

		public override string ToString()
		{
			return Value;
		}
	}
	public enum SubscriptionPlan
	{
		NotSet,
		Prime,
		Tier1,
		Tier2,
		Tier3
	}
	public enum ThrottleType
	{
		MessageTooShort,
		MessageTooLong
	}
	public enum UserType : byte
	{
		Viewer,
		Moderator,
		GlobalModerator,
		Broadcaster,
		Admin,
		Staff
	}
}
namespace TwitchLib.Client.Enums.Internal
{
	public enum IrcCommand
	{
		Unknown,
		PrivMsg,
		Notice,
		Ping,
		Pong,
		Join,
		Part,
		ClearChat,
		ClearMsg,
		UserState,
		GlobalUserState,
		Nick,
		Pass,
		Cap,
		RPL_001,
		RPL_002,
		RPL_003,
		RPL_004,
		RPL_353,
		RPL_366,
		RPL_372,
		RPL_375,
		RPL_376,
		Whisper,
		RoomState,
		Reconnect,
		ServerChange,
		UserNotice,
		Mode
	}
}

TwitchLib.Client.Models.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using TwitchLib.Client.Enums;
using TwitchLib.Client.Enums.Internal;
using TwitchLib.Client.Models.Builders;
using TwitchLib.Client.Models.Common;
using TwitchLib.Client.Models.Extensions.NetCore;
using TwitchLib.Client.Models.Extractors;
using TwitchLib.Client.Models.Internal;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright 2021")]
[assembly: AssemblyDescription("Project contains all of the models used in TwitchLib.Client.")]
[assembly: AssemblyFileVersion("3.3.1")]
[assembly: AssemblyInformationalVersion("3.3.1")]
[assembly: AssemblyProduct("TwitchLib.Client.Models")]
[assembly: AssemblyTitle("TwitchLib.Client.Models")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TwitchLib/TwitchLib.Client")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("3.3.1.0")]
namespace TwitchLib.Client.Models
{
	public class Announcement
	{
		public string Id { get; }

		public List<KeyValuePair<string, string>> Badges { get; }

		public List<KeyValuePair<string, string>> BadgeInfo { get; }

		public string SystemMessage { get; }

		public string SystemMessageParsed { get; }

		public bool IsBroadcaster { get; }

		public bool IsModerator { get; }

		public bool IsPartner { get; }

		public bool IsSubscriber { get; }

		public bool IsStaff { get; }

		public bool IsTurbo { get; }

		public string Login { get; }

		public string UserId { get; }

		public string RoomId { get; }

		public UserType UserType { get; }

		public string TmiSentTs { get; }

		public string EmoteSet { get; }

		public string RawIrc { get; }

		public string MsgId { get; }

		public string MsgParamColor { get; }

		public string ColorHex { get; }

		public Color Color { get; }

		public string Message { get; }

		public Announcement(IrcMessage ircMessage)
		{
			//IL_047c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0485: Unknown result type (might be due to invalid IL or missing references)
			//IL_048e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			RawIrc = ircMessage.ToString();
			Message = ircMessage.Message;
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
				{
					Badges = Helpers.ParseBadges(text);
					using (List<KeyValuePair<string, string>>.Enumerator enumerator2 = Badges.GetEnumerator())
					{
						while (enumerator2.MoveNext())
						{
							switch (enumerator2.Current.Key)
							{
							case "broadcaster":
								IsBroadcaster = true;
								break;
							case "turbo":
								IsTurbo = true;
								break;
							case "moderator":
								IsModerator = true;
								break;
							case "subscriber":
								IsSubscriber = true;
								break;
							case "admin":
								IsStaff = true;
								break;
							case "staff":
								IsStaff = true;
								break;
							case "partner":
								IsPartner = true;
								break;
							}
						}
					}
					break;
				}
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					ColorHex = text;
					if (!string.IsNullOrEmpty(ColorHex))
					{
						Color = TwitchLib.Client.Models.Extensions.NetCore.ColorTranslator.FromHtml(ColorHex);
					}
					break;
				case "msg-param-color":
					MsgParamColor = text;
					break;
				case "emotes":
					EmoteSet = text;
					break;
				case "id":
					Id = text;
					break;
				case "login":
					Login = text;
					break;
				case "msg-id":
					MsgId = text;
					break;
				case "room-id":
					RoomId = text;
					break;
				case "system-msg":
					SystemMessage = text;
					SystemMessageParsed = text.Replace("\\s", " ");
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "user-id":
					UserId = text;
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				}
			}
		}
	}
	public class ChannelState
	{
		public string BroadcasterLanguage { get; }

		public string Channel { get; }

		public bool? EmoteOnly { get; }

		public TimeSpan? FollowersOnly { get; }

		public bool Mercury { get; }

		public bool? R9K { get; }

		public bool? Rituals { get; }

		public string RoomId { get; }

		public int? SlowMode { get; }

		public bool? SubOnly { get; }

		public ChannelState(IrcMessage ircMessage)
		{
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "broadcaster-lang":
					BroadcasterLanguage = text;
					break;
				case "emote-only":
					EmoteOnly = Helpers.ConvertToBool(text);
					break;
				case "r9k":
					R9K = Helpers.ConvertToBool(text);
					break;
				case "rituals":
					Rituals = Helpers.ConvertToBool(text);
					break;
				case "slow":
				{
					int result2;
					bool flag = int.TryParse(text, out result2);
					SlowMode = (flag ? new int?(result2) : null);
					break;
				}
				case "subs-only":
					SubOnly = Helpers.ConvertToBool(text);
					break;
				case "followers-only":
				{
					if (int.TryParse(text, out var result) && result > -1)
					{
						FollowersOnly = TimeSpan.FromMinutes(result);
					}
					break;
				}
				case "room-id":
					RoomId = text;
					break;
				case "mercury":
					Mercury = Helpers.ConvertToBool(text);
					break;
				default:
					Console.WriteLine("[TwitchLib][ChannelState] Unaccounted for: " + key);
					break;
				}
			}
			Channel = ircMessage.Channel;
		}

		public ChannelState(bool r9k, bool rituals, bool subonly, int slowMode, bool emoteOnly, string broadcasterLanguage, string channel, TimeSpan followersOnly, bool mercury, string roomId)
		{
			R9K = r9k;
			Rituals = rituals;
			SubOnly = subonly;
			SlowMode = slowMode;
			EmoteOnly = emoteOnly;
			BroadcasterLanguage = broadcasterLanguage;
			Channel = channel;
			FollowersOnly = followersOnly;
			Mercury = mercury;
			RoomId = roomId;
		}
	}
	public class ChatCommand
	{
		public List<string> ArgumentsAsList { get; }

		public string ArgumentsAsString { get; }

		public ChatMessage ChatMessage { get; }

		public char CommandIdentifier { get; }

		public string CommandText { get; }

		public ChatCommand(ChatMessage chatMessage)
		{
			ChatCommand chatCommand = this;
			ChatMessage = chatMessage;
			string[] array = chatMessage.Message.Split(new char[1] { ' ' });
			CommandText = ((array != null) ? array[0].Substring(1, chatMessage.Message.Split(new char[1] { ' ' })[0].Length - 1) : null) ?? chatMessage.Message.Substring(1, chatMessage.Message.Length - 1);
			object obj;
			if (!chatMessage.Message.Contains(" "))
			{
				obj = "";
			}
			else
			{
				string message = chatMessage.Message;
				string[] array2 = chatMessage.Message.Split(new char[1] { ' ' });
				obj = message.Replace(((array2 != null) ? array2[0] : null) + " ", "");
			}
			ArgumentsAsString = (string)obj;
			if (!chatMessage.Message.Contains("\"") || chatMessage.Message.Count((char x) => x == '"') % 2 == 1)
			{
				ArgumentsAsList = chatMessage.Message.Split(new char[1] { ' ' })?.Where((string arg) => arg != chatMessage.Message[0] + chatCommand.CommandText).ToList() ?? new List<string>();
			}
			else
			{
				ArgumentsAsList = Helpers.ParseQuotesAndNonQuotes(ArgumentsAsString);
			}
			CommandIdentifier = chatMessage.Message[0];
		}

		public ChatCommand(ChatMessage chatMessage, string commandText, string argumentsAsString, List<string> argumentsAsList, char commandIdentifier)
		{
			ChatMessage = chatMessage;
			CommandText = commandText;
			ArgumentsAsString = argumentsAsString;
			ArgumentsAsList = argumentsAsList;
			CommandIdentifier = commandIdentifier;
		}
	}
	public class ChatMessage : TwitchLibMessage
	{
		protected readonly MessageEmoteCollection _emoteCollection;

		public List<KeyValuePair<string, string>> BadgeInfo { get; }

		public int Bits { get; }

		public double BitsInDollars { get; }

		public string Channel { get; }

		public CheerBadge CheerBadge { get; }

		public string CustomRewardId { get; }

		public string EmoteReplacedMessage { get; }

		public string Id { get; }

		public bool IsBroadcaster { get; }

		public bool IsFirstMessage { get; }

		public bool IsHighlighted { get; internal set; }

		public bool IsMe { get; }

		public bool IsModerator { get; }

		public bool IsSkippingSubMode { get; internal set; }

		public bool IsSubscriber { get; }

		public bool IsVip { get; }

		public bool IsStaff { get; }

		public bool IsPartner { get; }

		public string Message { get; }

		public Noisy Noisy { get; }

		public string RoomId { get; }

		public int SubscribedMonthCount { get; }

		public string TmiSentTs { get; }

		public ChatReply ChatReply { get; }

		public ChatMessage(string botUsername, IrcMessage ircMessage, ref MessageEmoteCollection emoteCollection, bool replaceEmotes = false)
		{
			//IL_06f9: Unknown result type (might be due to invalid IL or missing references)
			base.BotUsername = botUsername;
			base.RawIrcMessage = ircMessage.ToString();
			Message = ircMessage.Message;
			if (Message.Length > 0 && (byte)Message[0] == 1 && (byte)Message[Message.Length - 1] == 1 && Message.StartsWith("\u0001ACTION ") && Message.EndsWith("\u0001"))
			{
				Message = Message.Trim(new char[1] { '\u0001' }).Substring(7);
				IsMe = true;
			}
			_emoteCollection = emoteCollection;
			base.Username = ircMessage.User;
			Channel = ircMessage.Channel;
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
					base.Badges = Helpers.ParseBadges(text);
					foreach (KeyValuePair<string, string> badge in base.Badges)
					{
						switch (badge.Key)
						{
						case "bits":
							CheerBadge = new CheerBadge(int.Parse(badge.Value));
							break;
						case "subscriber":
							if (SubscribedMonthCount == 0)
							{
								SubscribedMonthCount = int.Parse(badge.Value);
							}
							break;
						case "vip":
							IsVip = true;
							break;
						case "admin":
							IsStaff = true;
							break;
						case "staff":
							IsStaff = true;
							break;
						case "partner":
							IsPartner = true;
							break;
						}
					}
					break;
				case "badge-info":
				{
					BadgeInfo = Helpers.ParseBadges(text);
					KeyValuePair<string, string> keyValuePair = BadgeInfo.Find((KeyValuePair<string, string> b) => b.Key == "founder");
					if (!keyValuePair.Equals(default(KeyValuePair<string, string>)))
					{
						IsSubscriber = true;
						SubscribedMonthCount = int.Parse(keyValuePair.Value);
						break;
					}
					KeyValuePair<string, string> keyValuePair2 = BadgeInfo.Find((KeyValuePair<string, string> b) => b.Key == "subscriber");
					if (!keyValuePair2.Equals(default(KeyValuePair<string, string>)))
					{
						SubscribedMonthCount = int.Parse(keyValuePair2.Value);
					}
					break;
				}
				case "bits":
					Bits = int.Parse(text);
					BitsInDollars = ConvertBitsToUsd(Bits);
					break;
				case "color":
					base.ColorHex = text;
					if (!string.IsNullOrWhiteSpace(base.ColorHex))
					{
						base.Color = TwitchLib.Client.Models.Extensions.NetCore.ColorTranslator.FromHtml(base.ColorHex);
					}
					break;
				case "custom-reward-id":
					CustomRewardId = text;
					break;
				case "display-name":
					base.DisplayName = text;
					break;
				case "emotes":
					base.EmoteSet = new EmoteSet(text, Message);
					break;
				case "first-msg":
					IsFirstMessage = text == "1";
					break;
				case "id":
					Id = text;
					break;
				case "msg-id":
					handleMsgId(text);
					break;
				case "mod":
					IsModerator = Helpers.ConvertToBool(text);
					break;
				case "noisy":
					Noisy = (Noisy)(Helpers.ConvertToBool(text) ? 1 : 2);
					break;
				case "reply-parent-display-name":
					if (ChatReply == null)
					{
						ChatReply = new ChatReply();
					}
					ChatReply.ParentDisplayName = text;
					break;
				case "reply-parent-msg-body":
					if (ChatReply == null)
					{
						ChatReply = new ChatReply();
					}
					ChatReply.ParentMsgBody = text;
					break;
				case "reply-parent-msg-id":
					if (ChatReply == null)
					{
						ChatReply = new ChatReply();
					}
					ChatReply.ParentMsgId = text;
					break;
				case "reply-parent-user-id":
					if (ChatReply == null)
					{
						ChatReply = new ChatReply();
					}
					ChatReply.ParentUserId = text;
					break;
				case "reply-parent-user-login":
					if (ChatReply == null)
					{
						ChatReply = new ChatReply();
					}
					ChatReply.ParentUserLogin = text;
					break;
				case "room-id":
					RoomId = text;
					break;
				case "subscriber":
					IsSubscriber = IsSubscriber || Helpers.ConvertToBool(text);
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "turbo":
					base.IsTurbo = Helpers.ConvertToBool(text);
					break;
				case "user-id":
					base.UserId = text;
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						base.UserType = (UserType)1;
						break;
					case "global_mod":
						base.UserType = (UserType)2;
						break;
					case "admin":
						base.UserType = (UserType)4;
						IsStaff = true;
						break;
					case "staff":
						base.UserType = (UserType)5;
						IsStaff = true;
						break;
					default:
						base.UserType = (UserType)0;
						break;
					}
					break;
				}
			}
			if (base.EmoteSet != null && Message != null && base.EmoteSet.Emotes.Count > 0)
			{
				string[] array = base.EmoteSet.RawEmoteSetString.Split(new char[1] { '/' });
				foreach (string text2 in array)
				{
					int num = text2.IndexOf(':');
					int num2 = text2.IndexOf(',');
					if (num2 == -1)
					{
						num2 = text2.Length;
					}
					int num3 = text2.IndexOf('-');
					if (num > 0 && num3 > num && num2 > num3 && int.TryParse(text2.Substring(num + 1, num3 - num - 1), out var result) && int.TryParse(text2.Substring(num3 + 1, num2 - num3 - 1), out var result2) && result >= 0 && result < result2 && result2 < Message.Length)
					{
						string id = text2.Substring(0, num);
						string text3 = Message.Substring(result, result2 - result + 1);
						_emoteCollection.Add(new MessageEmote(id, text3));
					}
				}
				if (replaceEmotes)
				{
					EmoteReplacedMessage = _emoteCollection.ReplaceEmotes(Message);
				}
			}
			if (base.EmoteSet == null)
			{
				base.EmoteSet = new EmoteSet((string)null, Message);
			}
			if (string.IsNullOrEmpty(base.DisplayName))
			{
				base.DisplayName = base.Username;
			}
			if (string.Equals(Channel, base.Username, StringComparison.InvariantCultureIgnoreCase))
			{
				base.UserType = (UserType)3;
				IsBroadcaster = true;
			}
			if (Channel.Split(new char[1] { ':' }).Length == 3 && string.Equals(Channel.Split(new char[1] { ':' })[1], base.UserId, StringComparison.InvariantCultureIgnoreCase))
			{
				base.UserType = (UserType)3;
				IsBroadcaster = true;
			}
		}

		public ChatMessage(string botUsername, string userId, string userName, string displayName, string colorHex, Color color, EmoteSet emoteSet, string message, UserType userType, string channel, string id, bool isSubscriber, int subscribedMonthCount, string roomId, bool isTurbo, bool isModerator, bool isMe, bool isBroadcaster, bool isVip, bool isPartner, bool isStaff, Noisy noisy, string rawIrcMessage, string emoteReplacedMessage, List<KeyValuePair<string, string>> badges, CheerBadge cheerBadge, int bits, double bitsInDollars)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			base.BotUsername = botUsername;
			base.UserId = userId;
			base.DisplayName = displayName;
			base.ColorHex = colorHex;
			base.Color = color;
			base.EmoteSet = emoteSet;
			Message = message;
			base.UserType = userType;
			Channel = channel;
			Id = id;
			IsSubscriber = isSubscriber;
			SubscribedMonthCount = subscribedMonthCount;
			RoomId = roomId;
			base.IsTurbo = isTurbo;
			IsModerator = isModerator;
			IsMe = isMe;
			IsBroadcaster = isBroadcaster;
			IsVip = isVip;
			IsPartner = isPartner;
			IsStaff = isStaff;
			Noisy = noisy;
			base.RawIrcMessage = rawIrcMessage;
			EmoteReplacedMessage = emoteReplacedMessage;
			base.Badges = badges;
			CheerBadge = cheerBadge;
			Bits = bits;
			BitsInDollars = bitsInDollars;
			base.Username = userName;
		}

		private void handleMsgId(string val)
		{
			if (!(val == "highlighted-message"))
			{
				if (val == "skip-subs-mode-message")
				{
					IsSkippingSubMode = true;
				}
			}
			else
			{
				IsHighlighted = true;
			}
		}

		private static double ConvertBitsToUsd(int bits)
		{
			if (bits < 1500)
			{
				return (double)bits / 100.0 * 1.4;
			}
			if (bits < 5000)
			{
				return (double)bits / 1500.0 * 19.95;
			}
			if (bits < 10000)
			{
				return (double)bits / 5000.0 * 64.4;
			}
			if (bits < 25000)
			{
				return (double)bits / 10000.0 * 126.0;
			}
			return (double)bits / 25000.0 * 308.0;
		}
	}
	public class ChatReply
	{
		public string ParentDisplayName { get; internal set; }

		public string ParentMsgBody { get; internal set; }

		public string ParentMsgId { get; internal set; }

		public string ParentUserId { get; internal set; }

		public string ParentUserLogin { get; internal set; }
	}
	public class CheerBadge
	{
		public int CheerAmount { get; }

		public BadgeColor Color { get; }

		public CheerBadge(int cheerAmount)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			CheerAmount = cheerAmount;
			Color = GetColor(cheerAmount);
		}

		private BadgeColor GetColor(int cheerAmount)
		{
			if (cheerAmount < 10000)
			{
				if (cheerAmount < 5000)
				{
					if (cheerAmount < 1000)
					{
						if (cheerAmount >= 100)
						{
							return (BadgeColor)100;
						}
						return (BadgeColor)1;
					}
					return (BadgeColor)1000;
				}
				return (BadgeColor)5000;
			}
			return (BadgeColor)10000;
		}
	}
	public class CommunitySubscription
	{
		private const string AnonymousGifterUserId = "274598607";

		public List<KeyValuePair<string, string>> Badges;

		public List<KeyValuePair<string, string>> BadgeInfo;

		public string Color;

		public string DisplayName;

		public string Emotes;

		public string Id;

		public string Login;

		public bool IsModerator;

		public bool IsAnonymous;

		public string MsgId;

		public int MsgParamMassGiftCount;

		public int MsgParamSenderCount;

		public SubscriptionPlan MsgParamSubPlan;

		public string RoomId;

		public bool IsSubscriber;

		public string SystemMsg;

		public string SystemMsgParsed;

		public string TmiSentTs;

		public bool IsTurbo;

		public string UserId;

		public UserType UserType;

		public string MsgParamMultiMonthGiftDuration;

		public CommunitySubscription(IrcMessage ircMessage)
		{
			//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0506: Unknown result type (might be due to invalid IL or missing references)
			//IL_050f: Unknown result type (might be due to invalid IL or missing references)
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
					Badges = Helpers.ParseBadges(text);
					break;
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					Color = text;
					break;
				case "display-name":
					DisplayName = text;
					break;
				case "emotes":
					Emotes = text;
					break;
				case "id":
					Id = text;
					break;
				case "login":
					Login = text;
					break;
				case "mod":
					IsModerator = Helpers.ConvertToBool(text);
					break;
				case "msg-id":
					MsgId = text;
					break;
				case "msg-param-sub-plan":
					switch (text)
					{
					case "prime":
						MsgParamSubPlan = (SubscriptionPlan)1;
						break;
					case "1000":
						MsgParamSubPlan = (SubscriptionPlan)2;
						break;
					case "2000":
						MsgParamSubPlan = (SubscriptionPlan)3;
						break;
					case "3000":
						MsgParamSubPlan = (SubscriptionPlan)4;
						break;
					default:
						throw new ArgumentOutOfRangeException("ToLower");
					}
					break;
				case "msg-param-mass-gift-count":
					MsgParamMassGiftCount = int.Parse(text);
					break;
				case "msg-param-sender-count":
					MsgParamSenderCount = int.Parse(text);
					break;
				case "room-id":
					RoomId = text;
					break;
				case "subscriber":
					IsSubscriber = Helpers.ConvertToBool(text);
					break;
				case "system-msg":
					SystemMsg = text;
					SystemMsgParsed = text.Replace("\\s", " ").Replace("\\n", "");
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "turbo":
					IsTurbo = Helpers.ConvertToBool(text);
					break;
				case "user-id":
					UserId = text;
					if (UserId == "274598607")
					{
						IsAnonymous = true;
					}
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				case "msg-param-gift-months":
					MsgParamMultiMonthGiftDuration = text;
					break;
				}
			}
		}
	}
	public class ConnectionCredentials
	{
		public const string DefaultWebSocketUri = "wss://irc-ws.chat.twitch.tv:443";

		public string TwitchWebsocketURI { get; }

		public string TwitchOAuth { get; }

		public string TwitchUsername { get; }

		public Capabilities Capabilities { get; }

		public ConnectionCredentials(string twitchUsername, string twitchOAuth, string twitchWebsocketURI = "wss://irc-ws.chat.twitch.tv:443", bool disableUsernameCheck = false, Capabilities capabilities = null)
		{
			if (!disableUsernameCheck && !new Regex("^([a-zA-Z0-9][a-zA-Z0-9_]{3,25})$").Match(twitchUsername).Success)
			{
				throw new Exception("Twitch username does not appear to be valid. " + twitchUsername);
			}
			TwitchUsername = twitchUsername.ToLower();
			TwitchOAuth = twitchOAuth;
			if (!twitchOAuth.Contains(":"))
			{
				TwitchOAuth = "oauth:" + twitchOAuth.Replace("oauth", "");
			}
			TwitchWebsocketURI = twitchWebsocketURI;
			if (capabilities == null)
			{
				capabilities = new Capabilities();
			}
			Capabilities = capabilities;
		}
	}
	public class Capabilities
	{
		public bool Membership { get; }

		public bool Tags { get; }

		public bool Commands { get; }

		public Capabilities(bool membership = true, bool tags = true, bool commands = true)
		{
			Membership = membership;
			Tags = tags;
			Commands = commands;
		}
	}
	public class ContinuedGiftedSubscription
	{
		public List<KeyValuePair<string, string>> Badges { get; }

		public List<KeyValuePair<string, string>> BadgeInfo { get; }

		public string Color { get; }

		public string DisplayName { get; }

		public string Emotes { get; }

		public string Flags { get; }

		public string Id { get; }

		public string Login { get; }

		public bool IsModerator { get; }

		public string MsgId { get; }

		public string MsgParamSenderLogin { get; }

		public string MsgParamSenderName { get; }

		public string RoomId { get; }

		public bool IsSubscriber { get; }

		public string SystemMsg { get; }

		public string TmiSentTs { get; }

		public string UserId { get; }

		public UserType UserType { get; }

		public ContinuedGiftedSubscription(IrcMessage ircMessage)
		{
			//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_040b: Unknown result type (might be due to invalid IL or missing references)
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "system-msg":
					SystemMsg = text;
					break;
				case "flags":
					Flags = text;
					break;
				case "msg-param-sender-login":
					MsgParamSenderLogin = text;
					break;
				case "msg-param-sender-name":
					MsgParamSenderName = text;
					break;
				case "badges":
					Badges = Helpers.ParseBadges(text);
					break;
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					Color = text;
					break;
				case "display-name":
					DisplayName = text;
					break;
				case "emotes":
					Emotes = text;
					break;
				case "id":
					Id = text;
					break;
				case "login":
					Login = text;
					break;
				case "mod":
					IsModerator = Helpers.ConvertToBool(text);
					break;
				case "msg-id":
					MsgId = text;
					break;
				case "room-id":
					RoomId = text;
					break;
				case "subscriber":
					IsSubscriber = Helpers.ConvertToBool(text);
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "user-id":
					UserId = text;
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				}
			}
		}
	}
	public class Emote
	{
		public string Id { get; }

		public string Name { get; }

		public int StartIndex { get; }

		public int EndIndex { get; }

		public string ImageUrl { get; }

		public Emote(string emoteId, string name, int emoteStartIndex, int emoteEndIndex)
		{
			Id = emoteId;
			Name = name;
			StartIndex = emoteStartIndex;
			EndIndex = emoteEndIndex;
			ImageUrl = "https://static-cdn.jtvnw.net/emoticons/v1/" + emoteId + "/1.0";
		}
	}
	public class EmoteSet
	{
		public List<Emote> Emotes { get; }

		public string RawEmoteSetString { get; }

		public EmoteSet(string rawEmoteSetString, string message)
		{
			RawEmoteSetString = rawEmoteSetString;
			EmoteExtractor emoteExtractor = new EmoteExtractor();
			Emotes = emoteExtractor.Extract(rawEmoteSetString, message).ToList();
		}

		public EmoteSet(IEnumerable<Emote> emotes, string emoteSetData)
		{
			RawEmoteSetString = emoteSetData;
			Emotes = emotes.ToList();
		}
	}
	public class ErrorEvent
	{
		public string Message { get; set; }
	}
	public class GiftedSubscription
	{
		private const string AnonymousGifterUserId = "274598607";

		public List<KeyValuePair<string, string>> Badges { get; }

		public List<KeyValuePair<string, string>> BadgeInfo { get; }

		public string Color { get; }

		public string DisplayName { get; }

		public string Emotes { get; }

		public string Id { get; }

		public bool IsModerator { get; }

		public bool IsSubscriber { get; }

		public bool IsTurbo { get; }

		public bool IsAnonymous { get; }

		public string Login { get; }

		public string MsgId { get; }

		public string MsgParamMonths { get; }

		public string MsgParamRecipientDisplayName { get; }

		public string MsgParamRecipientId { get; }

		public string MsgParamRecipientUserName { get; }

		public string MsgParamSubPlanName { get; }

		public SubscriptionPlan MsgParamSubPlan { get; }

		public string RoomId { get; }

		public string SystemMsg { get; }

		public string SystemMsgParsed { get; }

		public string TmiSentTs { get; }

		public string UserId { get; }

		public UserType UserType { get; }

		public string MsgParamMultiMonthGiftDuration { get; }

		public GiftedSubscription(IrcMessage ircMessage)
		{
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_0473: Unknown result type (might be due to invalid IL or missing references)
			//IL_047f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0565: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Unknown result type (might be due to invalid IL or missing references)
			//IL_056e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0577: Unknown result type (might be due to invalid IL or missing references)
			//IL_0580: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
					Badges = Helpers.ParseBadges(text);
					break;
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					Color = text;
					break;
				case "display-name":
					DisplayName = text;
					break;
				case "emotes":
					Emotes = text;
					break;
				case "id":
					Id = text;
					break;
				case "login":
					Login = text;
					break;
				case "mod":
					IsModerator = Helpers.ConvertToBool(text);
					break;
				case "msg-id":
					MsgId = text;
					break;
				case "msg-param-months":
					MsgParamMonths = text;
					break;
				case "msg-param-recipient-display-name":
					MsgParamRecipientDisplayName = text;
					break;
				case "msg-param-recipient-id":
					MsgParamRecipientId = text;
					break;
				case "msg-param-recipient-user-name":
					MsgParamRecipientUserName = text;
					break;
				case "msg-param-sub-plan-name":
					MsgParamSubPlanName = text;
					break;
				case "msg-param-sub-plan":
					switch (text)
					{
					case "prime":
						MsgParamSubPlan = (SubscriptionPlan)1;
						break;
					case "1000":
						MsgParamSubPlan = (SubscriptionPlan)2;
						break;
					case "2000":
						MsgParamSubPlan = (SubscriptionPlan)3;
						break;
					case "3000":
						MsgParamSubPlan = (SubscriptionPlan)4;
						break;
					default:
						throw new ArgumentOutOfRangeException("ToLower");
					}
					break;
				case "room-id":
					RoomId = text;
					break;
				case "subscriber":
					IsSubscriber = Helpers.ConvertToBool(text);
					break;
				case "system-msg":
					SystemMsg = text;
					SystemMsgParsed = text.Replace("\\s", " ").Replace("\\n", "");
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "turbo":
					IsTurbo = Helpers.ConvertToBool(text);
					break;
				case "user-id":
					UserId = text;
					if (UserId == "274598607")
					{
						IsAnonymous = true;
					}
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				case "msg-param-gift-months":
					MsgParamMultiMonthGiftDuration = text;
					break;
				}
			}
		}

		public GiftedSubscription(List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string color, string displayName, string emotes, string id, string login, bool isModerator, string msgId, string msgParamMonths, string msgParamRecipientDisplayName, string msgParamRecipientId, string msgParamRecipientUserName, string msgParamSubPlanName, string msgMultiMonthDuration, SubscriptionPlan msgParamSubPlan, string roomId, bool isSubscriber, string systemMsg, string systemMsgParsed, string tmiSentTs, bool isTurbo, UserType userType, string userId)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			Badges = badges;
			BadgeInfo = badgeInfo;
			Color = color;
			DisplayName = displayName;
			Emotes = emotes;
			Id = id;
			Login = login;
			IsModerator = isModerator;
			MsgId = msgId;
			MsgParamMonths = msgParamMonths;
			MsgParamRecipientDisplayName = msgParamRecipientDisplayName;
			MsgParamRecipientId = msgParamRecipientId;
			MsgParamRecipientUserName = msgParamRecipientUserName;
			MsgParamSubPlanName = msgParamSubPlanName;
			MsgParamSubPlan = msgParamSubPlan;
			MsgParamMultiMonthGiftDuration = msgMultiMonthDuration;
			RoomId = roomId;
			IsSubscriber = isSubscriber;
			SystemMsg = systemMsg;
			SystemMsgParsed = systemMsgParsed;
			TmiSentTs = tmiSentTs;
			IsTurbo = isTurbo;
			UserType = userType;
			UserId = userId;
		}
	}
	public class JoinedChannel
	{
		public string Channel { get; }

		public ChannelState ChannelState { get; protected set; }

		public ChatMessage PreviousMessage { get; protected set; }

		public JoinedChannel(string channel)
		{
			Channel = channel;
		}

		public void HandleMessage(ChatMessage message)
		{
			PreviousMessage = message;
		}
	}
	public class MessageEmote
	{
		public delegate string ReplaceEmoteDelegate(MessageEmote caller);

		public enum EmoteSource
		{
			Twitch,
			FrankerFaceZ,
			BetterTwitchTv
		}

		public enum EmoteSize
		{
			Small,
			Medium,
			Large
		}

		public static readonly ReadOnlyCollection<string> TwitchEmoteUrls = new ReadOnlyCollection<string>(new string[3] { "https://static-cdn.jtvnw.net/emoticons/v1/{0}/1.0", "https://static-cdn.jtvnw.net/emoticons/v1/{0}/2.0", "https://static-cdn.jtvnw.net/emoticons/v1/{0}/3.0" });

		public static readonly ReadOnlyCollection<string> FrankerFaceZEmoteUrls = new ReadOnlyCollection<string>(new string[3] { "//cdn.frankerfacez.com/emoticon/{0}/1", "//cdn.frankerfacez.com/emoticon/{0}/2", "//cdn.frankerfacez.com/emoticon/{0}/4" });

		public static readonly ReadOnlyCollection<string> BetterTwitchTvEmoteUrls = new ReadOnlyCollection<string>(new string[3] { "//cdn.betterttv.net/emote/{0}/1x", "//cdn.betterttv.net/emote/{0}/2x", "//cdn.betterttv.net/emote/{0}/3x" });

		private readonly string _id;

		private readonly string _text;

		private readonly string _escapedText;

		private readonly EmoteSource _source;

		private readonly EmoteSize _size;

		public string Id => _id;

		public string Text => _text;

		public EmoteSource Source => _source;

		public EmoteSize Size => _size;

		public string ReplacementString => ReplacementDelegate(this);

		public static ReplaceEmoteDelegate ReplacementDelegate { get; set; } = SourceMatchingReplacementText;


		public string EscapedText => _escapedText;

		public static string SourceMatchingReplacementText(MessageEmote caller)
		{
			int size = (int)caller.Size;
			return caller.Source switch
			{
				EmoteSource.BetterTwitchTv => string.Format(BetterTwitchTvEmoteUrls[size], caller.Id), 
				EmoteSource.FrankerFaceZ => string.Format(FrankerFaceZEmoteUrls[size], caller.Id), 
				EmoteSource.Twitch => string.Format(TwitchEmoteUrls[size], caller.Id), 
				_ => caller.Text, 
			};
		}

		public MessageEmote(string id, string text, EmoteSource source = EmoteSource.Twitch, EmoteSize size = EmoteSize.Small, ReplaceEmoteDelegate replacementDelegate = null)
		{
			_id = id;
			_text = text;
			_escapedText = Regex.Escape(text);
			_source = source;
			_size = size;
			if (replacementDelegate != null)
			{
				ReplacementDelegate = replacementDelegate;
			}
		}
	}
	public class MessageEmoteCollection
	{
		public delegate bool EmoteFilterDelegate(MessageEmote emote);

		private readonly SortedList<string, MessageEmote> _emoteList;

		private const string BasePattern = "(\\b{0}\\b)";

		private string _currentPattern;

		private Regex _regex;

		private readonly EmoteFilterDelegate _preferredFilter;

		private string CurrentPattern
		{
			get
			{
				return _currentPattern;
			}
			set
			{
				if (_currentPattern == null || !_currentPattern.Equals(value))
				{
					_currentPattern = value;
					PatternChanged = true;
				}
			}
		}

		private Regex CurrentRegex
		{
			get
			{
				if (PatternChanged)
				{
					if (CurrentPattern != null)
					{
						_regex = new Regex(string.Format(CurrentPattern, ""));
						PatternChanged = false;
					}
					else
					{
						_regex = null;
					}
				}
				return _regex;
			}
		}

		private bool PatternChanged { get; set; }

		private EmoteFilterDelegate CurrentEmoteFilter { get; set; } = AllInclusiveEmoteFilter;


		public MessageEmoteCollection()
		{
			_emoteList = new SortedList<string, MessageEmote>();
			_preferredFilter = AllInclusiveEmoteFilter;
		}

		public MessageEmoteCollection(EmoteFilterDelegate preferredFilter)
			: this()
		{
			_preferredFilter = preferredFilter;
		}

		public void Add(MessageEmote emote)
		{
			if (!_emoteList.TryGetValue(emote.Text, out var _))
			{
				_emoteList.Add(emote.Text, emote);
			}
			if (CurrentPattern == null)
			{
				CurrentPattern = $"(\\b{emote.EscapedText}\\b)";
			}
			else
			{
				CurrentPattern = CurrentPattern + "|" + $"(\\b{emote.EscapedText}\\b)";
			}
		}

		public void Merge(IEnumerable<MessageEmote> emotes)
		{
			IEnumerator<MessageEmote> enumerator = emotes.GetEnumerator();
			while (enumerator.MoveNext())
			{
				Add(enumerator.Current);
			}
			enumerator.Dispose();
		}

		public void Remove(MessageEmote emote)
		{
			if (_emoteList.ContainsKey(emote.Text))
			{
				_emoteList.Remove(emote.Text);
				string text = "(^\\(\\\\b" + emote.EscapedText + "\\\\b\\)\\|?)";
				string text2 = "(\\|\\(\\\\b" + emote.EscapedText + "\\\\b\\))";
				string text3 = Regex.Replace(CurrentPattern, text + "|" + text2, "");
				CurrentPattern = (text3.Equals("") ? null : text3);
			}
		}

		public void RemoveAll()
		{
			_emoteList.Clear();
			CurrentPattern = null;
		}

		public string ReplaceEmotes(string originalMessage, EmoteFilterDelegate del = null)
		{
			if (CurrentRegex == null)
			{
				return originalMessage;
			}
			if (del != null && del != CurrentEmoteFilter)
			{
				CurrentEmoteFilter = del;
			}
			string result = CurrentRegex.Replace(originalMessage, GetReplacementString);
			CurrentEmoteFilter = _preferredFilter;
			return result;
		}

		public static bool AllInclusiveEmoteFilter(MessageEmote emote)
		{
			return true;
		}

		public static bool TwitchOnlyEmoteFilter(MessageEmote emote)
		{
			return emote.Source == MessageEmote.EmoteSource.Twitch;
		}

		private string GetReplacementString(Match m)
		{
			if (!_emoteList.ContainsKey(m.Value))
			{
				return m.Value;
			}
			MessageEmote messageEmote = _emoteList[m.Value];
			if (!CurrentEmoteFilter(messageEmote))
			{
				return m.Value;
			}
			return messageEmote.ReplacementString;
		}
	}
	public class OutboundChatMessage
	{
		public string Channel { get; set; }

		public string Message { get; set; }

		public string Username { get; set; }

		public string ReplyToId { get; set; }

		public override string ToString()
		{
			string text = Username.ToLower();
			string text2 = Channel.ToLower();
			if (ReplyToId == null)
			{
				return ":" + text + "!" + text + "@" + text + ".tmi.twitch.tv PRIVMSG #" + text2 + " :" + Message;
			}
			return "@reply-parent-msg-id=" + ReplyToId + " PRIVMSG #" + text2 + " :" + Message;
		}
	}
	public class OutboundWhisperMessage
	{
		public string Username { get; set; }

		public string Receiver { get; set; }

		public string Message { get; set; }

		public override string ToString()
		{
			return ":" + Username + "~" + Username + "@" + Username + ".tmi.twitch.tv PRIVMSG #jtv :/w " + Receiver + " " + Message;
		}
	}
	public class OutgoingMessage
	{
		public string Channel { get; set; }

		public string Message { get; set; }

		public int Nonce { get; set; }

		public string Sender { get; set; }

		public MessageState State { get; set; }
	}
	public enum MessageState : byte
	{
		Normal,
		Queued,
		Failed
	}
	public class PrimePaidSubscriber : SubscriberBase
	{
		public PrimePaidSubscriber(IrcMessage ircMessage)
			: base(ircMessage)
		{
		}

		public PrimePaidSubscriber(List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, Color color, string displayName, string emoteSet, string id, string login, string systemMessage, string msgId, string msgParamCumulativeMonths, string msgParamStreakMonths, bool msgParamShouldShareStreak, string systemMessageParsed, string resubMessage, SubscriptionPlan subscriptionPlan, string subscriptionPlanName, string roomId, string userId, bool isModerator, bool isTurbo, bool isSubscriber, bool isPartner, string tmiSentTs, UserType userType, string rawIrc, string channel, int months = 0)
			: base(badges, badgeInfo, colorHex, color, displayName, emoteSet, id, login, systemMessage, msgId, msgParamCumulativeMonths, msgParamStreakMonths, msgParamShouldShareStreak, systemMessageParsed, resubMessage, subscriptionPlan, subscriptionPlanName, roomId, userId, isModerator, isTurbo, isSubscriber, isPartner, tmiSentTs, userType, rawIrc, channel, months)
		{
		}//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)

	}
	public class RaidNotification
	{
		public List<KeyValuePair<string, string>> Badges { get; }

		public List<KeyValuePair<string, string>> BadgeInfo { get; }

		public string Color { get; }

		public string DisplayName { get; }

		public string Emotes { get; }

		public string Id { get; }

		public string Login { get; }

		public bool Moderator { get; }

		public string MsgId { get; }

		public string MsgParamDisplayName { get; }

		public string MsgParamLogin { get; }

		public string MsgParamViewerCount { get; }

		public string RoomId { get; }

		public bool Subscriber { get; }

		public string SystemMsg { get; }

		public string SystemMsgParsed { get; }

		public string TmiSentTs { get; }

		public bool Turbo { get; }

		public string UserId { get; }

		public UserType UserType { get; }

		public RaidNotification(IrcMessage ircMessage)
		{
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0423: Unknown result type (might be due to invalid IL or missing references)
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
					Badges = Helpers.ParseBadges(text);
					break;
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					Color = text;
					break;
				case "display-name":
					DisplayName = text;
					break;
				case "emotes":
					Emotes = text;
					break;
				case "login":
					Login = text;
					break;
				case "mod":
					Moderator = Helpers.ConvertToBool(text);
					break;
				case "msg-id":
					MsgId = text;
					break;
				case "msg-param-displayName":
					MsgParamDisplayName = text;
					break;
				case "msg-param-login":
					MsgParamLogin = text;
					break;
				case "msg-param-viewerCount":
					MsgParamViewerCount = text;
					break;
				case "room-id":
					RoomId = text;
					break;
				case "subscriber":
					Subscriber = Helpers.ConvertToBool(text);
					break;
				case "system-msg":
					SystemMsg = text;
					SystemMsgParsed = text.Replace("\\s", " ").Replace("\\n", "");
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "turbo":
					Turbo = Helpers.ConvertToBool(text);
					break;
				case "user-id":
					UserId = text;
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				}
			}
		}

		public RaidNotification(List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string color, string displayName, string emotes, string id, string login, bool moderator, string msgId, string msgParamDisplayName, string msgParamLogin, string msgParamViewerCount, string roomId, bool subscriber, string systemMsg, string systemMsgParsed, string tmiSentTs, bool turbo, UserType userType, string userId)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			Badges = badges;
			BadgeInfo = badgeInfo;
			Color = color;
			DisplayName = displayName;
			Emotes = emotes;
			Id = id;
			Login = login;
			Moderator = moderator;
			MsgId = msgId;
			MsgParamDisplayName = msgParamDisplayName;
			MsgParamLogin = msgParamLogin;
			MsgParamViewerCount = msgParamViewerCount;
			RoomId = roomId;
			Subscriber = subscriber;
			SystemMsg = systemMsg;
			SystemMsgParsed = systemMsgParsed;
			TmiSentTs = tmiSentTs;
			Turbo = turbo;
			UserType = userType;
			UserId = userId;
		}
	}
	public class ReSubscriber : SubscriberBase
	{
		public int Months => monthsInternal;

		public ReSubscriber(IrcMessage ircMessage)
			: base(ircMessage)
		{
		}

		public ReSubscriber(List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, Color color, string displayName, string emoteSet, string id, string login, string systemMessage, string msgId, string msgParamCumulativeMonths, string msgParamStreakMonths, bool msgParamShouldShareStreak, string systemMessageParsed, string resubMessage, SubscriptionPlan subscriptionPlan, string subscriptionPlanName, string roomId, string userId, bool isModerator, bool isTurbo, bool isSubscriber, bool isPartner, string tmiSentTs, UserType userType, string rawIrc, string channel, int months = 0)
			: base(badges, badgeInfo, colorHex, color, displayName, emoteSet, id, login, systemMessage, msgId, msgParamCumulativeMonths, msgParamStreakMonths, msgParamShouldShareStreak, systemMessageParsed, resubMessage, subscriptionPlan, subscriptionPlanName, roomId, userId, isModerator, isTurbo, isSubscriber, isPartner, tmiSentTs, userType, rawIrc, channel, months)
		{
		}//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)

	}
	public class RitualNewChatter
	{
		public List<KeyValuePair<string, string>> Badges { get; }

		public List<KeyValuePair<string, string>> BadgeInfo { get; }

		public string Color { get; }

		public string DisplayName { get; }

		public string Emotes { get; }

		public string Id { get; }

		public bool IsModerator { get; }

		public bool IsSubscriber { get; }

		public bool IsTurbo { get; }

		public string Login { get; }

		public string Message { get; }

		public string MsgId { get; }

		public string MsgParamRitualName { get; }

		public string RoomId { get; }

		public string SystemMsgParsed { get; }

		public string SystemMsg { get; }

		public string TmiSentTs { get; }

		public string UserId { get; }

		public UserType UserType { get; }

		public RitualNewChatter(IrcMessage ircMessage)
		{
			//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0415: Unknown result type (might be due to invalid IL or missing references)
			Message = ircMessage.Message;
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
					Badges = Helpers.ParseBadges(text);
					break;
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					Color = text;
					break;
				case "display-name":
					DisplayName = text;
					break;
				case "emotes":
					Emotes = text;
					break;
				case "id":
					Id = text;
					break;
				case "login":
					Login = text;
					break;
				case "mod":
					IsModerator = Helpers.ConvertToBool(text);
					break;
				case "msg-id":
					MsgId = text;
					break;
				case "msg-param-ritual-name":
					MsgParamRitualName = text;
					break;
				case "room-id":
					RoomId = text;
					break;
				case "subscriber":
					IsSubscriber = Helpers.ConvertToBool(text);
					break;
				case "system-msg":
					SystemMsg = text;
					SystemMsgParsed = text.Replace("\\s", " ").Replace("\\n", "");
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "turbo":
					IsTurbo = Helpers.ConvertToBool(text);
					break;
				case "user-id":
					UserId = text;
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				}
			}
		}
	}
	public class SentMessage
	{
		public List<KeyValuePair<string, string>> Badges { get; }

		public string Channel { get; }

		public string ColorHex { get; }

		public string DisplayName { get; }

		public string EmoteSet { get; }

		public bool IsModerator { get; }

		public bool IsSubscriber { get; }

		public string Message { get; }

		public UserType UserType { get; }

		public SentMessage(UserState state, string message)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			Badges = state.Badges;
			Channel = state.Channel;
			ColorHex = state.ColorHex;
			DisplayName = state.DisplayName;
			EmoteSet = state.EmoteSet;
			IsModerator = state.IsModerator;
			IsSubscriber = state.IsSubscriber;
			UserType = state.UserType;
			Message = message;
		}

		public SentMessage(List<KeyValuePair<string, string>> badges, string channel, string colorHex, string displayName, string emoteSet, bool isModerator, bool isSubscriber, UserType userType, string message)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			Badges = badges;
			Channel = channel;
			ColorHex = colorHex;
			DisplayName = displayName;
			EmoteSet = emoteSet;
			IsModerator = isModerator;
			IsSubscriber = isSubscriber;
			UserType = userType;
			Message = message;
		}
	}
	public class Subscriber : SubscriberBase
	{
		public Subscriber(IrcMessage ircMessage)
			: base(ircMessage)
		{
		}

		public Subscriber(List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, Color color, string displayName, string emoteSet, string id, string login, string systemMessage, string msgId, string msgParamCumulativeMonths, string msgParamStreakMonths, bool msgParamShouldShareStreak, string systemMessageParsed, string resubMessage, SubscriptionPlan subscriptionPlan, string subscriptionPlanName, string roomId, string userId, bool isModerator, bool isTurbo, bool isSubscriber, bool isPartner, string tmiSentTs, UserType userType, string rawIrc, string channel)
			: base(badges, badgeInfo, colorHex, color, displayName, emoteSet, id, login, systemMessage, msgId, msgParamCumulativeMonths, msgParamStreakMonths, msgParamShouldShareStreak, systemMessageParsed, resubMessage, subscriptionPlan, subscriptionPlanName, roomId, userId, isModerator, isTurbo, isSubscriber, isPartner, tmiSentTs, userType, rawIrc, channel, 0)
		{
		}//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)

	}
	public class SubscriberBase
	{
		protected readonly int monthsInternal;

		public List<KeyValuePair<string, string>> Badges { get; }

		public List<KeyValuePair<string, string>> BadgeInfo { get; }

		public string ColorHex { get; }

		public Color Color { get; }

		public string DisplayName { get; }

		public string EmoteSet { get; }

		public string Id { get; }

		public bool IsModerator { get; }

		public bool IsPartner { get; }

		public bool IsSubscriber { get; }

		public bool IsTurbo { get; }

		public string Login { get; }

		public string MsgId { get; }

		public string MsgParamCumulativeMonths { get; }

		public bool MsgParamShouldShareStreak { get; }

		public string MsgParamStreakMonths { get; }

		public string RawIrc { get; }

		public string ResubMessage { get; }

		public string RoomId { get; }

		public SubscriptionPlan SubscriptionPlan { get; }

		public string SubscriptionPlanName { get; }

		public string SystemMessage { get; }

		public string SystemMessageParsed { get; }

		public string TmiSentTs { get; }

		public string UserId { get; }

		public UserType UserType { get; }

		public string Channel { get; }

		protected SubscriberBase(IrcMessage ircMessage)
		{
			//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_058d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0596: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_059f: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b1: Unknown result type (might be due to invalid IL or missing references)
			RawIrc = ircMessage.ToString();
			ResubMessage = ircMessage.Message;
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
					Badges = Helpers.ParseBadges(text);
					foreach (KeyValuePair<string, string> badge in Badges)
					{
						if (badge.Key == "partner")
						{
							IsPartner = true;
						}
					}
					break;
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					ColorHex = text;
					if (!string.IsNullOrEmpty(ColorHex))
					{
						Color = TwitchLib.Client.Models.Extensions.NetCore.ColorTranslator.FromHtml(ColorHex);
					}
					break;
				case "display-name":
					DisplayName = text;
					break;
				case "emotes":
					EmoteSet = text;
					break;
				case "id":
					Id = text;
					break;
				case "login":
					Login = text;
					break;
				case "mod":
					IsModerator = ConvertToBool(text);
					break;
				case "msg-id":
					MsgId = text;
					break;
				case "msg-param-cumulative-months":
					MsgParamCumulativeMonths = text;
					break;
				case "msg-param-streak-months":
					MsgParamStreakMonths = text;
					break;
				case "msg-param-should-share-streak":
					MsgParamShouldShareStreak = Helpers.ConvertToBool(text);
					break;
				case "msg-param-sub-plan":
					switch (text.ToLower())
					{
					case "prime":
						SubscriptionPlan = (SubscriptionPlan)1;
						break;
					case "1000":
						SubscriptionPlan = (SubscriptionPlan)2;
						break;
					case "2000":
						SubscriptionPlan = (SubscriptionPlan)3;
						break;
					case "3000":
						SubscriptionPlan = (SubscriptionPlan)4;
						break;
					default:
						throw new ArgumentOutOfRangeException("ToLower");
					}
					break;
				case "msg-param-sub-plan-name":
					SubscriptionPlanName = text.Replace("\\s", " ");
					break;
				case "room-id":
					RoomId = text;
					break;
				case "subscriber":
					IsSubscriber = ConvertToBool(text);
					break;
				case "system-msg":
					SystemMessage = text;
					SystemMessageParsed = text.Replace("\\s", " ");
					break;
				case "tmi-sent-ts":
					TmiSentTs = text;
					break;
				case "turbo":
					IsTurbo = ConvertToBool(text);
					break;
				case "user-id":
					UserId = text;
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				}
			}
		}

		internal SubscriberBase(List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, Color color, string displayName, string emoteSet, string id, string login, string systemMessage, string msgId, string msgParamCumulativeMonths, string msgParamStreakMonths, bool msgParamShouldShareStreak, string systemMessageParsed, string resubMessage, SubscriptionPlan subscriptionPlan, string subscriptionPlanName, string roomId, string userId, bool isModerator, bool isTurbo, bool isSubscriber, bool isPartner, string tmiSentTs, UserType userType, string rawIrc, string channel, int months)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			Badges = badges;
			BadgeInfo = badgeInfo;
			ColorHex = colorHex;
			Color = color;
			DisplayName = displayName;
			EmoteSet = emoteSet;
			Id = id;
			Login = login;
			MsgId = msgId;
			MsgParamCumulativeMonths = msgParamCumulativeMonths;
			MsgParamStreakMonths = msgParamStreakMonths;
			MsgParamShouldShareStreak = msgParamShouldShareStreak;
			SystemMessage = systemMessage;
			SystemMessageParsed = systemMessageParsed;
			ResubMessage = resubMessage;
			SubscriptionPlan = subscriptionPlan;
			SubscriptionPlanName = subscriptionPlanName;
			RoomId = roomId;
			UserId = UserId;
			IsModerator = isModerator;
			IsTurbo = isTurbo;
			IsSubscriber = isSubscriber;
			IsPartner = isPartner;
			TmiSentTs = tmiSentTs;
			UserType = userType;
			RawIrc = rawIrc;
			monthsInternal = months;
			UserId = userId;
			Channel = channel;
		}

		private static bool ConvertToBool(string data)
		{
			return data == "1";
		}

		public override string ToString()
		{
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			return $"Badges: {Badges.Count}, color hex: {ColorHex}, display name: {DisplayName}, emote set: {EmoteSet}, login: {Login}, system message: {SystemMessage}, msgId: {MsgId}, msgParamCumulativeMonths: {MsgParamCumulativeMonths}" + $"msgParamStreakMonths: {MsgParamStreakMonths}, msgParamShouldShareStreak: {MsgParamShouldShareStreak}, resub message: {ResubMessage}, months: {monthsInternal}, room id: {RoomId}, user id: {UserId}, mod: {IsModerator}, turbo: {IsTurbo}, sub: {IsSubscriber}, user type: {UserType}, raw irc: {RawIrc}";
		}
	}
	public abstract class TwitchLibMessage
	{
		public List<KeyValuePair<string, string>> Badges { get; protected set; }

		public string BotUsername { get; protected set; }

		public Color Color { get; protected set; }

		public string ColorHex { get; protected set; }

		public string DisplayName { get; protected set; }

		public EmoteSet EmoteSet { get; protected set; }

		public bool IsTurbo { get; protected set; }

		public string UserId { get; protected set; }

		public string Username { get; protected set; }

		public UserType UserType { get; protected set; }

		public string RawIrcMessage { get; protected set; }
	}
	public class UserBan
	{
		public string BanReason;

		public string Channel;

		public string Username;

		public string RoomId;

		public string TargetUserId;

		public UserBan(IrcMessage ircMessage)
		{
			Channel = ircMessage.Channel;
			Username = ircMessage.Message;
			if (ircMessage.Tags.TryGetValue("ban-reason", out var value))
			{
				BanReason = value;
			}
			if (ircMessage.Tags.TryGetValue("room-id", out var value2))
			{
				RoomId = value2;
			}
			if (ircMessage.Tags.TryGetValue("target-user-id", out var value3))
			{
				TargetUserId = value3;
			}
		}

		public UserBan(string channel, string username, string banReason, string roomId, string targetUserId)
		{
			Channel = channel;
			Username = username;
			BanReason = banReason;
			RoomId = roomId;
			TargetUserId = targetUserId;
		}
	}
	public class UserState
	{
		public List<KeyValuePair<string, string>> Badges { get; } = new List<KeyValuePair<string, string>>();


		public List<KeyValuePair<string, string>> BadgeInfo { get; } = new List<KeyValuePair<string, string>>();


		public string Channel { get; }

		public string ColorHex { get; }

		public string DisplayName { get; }

		public string EmoteSet { get; }

		public string Id { get; }

		public bool IsModerator { get; }

		public bool IsSubscriber { get; }

		public UserType UserType { get; }

		public UserState(IrcMessage ircMessage)
		{
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			Channel = ircMessage.Channel;
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
					Badges = Helpers.ParseBadges(text);
					break;
				case "badge-info":
					BadgeInfo = Helpers.ParseBadges(text);
					break;
				case "color":
					ColorHex = text;
					break;
				case "display-name":
					DisplayName = text;
					break;
				case "emote-sets":
					EmoteSet = text;
					break;
				case "id":
					Id = text;
					break;
				case "mod":
					IsModerator = Helpers.ConvertToBool(text);
					break;
				case "subscriber":
					IsSubscriber = Helpers.ConvertToBool(text);
					break;
				case "user-type":
					switch (text)
					{
					case "mod":
						UserType = (UserType)1;
						break;
					case "global_mod":
						UserType = (UserType)2;
						break;
					case "admin":
						UserType = (UserType)4;
						break;
					case "staff":
						UserType = (UserType)5;
						break;
					default:
						UserType = (UserType)0;
						break;
					}
					break;
				default:
					Console.WriteLine("Unaccounted for [UserState]: " + key);
					break;
				}
			}
			if (string.Equals(ircMessage.User, Channel, StringComparison.InvariantCultureIgnoreCase))
			{
				UserType = (UserType)3;
			}
		}

		public UserState(List<KeyValuePair<string, string>> badges, List<KeyValuePair<string, string>> badgeInfo, string colorHex, string displayName, string emoteSet, string channel, string id, bool isSubscriber, bool isModerator, UserType userType)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			Badges = badges;
			BadgeInfo = badgeInfo;
			ColorHex = colorHex;
			DisplayName = displayName;
			EmoteSet = emoteSet;
			Channel = channel;
			Id = id;
			IsSubscriber = isSubscriber;
			IsModerator = isModerator;
			UserType = userType;
		}
	}
	public class UserTimeout
	{
		public string Channel;

		public int TimeoutDuration;

		public string TimeoutReason;

		public string Username;

		public string TargetUserId;

		public UserTimeout(IrcMessage ircMessage)
		{
			Channel = ircMessage.Channel;
			Username = ircMessage.Message;
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "ban-duration":
					TimeoutDuration = int.Parse(text);
					break;
				case "ban-reason":
					TimeoutReason = text;
					break;
				case "target-user-id":
					TargetUserId = text;
					break;
				}
			}
		}

		public UserTimeout(string channel, string username, string targetuserId, int timeoutDuration, string timeoutReason)
		{
			Channel = channel;
			Username = username;
			TargetUserId = targetuserId;
			TimeoutDuration = timeoutDuration;
			TimeoutReason = timeoutReason;
		}
	}
	public class WhisperCommand
	{
		public List<string> ArgumentsAsList { get; }

		public string ArgumentsAsString { get; }

		public char CommandIdentifier { get; }

		public string CommandText { get; }

		public WhisperMessage WhisperMessage { get; }

		public WhisperCommand(WhisperMessage whisperMessage)
		{
			WhisperCommand whisperCommand = this;
			WhisperMessage = whisperMessage;
			string[] array = whisperMessage.Message.Split(new char[1] { ' ' });
			CommandText = ((array != null) ? array[0].Substring(1, whisperMessage.Message.Split(new char[1] { ' ' })[0].Length - 1) : null) ?? whisperMessage.Message.Substring(1, whisperMessage.Message.Length - 1);
			string message = whisperMessage.Message;
			string[] array2 = whisperMessage.Message.Split(new char[1] { ' ' });
			ArgumentsAsString = message.Replace(((array2 != null) ? array2[0] : null) + " ", "");
			ArgumentsAsList = whisperMessage.Message.Split(new char[1] { ' ' })?.Where((string arg) => arg != whisperMessage.Message[0] + whisperCommand.CommandText).ToList() ?? new List<string>();
			CommandIdentifier = whisperMessage.Message[0];
		}

		public WhisperCommand(WhisperMessage whisperMessage, string commandText, string argumentsAsString, List<string> argumentsAsList, char commandIdentifier)
		{
			WhisperMessage = whisperMessage;
			CommandText = commandText;
			ArgumentsAsString = argumentsAsString;
			ArgumentsAsList = argumentsAsList;
			CommandIdentifier = commandIdentifier;
		}
	}
	public class WhisperMessage : TwitchLibMessage
	{
		public string MessageId { get; }

		public string ThreadId { get; }

		public string Message { get; }

		public WhisperMessage(List<KeyValuePair<string, string>> badges, string colorHex, Color color, string username, string displayName, EmoteSet emoteSet, string threadId, string messageId, string userId, bool isTurbo, string botUsername, string message, UserType userType)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			base.Badges = badges;
			base.ColorHex = colorHex;
			base.Color = color;
			base.Username = username;
			base.DisplayName = displayName;
			base.EmoteSet = emoteSet;
			ThreadId = threadId;
			MessageId = messageId;
			base.UserId = userId;
			base.IsTurbo = isTurbo;
			base.BotUsername = botUsername;
			Message = message;
			base.UserType = userType;
		}

		public WhisperMessage(IrcMessage ircMessage, string botUsername)
		{
			base.Username = ircMessage.User;
			base.BotUsername = botUsername;
			base.RawIrcMessage = ircMessage.ToString();
			Message = ircMessage.Message;
			foreach (string key in ircMessage.Tags.Keys)
			{
				string text = ircMessage.Tags[key];
				switch (key)
				{
				case "badges":
				{
					base.Badges = new List<KeyValuePair<string, string>>();
					if (!Enumerable.Contains(text, '/'))
					{
						break;
					}
					if (!text.Contains(","))
					{
						base.Badges.Add(new KeyValuePair<string, string>(text.Split(new char[1] { '/' })[0], text.Split(new char[1] { '/' })[1]));
						break;
					}
					string[] array = text.Split(new char[1] { ',' });
					foreach (string text2 in array)
					{
						base.Badges.Add(new KeyValuePair<string, string>(text2.Split(new char[1] { '/' })[0], text2.Split(new char[1] { '/' })[1]));
					}
					break;
				}
				case "color":
					base.ColorHex = text;
					if (!string.IsNullOrEmpty(base.ColorHex))
					{
						base.Color = TwitchLib.Client.Models.Extensions.NetCore.ColorTranslator.FromHtml(base.ColorHex);
					}
					break;
				case "display-name":
					base.DisplayName = text;
					break;
				case "emotes":
					base.EmoteSet = new EmoteSet(text, Message);
					break;
				case "message-id":
					MessageId = text;
					break;
				case "thread-id":
					ThreadId = text;
					break;
				case "turbo":
					base.IsTurbo = Helpers.ConvertToBool(text);
					break;
				case "user-id":
					base.UserId = text;
					break;
				case "user-type":
					switch (text)
					{
					case "global_mod":
						base.UserType = (UserType)2;
						break;
					case "admin":
						base.UserType = (UserType)4;
						break;
					case "staff":
						base.UserType = (UserType)5;
						break;
					default:
						base.UserType = (UserType)0;
						break;
					}
					break;
				}
			}
			if (base.EmoteSet == null)
			{
				base.EmoteSet = new EmoteSet((string)null, Message);
			}
		}
	}
}
namespace TwitchLib.Client.Models.Internal
{
	public class IrcMessage
	{
		private readonly string[] _parameters;

		public readonly string User;

		public readonly string Hostmask;

		public readonly IrcCommand Command;

		public readonly Dictionary<string, string> Tags;

		public string Channel
		{
			get
			{
				if (!Params.StartsWith("#"))
				{
					return Params;
				}
				return Params.Remove(0, 1);
			}
		}

		public string Params
		{
			get
			{
				if (_parameters == null || _parameters.Length == 0)
				{
					return "";
				}
				return _parameters[0];
			}
		}

		public string Message => Trailing;

		public string Trailing
		{
			get
			{
				if (_parameters == null || _parameters.Length <= 1)
				{
					return "";
				}
				return _parameters[_parameters.Length - 1];
			}
		}

		public IrcMessage(string user)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			_parameters = null;
			User = user;
			Hostmask = null;
			Command = (IrcCommand)0;
			Tags = null;
		}

		public IrcMessage(IrcCommand command, string[] parameters, string hostmask, Dictionary<string, string> tags = null)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			int num = hostmask.IndexOf('!');
			User = ((num != -1) ? hostmask.Substring(0, num) : hostmask);
			Hostmask = hostmask;
			_parameters = parameters;
			Command = command;
			Tags = tags;
			if ((int)command == 18 && Params.Length > 0 && Params.Contains("#"))
			{
				_parameters[0] = "#" + _parameters[0].Split(new char[1] { '#' })[1];
			}
		}

		public new string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder(32);
			if (Tags != null)
			{
				string[] array = new string[Tags.Count];
				int num = 0;
				foreach (KeyValuePair<string, string> tag in Tags)
				{
					array[num] = tag.Key + "=" + tag.Value;
					num++;
				}
				if (array.Length != 0)
				{
					stringBuilder.Append("@").Append(string.Join(";", array)).Append(" ");
				}
			}
			if (!string.IsNullOrEmpty(Hostmask))
			{
				stringBuilder.Append(":").Append(Hostmask).Append(" ");
			}
			stringBuilder.Append(((object)(IrcCommand)(ref Command)).ToString().ToUpper().Replace("RPL_", ""));
			if (_parameters.Length == 0)
			{
				return stringBuilder.ToString();
			}
			if (_parameters[0] != null && _parameters[0].Length > 0)
			{
				stringBuilder.Append(" ").Append(_parameters[0]);
			}
			if (_parameters.Length > 1 && _parameters[1] != null && _parameters[1].Length > 0)
			{
				stringBuilder.Append(" :").Append(_parameters[1]);
			}
			return stringBuilder.ToString();
		}
	}
	public static class MsgIds
	{
		public const string AlreadyBanned = "already_banned";

		public const string AlreadyEmotesOff = "already_emotes_off";

		public const string AlreadyEmotesOn = "already_emotes_on";

		public const string AlreadyR9KOff = "already_r9k_off";

		public const string AlreadyR9KOn = "already_r9k_on";

		public const string AlreadySubsOff = "already_subs_off";

		public const string AlreadySubsOn = "already_subs_on";

		public const string Announcement = "announcement";

		public const string BadUnbanNoBan = "bad_unban_no_ban";

		public const string BanSuccess = "ban_success";

		public const string ColorChanged = "color_changed";

		public const string EmoteOnlyOff = "emote_only_off";

		public const string EmoteOnlyOn = "emote_only_on";

		public const string HighlightedMessage = "highlighted-message";

		public const string ModeratorsReceived = "room_mods";

		public const string NoMods = "no_mods";

		public const string NoVIPs = "no_vips";

		public const string MsgBannedEmailAlias = "msg_banned_email_alias";

		public const string MsgChannelSuspended = "msg_channel_suspended";

		public const string MsgRequiresVerifiedPhoneNumber = "msg_requires_verified_phone_number";

		public const string MsgVerifiedEmail = "msg_verified_email";

		public const string MsgRateLimit = "msg_ratelimit";

		public const string MsgDuplicate = "msg_duplicate";

		public const string MsgR9k = "msg_r9k";

		public const string MsgFollowersOnly = "msg_followersonly";

		public const string MsgSubsOnly = "msg_subsonly";

		public const string MsgEmoteOnly = "msg_emoteonly";

		public const string MsgSuspended = "msg_suspended";

		public const string MsgBanned = "msg_banned";

		public const string MsgSlowMode = "msg_slowmode";

		public const string NoPermission = "no_permission";

		public const string PrimePaidUprade = "primepaidupgrade";

		public const string Raid = "raid";

		public const string RaidErrorSelf = "raid_error_self";

		public const string RaidNoticeMature = "raid_notice_mature";

		public const string ReSubscription = "resub";

		public const string R9KOff = "r9k_off";

		public const string R9KOn = "r9k_on";

		public const string SubGift = "subgift";

		public const string CommunitySubscription = "submysterygift";

		public const string ContinuedGiftedSubscription = "giftpaidupgrade";

		public const string Subscription = "sub";

		public const string SubsOff = "subs_off";

		public const string SubsOn = "subs_on";

		public const string TimeoutSuccess = "timeout_success";

		public const string UnbanSuccess = "unban_success";

		public const string UnrecognizedCmd = "unrecognized_cmd";

		public const string UserIntro = "user-intro";

		public const string VIPsSuccess = "vips_success";

		public const string SkipSubsModeMessage = "skip-subs-mode-message";
	}
	public static class Tags
	{
		public const string Badges = "badges";

		public const string BadgeInfo = "badge-info";

		public const string BanDuration = "ban-duration";

		public const string BanReason = "ban-reason";

		public const string BroadcasterLang = "broadcaster-lang";

		public const string Bits = "bits";

		public const string Color = "color";

		public const string CustomRewardId = "custom-reward-id";

		public const string DisplayName = "display-name";

		public const string Emotes = "emotes";

		public const string EmoteOnly = "emote-only";

		public const string EmotesSets = "emote-sets";

		public const string FirstMessage = "first-msg";

		public const string Flags = "flags";

		public const string FollowersOnly = "followers-only";

		public const string Id = "id";

		public const string Login = "login";

		public const string Mercury = "mercury";

		public const string MessageId = "message-id";

		public const string Mod = "mod";

		public const string MsgId = "msg-id";

		public const string MsgParamColor = "msg-param-color";

		public const string MsgParamDisplayname = "msg-param-displayName";

		public const string MsgParamLogin = "msg-param-login";

		public const string MsgParamCumulativeMonths = "msg-param-cumulative-months";

		public const string MsgParamMonths = "msg-param-months";

		public const string MsgParamPromoGiftTotal = "msg-param-promo-gift-total";

		public const string MsgParamPromoName = "msg-param-promo-name";

		public const string MsgParamShouldShareStreak = "msg-param-should-share-streak";

		public const string MsgParamStreakMonths = "msg-param-streak-months";

		public const string MsgParamSubPlan = "msg-param-sub-plan";

		public const string MsgParamSubPlanName = "msg-param-sub-plan-name";

		public const string MsgParamViewerCount = "msg-param-viewerCount";

		public const string MsgParamRecipientDisplayname = "msg-param-recipient-display-name";

		public const string MsgParamRecipientId = "msg-param-recipient-id";

		public const string MsgParamRecipientUsername = "msg-param-recipient-user-name";

		public const string MsgParamRitualName = "msg-param-ritual-name";

		public const string MsgParamMassGiftCount = "msg-param-mass-gift-count";

		public const string MsgParamSenderCount = "msg-param-sender-count";

		public const string MsgParamSenderLogin = "msg-param-sender-login";

		public const string MsgParamSenderName = "msg-param-sender-name";

		public const string MsgParamThreshold = "msg-param-threshold";

		public const string Noisy = "noisy";

		public const string ReplyParentDisplayName = "reply-parent-display-name";

		public const string ReplyParentMsgBody = "reply-parent-msg-body";

		public const string ReplyParentMsgId = "reply-parent-msg-id";

		public const string ReplyParentUserId = "reply-parent-user-id";

		public const string ReplyParentUserLogin = "reply-parent-user-login";

		public const string Rituals = "rituals";

		public const string RoomId = "room-id";

		public const string R9K = "r9k";

		public const string Slow = "slow";

		public const string Subscriber = "subscriber";

		public const string SubsOnly = "subs-only";

		public const string SystemMsg = "system-msg";

		public const string ThreadId = "thread-id";

		public const string TmiSentTs = "tmi-sent-ts";

		public const string Turbo = "turbo";

		public const string UserId = "user-id";

		public const string UserType = "user-type";

		public const string MsgParamMultiMonthGiftDuration = "msg-param-gift-months";

		public const string TargetUserId = "target-user-id";
	}
}
namespace TwitchLib.Client.Models.Extractors
{
	public class EmoteExtractor
	{
		public IEnumerable<Emote> Extract(string rawEmoteSetString, string message)
		{
			if (string.IsNullOrEmpty(rawEmoteSetString) || string.IsNullOrEmpty(message))
			{
				yield break;
			}
			string emoteId2;
			if (rawEmoteSetString.Contains("/"))
			{
				string[] array = rawEmoteSetString.Split(new char[1] { '/' });
				foreach (string text in array)
				{
					emoteId2 = text.Split(new char[1] { ':' })[0];
					if (text.Contains(","))
					{
						string[] array2 = text.Replace(emoteId2 + ":", "").Split(new char[1] { ',' });
						foreach (string emoteData in array2)
						{
							yield return GetEmote(emoteData, emoteId2, message);
						}
					}
					else
					{
						yield return GetEmote(text, emoteId2, message, single: true);
					}
				}
				yield break;
			}
			emoteId2 = rawEmoteSetString.Split(new char[1] { ':' })[0];
			if (rawEmoteSetString.Contains(","))
			{
				string[] array = rawEmoteSetString.Replace(emoteId2 + ":", "").Split(new char[1] { ',' });
				foreach (string emoteData2 in array)
				{
					yield return GetEmote(emoteData2, emoteId2, message);
				}
			}
			else
			{
				yield return GetEmote(rawEmoteSetString, emoteId2, message, single: true);
			}
		}

		private Emote GetEmote(string emoteData, string emoteId, string message, bool single = false)
		{
			int num = -1;
			int num2 = -1;
			if (single)
			{
				num = int.Parse(emoteData.Split(new char[1] { ':' })[1].Split(new char[1] { '-' })[0]);
				num2 = int.Parse(emoteData.Split(new char[1] { ':' })[1].Split(new char[1] { '-' })[1]);
			}
			else
			{
				num = int.Parse(emoteData.Split(new char[1] { '-' })[0]);
				num2 = int.Parse(emoteData.Split(new char[1] { '-' })[1]);
			}
			string name = message.Substring(num, num2 - num + 1);
			return EmoteBuilder.Create().WithId(emoteId).WithName(name)
				.WithStartIndex(num)
				.WithEndIndex(num2)
				.Build();
		}
	}
	public interface IExtractor<TResult>
	{
		TResult Extract(IrcMessage ircMessage);
	}
}
namespace TwitchLib.Client.Models.Extensions.NetCore
{
	public static class ColorTranslator
	{
		public static Color FromHtml(string hexColor)
		{
			hexColor += 0;
			return Color.FromArgb(int.Parse(hexColor.Replace("#", ""), NumberStyles.HexNumber));
		}
	}
}
namespace TwitchLib.Client.Models.Common
{
	public static class Helpers
	{
		public static List<string> ParseQuotesAndNonQuotes(string message)
		{
			List<string> list = new List<string>();
			if (message == "")
			{
				return new List<string>();
			}
			bool flag = message[0] != '"';
			string[] array = message.Split(new char[1] { '"' });
			foreach (string text in array)
			{
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				if (!flag)
				{
					list.Add(text);
					flag = true;
				}
				else
				{
					if (!text.Contains(" "))
					{
						continue;
					}
					string[] array2 = text.Split(new char[1] { ' ' });
					foreach (string text2 in array2)
					{
						if (!string.IsNullOrWhiteSpace(text2))
						{
							list.Add(text2);
							flag = false;
						}
					}
				}
			}
			return list;
		}

		public static List<KeyValuePair<string, string>> ParseBadges(string badgesStr)
		{
			List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
			if (Enumerable.Contains(badgesStr, '/'))
			{
				if (!badgesStr.Contains(","))
				{
					list.Add(new KeyValuePair<string, string>(badgesStr.Split(new char[1] { '/' })[0], badgesStr.Split(new char[1] { '/' })[1]));
				}
				else
				{
					string[] array = badgesStr.Split(new char[1] { ',' });
					foreach (string text in array)
					{
						list.Add(new KeyValuePair<string, string>(text.Split(new char[1] { '/' })[0], text.Split(new char[1] { '/' })[1]));
					}
				}
			}
			return list;
		}

		public static string ParseToken(string token, string message)
		{
			string result = string.Empty;
			for (int num = message.IndexOf(token, StringComparison.InvariantCultureIgnoreCase); num > -1; num = message.IndexOf(token, num + token.Length, StringComparison.InvariantCultureIgnoreCase))
			{
				result = new string(message.Substring(num).TakeWhile((char x) => x != ';' && x != ' ').ToArray()).Split(new char[1] { '=' }).LastOrDefault();
			}
			return result;
		}

		public static bool ConvertToBool(string data)
		{
			return data == "1";
		}
	}
}
namespace TwitchLib.Client.Models.Builders
{
	public sealed class ChannelStateBuilder : IBuilder<ChannelState>, IFromIrcMessageBuilder<ChannelState>
	{
		private string _broadcasterLanguage;

		private string _channel;

		private bool _emoteOnly;

		private TimeSpan _followersOnly;

		private bool _mercury;

		private bool _r9K;

		private bool _rituals;

		private string _roomId;

		private int _slowMode;

		private bool _subOnly;

		private ChannelStateBuilder()
		{
		}

		public ChannelStateBuilder WithBroadcasterLanguage(string broadcasterLanguage)
		{
			_broadcasterLanguage = broadcasterLanguage;
			return this;
		}

		public ChannelStateBuilder WithChannel(string channel)
		{
			_channel = channel;
			return this;
		}

		public ChannelStateBuilder WithEmoteOnly(bool emoteOnly)
		{
			_emoteOnly = emoteOnly;
			return this;
		}

		public ChannelStateBuilder WIthFollowersOnly(TimeSpan followersOnly)
		{
			_followersOnly = followersOnly;
			return this;
		}

		public ChannelStateBuilder WithMercury(bool mercury)
		{
			_mercury = mercury;
			return this;
		}

		public ChannelStateBuilder WithR9K(bool r9k)
		{
			_r9K = r9k;
			return this;
		}

		public ChannelStateBuilder WithRituals(bool rituals)
		{
			_rituals = rituals;
			return this;
		}

		public ChannelStateBuilder WithRoomId(string roomId)
		{
			_roomId = roomId;
			return this;
		}

		public ChannelStateBuilder WithSlowMode(int slowMode)
		{
			_slowMode = slowMode;
			return this;
		}

		public ChannelStateBuilder WithSubOnly(bool subOnly)
		{
			_subOnly = subOnly;
			return this;
		}

		public static ChannelStateBuilder Create()
		{
			return new ChannelStateBuilder();
		}

		public ChannelState Build()
		{
			return new ChannelState(_r9K, _rituals, _subOnly, _slowMode, _emoteOnly, _broadcasterLanguage, _channel, _followersOnly, _mercury, _roomId);
		}

		public ChannelState BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
		{
			return new ChannelState(fromIrcMessageBuilderDataObject.Message);
		}
	}
	public sealed class ChatCommandBuilder : IBuilder<ChatCommand>
	{
		private readonly List<string> _argumentsAsList = new List<string>();

		private string _argumentsAsString;

		private ChatMessage _chatMessage;

		private char _commandIdentifier;

		private string _commandText;

		private ChatCommandBuilder()
		{
		}

		public ChatCommandBuilder WithArgumentsAsList(params string[] argumentsList)
		{
			_argumentsAsList.AddRange(argumentsList);
			return this;
		}

		public ChatCommandBuilder WithArgumentsAsString(string argumentsAsString)
		{
			_argumentsAsString = argumentsAsString;
			return this;
		}

		public ChatCommandBuilder WithChatMessage(ChatMessage chatMessage)
		{
			_chatMessage = chatMessage;
			return this;
		}

		public ChatCommandBuilder WithCommandIdentifier(char commandIdentifier)
		{
			_commandIdentifier = commandIdentifier;
			return this;
		}

		public ChatCommandBuilder WithCommandText(string commandText)
		{
			_commandText = commandText;
			return this;
		}

		public static ChatCommandBuilder Create()
		{
			return new ChatCommandBuilder();
		}

		public ChatCommand Build()
		{
			return new ChatCommand(_chatMessage, _commandText, _argumentsAsString, _argumentsAsList, _commandIdentifier);
		}

		public ChatCommand BuildFromChatMessage(ChatMessage chatMessage)
		{
			return new ChatCommand(chatMessage);
		}
	}
	public sealed class ChatMessageBuilder : IBuilder<ChatMessage>
	{
		private TwitchLibMessage _twitchLibMessage;

		private readonly List<KeyValuePair<string, string>> BadgeInfo = new List<KeyValuePair<string, string>>();

		private int _bits;

		private double _bitsInDollars;

		private string _channel;

		private CheerBadge _cheerBadge;

		private string _emoteReplacedMessage;

		private string _id;

		private bool _isBroadcaster;

		private bool _isMe;

		private bool _isModerator;

		private bool _isSubscriber;

		private bool _isVip;

		private bool _isStaff;

		private bool _isPartner;

		private string _message;

		private Noisy _noisy;

		private string _rawIrcMessage;

		private string _roomId;

		private int _subscribedMonthCount;

		private ChatMessageBuilder()
		{
			_twitchLibMessage = TwitchLibMessageBuilder.Create().Build();
		}

		public ChatMessageBuilder WithTwitchLibMessage(TwitchLibMessage twitchLibMessage)
		{
			_twitchLibMessage = twitchLibMessage;
			return this;
		}

		public ChatMessageBuilder WithBadgeInfos(params KeyValuePair<string, string>[] badgeInfos)
		{
			BadgeInfo.AddRange(badgeInfos);
			return this;
		}

		public ChatMessageBuilder WithBits(int bits)
		{
			_bits = bits;
			return this;
		}

		public ChatMessageBuilder WithBitsInDollars(double bitsInDollars)
		{
			_bitsInDollars = bitsInDollars;
			return this;
		}

		public ChatMessageBuilder WithChannel(string channel)
		{
			_channel = channel;
			return this;
		}

		public ChatMessageBuilder WithCheerBadge(CheerBadge cheerBadge)
		{
			_cheerBadge = cheerBadge;
			return this;
		}

		public ChatMessageBuilder WithEmoteReplaceMessage(string emoteReplaceMessage)
		{
			_emoteReplacedMessage = emoteReplaceMessage;
			return this;
		}

		public ChatMessageBuilder WithId(string id)
		{
			_id = id;
			return this;
		}

		public ChatMessageBuilder WithIsBroadcaster(bool isBroadcaster)
		{
			_isBroadcaster = isBroadcaster;
			return this;
		}

		public ChatMessageBuilder WithIsMe(bool isMe)
		{
			_isMe = isMe;
			return this;
		}

		public ChatMessageBuilder WithIsModerator(bool isModerator)
		{
			_isModerator = isModerator;
			return this;
		}

		public ChatMessageBuilder WithIsSubscriber(bool isSubscriber)
		{
			_isSubscriber = isSubscriber;
			return this;
		}

		public ChatMessageBuilder WithIsVip(bool isVip)
		{
			_isVip = isVip;
			return this;
		}

		public ChatMessageBuilder WithIsStaff(bool isStaff)
		{
			_isStaff = isStaff;
			return this;
		}

		public ChatMessageBuilder WithIsPartner(bool isPartner)
		{
			_isPartner = isPartner;
			return this;
		}

		public ChatMessageBuilder WithMessage(string message)
		{
			_message = message;
			return this;
		}

		public ChatMessageBuilder WithNoisy(Noisy noisy)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			_noisy = noisy;
			return this;
		}

		public ChatMessageBuilder WithRawIrcMessage(string rawIrcMessage)
		{
			_rawIrcMessage = rawIrcMessage;
			return this;
		}

		public ChatMessageBuilder WithRoomId(string roomId)
		{
			_roomId = roomId;
			return this;
		}

		public ChatMessageBuilder WithSubscribedMonthCount(int subscribedMonthCount)
		{
			_subscribedMonthCount = subscribedMonthCount;
			return this;
		}

		public static ChatMessageBuilder Create()
		{
			return new ChatMessageBuilder();
		}

		public ChatMessage Build()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			return new ChatMessage(_twitchLibMessage.BotUsername, _twitchLibMessage.UserId, _twitchLibMessage.Username, _twitchLibMessage.DisplayName, _twitchLibMessage.ColorHex, _twitchLibMessage.Color, _twitchLibMessage.EmoteSet, _message, _twitchLibMessage.UserType, _channel, _id, _isSubscriber, _subscribedMonthCount, _roomId, _twitchLibMessage.IsTurbo, _isModerator, _isMe, _isBroadcaster, _isVip, _isPartner, _isStaff, _noisy, _rawIrcMessage, _emoteReplacedMessage, _twitchLibMessage.Badges, _cheerBadge, _bits, _bitsInDollars);
		}
	}
	public sealed class CheerBadgeBuilder : IBuilder<CheerBadge>
	{
		private int _cheerAmount;

		public CheerBadgeBuilder WithCheerAmount(int cheerAmount)
		{
			_cheerAmount = cheerAmount;
			return this;
		}

		public CheerBadge Build()
		{
			return new CheerBadge(_cheerAmount);
		}
	}
	public sealed class CommunitySubscriptionBuilder : IFromIrcMessageBuilder<CommunitySubscription>
	{
		private CommunitySubscriptionBuilder()
		{
		}

		public static CommunitySubscriptionBuilder Create()
		{
			return new CommunitySubscriptionBuilder();
		}

		public CommunitySubscription BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
		{
			return new CommunitySubscription(fromIrcMessageBuilderDataObject.Message);
		}
	}
	public sealed class ConnectionCredentialsBuilder : IBuilder<ConnectionCredentials>
	{
		private string _twitchUsername;

		private string _twitchOAuth;

		private string _twitchWebsocketURI = "wss://irc-ws.chat.twitch.tv:443";

		private bool _disableUsernameCheck;

		private ConnectionCredentialsBuilder()
		{
		}

		public ConnectionCredentialsBuilder WithTwitchUsername(string twitchUsername)
		{
			_twitchUsername = twitchUsername;
			return this;
		}

		public ConnectionCredentialsBuilder WithTwitchOAuth(string twitchOAuth)
		{
			_twitchOAuth = twitchOAuth;
			return this;
		}

		public ConnectionCredentialsBuilder WithTwitchWebSocketUri(string twitchWebSocketUri)
		{
			_twitchWebsocketURI = twitchWebSocketUri;
			return this;
		}

		public ConnectionCredentialsBuilder WithDisableUsernameCheck(bool disableUsernameCheck)
		{
			_disableUsernameCheck = disableUsernameCheck;
			return this;
		}

		public static ConnectionCredentialsBuilder Create()
		{
			return new ConnectionCredentialsBuilder();
		}

		public ConnectionCredentials Build()
		{
			return new ConnectionCredentials(_twitchUsername, _twitchOAuth, _twitchWebsocketURI, _disableUsernameCheck);
		}
	}
	public sealed class EmoteBuilder : IBuilder<Emote>
	{
		private string _id;

		private string _name;

		private int _startIndex;

		private int _endIndex;

		private EmoteBuilder()
		{
		}

		public static EmoteBuilder Create()
		{
			return new EmoteBuilder();
		}

		public EmoteBuilder WithId(string id)
		{
			_id = id;
			return this;
		}

		public EmoteBuilder WithName(string name)
		{
			_name = name;
			return this;
		}

		public EmoteBuilder WithStartIndex(int startIndex)
		{
			_startIndex = startIndex;
			return this;
		}

		public EmoteBuilder WithEndIndex(int endIndex)
		{
			_endIndex = endIndex;
			return this;
		}

		public Emote Build()
		{
			return new Emote(_id, _name, _startIndex, _endIndex);
		}
	}
	public sealed class ErrorEventBuilder : IBuilder<ErrorEvent>
	{
		private string _message;

		private ErrorEventBuilder()
		{
		}

		public ErrorEventBuilder WithMessage(string message)
		{
			_message = message;
			return this;
		}

		public static ErrorEventBuilder Create()
		{
			return new ErrorEventBuilder();
		}

		public ErrorEvent Build()
		{
			return new ErrorEvent
			{
				Message = _message
			};
		}
	}
	public class FromIrcMessageBuilderDataObject
	{
		public IrcMessage Message { get; set; }

		public object AditionalData { get; set; }
	}
	public sealed class GiftedSubscriptionBuilder : IBuilder<GiftedSubscription>, IFromIrcMessageBuilder<GiftedSubscription>
	{
		private readonly List<KeyValuePair<string, string>> _badges = new List<KeyValuePair<string, string>>();

		private readonly List<KeyValuePair<string, string>> _badgeInfo = new List<KeyValuePair<string, string>>();

		private string _color;

		private string _displayName;

		private string _emotes;

		private string _id;

		private bool _isModerator;

		private bool _isSubscriber;

		private bool _isTurbo;

		private string _login;

		private string _msgId;

		private string _msgParamMonths;

		private string _msgParamRecipientDisplayName;

		private string _msgParamRecipientId;

		private string _msgParamRecipientUserName;

		private string _msgParamSubPlanName;

		private string _msgParamMultiMonthGiftDuration;

		private SubscriptionPlan _msgParamSubPlan;

		private string _roomId;

		private string _systemMsg;

		private string _systemMsgParsed;

		private string _tmiSentTs;

		private string _userId;

		private UserType _userType;

		private GiftedSubscriptionBuilder()
		{
		}

		public GiftedSubscriptionBuilder WithBadges(params KeyValuePair<string, string>[] badges)
		{
			_badges.AddRange(badges);
			return this;
		}

		public GiftedSubscriptionBuilder WithBadgeInfos(params KeyValuePair<string, string>[] badgeInfos)
		{
			_badgeInfo.AddRange(badgeInfos);
			return this;
		}

		public GiftedSubscriptionBuilder WithColor(string color)
		{
			_color = color;
			return this;
		}

		public GiftedSubscriptionBuilder WithDisplayName(string displayName)
		{
			_displayName = displayName;
			return this;
		}

		public GiftedSubscriptionBuilder WithEmotes(string emotes)
		{
			_emotes = emotes;
			return this;
		}

		public GiftedSubscriptionBuilder WithId(string id)
		{
			_id = id;
			return this;
		}

		public GiftedSubscriptionBuilder WithIsModerator(bool isModerator)
		{
			_isModerator = isModerator;
			return this;
		}

		public GiftedSubscriptionBuilder WithIsSubscriber(bool isSubscriber)
		{
			_isSubscriber = isSubscriber;
			return this;
		}

		public GiftedSubscriptionBuilder WithIsTurbo(bool isTurbo)
		{
			_isTurbo = isTurbo;
			return this;
		}

		public GiftedSubscriptionBuilder WithLogin(string login)
		{
			_login = login;
			return this;
		}

		public GiftedSubscriptionBuilder WithMessageId(string msgId)
		{
			_msgId = msgId;
			return this;
		}

		public GiftedSubscriptionBuilder WithMsgParamCumulativeMonths(string msgParamMonths)
		{
			_msgParamMonths = msgParamMonths;
			return this;
		}

		public GiftedSubscriptionBuilder WithMsgParamRecipientDisplayName(string msgParamRecipientDisplayName)
		{
			_msgParamRecipientDisplayName = msgParamRecipientDisplayName;
			return this;
		}

		public GiftedSubscriptionBuilder WithMsgParamRecipientId(string msgParamRecipientId)
		{
			_msgParamRecipientId = msgParamRecipientId;
			return this;
		}

		public GiftedSubscriptionBuilder WithMsgParamRecipientUserName(string msgParamRecipientUserName)
		{
			_msgParamRecipientUserName = msgParamRecipientUserName;
			return this;
		}

		public GiftedSubscriptionBuilder WithMsgParamSubPlanName(string msgParamSubPlanName)
		{
			_msgParamSubPlanName = msgParamSubPlanName;
			return this;
		}

		public GiftedSubscriptionBuilder WithMsgParamMultiMonthGiftDuration(string msgParamMultiMonthGiftDuration)
		{
			_msgParamMultiMonthGiftDuration = msgParamMultiMonthGiftDuration;
			return this;
		}

		public GiftedSubscriptionBuilder WithMsgParamSubPlan(SubscriptionPlan msgParamSubPlan)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			_msgParamSubPlan = msgParamSubPlan;
			return this;
		}

		public GiftedSubscriptionBuilder WithRoomId(string roomId)
		{
			_roomId = roomId;
			return this;
		}

		public GiftedSubscriptionBuilder WithSystemMsg(string systemMsg)
		{
			_systemMsg = systemMsg;
			return this;
		}

		public GiftedSubscriptionBuilder WithSystemMsgParsed(string systemMsgParsed)
		{
			_systemMsgParsed = systemMsgParsed;
			return this;
		}

		public GiftedSubscriptionBuilder WithTmiSentTs(string tmiSentTs)
		{
			_tmiSentTs = tmiSentTs;
			return this;
		}

		public GiftedSubscriptionBuilder WithUserId(string userId)
		{
			_userId = userId;
			return this;
		}

		public GiftedSubscriptionBuilder WithUserType(UserType userType)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			_userType = userType;
			return this;
		}

		public static GiftedSubscriptionBuilder Create()
		{
			return new GiftedSubscriptionBuilder();
		}

		public GiftedSubscription Build()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			return new GiftedSubscription(_badges, _badgeInfo, _color, _displayName, _emotes, _id, _login, _isModerator, _msgId, _msgParamMonths, _msgParamRecipientDisplayName, _msgParamRecipientId, _msgParamRecipientUserName, _msgParamSubPlanName, _msgParamMultiMonthGiftDuration, _msgParamSubPlan, _roomId, _isSubscriber, _systemMsg, _systemMsgParsed, _tmiSentTs, _isTurbo, _userType, _userId);
		}

		public GiftedSubscription BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject)
		{
			return new GiftedSubscription(fromIrcMessageBuilderDataObject.Message);
		}
	}
	public interface IBuilder<T>
	{
		T Build();
	}
	public interface IFromIrcMessageBuilder<T>
	{
		T BuildFromIrcMessage(FromIrcMessageBuilderDataObject fromIrcMessageBuilderDataObject);
	}
	public sealed class IrcMessageBuilder : IBuilder<IrcMessage>
	{
		private IrcCommand _ircCommand;

		private readonly List<string> _parameters = new List<string>();

		private string _hostmask;

		private Dictionary<string, string> _tags;

		public static IrcMessageBuilder Create()
		{
			return new IrcMessageBuilder();
		}

		private IrcMessageBuilder()
		{
		}

		public IrcMessageBuilder WithCommand(IrcCommand ircCommand)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			_ircCommand = ircCommand;
			return Builder();
		}

		public IrcMessageBuilder WithParameter(params string[] parameters)
		{
			_parameters.AddRange(parameters);
			return Builder();
		}

		private IrcMessageBuilder Builder()
		{
			return this;
		}

		public IrcMessage BuildWithUserOnly(string user)
		{
			return new IrcMessage(user);
		}

		public IrcMessageBuilder WithHostMask(string hostMask)
		{
			_hostmask = hostMask;
			return Builder();
		}

		public IrcMessageBuilder WithTags(Dictionary<string, string> tags)
		{
			_tags = tags;
			return Builder();
		}

		public IrcMessage Build()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return new IrcMessage(_ircCom

TwitchLib.Communication.dll

Decompiled a month ago
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TwitchLib.Communication.Clients;
using TwitchLib.Communication.Enums;
using TwitchLib.Communication.Events;
using TwitchLib.Communication.Interfaces;
using TwitchLib.Communication.Models;
using TwitchLib.Communication.Services;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("swiftyspiffy, Prom3theu5, Syzuna, LuckyNoS7evin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright 2022")]
[assembly: AssemblyDescription("Connection library used throughout TwitchLib to replace third party depedencies.")]
[assembly: AssemblyFileVersion("1.0.4")]
[assembly: AssemblyInformationalVersion("1.0.4")]
[assembly: AssemblyProduct("TwitchLib.Communication")]
[assembly: AssemblyTitle("TwitchLib.Communication")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/TwitchLib/TwitchLib.Communication")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("1.0.4.0")]
namespace TwitchLib.Communication.Services
{
	public class Throttlers
	{
		public readonly BlockingCollection<Tuple<DateTime, string>> SendQueue = new BlockingCollection<Tuple<DateTime, string>>();

		public readonly BlockingCollection<Tuple<DateTime, string>> WhisperQueue = new BlockingCollection<Tuple<DateTime, string>>();

		public bool ResetThrottlerRunning;

		public bool ResetWhisperThrottlerRunning;

		public int SentCount;

		public int WhispersSent;

		public Task ResetThrottler;

		public Task ResetWhisperThrottler;

		private readonly TimeSpan _throttlingPeriod;

		private readonly TimeSpan _whisperThrottlingPeriod;

		private readonly IClient _client;

		public bool Reconnecting { get; set; }

		public bool ShouldDispose { get; set; }

		public CancellationTokenSource TokenSource { get; set; }

		public Throttlers(IClient client, TimeSpan throttlingPeriod, TimeSpan whisperThrottlingPeriod)
		{
			_throttlingPeriod = throttlingPeriod;
			_whisperThrottlingPeriod = whisperThrottlingPeriod;
			_client = client;
		}

		public void StartThrottlingWindowReset()
		{
			ResetThrottler = Task.Run(async delegate
			{
				ResetThrottlerRunning = true;
				while (!ShouldDispose && !Reconnecting)
				{
					Interlocked.Exchange(ref SentCount, 0);
					await Task.Delay(_throttlingPeriod, TokenSource.Token);
				}
				ResetThrottlerRunning = false;
				return Task.CompletedTask;
			});
		}

		public void StartWhisperThrottlingWindowReset()
		{
			ResetWhisperThrottler = Task.Run(async delegate
			{
				ResetWhisperThrottlerRunning = true;
				while (!ShouldDispose && !Reconnecting)
				{
					Interlocked.Exchange(ref WhispersSent, 0);
					await Task.Delay(_whisperThrottlingPeriod, TokenSource.Token);
				}
				ResetWhisperThrottlerRunning = false;
				return Task.CompletedTask;
			});
		}

		public void IncrementSentCount()
		{
			Interlocked.Increment(ref SentCount);
		}

		public void IncrementWhisperCount()
		{
			Interlocked.Increment(ref WhispersSent);
		}

		public Task StartSenderTask()
		{
			StartThrottlingWindowReset();
			return Task.Run(async delegate
			{
				_ = 2;
				try
				{
					while (!ShouldDispose)
					{
						await Task.Delay(_client.Options.SendDelay);
						if (SentCount == _client.Options.MessagesAllowedInPeriod)
						{
							_client.MessageThrottled(new OnMessageThrottledEventArgs
							{
								Message = "Message Throttle Occured. Too Many Messages within the period specified in WebsocketClientOptions.",
								AllowedInPeriod = _client.Options.MessagesAllowedInPeriod,
								Period = _client.Options.ThrottlingPeriod,
								SentMessageCount = Interlocked.CompareExchange(ref SentCount, 0, 0)
							});
						}
						else if (_client.IsConnected && !ShouldDispose)
						{
							Tuple<DateTime, string> msg = SendQueue.Take(TokenSource.Token);
							if (!(msg.Item1.Add(_client.Options.SendCacheItemTimeout) < DateTime.UtcNow))
							{
								try
								{
									IClient client = _client;
									if (client is WebSocketClient webSocketClient)
									{
										await webSocketClient.SendAsync(Encoding.UTF8.GetBytes(msg.Item2));
									}
									else if (client is TwitchLib.Communication.Clients.TcpClient tcpClient)
									{
										await tcpClient.SendAsync(msg.Item2);
									}
									IncrementSentCount();
								}
								catch (Exception exception)
								{
									_client.SendFailed(new OnSendFailedEventArgs
									{
										Data = msg.Item2,
										Exception = exception
									});
									break;
								}
							}
						}
					}
				}
				catch (Exception exception2)
				{
					_client.SendFailed(new OnSendFailedEventArgs
					{
						Data = "",
						Exception = exception2
					});
					_client.Error(new OnErrorEventArgs
					{
						Exception = exception2
					});
				}
			});
		}

		public Task StartWhisperSenderTask()
		{
			StartWhisperThrottlingWindowReset();
			return Task.Run(async delegate
			{
				_ = 2;
				try
				{
					while (!ShouldDispose)
					{
						await Task.Delay(_client.Options.SendDelay);
						if (WhispersSent == _client.Options.WhispersAllowedInPeriod)
						{
							_client.WhisperThrottled(new OnWhisperThrottledEventArgs
							{
								Message = "Whisper Throttle Occured. Too Many Whispers within the period specified in ClientOptions.",
								AllowedInPeriod = _client.Options.WhispersAllowedInPeriod,
								Period = _client.Options.WhisperThrottlingPeriod,
								SentWhisperCount = Interlocked.CompareExchange(ref WhispersSent, 0, 0)
							});
						}
						else if (_client.IsConnected && !ShouldDispose)
						{
							Tuple<DateTime, string> msg = WhisperQueue.Take(TokenSource.Token);
							if (!(msg.Item1.Add(_client.Options.SendCacheItemTimeout) < DateTime.UtcNow))
							{
								try
								{
									IClient client = _client;
									if (client is WebSocketClient webSocketClient)
									{
										await webSocketClient.SendAsync(Encoding.UTF8.GetBytes(msg.Item2));
									}
									else if (client is TwitchLib.Communication.Clients.TcpClient tcpClient)
									{
										await tcpClient.SendAsync(msg.Item2);
									}
									IncrementWhisperCount();
								}
								catch (Exception exception)
								{
									_client.SendFailed(new OnSendFailedEventArgs
									{
										Data = msg.Item2,
										Exception = exception
									});
									break;
								}
							}
						}
					}
				}
				catch (Exception exception2)
				{
					_client.SendFailed(new OnSendFailedEventArgs
					{
						Data = "",
						Exception = exception2
					});
					_client.Error(new OnErrorEventArgs
					{
						Exception = exception2
					});
				}
			});
		}
	}
}
namespace TwitchLib.Communication.Models
{
	public class ClientOptions : IClientOptions
	{
		public int SendQueueCapacity { get; set; } = 10000;


		public TimeSpan SendCacheItemTimeout { get; set; } = TimeSpan.FromMinutes(30.0);


		public ushort SendDelay { get; set; } = 50;


		public ReconnectionPolicy ReconnectionPolicy { get; set; } = new ReconnectionPolicy(3000, (int?)10);


		public bool UseSsl { get; set; } = true;


		public int DisconnectWait { get; set; } = 20000;


		public ClientType ClientType { get; set; }

		public TimeSpan ThrottlingPeriod { get; set; } = TimeSpan.FromSeconds(30.0);


		public int MessagesAllowedInPeriod { get; set; } = 100;


		public TimeSpan WhisperThrottlingPeriod { get; set; } = TimeSpan.FromSeconds(60.0);


		public int WhispersAllowedInPeriod { get; set; } = 100;


		public int WhisperQueueCapacity { get; set; } = 10000;

	}
	public class ReconnectionPolicy
	{
		private readonly int _reconnectStepInterval;

		private readonly int? _initMaxAttempts;

		private int _minReconnectInterval;

		private readonly int _maxReconnectInterval;

		private int? _maxAttempts;

		private int _attemptsMade;

		public ReconnectionPolicy()
		{
			_reconnectStepInterval = 3000;
			_minReconnectInterval = 3000;
			_maxReconnectInterval = 30000;
			_maxAttempts = null;
			_initMaxAttempts = null;
			_attemptsMade = 0;
		}

		public void SetMaxAttempts(int attempts)
		{
			_maxAttempts = attempts;
		}

		public void Reset()
		{
			_attemptsMade = 0;
			_minReconnectInterval = _reconnectStepInterval;
			_maxAttempts = _initMaxAttempts;
		}

		public void SetAttemptsMade(int count)
		{
			_attemptsMade = count;
		}

		public ReconnectionPolicy(int minReconnectInterval, int maxReconnectInterval, int? maxAttempts)
		{
			_reconnectStepInterval = minReconnectInterval;
			_minReconnectInterval = ((minReconnectInterval > maxReconnectInterval) ? maxReconnectInterval : minReconnectInterval);
			_maxReconnectInterval = maxReconnectInterval;
			_maxAttempts = maxAttempts;
			_initMaxAttempts = maxAttempts;
			_attemptsMade = 0;
		}

		public ReconnectionPolicy(int minReconnectInterval, int maxReconnectInterval)
		{
			_reconnectStepInterval = minReconnectInterval;
			_minReconnectInterval = ((minReconnectInterval > maxReconnectInterval) ? maxReconnectInterval : minReconnectInterval);
			_maxReconnectInterval = maxReconnectInterval;
			_maxAttempts = null;
			_initMaxAttempts = null;
			_attemptsMade = 0;
		}

		public ReconnectionPolicy(int reconnectInterval)
		{
			_reconnectStepInterval = reconnectInterval;
			_minReconnectInterval = reconnectInterval;
			_maxReconnectInterval = reconnectInterval;
			_maxAttempts = null;
			_initMaxAttempts = null;
			_attemptsMade = 0;
		}

		public ReconnectionPolicy(int reconnectInterval, int? maxAttempts)
		{
			_reconnectStepInterval = reconnectInterval;
			_minReconnectInterval = reconnectInterval;
			_maxReconnectInterval = reconnectInterval;
			_maxAttempts = maxAttempts;
			_initMaxAttempts = maxAttempts;
			_attemptsMade = 0;
		}

		internal void ProcessValues()
		{
			_attemptsMade++;
			if (_minReconnectInterval < _maxReconnectInterval)
			{
				_minReconnectInterval += _reconnectStepInterval;
			}
			if (_minReconnectInterval > _maxReconnectInterval)
			{
				_minReconnectInterval = _maxReconnectInterval;
			}
		}

		public int GetReconnectInterval()
		{
			return _minReconnectInterval;
		}

		public bool AreAttemptsComplete()
		{
			return _attemptsMade == _maxAttempts;
		}
	}
}
namespace TwitchLib.Communication.Interfaces
{
	public interface IClient
	{
		TimeSpan DefaultKeepAliveInterval { get; set; }

		int SendQueueLength { get; }

		int WhisperQueueLength { get; }

		bool IsConnected { get; }

		IClientOptions Options { get; }

		event EventHandler<OnConnectedEventArgs> OnConnected;

		event EventHandler<OnDataEventArgs> OnData;

		event EventHandler<OnDisconnectedEventArgs> OnDisconnected;

		event EventHandler<OnErrorEventArgs> OnError;

		event EventHandler<OnFatalErrorEventArgs> OnFatality;

		event EventHandler<OnMessageEventArgs> OnMessage;

		event EventHandler<OnMessageThrottledEventArgs> OnMessageThrottled;

		event EventHandler<OnWhisperThrottledEventArgs> OnWhisperThrottled;

		event EventHandler<OnSendFailedEventArgs> OnSendFailed;

		event EventHandler<OnStateChangedEventArgs> OnStateChanged;

		event EventHandler<OnReconnectedEventArgs> OnReconnected;

		void Close(bool callDisconnect = true);

		void Dispose();

		bool Open();

		bool Send(string message);

		bool SendWhisper(string message);

		void Reconnect();

		void MessageThrottled(OnMessageThrottledEventArgs eventArgs);

		void SendFailed(OnSendFailedEventArgs eventArgs);

		void Error(OnErrorEventArgs eventArgs);

		void WhisperThrottled(OnWhisperThrottledEventArgs eventArgs);
	}
	public interface IClientOptions
	{
		ClientType ClientType { get; set; }

		int DisconnectWait { get; set; }

		int MessagesAllowedInPeriod { get; set; }

		ReconnectionPolicy ReconnectionPolicy { get; set; }

		TimeSpan SendCacheItemTimeout { get; set; }

		ushort SendDelay { get; set; }

		int SendQueueCapacity { get; set; }

		TimeSpan ThrottlingPeriod { get; set; }

		bool UseSsl { get; set; }

		TimeSpan WhisperThrottlingPeriod { get; set; }

		int WhispersAllowedInPeriod { get; set; }

		int WhisperQueueCapacity { get; set; }
	}
}
namespace TwitchLib.Communication.Events
{
	public class OnConnectedEventArgs : EventArgs
	{
	}
	public class OnDataEventArgs : EventArgs
	{
		public byte[] Data;
	}
	public class OnDisconnectedEventArgs : EventArgs
	{
	}
	public class OnErrorEventArgs : EventArgs
	{
		public Exception Exception { get; set; }
	}
	public class OnFatalErrorEventArgs : EventArgs
	{
		public string Reason;
	}
	public class OnMessageEventArgs : EventArgs
	{
		public string Message;
	}
	public class OnMessageThrottledEventArgs : EventArgs
	{
		public string Message { get; set; }

		public int SentMessageCount { get; set; }

		public TimeSpan Period { get; set; }

		public int AllowedInPeriod { get; set; }
	}
	public class OnReconnectedEventArgs : EventArgs
	{
	}
	public class OnSendFailedEventArgs : EventArgs
	{
		public string Data;

		public Exception Exception;
	}
	public class OnStateChangedEventArgs : EventArgs
	{
		public bool IsConnected;

		public bool WasConnected;
	}
	public class OnWhisperThrottledEventArgs : EventArgs
	{
		public string Message { get; set; }

		public int SentWhisperCount { get; set; }

		public TimeSpan Period { get; set; }

		public int AllowedInPeriod { get; set; }
	}
}
namespace TwitchLib.Communication.Enums
{
	public enum ClientType
	{
		Chat,
		PubSub
	}
}
namespace TwitchLib.Communication.Clients
{
	public class TcpClient : IClient
	{
		private int NotConnectedCounter;

		private readonly string _server = "irc.chat.twitch.tv";

		private StreamReader _reader;

		private StreamWriter _writer;

		private readonly Throttlers _throttlers;

		private CancellationTokenSource _tokenSource = new CancellationTokenSource();

		private bool _stopServices;

		private bool _networkServicesRunning;

		private Task[] _networkTasks;

		private Task _monitorTask;

		public TimeSpan DefaultKeepAliveInterval { get; set; }

		public int SendQueueLength => _throttlers.SendQueue.Count;

		public int WhisperQueueLength => _throttlers.WhisperQueue.Count;

		public bool IsConnected => Client?.Connected ?? false;

		public IClientOptions Options { get; }

		private int Port
		{
			get
			{
				if (Options == null)
				{
					return 0;
				}
				if (!Options.UseSsl)
				{
					return 80;
				}
				return 443;
			}
		}

		public System.Net.Sockets.TcpClient Client { get; private set; }

		public event EventHandler<OnConnectedEventArgs> OnConnected;

		public event EventHandler<OnDataEventArgs> OnData;

		public event EventHandler<OnDisconnectedEventArgs> OnDisconnected;

		public event EventHandler<OnErrorEventArgs> OnError;

		public event EventHandler<OnFatalErrorEventArgs> OnFatality;

		public event EventHandler<OnMessageEventArgs> OnMessage;

		public event EventHandler<OnMessageThrottledEventArgs> OnMessageThrottled;

		public event EventHandler<OnWhisperThrottledEventArgs> OnWhisperThrottled;

		public event EventHandler<OnSendFailedEventArgs> OnSendFailed;

		public event EventHandler<OnStateChangedEventArgs> OnStateChanged;

		public event EventHandler<OnReconnectedEventArgs> OnReconnected;

		public TcpClient(IClientOptions options = null)
		{
			Options = options ?? new ClientOptions();
			_throttlers = new Throttlers(this, Options.ThrottlingPeriod, Options.WhisperThrottlingPeriod)
			{
				TokenSource = _tokenSource
			};
			InitializeClient();
		}

		private void InitializeClient()
		{
			Client = new System.Net.Sockets.TcpClient();
			if (_monitorTask == null)
			{
				_monitorTask = StartMonitorTask();
			}
			else if (_monitorTask.IsCompleted)
			{
				_monitorTask = StartMonitorTask();
			}
		}

		public bool Open()
		{
			try
			{
				if (IsConnected)
				{
					return true;
				}
				Task.Run(delegate
				{
					InitializeClient();
					Client.Connect(_server, Port);
					if (Options.UseSsl)
					{
						SslStream sslStream = new SslStream(Client.GetStream(), leaveInnerStreamOpen: false);
						sslStream.AuthenticateAsClient(_server);
						_reader = new StreamReader(sslStream);
						_writer = new StreamWriter(sslStream);
					}
					else
					{
						_reader = new StreamReader(Client.GetStream());
						_writer = new StreamWriter(Client.GetStream());
					}
				}).Wait(10000);
				if (!IsConnected)
				{
					return Open();
				}
				StartNetworkServices();
				return true;
			}
			catch (Exception)
			{
				InitializeClient();
				return false;
			}
		}

		public void Close(bool callDisconnect = true)
		{
			_reader?.Dispose();
			_writer?.Dispose();
			Client?.Close();
			_stopServices = callDisconnect;
			CleanupServices();
			InitializeClient();
			this.OnDisconnected?.Invoke(this, new OnDisconnectedEventArgs());
		}

		public void Reconnect()
		{
			Task.Run(delegate
			{
				Task.Delay(20).Wait();
				Close();
				if (Open())
				{
					this.OnReconnected?.Invoke(this, new OnReconnectedEventArgs());
				}
			});
		}

		public bool Send(string message)
		{
			try
			{
				if (!IsConnected || SendQueueLength >= Options.SendQueueCapacity)
				{
					return false;
				}
				_throttlers.SendQueue.Add(new Tuple<DateTime, string>(DateTime.UtcNow, message));
				return true;
			}
			catch (Exception exception)
			{
				this.OnError?.Invoke(this, new OnErrorEventArgs
				{
					Exception = exception
				});
				throw;
			}
		}

		public bool SendWhisper(string message)
		{
			try
			{
				if (!IsConnected || WhisperQueueLength >= Options.WhisperQueueCapacity)
				{
					return false;
				}
				_throttlers.WhisperQueue.Add(new Tuple<DateTime, string>(DateTime.UtcNow, message));
				return true;
			}
			catch (Exception exception)
			{
				this.OnError?.Invoke(this, new OnErrorEventArgs
				{
					Exception = exception
				});
				throw;
			}
		}

		private void StartNetworkServices()
		{
			_networkServicesRunning = true;
			_networkTasks = new Task[3]
			{
				StartListenerTask(),
				_throttlers.StartSenderTask(),
				_throttlers.StartWhisperSenderTask()
			}.ToArray();
			if (_networkTasks.Any((Task c) => c.IsFaulted))
			{
				_networkServicesRunning = false;
				CleanupServices();
			}
		}

		public Task SendAsync(string message)
		{
			return Task.Run(async delegate
			{
				await _writer.WriteLineAsync(message);
				await _writer.FlushAsync();
			});
		}

		private Task StartListenerTask()
		{
			return Task.Run(async delegate
			{
				while (IsConnected && _networkServicesRunning)
				{
					try
					{
						string text = await _reader.ReadLineAsync();
						if (text == null && IsConnected)
						{
							Send("PING");
							Task.Delay(500).Wait();
						}
						this.OnMessage?.Invoke(this, new OnMessageEventArgs
						{
							Message = text
						});
					}
					catch (Exception exception)
					{
						this.OnError?.Invoke(this, new OnErrorEventArgs
						{
							Exception = exception
						});
					}
				}
			});
		}

		private Task StartMonitorTask()
		{
			return Task.Run(delegate
			{
				bool flag = false;
				int num = 0;
				try
				{
					bool isConnected = IsConnected;
					while (!_tokenSource.IsCancellationRequested)
					{
						if (isConnected == IsConnected)
						{
							Thread.Sleep(200);
							if (!IsConnected)
							{
								NotConnectedCounter++;
							}
							else
							{
								num++;
							}
							if (num >= 300)
							{
								Send("PING");
								num = 0;
							}
							switch (NotConnectedCounter)
							{
							case 25:
							case 75:
							case 150:
							case 300:
							case 600:
								Reconnect();
								break;
							default:
								if (NotConnectedCounter >= 1200 && NotConnectedCounter % 600 == 0)
								{
									Reconnect();
								}
								break;
							}
							if (NotConnectedCounter != 0 && IsConnected)
							{
								NotConnectedCounter = 0;
							}
						}
						else
						{
							this.OnStateChanged?.Invoke(this, new OnStateChangedEventArgs
							{
								IsConnected = IsConnected,
								WasConnected = isConnected
							});
							if (IsConnected)
							{
								this.OnConnected?.Invoke(this, new OnConnectedEventArgs());
							}
							if (!IsConnected && !_stopServices)
							{
								if (isConnected && Options.ReconnectionPolicy != null && !Options.ReconnectionPolicy.AreAttemptsComplete())
								{
									flag = true;
									break;
								}
								this.OnDisconnected?.Invoke(this, new OnDisconnectedEventArgs());
							}
							isConnected = IsConnected;
						}
					}
				}
				catch (Exception exception)
				{
					this.OnError?.Invoke(this, new OnErrorEventArgs
					{
						Exception = exception
					});
				}
				if (flag && !_stopServices)
				{
					Reconnect();
				}
			}, _tokenSource.Token);
		}

		private void CleanupServices()
		{
			_tokenSource.Cancel();
			_tokenSource = new CancellationTokenSource();
			_throttlers.TokenSource = _tokenSource;
			if (_stopServices)
			{
				Task[] networkTasks = _networkTasks;
				if (networkTasks != null && networkTasks.Length != 0 && !Task.WaitAll(_networkTasks, 15000))
				{
					this.OnFatality?.Invoke(this, new OnFatalErrorEventArgs
					{
						Reason = "Fatal network error. Network services fail to shut down."
					});
					_stopServices = false;
					_throttlers.Reconnecting = false;
					_networkServicesRunning = false;
				}
			}
		}

		public void WhisperThrottled(OnWhisperThrottledEventArgs eventArgs)
		{
			this.OnWhisperThrottled?.Invoke(this, eventArgs);
		}

		public void MessageThrottled(OnMessageThrottledEventArgs eventArgs)
		{
			this.OnMessageThrottled?.Invoke(this, eventArgs);
		}

		public void SendFailed(OnSendFailedEventArgs eventArgs)
		{
			this.OnSendFailed?.Invoke(this, eventArgs);
		}

		public void Error(OnErrorEventArgs eventArgs)
		{
			this.OnError?.Invoke(this, eventArgs);
		}

		public void Dispose()
		{
			Close();
			_throttlers.ShouldDispose = true;
			_tokenSource.Cancel();
			Thread.Sleep(500);
			_tokenSource.Dispose();
			Client?.Dispose();
			GC.Collect();
		}
	}
	public class WebSocketClient : IClient
	{
		private int NotConnectedCounter;

		private readonly Throttlers _throttlers;

		private CancellationTokenSource _tokenSource = new CancellationTokenSource();

		private bool _stopServices;

		private bool _networkServicesRunning;

		private Task[] _networkTasks;

		private Task _monitorTask;

		public TimeSpan DefaultKeepAliveInterval { get; set; }

		public int SendQueueLength => _throttlers.SendQueue.Count;

		public int WhisperQueueLength => _throttlers.WhisperQueue.Count;

		public bool IsConnected
		{
			get
			{
				ClientWebSocket client = Client;
				if (client == null)
				{
					return false;
				}
				return client.State == WebSocketState.Open;
			}
		}

		public IClientOptions Options { get; }

		public ClientWebSocket Client { get; private set; }

		private string Url { get; }

		public event EventHandler<OnConnectedEventArgs> OnConnected;

		public event EventHandler<OnDataEventArgs> OnData;

		public event EventHandler<OnDisconnectedEventArgs> OnDisconnected;

		public event EventHandler<OnErrorEventArgs> OnError;

		public event EventHandler<OnFatalErrorEventArgs> OnFatality;

		public event EventHandler<OnMessageEventArgs> OnMessage;

		public event EventHandler<OnMessageThrottledEventArgs> OnMessageThrottled;

		public event EventHandler<OnWhisperThrottledEventArgs> OnWhisperThrottled;

		public event EventHandler<OnSendFailedEventArgs> OnSendFailed;

		public event EventHandler<OnStateChangedEventArgs> OnStateChanged;

		public event EventHandler<OnReconnectedEventArgs> OnReconnected;

		public WebSocketClient(IClientOptions options = null)
		{
			Options = options ?? new ClientOptions();
			switch (Options.ClientType)
			{
			case ClientType.Chat:
				Url = (Options.UseSsl ? "wss://irc-ws.chat.twitch.tv:443" : "ws://irc-ws.chat.twitch.tv:80");
				break;
			case ClientType.PubSub:
				Url = (Options.UseSsl ? "wss://pubsub-edge.twitch.tv:443" : "ws://pubsub-edge.twitch.tv:80");
				break;
			default:
				throw new ArgumentOutOfRangeException();
			}
			_throttlers = new Throttlers(this, Options.ThrottlingPeriod, Options.WhisperThrottlingPeriod)
			{
				TokenSource = _tokenSource
			};
		}

		private void InitializeClient()
		{
			Client?.Abort();
			Client = new ClientWebSocket();
			if (_monitorTask == null)
			{
				_monitorTask = StartMonitorTask();
			}
			else if (_monitorTask.IsCompleted)
			{
				_monitorTask = StartMonitorTask();
			}
		}

		public bool Open()
		{
			try
			{
				if (IsConnected)
				{
					return true;
				}
				InitializeClient();
				Client.ConnectAsync(new Uri(Url), _tokenSource.Token).Wait(10000);
				if (!IsConnected)
				{
					return Open();
				}
				StartNetworkServices();
				return true;
			}
			catch (WebSocketException)
			{
				InitializeClient();
				return false;
			}
		}

		public void Close(bool callDisconnect = true)
		{
			Client?.Abort();
			_stopServices = callDisconnect;
			CleanupServices();
			InitializeClient();
			this.OnDisconnected?.Invoke(this, new OnDisconnectedEventArgs());
		}

		public void Reconnect()
		{
			Task.Run(delegate
			{
				Task.Delay(20).Wait();
				Close();
				if (Open())
				{
					this.OnReconnected?.Invoke(this, new OnReconnectedEventArgs());
				}
			});
		}

		public bool Send(string message)
		{
			try
			{
				if (!IsConnected || SendQueueLength >= Options.SendQueueCapacity)
				{
					return false;
				}
				_throttlers.SendQueue.Add(new Tuple<DateTime, string>(DateTime.UtcNow, message));
				return true;
			}
			catch (Exception exception)
			{
				this.OnError?.Invoke(this, new OnErrorEventArgs
				{
					Exception = exception
				});
				throw;
			}
		}

		public bool SendWhisper(string message)
		{
			try
			{
				if (!IsConnected || WhisperQueueLength >= Options.WhisperQueueCapacity)
				{
					return false;
				}
				_throttlers.WhisperQueue.Add(new Tuple<DateTime, string>(DateTime.UtcNow, message));
				return true;
			}
			catch (Exception exception)
			{
				this.OnError?.Invoke(this, new OnErrorEventArgs
				{
					Exception = exception
				});
				throw;
			}
		}

		private void StartNetworkServices()
		{
			_networkServicesRunning = true;
			_networkTasks = new Task[3]
			{
				StartListenerTask(),
				_throttlers.StartSenderTask(),
				_throttlers.StartWhisperSenderTask()
			}.ToArray();
			if (_networkTasks.Any((Task c) => c.IsFaulted))
			{
				_networkServicesRunning = false;
				CleanupServices();
			}
		}

		public Task SendAsync(byte[] message)
		{
			return Client.SendAsync(new ArraySegment<byte>(message), WebSocketMessageType.Text, endOfMessage: true, _tokenSource.Token);
		}

		private Task StartListenerTask()
		{
			return Task.Run(async delegate
			{
				string message = "";
				while (IsConnected && _networkServicesRunning)
				{
					byte[] buffer = new byte[1024];
					WebSocketReceiveResult webSocketReceiveResult;
					try
					{
						webSocketReceiveResult = await Client.ReceiveAsync(new ArraySegment<byte>(buffer), _tokenSource.Token);
					}
					catch
					{
						InitializeClient();
						break;
					}
					if (webSocketReceiveResult != null)
					{
						switch (webSocketReceiveResult.MessageType)
						{
						case WebSocketMessageType.Close:
							Close();
							break;
						case WebSocketMessageType.Text:
							if (!webSocketReceiveResult.EndOfMessage)
							{
								message += Encoding.UTF8.GetString(buffer).TrimEnd(new char[1]);
								continue;
							}
							message += Encoding.UTF8.GetString(buffer).TrimEnd(new char[1]);
							this.OnMessage?.Invoke(this, new OnMessageEventArgs
							{
								Message = message
							});
							break;
						default:
							throw new ArgumentOutOfRangeException();
						case WebSocketMessageType.Binary:
							break;
						}
						message = "";
					}
				}
			});
		}

		private Task StartMonitorTask()
		{
			return Task.Run(delegate
			{
				bool flag = false;
				int num = 0;
				try
				{
					bool isConnected = IsConnected;
					while (!_tokenSource.IsCancellationRequested)
					{
						if (isConnected == IsConnected)
						{
							Thread.Sleep(200);
							if (!IsConnected)
							{
								NotConnectedCounter++;
							}
							else
							{
								num++;
							}
							if (num >= 300)
							{
								Send("PING");
								num = 0;
							}
							switch (NotConnectedCounter)
							{
							case 25:
							case 75:
							case 150:
							case 300:
							case 600:
								Reconnect();
								break;
							default:
								if (NotConnectedCounter >= 1200 && NotConnectedCounter % 600 == 0)
								{
									Reconnect();
								}
								break;
							}
							if (NotConnectedCounter != 0 && IsConnected)
							{
								NotConnectedCounter = 0;
							}
						}
						else
						{
							this.OnStateChanged?.Invoke(this, new OnStateChangedEventArgs
							{
								IsConnected = (Client.State == WebSocketState.Open),
								WasConnected = isConnected
							});
							if (IsConnected)
							{
								this.OnConnected?.Invoke(this, new OnConnectedEventArgs());
							}
							if (!IsConnected && !_stopServices)
							{
								if (isConnected && Options.ReconnectionPolicy != null && !Options.ReconnectionPolicy.AreAttemptsComplete())
								{
									flag = true;
									break;
								}
								this.OnDisconnected?.Invoke(this, new OnDisconnectedEventArgs());
								if (Client.CloseStatus.HasValue && Client.CloseStatus != WebSocketCloseStatus.NormalClosure)
								{
									this.OnError?.Invoke(this, new OnErrorEventArgs
									{
										Exception = new Exception(Client.CloseStatus.ToString() + " " + Client.CloseStatusDescription)
									});
								}
							}
							isConnected = IsConnected;
						}
					}
				}
				catch (Exception exception)
				{
					this.OnError?.Invoke(this, new OnErrorEventArgs
					{
						Exception = exception
					});
				}
				if (flag && !_stopServices)
				{
					Reconnect();
				}
			}, _tokenSource.Token);
		}

		private void CleanupServices()
		{
			_tokenSource.Cancel();
			_tokenSource = new CancellationTokenSource();
			_throttlers.TokenSource = _tokenSource;
			if (_stopServices)
			{
				Task[] networkTasks = _networkTasks;
				if (networkTasks != null && networkTasks.Length != 0 && !Task.WaitAll(_networkTasks, 15000))
				{
					this.OnFatality?.Invoke(this, new OnFatalErrorEventArgs
					{
						Reason = "Fatal network error. Network services fail to shut down."
					});
					_stopServices = false;
					_throttlers.Reconnecting = false;
					_networkServicesRunning = false;
				}
			}
		}

		public void WhisperThrottled(OnWhisperThrottledEventArgs eventArgs)
		{
			this.OnWhisperThrottled?.Invoke(this, eventArgs);
		}

		public void MessageThrottled(OnMessageThrottledEventArgs eventArgs)
		{
			this.OnMessageThrottled?.Invoke(this, eventArgs);
		}

		public void SendFailed(OnSendFailedEventArgs eventArgs)
		{
			this.OnSendFailed?.Invoke(this, eventArgs);
		}

		public void Error(OnErrorEventArgs eventArgs)
		{
			this.OnError?.Invoke(this, eventArgs);
		}

		public void Dispose()
		{
			Close();
			_throttlers.ShouldDispose = true;
			_tokenSource.Cancel();
			Thread.Sleep(500);
			_tokenSource.Dispose();
			Client?.Dispose();
			GC.Collect();
		}
	}
}