Decompiled source of BingBong FR v1.0.0

BepInEx/plugins/BingBong_FR/BingBong_FR.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BingBong_FR")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BingBong_FR")]
[assembly: AssemblyTitle("BingBong_FR")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 BingBongFR
{
	[BepInPlugin("fr.bingbong.standalone", "BingBong_FR", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "fr.bingbong.standalone";

		public const string PluginName = "BingBong_FR";

		public const string PluginVersion = "1.0.0";

		internal static Plugin Instance;

		internal static ManualLogSource ModLog;

		internal static string PluginDirectory;

		internal static ConfigEntry<bool> TwitchInteractionEnabled;

		internal static ConfigEntry<string> TwitchChannel;

		internal static ConfigEntry<string> TwitchBannedWords;

		internal static ConfigEntry<int> TwitchUserCooldownSeconds;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			Instance = this;
			ModLog = ((BaseUnityPlugin)this).Logger;
			PluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			TwitchInteractionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Twitch", "Interaction du chat", false, "Active la commande !bingbong dans le chat Twitch.");
			TwitchChannel = ((BaseUnityPlugin)this).Config.Bind<string>("Twitch", "Chaine", string.Empty, "Nom de la chaine Twitch a lire, sans # ni URL.");
			TwitchBannedWords = ((BaseUnityPlugin)this).Config.Bind<string>("Twitch", "Mots bannis", string.Empty, "Mots ou expressions interdits, separes par des virgules.");
			TwitchUserCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Twitch", "Delai par spectateur (secondes)", 15, new ConfigDescription("Temps d'attente avant qu'un meme spectateur puisse reutiliser !bingbong. Mettre 0 pour desactiver.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 300), Array.Empty<object>()));
			GameObject val = new GameObject("BingBong_FR_Runtime");
			val.AddComponent<SoundPackRuntime>();
			val.AddComponent<TwitchRuntime>();
			val.AddComponent<TwitchMenu>();
			Object.DontDestroyOnLoad((Object)val);
			_harmony = new Harmony("fr.bingbong.standalone");
			_harmony.PatchAll(typeof(InstantiatePatch));
			_harmony.PatchAll(typeof(AddComponentPatch));
			_harmony.PatchAll(typeof(BingBongUsedPatch));
			_harmony.PatchAll(typeof(BingBongAnswerPatch));
			_harmony.PatchAll(typeof(TwitchMenuCursorPatch));
			_harmony.PatchAll(typeof(ItemInstanceDataPatch));
			ModLog.LogInfo((object)"BingBong_FR 1.0.0 charge.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static void SaveConfiguration()
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Config.Save();
			}
		}
	}
	[Serializable]
	public sealed class SoundPackConfig
	{
		public string name = string.Empty;

		public SoundPackEntry[] entries = Array.Empty<SoundPackEntry>();
	}
	[Serializable]
	public sealed class SoundPackEntry
	{
		public string file = string.Empty;

		public string subtitle = string.Empty;
	}
	public sealed class SoundPackRuntime : MonoBehaviour
	{
		private sealed class LoadedResponse
		{
			internal readonly int Index;

			internal readonly SoundPackEntry Entry;

			internal readonly AudioClip Clip;

			internal LoadedResponse(int index, SoundPackEntry entry, AudioClip clip)
			{
				Index = index;
				Entry = entry;
				Clip = clip;
			}
		}

		private static SoundPackRuntime _instance;

		private readonly List<LoadedResponse> _responses = new List<LoadedResponse>();

		private bool _loaded;

		private void Awake()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			_instance = this;
			((MonoBehaviour)this).StartCoroutine(LoadSoundPack());
		}

		private IEnumerator LoadSoundPack()
		{
			string path = Path.Combine(Plugin.PluginDirectory, "response_sound_pack.json");
			if (!File.Exists(path))
			{
				Plugin.ModLog.LogError((object)"response_sound_pack.json est introuvable a cote de BingBong_FR.dll.");
				yield break;
			}
			SoundPackConfig config;
			try
			{
				config = JsonConvert.DeserializeObject<SoundPackConfig>(File.ReadAllText(path));
			}
			catch (Exception ex)
			{
				Plugin.ModLog.LogError((object)("Impossible de lire response_sound_pack.json : " + ex));
				yield break;
			}
			if (config == null || config.entries == null || config.entries.Length == 0)
			{
				Plugin.ModLog.LogError((object)"Le pack de sons ne contient aucune replique.");
				yield break;
			}
			Plugin.ModLog.LogInfo((object)("Pack '" + (config.name ?? "sans nom") + "' lu : " + config.entries.Length + " repliques declarees."));
			Array.Sort(config.entries, (SoundPackEntry left, SoundPackEntry right) => string.Compare(left?.file ?? string.Empty, right?.file ?? string.Empty, StringComparison.Ordinal));
			for (int index = 0; index < config.entries.Length; index++)
			{
				SoundPackEntry entry = config.entries[index];
				string text = Path.Combine(Plugin.PluginDirectory, entry.file);
				if (!File.Exists(text))
				{
					Plugin.ModLog.LogError((object)("Fichier audio introuvable : " + entry.file));
					continue;
				}
				string absoluteUri = new Uri(text).AbsoluteUri;
				UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(absoluteUri, (AudioType)20);
				try
				{
					yield return request.SendWebRequest();
					if ((int)request.result != 1)
					{
						Plugin.ModLog.LogError((object)("Impossible de charger " + entry.file + " : " + request.error));
						continue;
					}
					AudioClip content = DownloadHandlerAudioClip.GetContent(request);
					if ((Object)(object)content == (Object)null)
					{
						Plugin.ModLog.LogError((object)("Le fichier audio est invalide : " + entry.file));
					}
					else
					{
						_responses.Add(new LoadedResponse(index, entry, content));
					}
				}
				finally
				{
					((IDisposable)request)?.Dispose();
				}
			}
			if (_responses.Count != config.entries.Length)
			{
				Plugin.ModLog.LogError((object)("Le pack est incomplet : " + _responses.Count + "/" + config.entries.Length + " repliques chargees. Le remplacement est annule pour conserver une synchronisation coherente."));
			}
			else
			{
				_loaded = true;
				Plugin.ModLog.LogInfo((object)(_responses.Count + " repliques francaises chargees."));
				ApplyToExistingBingBongs();
			}
		}

		private void ApplyToExistingBingBongs()
		{
			Action_AskBingBong[] array = Resources.FindObjectsOfTypeAll<Action_AskBingBong>();
			foreach (Action_AskBingBong action in array)
			{
				ApplyResponses(action);
			}
		}

		private void ApplyResponses(Action_AskBingBong action)
		{
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			if (_loaded && !((Object)(object)action == (Object)null) && (action.responses == null || action.responses.Length != _responses.Count || action.responses.Length == 0 || !(action.responses[0].subtitleID == "BINGBONG_FR_00")))
			{
				BingBongResponse[] array = (BingBongResponse[])(object)new BingBongResponse[_responses.Count];
				for (int i = 0; i < _responses.Count; i++)
				{
					LoadedResponse loadedResponse = _responses[i];
					string text = "BINGBONG_FR_" + loadedResponse.Index.ToString("D2");
					AddSubtitle(text, loadedResponse.Entry.subtitle ?? string.Empty);
					SFX_Instance val = ScriptableObject.CreateInstance<SFX_Instance>();
					val.clips = (AudioClip[])(object)new AudioClip[1] { loadedResponse.Clip };
					array[i] = new BingBongResponse
					{
						subtitleID = text,
						sfx = val,
						mouthCurve = new AnimationCurve(),
						mouthCurveTime = 1f
					};
				}
				action.responses = array;
				Plugin.ModLog.LogInfo((object)("Repliques de Bing Bong remplacees sur l'instance " + ((Object)action).GetInstanceID() + "."));
			}
		}

		private static void AddSubtitle(string id, string text)
		{
			LocalizedText.TryInitTables();
			List<string> list = new List<string>();
			for (int i = 0; i < 14; i++)
			{
				list.Add(text);
			}
			LocalizedText.mainTable[id] = list;
		}

		internal static void TryApplyToCreatedObject(Object created)
		{
			if ((Object)(object)_instance == (Object)null || !_instance._loaded || created == (Object)null)
			{
				return;
			}
			GameObject val = (GameObject)(object)((created is GameObject) ? created : null);
			Component val2 = (Component)(object)((created is Component) ? created : null);
			if ((Object)(object)val == (Object)null && (Object)(object)val2 != (Object)null)
			{
				val = val2.gameObject;
			}
			if (!((Object)(object)val == (Object)null))
			{
				Action_AskBingBong val3 = val.GetComponent<Action_AskBingBong>() ?? val.GetComponentInChildren<Action_AskBingBong>(true);
				if ((Object)(object)val3 != (Object)null)
				{
					_instance.ApplyResponses(val3);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Object))]
	internal static class InstantiatePatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("Instantiate", new Type[] { typeof(Object) })]
		private static void Postfix(Object __result)
		{
			SoundPackRuntime.TryApplyToCreatedObject(__result);
		}
	}
	[HarmonyPatch(typeof(GameObject))]
	internal static class AddComponentPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch("AddComponent", new Type[] { typeof(Type) })]
		private static void Postfix(Component __result)
		{
			SoundPackRuntime.TryApplyToCreatedObject((Object)(object)__result);
		}
	}
	[HarmonyPatch(typeof(Action_AskBingBong), "RunAction")]
	internal static class BingBongUsedPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Action_AskBingBong __instance)
		{
			TwitchRuntime.RecordUsedBingBong(__instance);
		}
	}
	[HarmonyPatch(typeof(Action_AskBingBong), "Ask")]
	internal static class BingBongAnswerPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Action_AskBingBong __instance)
		{
			TwitchRuntime.StopTwitchPresentation(__instance);
		}
	}
	[HarmonyPatch(typeof(GUIManager), "UpdateWindowStatus")]
	internal static class TwitchMenuCursorPatch
	{
		[HarmonyPostfix]
		private static void Postfix(GUIManager __instance)
		{
			if (TwitchMenu.IsOpen)
			{
				TwitchMenu.ApplyGameUiState(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Item), "SetItemInstanceDataRPC")]
	internal static class ItemInstanceDataPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Item __instance)
		{
			TwitchRuntime.TrackRecreatedItem(__instance);
		}
	}
	internal static class ToySpeechEngine
	{
		private sealed class SynthesisResult
		{
			internal readonly string VoiceName;

			internal readonly string Error;

			private SynthesisResult(string voiceName, string error)
			{
				VoiceName = voiceName;
				Error = error;
			}

			internal static SynthesisResult Succeeded(string voiceName)
			{
				return new SynthesisResult(voiceName, null);
			}

			internal static SynthesisResult Failed(string error)
			{
				return new SynthesisResult(null, error);
			}
		}

		internal static IEnumerator Synthesize(string text, Action<string, string> completed)
		{
			string scriptPath = Path.Combine(Plugin.PluginDirectory, "BingBongFR-TTS.ps1");
			if (!File.Exists(scriptPath))
			{
				completed(null, "Le moteur vocal BingBongFR-TTS.ps1 est introuvable.");
				yield break;
			}
			string path = Path.Combine(Paths.CachePath, "BingBong_FR", "TTS");
			string outputPath = Path.Combine(path, "bingbong-" + Guid.NewGuid().ToString("N") + ".wav");
			Task<SynthesisResult> synthesisTask;
			try
			{
				synthesisTask = Task.Run(() => SynthesizeInBackground(text, scriptPath, outputPath));
			}
			catch (Exception ex)
			{
				completed(null, "Impossible de lancer la synthese vocale : " + ex.Message);
				yield break;
			}
			while (!synthesisTask.IsCompleted)
			{
				yield return null;
			}
			if (synthesisTask.IsCanceled)
			{
				SafeDelete(outputPath);
				completed(null, "La synthese vocale a ete annulee.");
				yield break;
			}
			if (synthesisTask.IsFaulted)
			{
				SafeDelete(outputPath);
				completed(null, "Echec de la voix francaise : " + ShortError((synthesisTask.Exception?.GetBaseException())?.Message));
				yield break;
			}
			SynthesisResult result = synthesisTask.Result;
			if (!string.IsNullOrWhiteSpace(result.Error))
			{
				SafeDelete(outputPath);
				completed(null, result.Error);
				yield break;
			}
			if (!string.IsNullOrWhiteSpace(result.VoiceName))
			{
				Plugin.ModLog.LogDebug((object)("Voix TTS : " + result.VoiceName));
			}
			completed(outputPath, null);
		}

		private static SynthesisResult SynthesizeInBackground(string text, string scriptPath, string outputPath)
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
				string text2 = Convert.ToBase64String(Encoding.UTF8.GetBytes(text));
				string text3 = Convert.ToBase64String(Encoding.UTF8.GetBytes(scriptPath));
				string text4 = Convert.ToBase64String(Encoding.UTF8.GetBytes(outputPath));
				string s = "$script=[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('" + text3 + "'));& $script -TextBase64 '" + text2 + "' -OutputBase64 '" + text4 + "'";
				string text5 = Convert.ToBase64String(Encoding.Unicode.GetBytes(s));
				string text6 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "WindowsPowerShell", "v1.0", "powershell.exe");
				if (!File.Exists(text6))
				{
					text6 = "powershell.exe";
				}
				using Process process = new Process
				{
					StartInfo = new ProcessStartInfo
					{
						FileName = text6,
						Arguments = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -EncodedCommand " + text5,
						UseShellExecute = false,
						RedirectStandardOutput = true,
						RedirectStandardError = true,
						CreateNoWindow = true
					}
				};
				process.Start();
				if (!process.WaitForExit(25000))
				{
					try
					{
						process.Kill();
					}
					catch
					{
					}
					SafeDelete(outputPath);
					return SynthesisResult.Failed("La synthese vocale a depasse 25 secondes.");
				}
				string text7 = process.StandardOutput.ReadToEnd().Trim();
				string text8 = process.StandardError.ReadToEnd().Trim();
				if (process.ExitCode != 0 || !File.Exists(outputPath))
				{
					SafeDelete(outputPath);
					string message = (string.IsNullOrWhiteSpace(text8) ? text7 : text8);
					return SynthesisResult.Failed("Echec de la voix francaise : " + ShortError(message));
				}
				return SynthesisResult.Succeeded(text7);
			}
			catch (Exception ex)
			{
				SafeDelete(outputPath);
				return SynthesisResult.Failed("Impossible de demarrer la voix Windows : " + ShortError(ex.Message));
			}
		}

		internal static void SafeDelete(string path)
		{
			if (string.IsNullOrEmpty(path))
			{
				return;
			}
			try
			{
				if (File.Exists(path))
				{
					File.Delete(path);
				}
			}
			catch (Exception ex)
			{
				Plugin.ModLog.LogDebug((object)("Nettoyage TTS impossible : " + ex.Message));
			}
		}

		private static string ShortError(string message)
		{
			if (string.IsNullOrWhiteSpace(message))
			{
				return "aucun detail disponible";
			}
			string text = message.Replace('\r', ' ').Replace('\n', ' ').Trim();
			if (text.Length > 180)
			{
				return text.Substring(0, 180) + "...";
			}
			return text;
		}
	}
	internal sealed class TwitchChatMessage
	{
		internal readonly string Id;

		internal readonly string UserId;

		internal readonly string DisplayName;

		internal readonly string Text;

		internal TwitchChatMessage(string id, string userId, string displayName, string text)
		{
			Id = id;
			UserId = userId;
			DisplayName = displayName;
			Text = text;
		}
	}
	internal sealed class TwitchIrcClient : IDisposable
	{
		private const string Host = "irc.chat.twitch.tv";

		private const int Port = 6697;

		private readonly Action<TwitchChatMessage> _messageHandler;

		private readonly object _stateLock = new object();

		private CancellationTokenSource _cancellation;

		private TcpClient _tcpClient;

		private string _status = "Deconnecte";

		private string _channel = string.Empty;

		internal string Channel
		{
			get
			{
				lock (_stateLock)
				{
					return _channel;
				}
			}
		}

		internal string Status
		{
			get
			{
				lock (_stateLock)
				{
					return _status;
				}
			}
		}

		internal bool IsRunning
		{
			get
			{
				lock (_stateLock)
				{
					return _cancellation != null && !_cancellation.IsCancellationRequested;
				}
			}
		}

		internal TwitchIrcClient(Action<TwitchChatMessage> messageHandler)
		{
			_messageHandler = messageHandler;
		}

		internal void Connect(string channel)
		{
			Disconnect();
			CancellationTokenSource cancellation = new CancellationTokenSource();
			lock (_stateLock)
			{
				_channel = channel;
				_status = "Connexion a #" + channel + "...";
				_cancellation = cancellation;
			}
			Task.Run(() => RunConnectionLoop(channel, cancellation.Token));
		}

		internal void Disconnect(string status = "Deconnecte")
		{
			CancellationTokenSource cancellation;
			TcpClient tcpClient;
			lock (_stateLock)
			{
				cancellation = _cancellation;
				tcpClient = _tcpClient;
				_cancellation = null;
				_tcpClient = null;
				_channel = string.Empty;
				_status = status;
			}
			try
			{
				cancellation?.Cancel();
				tcpClient?.Close();
			}
			catch
			{
			}
			finally
			{
				cancellation?.Dispose();
			}
		}

		public void Dispose()
		{
			Disconnect();
		}

		private async Task RunConnectionLoop(string channel, CancellationToken cancellationToken)
		{
			while (!cancellationToken.IsCancellationRequested)
			{
				try
				{
					await RunSingleConnection(channel, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				}
				catch (OperationCanceledException)
				{
					break;
				}
				catch (Exception ex2)
				{
					if (!cancellationToken.IsCancellationRequested)
					{
						SetStatus("Connexion perdue : " + ShortError(ex2.Message));
						Plugin.ModLog.LogWarning((object)("Twitch IRC : " + ex2.Message));
					}
				}
				if (cancellationToken.IsCancellationRequested)
				{
					break;
				}
				SetStatus("Reconnexion a #" + channel + " dans 5 s...");
				try
				{
					await Task.Delay(5000, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
				}
				catch (OperationCanceledException)
				{
					break;
				}
			}
		}

		private async Task RunSingleConnection(string channel, CancellationToken cancellationToken)
		{
			TcpClient client = new TcpClient();
			lock (_stateLock)
			{
				if (_cancellation == null || cancellationToken.IsCancellationRequested)
				{
					client.Dispose();
					return;
				}
				_tcpClient = client;
			}
			await client.ConnectAsync("irc.chat.twitch.tv", 6697).ConfigureAwait(continueOnCapturedContext: false);
			cancellationToken.ThrowIfCancellationRequested();
			using SslStream sslStream = new SslStream(client.GetStream(), leaveInnerStreamOpen: false);
			await sslStream.AuthenticateAsClientAsync("irc.chat.twitch.tv").ConfigureAwait(continueOnCapturedContext: false);
			cancellationToken.ThrowIfCancellationRequested();
			using StreamReader reader = new StreamReader(sslStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			using StreamWriter writer = new StreamWriter(sslStream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false))
			{
				AutoFlush = true,
				NewLine = "\r\n"
			};
			string anonymousNick = "justinfan" + (uint)Guid.NewGuid().GetHashCode();
			await writer.WriteLineAsync("PASS SCHMOOPIIE").ConfigureAwait(continueOnCapturedContext: false);
			await writer.WriteLineAsync("NICK " + anonymousNick).ConfigureAwait(continueOnCapturedContext: false);
			await writer.WriteLineAsync("CAP REQ :twitch.tv/tags twitch.tv/commands").ConfigureAwait(continueOnCapturedContext: false);
			await writer.WriteLineAsync("JOIN #" + channel).ConfigureAwait(continueOnCapturedContext: false);
			SetStatus("Connexion a #" + channel + "...");
			while (!cancellationToken.IsCancellationRequested)
			{
				string text = await reader.ReadLineAsync().ConfigureAwait(continueOnCapturedContext: false);
				if (text == null)
				{
					throw new IOException("Twitch a ferme la connexion.");
				}
				if (text.StartsWith("PING", StringComparison.Ordinal))
				{
					await writer.WriteLineAsync("PONG" + text.Substring(4)).ConfigureAwait(continueOnCapturedContext: false);
					continue;
				}
				if (text.IndexOf(" 001 ", StringComparison.Ordinal) >= 0 || text.IndexOf(" JOIN #" + channel, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					SetStatus("Connecte a #" + channel);
				}
				TwitchChatMessage twitchChatMessage = TryParsePrivateMessage(text);
				if (twitchChatMessage != null)
				{
					_messageHandler(twitchChatMessage);
				}
			}
		}

		internal static TwitchChatMessage TryParsePrivateMessage(string line)
		{
			int num = line.IndexOf(" PRIVMSG #", StringComparison.Ordinal);
			if (num < 0)
			{
				return null;
			}
			int num2 = line.IndexOf(" :", num + 10, StringComparison.Ordinal);
			if (num2 < 0 || num2 + 2 >= line.Length)
			{
				return null;
			}
			Dictionary<string, string> dictionary = ParseTags(line);
			string text = ((line[0] == '@') ? line.Substring(line.IndexOf(' ') + 1, num - line.IndexOf(' ') - 1) : line.Substring(0, num));
			int num3 = text.IndexOf(':');
			int num4 = text.IndexOf('!');
			string text2 = ((num4 > num3) ? text.Substring(num3 + 1, num4 - num3 - 1) : "spectateur");
			if (!dictionary.TryGetValue("display-name", out var value) || string.IsNullOrWhiteSpace(value))
			{
				value = text2;
			}
			if (!dictionary.TryGetValue("id", out var value2))
			{
				value2 = string.Empty;
			}
			if (!dictionary.TryGetValue("user-id", out var value3))
			{
				value3 = string.Empty;
			}
			string text3 = line.Substring(num2 + 2);
			return new TwitchChatMessage(value2, value3, value, text3);
		}

		private static Dictionary<string, string> ParseTags(string line)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			if (string.IsNullOrEmpty(line) || line[0] != '@')
			{
				return dictionary;
			}
			int num = line.IndexOf(' ');
			if (num <= 1)
			{
				return dictionary;
			}
			string[] array = line.Substring(1, num - 1).Split(';');
			foreach (string text in array)
			{
				int num2 = text.IndexOf('=');
				if (num2 < 0)
				{
					dictionary[text] = string.Empty;
				}
				else
				{
					dictionary[text.Substring(0, num2)] = UnescapeTag(text.Substring(num2 + 1));
				}
			}
			return dictionary;
		}

		private static string UnescapeTag(string value)
		{
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			for (int i = 0; i < value.Length; i++)
			{
				if (value[i] != '\\' || i + 1 >= value.Length)
				{
					stringBuilder.Append(value[i]);
					continue;
				}
				char c = value[++i];
				switch (c)
				{
				case 's':
					stringBuilder.Append(' ');
					break;
				case ':':
					stringBuilder.Append(';');
					break;
				case 'r':
					stringBuilder.Append('\r');
					break;
				case 'n':
					stringBuilder.Append('\n');
					break;
				case '\\':
					stringBuilder.Append('\\');
					break;
				default:
					stringBuilder.Append(c);
					break;
				}
			}
			return stringBuilder.ToString();
		}

		private void SetStatus(string status)
		{
			lock (_stateLock)
			{
				if (_cancellation != null && !_cancellation.IsCancellationRequested)
				{
					_status = status;
				}
			}
		}

		private static string ShortError(string message)
		{
			if (string.IsNullOrWhiteSpace(message))
			{
				return "erreur inconnue";
			}
			if (message.Length > 80)
			{
				return message.Substring(0, 80) + "...";
			}
			return message;
		}
	}
	public sealed class TwitchMenu : MonoBehaviour
	{
		private static TwitchMenu _instance;

		private static readonly FieldInfo WindowShowingCursorField = typeof(GUIManager).GetField("<windowShowingCursor>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo WindowBlockingInputField = typeof(GUIManager).GetField("<windowBlockingInput>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);

		private bool _isOpen;

		private bool _enabledDraft;

		private string _channelDraft = string.Empty;

		private string _bannedWordsDraft = string.Empty;

		private int _userCooldownDraft;

		private Vector2 _bannedWordsScroll;

		private CursorLockMode _previousCursorLock;

		private bool _previousCursorVisible;

		private Texture2D _overlayTexture;

		private Texture2D _shadowTexture;

		private Texture2D _panelTexture;

		private Texture2D _headerTexture;

		private Texture2D _fieldTexture;

		private Texture2D _greenTexture;

		private Texture2D _orangeTexture;

		private Texture2D _mutedTexture;

		private GUIStyle _titleStyle;

		private GUIStyle _versionStyle;

		private GUIStyle _subtitleStyle;

		private GUIStyle _labelStyle;

		private GUIStyle _smallStyle;

		private GUIStyle _fieldStyle;

		private GUIStyle _areaStyle;

		private GUIStyle _primaryButtonStyle;

		private GUIStyle _secondaryButtonStyle;

		private GUIStyle _toggleOnStyle;

		private GUIStyle _toggleOffStyle;

		private GUIStyle _statusStyle;

		private GUIStyle _counterStyle;

		internal static bool IsOpen
		{
			get
			{
				if ((Object)(object)_instance != (Object)null)
				{
					return _instance._isOpen;
				}
				return false;
			}
		}

		private void Awake()
		{
			_instance = this;
			LoadDrafts();
			CreateTextures();
		}

		private void Update()
		{
			if (Input.GetKeyDown((KeyCode)291))
			{
				SetOpen(!_isOpen);
			}
			else if (_isOpen && Input.GetKeyDown((KeyCode)27))
			{
				SetOpen(open: false);
			}
			if (_isOpen)
			{
				ForceMenuInputState();
			}
		}

		private void LateUpdate()
		{
			if (_isOpen)
			{
				ForceMenuInputState();
			}
		}

		private void OnGUI()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_032b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_037d: Unknown result type (might be due to invalid IL or missing references)
			//IL_039a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_0482: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0500: Unknown result type (might be due to invalid IL or missing references)
			//IL_0512: Expected O, but got Unknown
			//IL_051d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0520: Unknown result type (might be due to invalid IL or missing references)
			//IL_0525: Unknown result type (might be due to invalid IL or missing references)
			//IL_0529: Unknown result type (might be due to invalid IL or missing references)
			//IL_052e: Unknown result type (might be due to invalid IL or missing references)
			//IL_054c: Unknown result type (might be due to invalid IL or missing references)
			//IL_058b: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_060b: Unknown result type (might be due to invalid IL or missing references)
			//IL_066a: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_072c: Unknown result type (might be due to invalid IL or missing references)
			if (_isOpen)
			{
				ForceMenuInputState();
				if (_titleStyle == null)
				{
					CreateStyles();
				}
				GUI.depth = -1000;
				GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)_overlayTexture);
				float num = Mathf.Min(700f, (float)Screen.width - 32f);
				float num2 = Mathf.Min(700f, (float)Screen.height - 24f);
				Rect val = default(Rect);
				((Rect)(ref val))..ctor(Mathf.Round(((float)Screen.width - num) * 0.5f), Mathf.Round(((float)Screen.height - num2) * 0.5f), num, num2);
				GUI.DrawTexture(new Rect(((Rect)(ref val)).x + 9f, ((Rect)(ref val)).y + 11f, ((Rect)(ref val)).width, ((Rect)(ref val)).height), (Texture)(object)_shadowTexture);
				GUI.DrawTexture(val, (Texture)(object)_panelTexture);
				GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 102f), (Texture)(object)_headerTexture);
				GUI.DrawTexture(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + 102f, ((Rect)(ref val)).width, 4f), (Texture)(object)_greenTexture);
				GUI.Label(new Rect(((Rect)(ref val)).x + 34f, ((Rect)(ref val)).y + 20f, ((Rect)(ref val)).width - 220f, 38f), "BINGBONG_FR  /  TWITCH LINK", _titleStyle);
				GUI.Label(new Rect(((Rect)(ref val)).x + ((Rect)(ref val)).width - 180f, ((Rect)(ref val)).y + 22f, 146f, 34f), "VERSION 1.0.0", _versionStyle);
				GUI.Label(new Rect(((Rect)(ref val)).x + 35f, ((Rect)(ref val)).y + 61f, ((Rect)(ref val)).width - 70f, 28f), "Chaque joueur relie son tchat. Bing Bong parle pour toute la partie.", _subtitleStyle);
				float num3 = ((Rect)(ref val)).x + 36f;
				float num4 = ((Rect)(ref val)).width - 72f;
				float num5 = ((Rect)(ref val)).y + 128f;
				GUI.Label(new Rect(num3, num5, num4, 24f), "INTERACTION DU TCHAT", _labelStyle);
				num5 += 30f;
				GUIStyle val2 = (_enabledDraft ? _toggleOnStyle : _toggleOffStyle);
				string text = (_enabledDraft ? "●  ACTIVE  -  !bingbong est autorise" : "○  DESACTIVE  -  les messages sont ignores");
				if (GUI.Button(new Rect(num3, num5, num4, 44f), text, val2))
				{
					_enabledDraft = !_enabledDraft;
				}
				num5 += 65f;
				GUI.Label(new Rect(num3, num5, num4, 24f), "CHAINE TWITCH", _labelStyle);
				num5 += 29f;
				_channelDraft = GUI.TextField(new Rect(num3, num5, num4, 42f), _channelDraft, 80, _fieldStyle);
				num5 += 47f;
				GUI.Label(new Rect(num3 + 3f, num5, num4, 22f), "Nom de la chaine, lien Twitch ou #chaine", _smallStyle);
				num5 += 38f;
				GUI.Label(new Rect(num3, num5, num4, 24f), "DELAI PAR SPECTATEUR", _labelStyle);
				num5 += 29f;
				GUI.DrawTexture(new Rect(num3, num5, num4, 42f), (Texture)(object)_fieldTexture);
				if (GUI.Button(new Rect(num3, num5, 52f, 42f), "-", _secondaryButtonStyle))
				{
					_userCooldownDraft = Mathf.Max(0, _userCooldownDraft - 1);
				}
				string text2 = ((_userCooldownDraft == 0) ? "DESACTIVE" : (_userCooldownDraft + ((_userCooldownDraft == 1) ? " SECONDE" : " SECONDES")));
				GUI.Label(new Rect(num3 + 58f, num5, num4 - 116f, 42f), text2, _counterStyle);
				if (GUI.Button(new Rect(num3 + num4 - 52f, num5, 52f, 42f), "+", _secondaryButtonStyle))
				{
					_userCooldownDraft = Mathf.Min(300, _userCooldownDraft + 1);
				}
				num5 += 47f;
				GUI.Label(new Rect(num3 + 3f, num5, num4, 22f), "Attente imposee a chaque utilisateur entre deux commandes", _smallStyle);
				num5 += 38f;
				GUI.Label(new Rect(num3, num5, num4, 24f), "LISTE DE MOTS BANNIS", _labelStyle);
				num5 += 29f;
				Rect val3 = default(Rect);
				((Rect)(ref val3))..ctor(num3, num5, num4, 84f);
				Rect val4 = default(Rect);
				((Rect)(ref val4))..ctor(0f, 0f, num4 - 22f, Mathf.Max(78f, _areaStyle.CalcHeight(new GUIContent(_bannedWordsDraft), num4 - 28f)));
				_bannedWordsScroll = GUI.BeginScrollView(val3, _bannedWordsScroll, val4, false, true);
				_bannedWordsDraft = GUI.TextArea(new Rect(0f, 0f, ((Rect)(ref val4)).width, ((Rect)(ref val4)).height), _bannedWordsDraft, 2000, _areaStyle);
				GUI.EndScrollView();
				num5 += 90f;
				GUI.Label(new Rect(num3 + 3f, num5, num4, 22f), "Un mot par ligne, ou separes par des virgules", _smallStyle);
				float num6 = ((Rect)(ref val)).y + ((Rect)(ref val)).height - 100f;
				float num7 = 164f;
				if (GUI.Button(new Rect(num3, num6, num7, 46f), "TESTER LA VOIX", _secondaryButtonStyle))
				{
					TwitchRuntime.Instance?.TestVoice();
				}
				if (GUI.Button(new Rect(num3 + num7 + 14f, num6, num4 - num7 - 14f, 46f), "ENREGISTRER ET CONNECTER", _primaryButtonStyle))
				{
					SaveDrafts();
				}
				Rect val5 = default(Rect);
				((Rect)(ref val5))..ctor(num3, ((Rect)(ref val)).y + ((Rect)(ref val)).height - 42f, num4, 25f);
				GUI.DrawTexture(new Rect(((Rect)(ref val5)).x, ((Rect)(ref val5)).y + 2f, 8f, 8f), (Texture)(object)StatusColorTexture());
				string text3 = TwitchRuntime.Instance?.ConnectionStatus ?? "Initialisation...";
				string text4 = TwitchRuntime.Instance?.ActivityStatus ?? string.Empty;
				GUI.Label(new Rect(((Rect)(ref val5)).x + 16f, ((Rect)(ref val5)).y - 3f, ((Rect)(ref val5)).width - 120f, ((Rect)(ref val5)).height), text3 + "  |  " + text4, _statusStyle);
				GUI.Label(new Rect(((Rect)(ref val5)).x + ((Rect)(ref val5)).width - 92f, ((Rect)(ref val5)).y - 3f, 92f, ((Rect)(ref val5)).height), "F10  FERMER", _smallStyle);
			}
		}

		private void SetOpen(bool open)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (_isOpen != open)
			{
				_isOpen = open;
				if (open)
				{
					LoadDrafts();
					_previousCursorLock = Cursor.lockState;
					_previousCursorVisible = Cursor.visible;
					ForceMenuInputState();
				}
				else
				{
					Cursor.lockState = _previousCursorLock;
					Cursor.visible = _previousCursorVisible;
				}
			}
		}

		private static void ForceMenuInputState()
		{
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
			ApplyGameUiState(GUIManager.instance);
		}

		internal static void ApplyGameUiState(GUIManager manager)
		{
			if (!((Object)(object)manager == (Object)null))
			{
				WindowShowingCursorField?.SetValue(manager, true);
				WindowBlockingInputField?.SetValue(manager, true);
			}
		}

		private void LoadDrafts()
		{
			_enabledDraft = Plugin.TwitchInteractionEnabled.Value;
			_channelDraft = Plugin.TwitchChannel.Value;
			_bannedWordsDraft = string.Join("\n", TwitchRuntime.ParseBannedWords(Plugin.TwitchBannedWords.Value));
			_userCooldownDraft = Plugin.TwitchUserCooldownSeconds.Value;
		}

		private void SaveDrafts()
		{
			string text = TwitchRuntime.NormalizeChannel(_channelDraft);
			string[] value = TwitchRuntime.ParseBannedWords(_bannedWordsDraft);
			Plugin.TwitchInteractionEnabled.Value = _enabledDraft;
			Plugin.TwitchChannel.Value = text;
			Plugin.TwitchBannedWords.Value = string.Join(", ", value);
			Plugin.TwitchUserCooldownSeconds.Value = Mathf.Clamp(_userCooldownDraft, 0, 300);
			Plugin.SaveConfiguration();
			_channelDraft = text;
			_bannedWordsDraft = string.Join("\n", value);
			TwitchRuntime.Instance?.ApplyConfiguration();
		}

		private Texture2D StatusColorTexture()
		{
			string text = TwitchRuntime.Instance?.ConnectionStatus ?? string.Empty;
			if (text.StartsWith("Connecte", StringComparison.OrdinalIgnoreCase))
			{
				return _greenTexture;
			}
			if (text.IndexOf("Connexion", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Reconnexion", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return _orangeTexture;
			}
			return _mutedTexture;
		}

		private void CreateTextures()
		{
			//IL_0015: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			_overlayTexture = MakeTexture(new Color(0.015f, 0.027f, 0.032f, 0.78f));
			_shadowTexture = MakeTexture(new Color(0f, 0f, 0f, 0.55f));
			_panelTexture = MakeTexture(new Color(0.055f, 0.082f, 0.088f, 0.99f));
			_headerTexture = MakeTexture(new Color(0.075f, 0.13f, 0.13f, 1f));
			_fieldTexture = MakeTexture(new Color(0.025f, 0.045f, 0.048f, 1f));
			_greenTexture = MakeTexture(new Color(0.52f, 0.78f, 0.24f, 1f));
			_orangeTexture = MakeTexture(new Color(0.96f, 0.58f, 0.17f, 1f));
			_mutedTexture = MakeTexture(new Color(0.42f, 0.49f, 0.48f, 1f));
		}

		private void CreateStyles()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_004a: Expected O, but got Unknown
			//IL_0055: 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: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_008d: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Expected O, but got Unknown
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Expected O, but got Unknown
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Expected O, but got Unknown
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				fontSize = 25,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)3
			};
			val.normal.textColor = new Color(0.91f, 0.98f, 0.83f);
			_titleStyle = val;
			GUIStyle val2 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 14,
				alignment = (TextAnchor)3
			};
			val2.normal.textColor = new Color(0.66f, 0.76f, 0.72f);
			_subtitleStyle = val2;
			GUIStyle val3 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 12,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)5
			};
			val3.normal.textColor = new Color(0.52f, 0.78f, 0.24f, 1f);
			_versionStyle = val3;
			GUIStyle val4 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 12,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)3
			};
			val4.normal.textColor = new Color(0.72f, 0.85f, 0.48f);
			_labelStyle = val4;
			GUIStyle val5 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 11,
				alignment = (TextAnchor)3,
				clipping = (TextClipping)1
			};
			val5.normal.textColor = new Color(0.5f, 0.59f, 0.58f);
			_smallStyle = val5;
			GUIStyle val6 = new GUIStyle(_smallStyle)
			{
				fontSize = 11
			};
			val6.normal.textColor = new Color(0.72f, 0.8f, 0.77f);
			_statusStyle = val6;
			_fieldStyle = CreateFieldStyle(16, 12, (TextAnchor)3);
			_areaStyle = CreateFieldStyle(14, 10, (TextAnchor)0);
			_areaStyle.wordWrap = true;
			GUIStyle val7 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 14,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4
			};
			val7.normal.textColor = new Color(0.9f, 0.94f, 0.9f);
			_counterStyle = val7;
			_primaryButtonStyle = CreateButtonStyle(_greenTexture, new Color(0.07f, 0.12f, 0.08f), 13);
			_secondaryButtonStyle = CreateButtonStyle(_fieldTexture, new Color(0.76f, 0.84f, 0.79f), 12);
			_toggleOnStyle = CreateButtonStyle(_greenTexture, new Color(0.07f, 0.12f, 0.08f), 13);
			_toggleOnStyle.alignment = (TextAnchor)3;
			_toggleOnStyle.padding = new RectOffset(18, 12, 0, 0);
			_toggleOffStyle = CreateButtonStyle(_fieldTexture, new Color(0.66f, 0.7f, 0.68f), 13);
			_toggleOffStyle.alignment = (TextAnchor)3;
			_toggleOffStyle.padding = new RectOffset(18, 12, 0, 0);
		}

		private GUIStyle CreateFieldStyle(int fontSize, int padding, TextAnchor alignment)
		{
			//IL_000a: 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_0016: 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: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			//IL_00b5: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.textField)
			{
				fontSize = fontSize,
				alignment = alignment,
				wordWrap = false
			};
			val.normal.background = _fieldTexture;
			val.normal.textColor = new Color(0.9f, 0.94f, 0.9f);
			val.focused.background = _fieldTexture;
			val.focused.textColor = Color.white;
			val.hover.background = _fieldTexture;
			val.hover.textColor = Color.white;
			val.border = new RectOffset(0, 0, 0, 0);
			val.padding = new RectOffset(padding, padding, padding, padding);
			return val;
		}

		private static GUIStyle CreateButtonStyle(Texture2D background, Color textColor, int fontSize)
		{
			//IL_000a: 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_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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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)
			//IL_003c: 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)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_0084: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.button)
			{
				fontSize = fontSize,
				fontStyle = (FontStyle)1,
				alignment = (TextAnchor)4
			};
			val.normal.background = background;
			val.normal.textColor = textColor;
			val.hover.background = background;
			val.hover.textColor = Color.white;
			val.active.background = background;
			val.active.textColor = Color.white;
			val.border = new RectOffset(0, 0, 0, 0);
			return val;
		}

		private static Texture2D MakeTexture(Color color)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false)
			{
				hideFlags = (HideFlags)61,
				wrapMode = (TextureWrapMode)1
			};
			val.SetPixel(0, 0, color);
			val.Apply(false, true);
			return val;
		}

		private void OnDestroy()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_instance == (Object)(object)this)
			{
				_instance = null;
			}
			if (_isOpen)
			{
				Cursor.lockState = _previousCursorLock;
				Cursor.visible = _previousCursorVisible;
			}
			Texture2D[] array = (Texture2D[])(object)new Texture2D[8] { _overlayTexture, _shadowTexture, _panelTexture, _headerTexture, _fieldTexture, _greenTexture, _orangeTexture, _mutedTexture };
			foreach (Texture2D val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}
	}
	public sealed class TwitchRuntime : MonoBehaviour
	{
		private sealed class TwitchSpeechRequest
		{
			internal readonly int ViewId;

			internal readonly string Spectator;

			internal readonly string Text;

			internal TwitchSpeechRequest(int viewId, string spectator, string text)
			{
				ViewId = viewId;
				Spectator = spectator;
				Text = text;
			}
		}

		private const byte SpeechRequestEventCode = 197;

		private const byte SpeechEventCode = 198;

		private const string SpeechEventSignature = "BINGBONG_FR_TTS_V2";

		private const string Command = "!bingbong";

		private const float SpeechHearingDistance = 32f;

		private const float SubtitleReferenceDistance = 7f;

		private const float SubtitleMinimumScale = 0.22f;

		private static TwitchRuntime _instance;

		private static Action_AskBingBong _lastUsedBingBong;

		private readonly ConcurrentQueue<TwitchChatMessage> _incomingChat = new ConcurrentQueue<TwitchChatMessage>();

		private readonly ConcurrentQueue<TwitchSpeechRequest> _speechQueue = new ConcurrentQueue<TwitchSpeechRequest>();

		private readonly HashSet<string> _recentMessageIds = new HashSet<string>(StringComparer.Ordinal);

		private readonly Queue<string> _recentMessageOrder = new Queue<string>();

		private readonly Dictionary<string, float> _lastCommandByUser = new Dictionary<string, float>(StringComparer.Ordinal);

		private TwitchIrcClient _twitchClient;

		private string[] _bannedWords = Array.Empty<string>();

		private float _nextConnectionCheck;

		private bool _speaking;

		private bool _photonEventsSubscribed;

		private int _speechGeneration;

		private Action_AskBingBong _activeSpeechAction;

		private GameObject _activeVoiceObject;

		private AudioClip _activeClip;

		private int _activeViewId;

		private Guid _activeItemGuid;

		private string _activeSubtitleText = string.Empty;

		private Vector3 _activeWorldPosition;

		private Vector3 _activeSubtitleWorldPosition;

		private GUIStyle _subtitleStyle;

		private GUIStyle _subtitleShadowStyle;

		private string _activityStatus = "En attente d'une commande !bingbong";

		internal static TwitchRuntime Instance => _instance;

		internal string ConnectionStatus
		{
			get
			{
				if (!Plugin.TwitchInteractionEnabled.Value)
				{
					return "Interaction du tchat desactivee";
				}
				if (string.IsNullOrEmpty(NormalizeChannel(Plugin.TwitchChannel.Value)))
				{
					return "Aucune chaine configuree";
				}
				return _twitchClient?.Status ?? "Deconnecte";
			}
		}

		internal string ActivityStatus => _activityStatus;

		private void Awake()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			_instance = this;
			_twitchClient = new TwitchIrcClient(delegate(TwitchChatMessage message)
			{
				_incomingChat.Enqueue(message);
			});
			_bannedWords = ParseBannedWords(Plugin.TwitchBannedWords.Value);
			TrySubscribePhotonEvents();
			MaintainTwitchConnection(forceRestart: true);
		}

		private void OnDestroy()
		{
			if ((Object)(object)_instance == (Object)(object)this)
			{
				if (_photonEventsSubscribed && PhotonNetwork.NetworkingClient != null)
				{
					PhotonNetwork.NetworkingClient.EventReceived -= OnPhotonEvent;
				}
				_twitchClient?.Dispose();
				_instance = null;
			}
		}

		private void Update()
		{
			TrySubscribePhotonEvents();
			if (Time.unscaledTime >= _nextConnectionCheck)
			{
				_nextConnectionCheck = Time.unscaledTime + 1f;
				MaintainTwitchConnection(forceRestart: false);
			}
			ProcessIncomingChat();
			if (!_speaking && _speechQueue.TryDequeue(out var result))
			{
				((MonoBehaviour)this).StartCoroutine(SpeakMessage(result));
			}
		}

		private void LateUpdate()
		{
			UpdateActivePresentationPosition();
		}

		internal void ApplyConfiguration()
		{
			_bannedWords = ParseBannedWords(Plugin.TwitchBannedWords.Value);
			_lastCommandByUser.Clear();
			MaintainTwitchConnection(forceRestart: true);
		}

		internal void TestVoice()
		{
			Action_AskBingBong val = FindBestBingBong();
			if ((Object)(object)val == (Object)null)
			{
				_activityStatus = "Aucun Bing Bong actif pour tester la voix";
				return;
			}
			int viewId = GetViewId(val);
			_speechQueue.Enqueue(new TwitchSpeechRequest(viewId, "Bing Bong", "Connexion au tchat prete."));
			_activityStatus = "Test de la voix en cours...";
		}

		internal static void RecordUsedBingBong(Action_AskBingBong action)
		{
			if ((Object)(object)action != (Object)null)
			{
				_lastUsedBingBong = action;
			}
		}

		internal static void StopTwitchPresentation(Action_AskBingBong action)
		{
			_instance?.StopActiveTwitchPresentation(action);
		}

		internal static void TrackRecreatedItem(Item item)
		{
			_instance?.AdoptTrackedItem(item);
		}

		internal static string NormalizeChannel(string rawChannel)
		{
			if (string.IsNullOrWhiteSpace(rawChannel))
			{
				return string.Empty;
			}
			string text = rawChannel.Trim();
			if (Uri.TryCreate(text, UriKind.Absolute, out Uri result))
			{
				string[] array = result.AbsolutePath.Split(new char[1] { '/' }, StringSplitOptions.RemoveEmptyEntries);
				text = ((array.Length != 0) ? array[0] : string.Empty);
			}
			text = text.Trim().TrimStart('#').ToLowerInvariant();
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			string text2 = text;
			foreach (char c in text2)
			{
				if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_')
				{
					stringBuilder.Append(c);
				}
			}
			if (stringBuilder.Length > 25)
			{
				return stringBuilder.ToString(0, 25);
			}
			return stringBuilder.ToString();
		}

		internal static string[] ParseBannedWords(string value)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return Array.Empty<string>();
			}
			return (from word in value.Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
				select word.Trim() into word
				where word.Length > 0
				select word).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToArray();
		}

		private void MaintainTwitchConnection(bool forceRestart)
		{
			bool value = Plugin.TwitchInteractionEnabled.Value;
			string text = NormalizeChannel(Plugin.TwitchChannel.Value);
			if (!value || text.Length <= 0)
			{
				if (_twitchClient.IsRunning)
				{
					string status = ((!value) ? "Interaction du tchat desactivee" : "Aucune chaine configuree");
					_twitchClient.Disconnect(status);
				}
			}
			else if (forceRestart || !_twitchClient.IsRunning || !string.Equals(_twitchClient.Channel, text, StringComparison.Ordinal))
			{
				_twitchClient.Connect(text);
			}
		}

		private void TrySubscribePhotonEvents()
		{
			if (!_photonEventsSubscribed && PhotonNetwork.NetworkingClient != null)
			{
				PhotonNetwork.NetworkingClient.EventReceived += OnPhotonEvent;
				_photonEventsSubscribed = true;
			}
		}

		private void ProcessIncomingChat()
		{
			TwitchChatMessage result;
			while (_incomingChat.TryDequeue(out result))
			{
				if (!Plugin.TwitchInteractionEnabled.Value || !TryExtractCommand(result.Text, out var spokenText) || !RememberMessage(result.Id))
				{
					continue;
				}
				spokenText = SanitizeForGame(spokenText);
				string text = SanitizeForGame(result.DisplayName, 32);
				if (spokenText.Length == 0 || ContainsBannedWord(spokenText) || ContainsBannedWord(text))
				{
					_activityStatus = "Message bloque par la liste de mots bannis";
					continue;
				}
				int value = Plugin.TwitchUserCooldownSeconds.Value;
				string userKey = GetUserKey(result);
				if (value > 0 && _lastCommandByUser.TryGetValue(userKey, out var value2))
				{
					float num = Time.unscaledTime - value2;
					if (num < (float)value)
					{
						_activityStatus = text + " doit attendre encore " + Mathf.CeilToInt((float)value - num) + " s";
						continue;
					}
				}
				Action_AskBingBong val = FindBestBingBong();
				if ((Object)(object)val == (Object)null)
				{
					_activityStatus = "Commande ignoree : aucun Bing Bong actif";
					continue;
				}
				_lastCommandByUser[userKey] = Time.unscaledTime;
				BroadcastSpeech(GetViewId(val), text, spokenText);
			}
		}

		private void BroadcastSpeech(int viewId, string spectator, string text)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.InRoom)
			{
				_speechQueue.Enqueue(new TwitchSpeechRequest(viewId, spectator, text));
				return;
			}
			object[] array = new object[4] { "BINGBONG_FR_TTS_V2", viewId, spectator, text };
			bool isMasterClient = PhotonNetwork.IsMasterClient;
			int num = (isMasterClient ? 198 : 197);
			RaiseEventOptions val = new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)(isMasterClient ? 1 : 2)
			};
			if (!PhotonNetwork.RaiseEvent((byte)num, (object)array, val, SendOptions.SendReliable))
			{
				_activityStatus = "Impossible de synchroniser le message avec la partie";
				Plugin.ModLog.LogWarning((object)"L'evenement Twitch n'a pas pu etre envoye par Photon.");
			}
			else
			{
				_activityStatus = (isMasterClient ? (spectator + " : " + text) : "Commande envoyee a l'hote pour synchronisation");
			}
		}

		private void OnPhotonEvent(EventData eventData)
		{
			if ((eventData.Code != 197 && eventData.Code != 198) || !(eventData.CustomData is object[] array) || array.Length != 4 || !string.Equals(array[0] as string, "BINGBONG_FR_TTS_V2", StringComparison.Ordinal))
			{
				return;
			}
			if (!IsCurrentPartyMember(eventData.Sender))
			{
				Plugin.ModLog.LogWarning((object)"Evenement Twitch ignore car son emetteur n'est pas dans la partie.");
				return;
			}
			int viewId;
			try
			{
				viewId = Convert.ToInt32(array[1]);
			}
			catch
			{
				return;
			}
			string text = SanitizeForGame((array[2] as string) ?? "spectateur", 32);
			string text2 = SanitizeForGame((array[3] as string) ?? string.Empty);
			if (text2.Length == 0)
			{
				return;
			}
			if (eventData.Code == 197)
			{
				if (PhotonNetwork.IsMasterClient)
				{
					BroadcastSpeech(viewId, text, text2);
				}
			}
			else if (PhotonNetwork.InRoom && PhotonNetwork.MasterClient != null && eventData.Sender != PhotonNetwork.MasterClient.ActorNumber)
			{
				Plugin.ModLog.LogWarning((object)"Message vocal ignore car il n'a pas ete relaye par l'hote.");
			}
			else
			{
				_speechQueue.Enqueue(new TwitchSpeechRequest(viewId, text, text2));
				_activityStatus = text + " : " + text2;
			}
		}

		private static bool IsCurrentPartyMember(int actorNumber)
		{
			if (PhotonNetwork.InRoom)
			{
				if (PhotonNetwork.CurrentRoom != null && actorNumber > 0)
				{
					return PhotonNetwork.CurrentRoom.Players.ContainsKey(actorNumber);
				}
				return false;
			}
			return true;
		}

		private IEnumerator SpeakMessage(TwitchSpeechRequest request)
		{
			_speaking = true;
			int generation = ++_speechGeneration;
			Action_AskBingBong action = FindBingBongByViewId(request.ViewId) ?? FindBestBingBong();
			if ((Object)(object)action == (Object)null)
			{
				_activityStatus = "Lecture annulee : Bing Bong est introuvable";
				_speaking = false;
				yield break;
			}
			_activeSpeechAction = action;
			_activeViewId = request.ViewId;
			Item speechItem = FindPresentationItem(action, request.ViewId, Guid.Empty);
			_activeItemGuid = (((Object)(object)speechItem != (Object)null && speechItem.data != null) ? speechItem.data.guid : Guid.Empty);
			string wavPath = null;
			string synthesisError = null;
			yield return ToySpeechEngine.Synthesize(request.Text, delegate(string path, string error)
			{
				wavPath = path;
				synthesisError = error;
			});
			if (generation != _speechGeneration)
			{
				ToySpeechEngine.SafeDelete(wavPath);
				_speaking = false;
				yield break;
			}
			if (!string.IsNullOrEmpty(synthesisError))
			{
				_activityStatus = synthesisError;
				Plugin.ModLog.LogError((object)synthesisError);
				_speaking = false;
				yield break;
			}
			AudioClip clip = null;
			UnityWebRequest audioRequest = UnityWebRequestMultimedia.GetAudioClip(new Uri(wavPath).AbsoluteUri, (AudioType)20);
			try
			{
				yield return audioRequest.SendWebRequest();
				if ((int)audioRequest.result == 1)
				{
					clip = DownloadHandlerAudioClip.GetContent(audioRequest);
				}
				else
				{
					_activityStatus = "Impossible de charger la voix generee";
					Plugin.ModLog.LogError((object)("Chargement TTS impossible : " + audioRequest.error));
				}
			}
			finally
			{
				((IDisposable)audioRequest)?.Dispose();
			}
			ToySpeechEngine.SafeDelete(wavPath);
			if (generation != _speechGeneration)
			{
				if ((Object)(object)clip != (Object)null)
				{
					Object.Destroy((Object)(object)clip);
				}
				_speaking = false;
				yield break;
			}
			if ((Object)(object)clip == (Object)null)
			{
				_speaking = false;
				yield break;
			}
			_activeClip = clip;
			string activeSubtitleText = request.Spectator + " : " + request.Text;
			_activeSubtitleText = activeSubtitleText;
			if ((Object)(object)action.squishAnim != (Object)null)
			{
				action.squishAnim.SetTrigger("Squish");
			}
			GameObject voiceObject = new GameObject("BingBong_FR_TwitchVoice");
			if (!TryGetPresentationPosition(speechItem, action, out _activeWorldPosition))
			{
				_activeWorldPosition = ((Component)action).transform.position;
			}
			if (!TryGetSubtitleAnchorPosition(speechItem, action, out _activeSubtitleWorldPosition))
			{
				_activeSubtitleWorldPosition = _activeWorldPosition + Vector3.up * 0.55f;
			}
			voiceObject.transform.position = _activeWorldPosition;
			AudioSource source = voiceObject.AddComponent<AudioSource>();
			source.clip = clip;
			source.playOnAwake = false;
			source.loop = false;
			source.spatialBlend = 1f;
			source.minDistance = 1.2f;
			source.maxDistance = 32f;
			source.rolloffMode = (AudioRolloffMode)0;
			source.pitch = 0.88f;
			source.volume = 0.9f;
			if ((Object)(object)action.source != (Object)null)
			{
				source.outputAudioMixerGroup = action.source.outputAudioMixerGroup;
			}
			_activeVoiceObject = voiceObject;
			GameObject val = new GameObject("BingBong_FR_MetalLayer");
			val.transform.SetParent(voiceObject.transform, false);
			AudioSource obj = val.AddComponent<AudioSource>();
			obj.clip = clip;
			obj.playOnAwake = false;
			obj.loop = false;
			obj.spatialBlend = 1f;
			obj.minDistance = source.minDistance;
			obj.maxDistance = source.maxDistance;
			obj.rolloffMode = source.rolloffMode;
			obj.pitch = 0.92f;
			obj.volume = 0.26f;
			obj.outputAudioMixerGroup = source.outputAudioMixerGroup;
			AudioHighPassFilter obj2 = voiceObject.AddComponent<AudioHighPassFilter>();
			obj2.cutoffFrequency = 220f;
			obj2.highpassResonanceQ = 1.45f;
			voiceObject.AddComponent<AudioDistortionFilter>().distortionLevel = 0.24f;
			AudioChorusFilter obj3 = voiceObject.AddComponent<AudioChorusFilter>();
			obj3.dryMix = 0.68f;
			obj3.wetMix1 = 0.38f;
			obj3.wetMix2 = 0.16f;
			obj3.wetMix3 = 0.07f;
			obj3.delay = 12f;
			obj3.rate = 0.6f;
			obj3.depth = 0.65f;
			AudioLowPassFilter obj4 = voiceObject.AddComponent<AudioLowPassFilter>();
			obj4.cutoffFrequency = 4800f;
			obj4.lowpassResonanceQ = 1.3f;
			AudioEchoFilter obj5 = voiceObject.AddComponent<AudioEchoFilter>();
			obj5.delay = 31f;
			obj5.decayRatio = 0.12f;
			obj5.dryMix = 0.84f;
			obj5.wetMix = 0.22f;
			source.Play();
			obj.Play();
			_activityStatus = request.Spectator + " fait parler Bing Bong";
			while (generation == _speechGeneration && (Object)(object)source != (Object)null && source.isPlaying)
			{
				Action_AskBingBong activeSpeechAction = _activeSpeechAction;
				if ((Object)(object)activeSpeechAction != (Object)null && (Object)(object)activeSpeechAction.anim != (Object)null)
				{
					float num = 0.2f + Mathf.PingPong(Time.unscaledTime * 10f, 0.75f);
					activeSpeechAction.anim.SetFloat(Action_AskBingBong.MOUTHBLENDID, num);
				}
				yield return null;
			}
			if ((Object)(object)_activeSpeechAction != (Object)null && (Object)(object)_activeSpeechAction.anim != (Object)null)
			{
				_activeSpeechAction.anim.SetFloat(Action_AskBingBong.MOUTHBLENDID, 0f);
			}
			if ((Object)(object)voiceObject != (Object)null)
			{
				Object.Destroy((Object)(object)voiceObject);
			}
			Object.Destroy((Object)(object)clip);
			ClearActivePresentationReferences(generation);
			if (generation == _speechGeneration)
			{
				_activityStatus = "En attente d'une commande !bingbong";
			}
			_speaking = false;
		}

		private void StopActiveTwitchPresentation(Action_AskBingBong action)
		{
			Item val = (((Object)(object)action != (Object)null) ? ((Component)action).GetComponentInParent<Item>() : null);
			if (!((Object)(object)action != (Object)null) || (!((Object)(object)_activeSpeechAction == (Object)(object)action) && (!(_activeItemGuid != Guid.Empty) || !((Object)(object)val != (Object)null) || val.data == null || !(val.data.guid == _activeItemGuid))))
			{
				return;
			}
			_speechGeneration++;
			if ((Object)(object)_activeVoiceObject != (Object)null)
			{
				AudioSource component = _activeVoiceObject.GetComponent<AudioSource>();
				if (component != null)
				{
					component.Stop();
				}
				Object.Destroy((Object)(object)_activeVoiceObject);
			}
			if ((Object)(object)_activeClip != (Object)null)
			{
				Object.Destroy((Object)(object)_activeClip);
			}
			if ((Object)(object)action.anim != (Object)null)
			{
				action.anim.SetFloat(Action_AskBingBong.MOUTHBLENDID, 0f);
			}
			_activeSpeechAction = null;
			_activeVoiceObject = null;
			_activeClip = null;
			_activeViewId = 0;
			_activeItemGuid = Guid.Empty;
			_activeSubtitleText = string.Empty;
			_activityStatus = "Activation manuelle : repliques preenregistrees";
		}

		private void ClearActivePresentationReferences(int generation)
		{
			if (generation == _speechGeneration)
			{
				_activeSpeechAction = null;
				_activeVoiceObject = null;
				_activeClip = null;
				_activeViewId = 0;
				_activeItemGuid = Guid.Empty;
				_activeSubtitleText = string.Empty;
			}
		}

		private void UpdateActivePresentationPosition()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_activeVoiceObject == (Object)null)
			{
				return;
			}
			Item val = FindPresentationItem(_activeSpeechAction, _activeViewId, _activeItemGuid);
			if (!TryGetPresentationPosition(val, _activeSpeechAction, out var position))
			{
				return;
			}
			if ((Object)(object)val != (Object)null)
			{
				Action_AskBingBong componentInChildren = ((Component)val).GetComponentInChildren<Action_AskBingBong>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					_activeSpeechAction = componentInChildren;
				}
				PhotonView val2 = ((Component)val).GetComponent<PhotonView>() ?? ((Component)val).GetComponentInParent<PhotonView>();
				if ((Object)(object)val2 != (Object)null)
				{
					_activeViewId = val2.ViewID;
				}
			}
			_activeWorldPosition = position;
			_activeVoiceObject.transform.position = position;
			if (!TryGetSubtitleAnchorPosition(val, _activeSpeechAction, out _activeSubtitleWorldPosition))
			{
				_activeSubtitleWorldPosition = position + Vector3.up * 0.55f;
			}
		}

		private void AdoptTrackedItem(Item item)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_activeVoiceObject == (Object)null) && !(_activeItemGuid == Guid.Empty) && !((Object)(object)item == (Object)null) && item.data != null && !(item.data.guid != _activeItemGuid))
			{
				Action_AskBingBong componentInChildren = ((Component)item).GetComponentInChildren<Action_AskBingBong>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					_activeSpeechAction = componentInChildren;
				}
				PhotonView val = ((Component)item).GetComponent<PhotonView>() ?? ((Component)item).GetComponentInParent<PhotonView>();
				if ((Object)(object)val != (Object)null)
				{
					_activeViewId = val.ViewID;
				}
				if (TryGetPresentationPosition(item, _activeSpeechAction, out var position))
				{
					_activeWorldPosition = position;
					_activeVoiceObject.transform.position = position;
				}
				if (!TryGetSubtitleAnchorPosition(item, _activeSpeechAction, out _activeSubtitleWorldPosition))
				{
					_activeSubtitleWorldPosition = _activeWorldPosition + Vector3.up * 0.55f;
				}
			}
		}

		private void OnGUI()
		{
			//IL_005e: 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)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Expected O, but got Unknown
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Expected O, but got Unknown
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(_activeSubtitleText) || (Object)(object)_activeVoiceObject == (Object)null)
			{
				return;
			}
			Camera val = Camera.main ?? Camera.current;
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			AudioSource component = _activeVoiceObject.GetComponent<AudioSource>();
			float num = (((Object)(object)component != (Object)null) ? component.maxDistance : 32f);
			float num2 = Vector3.Distance(((Component)val).transform.position, _activeWorldPosition);
			if (num2 >= num)
			{
				return;
			}
			float num3 = num * 0.75f;
			float num4 = 1f - Mathf.InverseLerp(num3, num, num2);
			Vector3 val2 = val.WorldToScreenPoint(_activeSubtitleWorldPosition);
			if (!(val2.z <= 0f))
			{
				float num5 = (val.orthographic ? 1f : Mathf.Clamp(7f / Mathf.Max(val2.z, 0.01f), 0.22f, 1f));
				if (_subtitleStyle == null)
				{
					GUIStyle val3 = new GUIStyle(GUI.skin.label)
					{
						alignment = (TextAnchor)4,
						fontSize = 18,
						fontStyle = (FontStyle)1,
						wordWrap = true,
						richText = false
					};
					val3.normal.textColor = new Color(0.86f, 1f, 0.68f, 1f);
					_subtitleStyle = val3;
					GUIStyle val4 = new GUIStyle(_subtitleStyle);
					val4.normal.textColor = new Color(0f, 0f, 0f, 0.92f);
					_subtitleShadowStyle = val4;
				}
				int fontSize = Mathf.Max(6, Mathf.RoundToInt(18f * num5));
				_subtitleStyle.fontSize = fontSize;
				_subtitleShadowStyle.fontSize = fontSize;
				float num6 = Mathf.Min(540f, (float)Screen.width - 32f);
				float num7 = Mathf.Max(80f, num6 * num5);
				float num8 = Mathf.Max(14f, _subtitleStyle.CalcHeight(new GUIContent(_activeSubtitleText), num7));
				float num9 = val2.x - num7 * 0.5f;
				float num10 = (float)Screen.height - val2.y - num8;
				if (!(num9 + num7 < 0f) && !(num9 > (float)Screen.width) && !(num10 + num8 < 0f) && !(num10 > (float)Screen.height))
				{
					GUI.depth = -900;
					Color color = GUI.color;
					GUI.color = new Color(1f, 1f, 1f, num4);
					float num11 = Mathf.Max(1f, 2f * num5);
					GUI.Label(new Rect(num9 + num11, num10 + num11, num7, num8), _activeSubtitleText, _subtitleShadowStyle);
					GUI.Label(new Rect(num9, num10, num7, num8), _activeSubtitleText, _subtitleStyle);
					GUI.color = color;
				}
			}
		}

		private static string GetUserKey(TwitchChatMessage chatMessage)
		{
			if (!string.IsNullOrWhiteSpace(chatMessage.UserId))
			{
				return "id:" + chatMessage.UserId;
			}
			return "name:" + (chatMessage.DisplayName ?? "spectateur").Trim().ToLowerInvariant();
		}

		private bool ContainsBannedWord(string text)
		{
			string[] bannedWords = _bannedWords;
			foreach (string value in bannedWords)
			{
				if (text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private bool RememberMessage(string messageId)
		{
			if (string.IsNullOrEmpty(messageId))
			{
				return true;
			}
			if (!_recentMessageIds.Add(messageId))
			{
				return false;
			}
			_recentMessageOrder.Enqueue(messageId);
			while (_recentMessageOrder.Count > 256)
			{
				_recentMessageIds.Remove(_recentMessageOrder.Dequeue());
			}
			return true;
		}

		private static bool TryExtractCommand(string chatText, out string spokenText)
		{
			spokenText = string.Empty;
			if (string.IsNullOrWhiteSpace(chatText) || !chatText.StartsWith("!bingbong", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (chatText.Length > "!bingbong".Length && !char.IsWhiteSpace(chatText["!bingbong".Length]))
			{
				return false;
			}
			spokenText = ((chatText.Length == "!bingbong".Length) ? string.Empty : chatText.Substring("!bingbong".Length).Trim());
			return spokenText.Length > 0;
		}

		private static string SanitizeForGame(string value, int maximumLength = 0)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder((maximumLength > 0) ? Math.Min(value.Length, maximumLength) : value.Length);
			bool flag = false;
			string text = value.Trim();
			foreach (char c in text)
			{
				char c2 = c switch
				{
					'>' => '›', 
					'<' => '‹', 
					_ => c, 
				};
				if (char.IsControl(c2))
				{
					c2 = ' ';
				}
				if (char.IsWhiteSpace(c2))
				{
					if (flag)
					{
						continue;
					}
					c2 = ' ';
					flag = true;
				}
				else
				{
					flag = false;
				}
				stringBuilder.Append(c2);
				if (maximumLength > 0 && stringBuilder.Length >= maximumLength)
				{
					break;
				}
			}
			return stringBuilder.ToString().Trim();
		}

		private static Action_AskBingBong FindBestBingBong()
		{
			if (IsUsableBingBong(_lastUsedBingBong))
			{
				return _lastUsedBingBong;
			}
			Action_AskBingBong val = null;
			Action_AskBingBong[] array = Resources.FindObjectsOfTypeAll<Action_AskBingBong>();
			foreach (Action_AskBingBong val2 in array)
			{
				if (IsUsableBingBong(val2))
				{
					Item componentInParent = ((Component)val2).GetComponentInParent<Item>();
					if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.holderCharacter != (Object)null)
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null)
					{
						val = val2;
					}
				}
			}
			return val;
		}

		private static Action_AskBingBong FindBingBongByViewId(int viewId)
		{
			if (viewId <= 0)
			{
				return null;
			}
			PhotonView val = PhotonView.Find(viewId);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			return ((Component)val).GetComponentInChildren<Action_AskBingBong>(true) ?? ((Component)val).GetComponentInParent<Action_AskBingBong>();
		}

		private static int GetViewId(Action_AskBingBong action)
		{
			Item componentInParent = ((Component)action).GetComponentInParent<Item>();
			PhotonView val = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponent<PhotonView>() : ((Component)action).GetComponentInParent<PhotonView>());
			if (!((Object)(object)val != (Object)null))
			{
				return 0;
			}
			return val.ViewID;
		}

		private static Item FindPresentationItem(Action_AskBingBong action, int viewId, Guid preferredGuid)
		{
			Item val = null;
			if (preferredGuid != Guid.Empty)
			{
				val = FindItemByGuid(Item.ALL_ACTIVE_ITEMS, preferredGuid) ?? FindItemByGuid(Item.ALL_ITEMS, preferredGuid);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			if (viewId > 0)
			{
				PhotonView val2 = PhotonView.Find(viewId);
				if ((Object)(object)val2 != (Object)null)
				{
					val = ((Component)val2).GetComponent<Item>() ?? ((Component)val2).GetComponentInParent<Item>() ?? ((Component)val2).GetComponentInChildren<Item>(true);
				}
			}
			if ((Object)(object)val == (Object)null && (Object)(object)action != (Object)null)
			{
				val = ((Component)action).GetComponentInParent<Item>();
			}
			return val;
		}

		private static Item FindItemByGuid(IEnumerable<Item> items, Guid guid)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (items == null)
			{
				return null;
			}
			foreach (Item item in items)
			{
				if ((Object)(object)item != (Object)null && item.data != null && item.data.guid == guid)
				{
					Scene scene = ((Component)item).gameObject.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						return item;
					}
				}
			}
			return null;
		}

		private static bool TryGetPresentationPosition(Item item, Action_AskBingBong action, out Vector3 position)
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item != (Object)null && (Object)(object)item.mainRenderer != (Object)null)
			{
				Bounds bounds = item.mainRenderer.bounds;
				position = ((Bounds)(ref bounds)).center;
				return true;
			}
			if ((Object)(object)item != (Object)null && (Object)(object)item.rig != (Object)null)
			{
				position = item.rig.worldCenterOfMass;
				return true;
			}
			if ((Object)(object)item != (Object)null)
			{
				position = item.transform.position;
				return true;
			}
			if ((Object)(object)action != (Object)null)
			{
				position = ((Component)action).transform.position;
				return true;
			}
			position = default(Vector3);
			return false;
		}

		private static bool TryGetSubtitleAnchorPosition(Item item, Action_AskBingBong action, out Vector3 position)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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)
			//IL_00a0: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item != (Object)null && (Object)(object)item.mainRenderer != (Object)null)
			{
				Bounds bounds = item.mainRenderer.bounds;
				float num = Mathf.Max(0.12f, ((Bounds)(ref bounds)).extents.y * 0.2f);
				position = new Vector3(((Bounds)(ref bounds)).center.x, ((Bounds)(ref bounds)).max.y + num, ((Bounds)(ref bounds)).center.z);
				return true;
			}
			if ((Object)(object)item != (Object)null && (Object)(object)item.rig != (Object)null)
			{
				position = item.rig.worldCenterOfMass + Vector3.up * 0.65f;
				return true;
			}
			if ((Object)(object)item != (Object)null)
			{
				position = item.transform.position + Vector3.up * 0.65f;
				return true;
			}
			if ((Object)(object)action != (Object)null)
			{
				position = ((Component)action).transform.position + Vector3.up * 0.65f;
				return true;
			}
			position = default(Vector3);
			return false;
		}

		private static bool IsUsableBingBong(Action_AskBingBong action)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)action != (Object)null && (Object)(object)((Component)action).gameObject != (Object)null)
			{
				Scene scene = ((Component)action).gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					return ((Component)action).gameObject.activeInHierarchy;
				}
			}
			return false;
		}
	}
}