Decompiled source of OnTogetherPomoNotify v1.0.0

OnTogetherPomoNotify.dll

Decompiled 3 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SleepeyDev.OnTogetherPomoNotify.Notify;
using SleepeyDev.OnTogetherPomoNotify.Pomodoro;
using SleepeyDev.OnTogetherPomoNotify.UI;
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("SleepeyDev")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+65ccdec13cf2eefc4f6e6c5978198ed29bf47af6")]
[assembly: AssemblyProduct("OnTogetherPomoNotify")]
[assembly: AssemblyTitle("OnTogetherPomoNotify")]
[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 SleepeyDev.OnTogetherPomoNotify
{
	public static class ModConfig
	{
		public static ConfigEntry<bool> Enabled;

		public static ConfigEntry<string> BotToken;

		public static ConfigEntry<string> ChatId;

		public static ConfigEntry<string> Prefix;

		public static ConfigEntry<bool> NotifyPhaseChange;

		public static ConfigEntry<bool> NotifyWarning;

		public static ConfigEntry<string> WarningMinutes;

		public static ConfigEntry<bool> NotifyStartStop;

		public static ConfigEntry<bool> NotifySummary;

		public static ConfigEntry<KeyCode> MenuKey;

		public static ConfigEntry<float> UiScale;

		public static ConfigEntry<float> WindowX;

		public static ConfigEntry<float> WindowY;

		public static ConfigEntry<bool> VerboseLogging;

		public static void Bind(ConfigFile cfg)
		{
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Expected O, but got Unknown
			Enabled = cfg.Bind<bool>("Telegram", "Enabled", true, "Master switch. Off means nothing is sent and no network call is made at all.");
			BotToken = cfg.Bind<string>("Telegram", "BotToken", "", "Bot token from @BotFather, of the form 8123456789:AA...\nTREAT THIS AS A PASSWORD. Anyone who has it can read everything your bot receives\nand post as it. It is stored here in plain text because there is nowhere else to put\nit; do not paste this file, or a screenshot of it, into a chat or a bug report.\nThe mod never writes the token to the log.");
			ChatId = cfg.Bind<string>("Telegram", "ChatId", "", "Where to send. Your own numeric id for a private chat, or -100... for a channel.\nMessage your bot once, then press 'Find chat id' in the mod's window and it fills\nthis in for you.");
			Prefix = cfg.Bind<string>("Telegram", "Prefix", "", "Put in front of every message. Useful if one chat receives several sources.");
			NotifyPhaseChange = cfg.Bind<bool>("Notify", "PhaseChange", true, "Focus ending and a break starting are the same moment, and get one message, not two.");
			NotifyWarning = cfg.Bind<bool>("Notify", "Warning", true, "Send a heads-up shortly before the current phase ends.");
			WarningMinutes = cfg.Bind<string>("Notify", "WarningMinutes", "5, 1", "Minutes-remaining marks that trigger a warning. Comma separated; 1 to 180.\nA mark is skipped when the timer jumps past it - syncing with another player moves\nthe clock, and 'five minutes left' arriving with forty seconds left helps nobody.");
			NotifyStartStop = cfg.Bind<bool>("Notify", "StartStop", true, "Send when the timer is started, stopped, paused or resumed.");
			NotifySummary = cfg.Bind<bool>("Notify", "Summary", true, "Append what the run has achieved to the long-break and stopped messages.");
			MenuKey = cfg.Bind<KeyCode>("UI", "MenuKey", (KeyCode)282, "Opens the settings window. F3 is taken by Local Playlist, F2 by Pomodoro Sound,\nF4-F6 and F10-F11 by Day and Night, F7-F9 by FrameCare and BlueSage.");
			UiScale = cfg.Bind<float>("UI", "UiScale", 0f, new ConfigDescription("Size of the settings window. 0 picks a size from your screen height, which is what\nyou want on anything above 1080p.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f), Array.Empty<object>()));
			WindowX = cfg.Bind<float>("UI", "WindowX", 60f, "Remembered window position, in unscaled points. Moving the window writes these.");
			WindowY = cfg.Bind<float>("UI", "WindowY", 60f, "See WindowX.");
			VerboseLogging = cfg.Bind<bool>("Diagnostics", "VerboseLogging", false, "Log every snapshot decision and every send. Noisy; for bug reports.\nThe bot token is redacted out of anything logged, verbose or not.");
		}

		public static bool On(ConfigEntry<bool> entry)
		{
			return entry?.Value ?? false;
		}

		public static string Str(ConfigEntry<string> entry)
		{
			if (entry != null && entry.Value != null)
			{
				return entry.Value;
			}
			return "";
		}

		public static PlannerSettings Planner()
		{
			return new PlannerSettings
			{
				PhaseChange = On(NotifyPhaseChange),
				Warning = On(NotifyWarning),
				StartStop = On(NotifyStartStop),
				WarningMinutes = PlannerSettings.ParseMinutes(Str(WarningMinutes))
			};
		}
	}
	public sealed class ModRoot : MonoBehaviour
	{
		public static ModRoot Instance { get; private set; }

		public static void Create()
		{
			//IL_0013: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (!((Object)(object)Instance != (Object)null))
			{
				GameObject val = new GameObject("OTPN_ModRoot")
				{
					hideFlags = (HideFlags)61
				};
				Object.DontDestroyOnLoad((Object)val);
				Instance = val.AddComponent<ModRoot>();
			}
		}

		public static Coroutine Run(IEnumerator routine)
		{
			if ((Object)(object)Instance == (Object)null || routine == null)
			{
				return null;
			}
			return ((MonoBehaviour)Instance).StartCoroutine(routine);
		}

		private void Update()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.MenuKey != null && Input.GetKeyDown(ModConfig.MenuKey.Value))
			{
				SettingsWindow.Toggle();
			}
			try
			{
				PhaseWatcher.Tick();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("PhaseWatcher.Tick threw, notifications off: " + ex));
				PhaseWatcher.Disable();
			}
			try
			{
				Telegram.Tick();
			}
			catch (Exception ex2)
			{
				Plugin.Log.LogError((object)("Telegram.Tick threw: " + ex2));
			}
		}

		private void OnGUI()
		{
			try
			{
				SettingsWindow.Draw();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Settings window failed, closing it: " + ex));
				SettingsWindow.Close();
			}
		}

		private void OnDestroy()
		{
			InputGate.Release();
		}

		private void OnApplicationQuit()
		{
			InputGate.Release();
		}
	}
	[BepInPlugin("com.sleepeydev.ontogether.pomonotify", "On-Together PomoNotify", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.sleepeydev.ontogether.pomonotify";

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				ModConfig.Bind(((BaseUnityPlugin)this).Config);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Config binding failed - some features will switch themselves off: " + ex));
			}
			ModRoot.Create();
			SceneManager.activeSceneChanged += OnSceneChanged;
			Log.LogInfo((object)("PomoNotify " + ((BaseUnityPlugin)this).Info.Metadata.Version?.ToString() + " ready. Press " + ((ModConfig.MenuKey != null) ? ((object)ModConfig.MenuKey.Value/*cast due to .constrained prefix*/).ToString() : "F1") + " for its settings."));
			Refl.LogHealth();
		}

		private void OnSceneChanged(Scene from, Scene to)
		{
			try
			{
				PhaseWatcher.Forget();
				Telegram.Clear();
				InputGate.Release();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Scene change handling: " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			SceneManager.activeSceneChanged -= OnSceneChanged;
		}
	}
	public static class Refl
	{
		private static readonly List<string> Missing = new List<string>();

		private static readonly MethodInfo GetInfo = Method(typeof(PomodoroController), "GetPomodoroInfo");

		private static readonly PropertyInfo WritingProp = Property(typeof(TaskManager), "IsWriting");

		private static readonly FieldInfo FType = Field(typeof(PomodoroInfo), "Type");

		private static readonly FieldInfo FMinute = Field(typeof(PomodoroInfo), "CurrentMinute");

		private static readonly FieldInfo FSecond = Field(typeof(PomodoroInfo), "CurrentSecond");

		private static readonly FieldInfo FTotalSession = Field(typeof(PomodoroInfo), "TotalSession");

		private static readonly FieldInfo FSession = Field(typeof(PomodoroInfo), "CurrentSession");

		private static readonly FieldInfo FBreak = Field(typeof(PomodoroInfo), "BreakTime");

		private static readonly FieldInfo FLongBreak = Field(typeof(PomodoroInfo), "LongBreakTime");

		private static readonly FieldInfo FStudy = Field(typeof(PomodoroInfo), "StudyTime");

		private static readonly FieldInfo FPaused = Field(typeof(PomodoroInfo), "IsPaused");

		public static bool Ready
		{
			get
			{
				if (GetInfo != null && FType != null && FMinute != null && FSecond != null && FTotalSession != null && FSession != null && FBreak != null && FLongBreak != null && FStudy != null)
				{
					return FPaused != null;
				}
				return false;
			}
		}

		public static bool CanGateInput
		{
			get
			{
				if (WritingProp != null)
				{
					return WritingProp.CanWrite;
				}
				return false;
			}
		}

		public static PomodoroSnapshot Read()
		{
			PomodoroSnapshot result = default(PomodoroSnapshot);
			if (!Ready)
			{
				return result;
			}
			PomodoroController i = MonoSingleton<PomodoroController>.I;
			if ((Object)(object)i == (Object)null)
			{
				return result;
			}
			object obj = GetInfo.Invoke(i, null);
			if (obj == null)
			{
				return result;
			}
			result.Present = true;
			result.Phase = (Phase)(int)FType.GetValue(obj);
			result.Minute = (int)FMinute.GetValue(obj);
			result.Second = (float)FSecond.GetValue(obj);
			result.SessionsPerLongBreak = (int)FTotalSession.GetValue(obj);
			result.Session = (int)FSession.GetValue(obj);
			result.BreakMinutes = (int)FBreak.GetValue(obj);
			result.LongBreakMinutes = (int)FLongBreak.GetValue(obj);
			result.StudyMinutes = (int)FStudy.GetValue(obj);
			result.Paused = (bool)FPaused.GetValue(obj);
			return result;
		}

		public static bool GetWriting()
		{
			try
			{
				TaskManager i = MonoSingleton<TaskManager>.I;
				if ((Object)(object)i == (Object)null || WritingProp == null)
				{
					return false;
				}
				return (bool)WritingProp.GetValue(i, null);
			}
			catch (Exception)
			{
				return false;
			}
		}

		public static void SetWriting(bool value)
		{
			try
			{
				TaskManager i = MonoSingleton<TaskManager>.I;
				if (!((Object)(object)i == (Object)null) && !(WritingProp == null) && WritingProp.CanWrite)
				{
					WritingProp.SetValue(i, value, null);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not set the game's typing flag: " + ex.Message));
			}
		}

		private static MethodInfo Method(Type owner, string name)
		{
			MethodInfo methodInfo = AccessTools.Method(owner, name, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Missing.Add(owner.Name + "." + name + "()");
			}
			return methodInfo;
		}

		private static PropertyInfo Property(Type owner, string name)
		{
			PropertyInfo propertyInfo = AccessTools.Property(owner, name);
			if (propertyInfo == null)
			{
				Missing.Add(owner.Name + "." + name);
			}
			return propertyInfo;
		}

		private static FieldInfo Field(Type owner, string name)
		{
			FieldInfo fieldInfo = AccessTools.Field(owner, name);
			if (fieldInfo == null)
			{
				Missing.Add(owner.Name + "." + name);
			}
			return fieldInfo;
		}

		public static void LogHealth()
		{
			if (Missing.Count == 0)
			{
				Plugin.Log.LogInfo((object)"Game handles: all resolved.");
				return;
			}
			Plugin.Log.LogWarning((object)("Game handles missing (the game was probably updated): " + string.Join(", ", Missing.ToArray())));
			Plugin.Log.LogWarning((object)("Feature availability - notifications: " + (Ready ? "on" : "OFF") + ", input blocking while the window is open: " + (CanGateInput ? "on" : "OFF")));
		}
	}
}
namespace SleepeyDev.OnTogetherPomoNotify.UI
{
	public static class InputGate
	{
		private static bool _ours;

		public static void Hold()
		{
			if (Refl.CanGateInput && !Refl.GetWriting())
			{
				Refl.SetWriting(value: true);
				_ours = true;
			}
		}

		public static void Release()
		{
			if (_ours)
			{
				_ours = false;
				if (Refl.CanGateInput && Refl.GetWriting())
				{
					Refl.SetWriting(value: false);
				}
			}
		}
	}
	public static class SettingsWindow
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static WindowFunction <0>__Body;
		}

		private const int WindowId = 1879;

		private const float Width = 440f;

		private const float LabelWidth = 74f;

		private static Rect _rect = new Rect(60f, 60f, 440f, 120f);

		private static bool _placed;

		private static bool _showToken;

		private static string _status = "";

		private static string _token = "";

		private static string _chat = "";

		private static string _prefix = "";

		private static string _warn = "";

		public static bool Open { get; private set; }

		private static bool Usable
		{
			get
			{
				if (ModConfig.Enabled != null && ModConfig.BotToken != null && ModConfig.ChatId != null && ModConfig.Prefix != null && ModConfig.WarningMinutes != null && ModConfig.NotifyPhaseChange != null && ModConfig.NotifyStartStop != null && ModConfig.NotifySummary != null && ModConfig.NotifyWarning != null && ModConfig.WindowX != null && ModConfig.WindowY != null)
				{
					return ModConfig.UiScale != null;
				}
				return false;
			}
		}

		public static void Toggle()
		{
			if (Open)
			{
				Close();
			}
			else
			{
				Show();
			}
		}

		public static void Show()
		{
			if (!Usable)
			{
				Plugin.Log.LogWarning((object)"The settings window needs the config, and binding it failed. Edit com.sleepeydev.ontogether.pomonotify.cfg by hand.");
				return;
			}
			if (!_placed)
			{
				((Rect)(ref _rect)).x = ModConfig.WindowX.Value;
				((Rect)(ref _rect)).y = ModConfig.WindowY.Value;
				_placed = true;
			}
			_token = ModConfig.Str(ModConfig.BotToken);
			_chat = ModConfig.Str(ModConfig.ChatId);
			_prefix = ModConfig.Str(ModConfig.Prefix);
			_warn = ModConfig.Str(ModConfig.WarningMinutes);
			_showToken = false;
			_status = "";
			Open = true;
		}

		public static void Close()
		{
			if (Open)
			{
				Open = false;
				Commit();
				InputGate.Release();
			}
		}

		private static void Commit()
		{
			Set(ModConfig.BotToken, _token.Trim());
			Set(ModConfig.ChatId, _chat.Trim());
			Set(ModConfig.Prefix, _prefix);
			Set(ModConfig.WarningMinutes, _warn);
			if (!Mathf.Approximately(ModConfig.WindowX.Value, ((Rect)(ref _rect)).x))
			{
				ModConfig.WindowX.Value = ((Rect)(ref _rect)).x;
			}
			if (!Mathf.Approximately(ModConfig.WindowY.Value, ((Rect)(ref _rect)).y))
			{
				ModConfig.WindowY.Value = ((Rect)(ref _rect)).y;
			}
		}

		private static void Set(ConfigEntry<string> entry, string value)
		{
			if (entry != null && entry.Value != value)
			{
				entry.Value = value;
			}
		}

		public static void Draw()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0070: 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_0093: 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_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: 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_00b3: Expected O, but got Unknown
			if (Open && Usable)
			{
				InputGate.Hold();
				float num = ((ModConfig.UiScale.Value > 0f) ? ModConfig.UiScale.Value : Mathf.Max(1f, Mathf.Round((float)Screen.height / 900f * 2f) / 2f));
				Matrix4x4 matrix = GUI.matrix;
				GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(num, num, 1f));
				((Rect)(ref _rect)).width = 440f;
				Rect rect = _rect;
				object obj = <>O.<0>__Body;
				if (obj == null)
				{
					WindowFunction val = Body;
					<>O.<0>__Body = val;
					obj = (object)val;
				}
				_rect = GUILayout.Window(1879, rect, (WindowFunction)obj, "Pomodoro → Telegram", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(440f) });
				((Rect)(ref _rect)).x = Mathf.Clamp(((Rect)(ref _rect)).x, -300f, Mathf.Max(0f, (float)Screen.width / num - 90f));
				((Rect)(ref _rect)).y = Mathf.Clamp(((Rect)(ref _rect)).y, 0f, Mathf.Max(0f, (float)Screen.height / num - 40f));
				GUI.matrix = matrix;
			}
		}

		private static void Body(int id)
		{
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Space(4f);
			ModConfig.Enabled.Value = GUILayout.Toggle(ModConfig.Enabled.Value, " Send notifications", Array.Empty<GUILayoutOption>());
			GUILayout.Space(8f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Bot token", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(74f) });
			_token = (_showToken ? GUILayout.TextField(_token, 100, Array.Empty<GUILayoutOption>()) : GUILayout.PasswordField(_token, '*', 100, Array.Empty<GUILayoutOption>()));
			_showToken = GUILayout.Toggle(_showToken, "Show", GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) });
			GUILayout.EndHorizontal();
			if (_token.Trim().Length > 0 && !Secret.LooksLikeToken(_token.Trim()))
			{
				Note("That does not look like a token. BotFather gives you 8123456789:AA…");
			}
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Chat id", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(74f) });
			_chat = GUILayout.TextField(_chat, 32, Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Find", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) }))
			{
				Commit();
				_status = "Asking Telegram…";
				Telegram.FindChatId(delegate(bool ok, string msg)
				{
					_status = msg;
					if (ok)
					{
						_chat = ModConfig.Str(ModConfig.ChatId);
					}
				});
			}
			GUILayout.EndHorizontal();
			Note("Write anything to your bot in Telegram, then press Find.");
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Prefix", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(74f) });
			_prefix = GUILayout.TextField(_prefix, 40, Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			GUILayout.Label("Send me", Array.Empty<GUILayoutOption>());
			ModConfig.NotifyPhaseChange.Value = GUILayout.Toggle(ModConfig.NotifyPhaseChange.Value, " focus and break changes", Array.Empty<GUILayoutOption>());
			ModConfig.NotifyStartStop.Value = GUILayout.Toggle(ModConfig.NotifyStartStop.Value, " start, stop, pause and resume", Array.Empty<GUILayoutOption>());
			ModConfig.NotifySummary.Value = GUILayout.Toggle(ModConfig.NotifySummary.Value, " a summary at the long break and at the end", Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			ModConfig.NotifyWarning.Value = GUILayout.Toggle(ModConfig.NotifyWarning.Value, " a warning at", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) });
			_warn = GUILayout.TextField(_warn, 24, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) });
			GUILayout.Label("minutes left", Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			if (ModConfig.NotifyWarning.Value && PlannerSettings.ParseMinutes(_warn).Length == 0)
			{
				Note("No usable numbers there, so no warnings would be sent.");
			}
			GUILayout.Space(10f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Send a test message", Array.Empty<GUILayoutOption>()))
			{
				Commit();
				_status = "Sending…";
				Telegram.SendNow("✅ On-Together is connected to this chat.", delegate(bool ok, string msg)
				{
					_status = msg;
				});
			}
			if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }))
			{
				Close();
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(6f);
			GUILayout.Label(string.IsNullOrEmpty(_status) ? Readiness() : _status, Array.Empty<GUILayoutOption>());
			GUILayout.Label(TimerLine(), Array.Empty<GUILayoutOption>());
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f));
		}

		private static void Note(string text)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_0024: 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)
			Color color = GUI.color;
			GUI.color = new Color(color.r, color.g, color.b, color.a * 0.6f);
			GUILayout.Label(text, Array.Empty<GUILayoutOption>());
			GUI.color = color;
		}

		private static string Readiness()
		{
			if (!Telegram.Configured(out var why))
			{
				return why;
			}
			return "Ready.";
		}

		private static string TimerLine()
		{
			PomodoroSnapshot current = PhaseWatcher.Current;
			if (!current.Running)
			{
				return "Timer: not running.";
			}
			string text = ((current.Phase == Phase.Study) ? "focus" : ((current.Phase == Phase.Break) ? "break" : "long break"));
			string text2 = "Timer: " + text + ", " + current.RemainingClock() + " left" + (current.Paused ? " (paused)" : "");
			RunTally progress = PhaseWatcher.Progress;
			if (progress.Any)
			{
				text2 = text2 + "  ·  " + progress.FocusBlocks + " done, " + progress.FocusMinutes + " min";
			}
			return text2;
		}
	}
}
namespace SleepeyDev.OnTogetherPomoNotify.Pomodoro
{
	public static class MessageText
	{
		public static string Render(NotifyEvent e, RunTally tally, bool includeSummary, string prefix)
		{
			StringBuilder stringBuilder = new StringBuilder();
			if (!string.IsNullOrEmpty(prefix))
			{
				stringBuilder.Append(prefix.Trim()).Append(' ');
			}
			switch (e.Kind)
			{
			case NotifyKind.Started:
				stringBuilder.Append("▶\ufe0f ").Append(Starting(e.To, e.ToMinutes));
				break;
			case NotifyKind.PhaseChanged:
				stringBuilder.Append(Changed(e, tally));
				break;
			case NotifyKind.Warning:
				stringBuilder.Append("⏳ ").Append(e.WarningMinutes).Append((e.WarningMinutes == 1) ? " minute left of " : " minutes left of ")
					.Append(Of(e.To));
				break;
			case NotifyKind.Paused:
				stringBuilder.Append("⏸\ufe0f Paused — ").Append(e.Clock).Append(" left of ")
					.Append(Of(e.To));
				break;
			case NotifyKind.Resumed:
				stringBuilder.Append("▶\ufe0f Resumed — ").Append(e.Clock).Append(" left of ")
					.Append(Of(e.To));
				break;
			case NotifyKind.Stopped:
				stringBuilder.Append("⏹\ufe0f Timer stopped.");
				break;
			}
			bool flag = e.Kind == NotifyKind.Stopped || (e.Kind == NotifyKind.PhaseChanged && e.To == Phase.LongBreak);
			if (includeSummary && flag && tally.Any)
			{
				stringBuilder.Append('\n').Append(Summary(tally));
			}
			return stringBuilder.ToString();
		}

		public static string Summary(RunTally t)
		{
			return "\ud83d\udcca " + t.FocusBlocks + ((t.FocusBlocks == 1) ? " focus block, " : " focus blocks, ") + t.FocusMinutes + " min focused";
		}

		private static string Changed(NotifyEvent e, RunTally tally)
		{
			return e.To switch
			{
				Phase.Break => "\ud83c\udf45 Focus done" + OfCycle(tally, e) + ". Break: " + e.ToMinutes + " min", 
				Phase.LongBreak => "\ud83c\udf45 Focus done. Long break: " + e.ToMinutes + " min", 
				Phase.Study => ((e.From == Phase.LongBreak) ? "\ud83c\udf34 Long break over — " : "☕ Break over — ") + "focus for " + e.ToMinutes + " min", 
				_ => "⏹\ufe0f Timer stopped.", 
			};
		}

		private static string OfCycle(RunTally tally, NotifyEvent e)
		{
			if (e.SessionsPerLongBreak <= 1 || tally.FocusBlocks <= 0)
			{
				return "";
			}
			return " — block " + tally.FocusBlocks + " of " + e.SessionsPerLongBreak;
		}

		private static string Starting(Phase p, int minutes)
		{
			return p switch
			{
				Phase.Study => "Focus started — " + minutes + " min", 
				Phase.Break => "Break started — " + minutes + " min", 
				Phase.LongBreak => "Long break started — " + minutes + " min", 
				_ => "Timer started", 
			};
		}

		private static string Of(Phase p)
		{
			return p switch
			{
				Phase.Study => "focus", 
				Phase.Break => "the break", 
				Phase.LongBreak => "the long break", 
				_ => "the timer", 
			};
		}
	}
	public enum NotifyKind
	{
		Started,
		PhaseChanged,
		Warning,
		Paused,
		Resumed,
		Stopped
	}
	public struct NotifyEvent
	{
		public NotifyKind Kind;

		public Phase From;

		public Phase To;

		public int ToMinutes;

		public int WarningMinutes;

		public int Session;

		public int SessionsPerLongBreak;

		public string Clock;
	}
	public struct PlannerSettings
	{
		public bool PhaseChange;

		public bool Warning;

		public bool StartStop;

		public int[] WarningMinutes;

		public static PlannerSettings Everything(params int[] warnings)
		{
			return new PlannerSettings
			{
				PhaseChange = true,
				Warning = true,
				StartStop = true,
				WarningMinutes = (warnings ?? new int[0])
			};
		}

		public static int[] ParseMinutes(string csv)
		{
			List<int> list = new List<int>();
			if (!string.IsNullOrEmpty(csv))
			{
				string[] array = csv.Split(',', ';', ' ');
				for (int i = 0; i < array.Length; i++)
				{
					string text = array[i].Trim();
					if (text.Length != 0 && int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result > 0 && result <= 180 && !list.Contains(result))
					{
						list.Add(result);
					}
				}
			}
			list.Sort();
			list.Reverse();
			return list.ToArray();
		}
	}
	public static class NotificationPlanner
	{
		public const double StepSlackSeconds = 5.0;

		public static List<NotifyEvent> Plan(PomodoroSnapshot prev, PomodoroSnapshot cur, PlannerSettings s, double elapsedSeconds)
		{
			List<NotifyEvent> list = new List<NotifyEvent>(2);
			if (!prev.Present || !cur.Present)
			{
				return list;
			}
			if (!prev.Running && cur.Running)
			{
				if (s.StartStop)
				{
					list.Add(Start(cur));
				}
				return list;
			}
			if (prev.Running && !cur.Running)
			{
				if (s.StartStop)
				{
					list.Add(new NotifyEvent
					{
						Kind = NotifyKind.Stopped,
						From = prev.Phase
					});
				}
				return list;
			}
			if (!cur.Running)
			{
				return list;
			}
			if (prev.Phase != cur.Phase)
			{
				if (s.PhaseChange)
				{
					list.Add(Change(prev, cur));
				}
				return list;
			}
			if (prev.Paused != cur.Paused)
			{
				if (s.StartStop)
				{
					list.Add(new NotifyEvent
					{
						Kind = (cur.Paused ? NotifyKind.Paused : NotifyKind.Resumed),
						To = cur.Phase,
						Clock = cur.RemainingClock()
					});
				}
				return list;
			}
			if (cur.Paused)
			{
				return list;
			}
			if (s.Warning)
			{
				int num = CrossedThreshold(prev, cur, s.WarningMinutes, elapsedSeconds);
				if (num > 0)
				{
					list.Add(new NotifyEvent
					{
						Kind = NotifyKind.Warning,
						To = cur.Phase,
						WarningMinutes = num,
						Clock = cur.RemainingClock()
					});
				}
			}
			return list;
		}

		private static NotifyEvent Start(PomodoroSnapshot cur)
		{
			return new NotifyEvent
			{
				Kind = NotifyKind.Started,
				To = cur.Phase,
				ToMinutes = cur.PhaseLengthMinutes,
				Session = cur.Session,
				SessionsPerLongBreak = cur.SessionsPerLongBreak,
				Clock = cur.RemainingClock()
			};
		}

		private static NotifyEvent Change(PomodoroSnapshot prev, PomodoroSnapshot cur)
		{
			return new NotifyEvent
			{
				Kind = NotifyKind.PhaseChanged,
				From = prev.Phase,
				To = cur.Phase,
				ToMinutes = cur.PhaseLengthMinutes,
				Session = cur.Session,
				SessionsPerLongBreak = cur.SessionsPerLongBreak,
				Clock = cur.RemainingClock()
			};
		}

		private static int CrossedThreshold(PomodoroSnapshot prev, PomodoroSnapshot cur, int[] thresholds, double elapsedSeconds)
		{
			if (thresholds == null || thresholds.Length == 0)
			{
				return 0;
			}
			double remainingSeconds = prev.RemainingSeconds;
			double remainingSeconds2 = cur.RemainingSeconds;
			if (remainingSeconds2 >= remainingSeconds)
			{
				return 0;
			}
			double num = Math.Max(0.0, elapsedSeconds) * 2.0 + 5.0;
			if (remainingSeconds - remainingSeconds2 > num)
			{
				return 0;
			}
			int num2 = 0;
			foreach (int num3 in thresholds)
			{
				double num4 = (double)num3 * 60.0;
				if (remainingSeconds > num4 && remainingSeconds2 <= num4 && (num2 == 0 || num3 < num2))
				{
					num2 = num3;
				}
			}
			return num2;
		}
	}
	public static class PhaseWatcher
	{
		private const float PollInterval = 1f;

		private static bool _disabled;

		private static bool _havePrev;

		private static PomodoroSnapshot _prev;

		private static double _prevAt;

		private static RunTally _tally;

		private static float _nextPoll;

		public static PomodoroSnapshot Current => _prev;

		public static RunTally Progress => _tally;

		public static void Disable()
		{
			_disabled = true;
		}

		public static void Tick()
		{
			if (_disabled || !Refl.Ready || Time.unscaledTime < _nextPoll)
			{
				return;
			}
			_nextPoll = Time.unscaledTime + 1f;
			PomodoroSnapshot pomodoroSnapshot = Refl.Read();
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			if (!_havePrev || !_prev.Present || !pomodoroSnapshot.Present)
			{
				if (!pomodoroSnapshot.Present)
				{
					_tally = default(RunTally);
				}
				_prev = pomodoroSnapshot;
				_prevAt = realtimeSinceStartupAsDouble;
				_havePrev = true;
				return;
			}
			double elapsedSeconds = realtimeSinceStartupAsDouble - _prevAt;
			RunTally tally = _tally;
			_tally = Tally.Advance(_tally, _prev, pomodoroSnapshot);
			List<NotifyEvent> list;
			try
			{
				list = NotificationPlanner.Plan(_prev, pomodoroSnapshot, ModConfig.Planner(), elapsedSeconds);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Planning failed, notifications off: " + ex));
				_disabled = true;
				return;
			}
			_prev = pomodoroSnapshot;
			_prevAt = realtimeSinceStartupAsDouble;
			if (list.Count == 0)
			{
				return;
			}
			bool includeSummary = ModConfig.On(ModConfig.NotifySummary);
			string prefix = ModConfig.Str(ModConfig.Prefix);
			foreach (NotifyEvent item in list)
			{
				RunTally tally2 = ((item.Kind == NotifyKind.Stopped) ? tally : _tally);
				string text = MessageText.Render(item, tally2, includeSummary, prefix);
				if (ModConfig.On(ModConfig.VerboseLogging))
				{
					ManualLogSource log = Plugin.Log;
					NotifyKind kind = item.Kind;
					log.LogInfo((object)("Event " + kind.ToString() + " -> " + text.Replace("\n", " / ")));
				}
				Telegram.Queue(text);
			}
		}

		public static void Forget()
		{
			_havePrev = false;
			_prev = default(PomodoroSnapshot);
			_tally = default(RunTally);
		}
	}
	public enum Phase
	{
		None,
		Study,
		Break,
		LongBreak
	}
	public struct PomodoroSnapshot
	{
		public bool Present;

		public Phase Phase;

		public bool Paused;

		public int Minute;

		public float Second;

		public int Session;

		public int SessionsPerLongBreak;

		public int StudyMinutes;

		public int BreakMinutes;

		public int LongBreakMinutes;

		public bool Running
		{
			get
			{
				if (Present)
				{
					return Phase != Phase.None;
				}
				return false;
			}
		}

		public int PhaseLengthMinutes => Phase switch
		{
			Phase.Study => StudyMinutes, 
			Phase.Break => BreakMinutes, 
			Phase.LongBreak => LongBreakMinutes, 
			_ => 0, 
		};

		public double ElapsedSeconds => (double)Minute * 60.0 + (double)Second;

		public double RemainingSeconds
		{
			get
			{
				if (!Running)
				{
					return 0.0;
				}
				double num = (double)PhaseLengthMinutes * 60.0 - ElapsedSeconds;
				if (!(num > 0.0))
				{
					return 0.0;
				}
				return num;
			}
		}

		public string RemainingClock()
		{
			int num = (int)Math.Round(RemainingSeconds);
			int num2 = num / 60;
			int num3 = num % 60;
			return ((num2 < 10) ? "0" : "") + num2 + ":" + ((num3 < 10) ? "0" : "") + num3;
		}
	}
	public struct RunTally
	{
		public int FocusBlocks;

		public int FocusMinutes;

		public bool Any => FocusBlocks > 0;
	}
	public static class Tally
	{
		public static RunTally Advance(RunTally t, PomodoroSnapshot prev, PomodoroSnapshot cur)
		{
			if (!cur.Running)
			{
				return default(RunTally);
			}
			if (!prev.Present || !cur.Present)
			{
				return t;
			}
			if (!prev.Running)
			{
				return default(RunTally);
			}
			if (prev.Phase != Phase.Study || cur.Phase == Phase.Study)
			{
				return t;
			}
			if (prev.StudyMinutes <= 0 || prev.Minute + 1 < prev.StudyMinutes)
			{
				return t;
			}
			t.FocusBlocks++;
			t.FocusMinutes += prev.StudyMinutes;
			return t;
		}
	}
}
namespace SleepeyDev.OnTogetherPomoNotify.Notify
{
	public static class Secret
	{
		public static bool LooksLikeToken(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return false;
			}
			int num = token.IndexOf(':');
			if (num < 3 || num > 16)
			{
				return false;
			}
			if (num + 20 > token.Length)
			{
				return false;
			}
			for (int i = 0; i < num; i++)
			{
				if (token[i] < '0' || token[i] > '9')
				{
					return false;
				}
			}
			for (int j = num + 1; j < token.Length; j++)
			{
				char c = token[j];
				if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && c != '_' && c != '-')
				{
					return false;
				}
			}
			return true;
		}

		public static string Mask(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "";
			}
			int num = token.IndexOf(':');
			if (num <= 0)
			{
				return new string('*', Math.Min(token.Length, 12));
			}
			return token.Substring(0, num) + ":" + new string('*', 8);
		}

		public static string Redact(string text, string token)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			if (!string.IsNullOrEmpty(token) && token.Length >= 8)
			{
				text = text.Replace(token, "<token>");
			}
			return RedactBotPath(text);
		}

		private static string RedactBotPath(string text)
		{
			int num = text.IndexOf("/bot", StringComparison.Ordinal);
			if (num < 0)
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			int num2 = 0;
			while (num >= 0)
			{
				int num3 = num + "/bot".Length;
				int i;
				for (i = num3; i < text.Length && text[i] >= '0' && text[i] <= '9'; i++)
				{
				}
				if (i > num3 && i < text.Length && text[i] == ':')
				{
					int j;
					for (j = i + 1; j < text.Length && text[j] != '/' && text[j] != '?' && text[j] != ' ' && text[j] != '"'; j++)
					{
					}
					stringBuilder.Append(text, num2, num - num2).Append("/bot<token>");
					num2 = j;
				}
				num = text.IndexOf("/bot", num + "/bot".Length, StringComparison.Ordinal);
			}
			stringBuilder.Append(text, num2, text.Length - num2);
			return stringBuilder.ToString();
		}
	}
	public static class Telegram
	{
		private const string Api = "https://api.telegram.org/bot";

		private const float MinIntervalSeconds = 1.2f;

		private const int MaxQueued = 20;

		private const int MaxAttempts = 3;

		private const int TimeoutSeconds = 15;

		private static readonly Queue<string> Pending = new Queue<string>();

		private static bool _busy;

		private static float _nextSendAt;

		private static bool _warnedFull;

		public static string Status { get; private set; } = "";

		public static int Queued => Pending.Count;

		public static void Queue(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			if (!Configured(out var why))
			{
				Status = why;
				Plugin.Log.LogWarning((object)("Not sending: " + why));
				return;
			}
			if (Pending.Count >= 20)
			{
				Pending.Dequeue();
				if (!_warnedFull)
				{
					_warnedFull = true;
					Plugin.Log.LogWarning((object)"Telegram queue is full; dropping the oldest message. Something is wrong with the connection, not with the timer.");
				}
			}
			Pending.Enqueue(text);
		}

		public static void Tick()
		{
			if (!_busy && Pending.Count != 0 && !(Time.unscaledTime < _nextSendAt) && !((Object)(object)ModRoot.Instance == (Object)null))
			{
				string text = Pending.Dequeue();
				_busy = true;
				ModRoot.Run(SendRoutine(text, null, ownsQueue: true));
			}
		}

		public static void SendNow(string text, Action<bool, string> done)
		{
			if (!Configured(out var why))
			{
				Status = why;
				done?.Invoke(arg1: false, why);
			}
			else if ((Object)(object)ModRoot.Instance == (Object)null)
			{
				done?.Invoke(arg1: false, "The mod is not running yet.");
			}
			else
			{
				ModRoot.Run(SendRoutine(text, done, ownsQueue: false));
			}
		}

		public static void FindChatId(Action<bool, string> done)
		{
			string token = Token();
			if (!Secret.LooksLikeToken(token))
			{
				done?.Invoke(arg1: false, "Enter the bot token first.");
			}
			else if ((Object)(object)ModRoot.Instance == (Object)null)
			{
				done?.Invoke(arg1: false, "The mod is not running yet.");
			}
			else
			{
				ModRoot.Run(FindChatIdRoutine(token, done));
			}
		}

		private static IEnumerator SendRoutine(string text, Action<bool, string> done, bool ownsQueue)
		{
			string token = Token();
			string chat = ModConfig.Str(ModConfig.ChatId).Trim();
			string url = "https://api.telegram.org/bot" + token + "/sendMessage";
			bool ok = false;
			string message = "";
			for (int attempt = 1; attempt <= 3; attempt++)
			{
				WWWForm val = new WWWForm();
				val.AddField("chat_id", chat);
				val.AddField("text", text, Encoding.UTF8);
				val.AddField("disable_web_page_preview", "true");
				UnityWebRequest req = UnityWebRequest.Post(url, val);
				try
				{
					req.timeout = 15;
					yield return req.SendWebRequest();
					string body = "";
					try
					{
						body = ((req.downloadHandler != null) ? req.downloadHandler.text : "");
					}
					catch (Exception)
					{
					}
					if ((int)req.result == 1 && TelegramJson.IsOk(body))
					{
						ok = true;
						message = "Sent.";
						break;
					}
					message = Describe(req, body, token);
					if (((int)req.result == 2 || req.responseCode >= 500 || req.responseCode == 429) && attempt != 3)
					{
						goto IL_01bf;
					}
				}
				finally
				{
					((IDisposable)req)?.Dispose();
				}
				break;
				IL_01bf:
				yield return (object)new WaitForSecondsRealtime((float)attempt * 5f);
			}
			if (ownsQueue)
			{
				_busy = false;
			}
			_nextSendAt = Time.unscaledTime + 1.2f;
			Status = (ok ? ("Sent " + Stamp()) : message);
			if (!ok)
			{
				Plugin.Log.LogWarning((object)("Telegram send failed: " + message));
			}
			else if (ModConfig.On(ModConfig.VerboseLogging))
			{
				Plugin.Log.LogInfo((object)"Telegram: sent.");
			}
			done?.Invoke(ok, message);
		}

		private static IEnumerator FindChatIdRoutine(string token, Action<bool, string> done)
		{
			string text = "https://api.telegram.org/bot" + token + "/getUpdates?limit=10&timeout=0";
			UnityWebRequest req = UnityWebRequest.Get(text);
			try
			{
				req.timeout = 15;
				yield return req.SendWebRequest();
				string body = ((req.downloadHandler != null) ? req.downloadHandler.text : "");
				if ((int)req.result != 1 || !TelegramJson.IsOk(body))
				{
					string arg = (Status = Describe(req, body, token));
					done?.Invoke(arg1: false, arg);
					yield break;
				}
				string text3 = TelegramJson.LastChatId(body);
				if (string.IsNullOrEmpty(text3))
				{
					Status = "No messages yet. Write anything to your bot in Telegram, then press this again.";
					done?.Invoke(arg1: false, "No messages yet. Write anything to your bot in Telegram, then press this again.");
					yield break;
				}
				ModConfig.ChatId.Value = text3;
				Status = "Chat id found: " + text3;
				Plugin.Log.LogInfo((object)"Chat id resolved from getUpdates.");
				done?.Invoke(arg1: true, text3);
			}
			finally
			{
				((IDisposable)req)?.Dispose();
			}
		}

		private static string Token()
		{
			return ModConfig.Str(ModConfig.BotToken).Trim();
		}

		public static bool Configured(out string why)
		{
			if (!ModConfig.On(ModConfig.Enabled))
			{
				why = "Notifications are switched off.";
				return false;
			}
			if (!Secret.LooksLikeToken(Token()))
			{
				why = "No bot token, or it does not look like one.";
				return false;
			}
			if (ModConfig.Str(ModConfig.ChatId).Trim().Length == 0)
			{
				why = "No chat id yet.";
				return false;
			}
			why = "";
			return true;
		}

		private static string Describe(UnityWebRequest req, string body, string token)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			string text = TelegramJson.Description(body);
			if (!string.IsNullOrEmpty(text))
			{
				return "Telegram says: " + Secret.Redact(text, token) + ((req.responseCode > 0) ? (" (" + req.responseCode + ")") : "");
			}
			if ((int)req.result == 2)
			{
				return "Could not reach Telegram: " + Secret.Redact(req.error ?? "no connection", token);
			}
			return "Telegram refused the message" + ((req.responseCode > 0) ? (" (" + req.responseCode + ")") : "") + (string.IsNullOrEmpty(req.error) ? "" : (": " + Secret.Redact(req.error, token)));
		}

		private static string Stamp()
		{
			return DateTime.Now.ToString("HH:mm:ss");
		}

		public static void Clear()
		{
			Pending.Clear();
			_warnedFull = false;
		}
	}
	public static class TelegramJson
	{
		public static string Description(string body)
		{
			return StringField(body, "\"description\"");
		}

		public static bool IsOk(string body)
		{
			if (string.IsNullOrEmpty(body))
			{
				return false;
			}
			int num = body.IndexOf("\"ok\"", StringComparison.Ordinal);
			if (num < 0)
			{
				return false;
			}
			num = SkipToValue(body, num + 4);
			if (num >= 0 && num + 4 <= body.Length)
			{
				return body.Substring(num, 4) == "true";
			}
			return false;
		}

		public static string LastChatId(string body)
		{
			if (string.IsNullOrEmpty(body))
			{
				return null;
			}
			string result = null;
			int startIndex = 0;
			while (true)
			{
				startIndex = body.IndexOf("\"chat\":", startIndex, StringComparison.Ordinal);
				if (startIndex < 0)
				{
					break;
				}
				int num = SkipWhitespace(body, startIndex + "\"chat\":".Length);
				if (num >= 0 && num < body.Length && body[num] == '{')
				{
					int num2 = body.IndexOf("\"id\"", num, StringComparison.Ordinal);
					if (num2 >= 0)
					{
						string text = NumberField(body, num2);
						if (text != null)
						{
							result = text;
						}
					}
				}
				startIndex += "\"chat\":".Length;
			}
			return result;
		}

		private static string StringField(string body, string key)
		{
			if (string.IsNullOrEmpty(body))
			{
				return null;
			}
			int num = body.IndexOf(key, StringComparison.Ordinal);
			if (num < 0)
			{
				return null;
			}
			num = SkipToValue(body, num + key.Length);
			if (num < 0 || num >= body.Length || body[num] != '"')
			{
				return null;
			}
			num++;
			StringBuilder stringBuilder = new StringBuilder();
			for (; num < body.Length && body[num] != '"'; num++)
			{
				if (body[num] == '\\' && num + 1 < body.Length)
				{
					num++;
				}
				stringBuilder.Append(body[num]);
			}
			return stringBuilder.ToString();
		}

		private static string NumberField(string body, int keyAt)
		{
			int i = SkipToValue(body, keyAt + 4);
			if (i < 0)
			{
				return null;
			}
			int num = i;
			if (i < body.Length && body[i] == '-')
			{
				i++;
			}
			for (; i < body.Length && body[i] >= '0' && body[i] <= '9'; i++)
			{
			}
			if (i <= num || (i == num + 1 && body[num] == '-'))
			{
				return null;
			}
			return body.Substring(num, i - num);
		}

		private static int SkipToValue(string body, int at)
		{
			at = SkipWhitespace(body, at);
			if (at < 0 || at >= body.Length || body[at] != ':')
			{
				return -1;
			}
			return SkipWhitespace(body, at + 1);
		}

		private static int SkipWhitespace(string body, int at)
		{
			while (at < body.Length && (body[at] == ' ' || body[at] == '\t' || body[at] == '\r' || body[at] == '\n'))
			{
				at++;
			}
			return at;
		}
	}
}