Decompiled source of Discord Screenshots v1.6.5

discordScreenshots.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Extensions;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using discordScreenshots.Patches;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("discordScreenshots")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("discordScreenshots")]
[assembly: AssemblyCopyright("Copyright ©  2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("CA695DDD-2CE8-407B-B7A2-9FB6D3783286")]
[assembly: AssemblyFileVersion("0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
[HarmonyPatch(typeof(Player), "OnDamaged")]
public static class RemoveDamageFlashOnDeath
{
	[HarmonyPostfix]
	private static bool Prefix(Player __instance, HitData hit)
	{
		if (hit.GetTotalDamage() >= ((Character)__instance).GetHealth())
		{
			return false;
		}
		return true;
	}
}
namespace discordScreenshots
{
	public class BepinexConfiguration
	{
		public static ConfigFile Config;

		public static ConfigEntry<string> WebhookURL;

		public static ConfigEntry<string> WebhookUsername;

		public static ConfigEntry<string> WebhookAvatarURL;

		public static ConfigEntry<string> DeathMessage;

		public static ConfigEntry<KeyCode> ScreenshotHotkey;

		public static ConfigEntry<string> HotkeyScreenshotWebhookURL;

		public static ConfigEntry<string> HotkeyScreenshotWebhookUsername;

		public static ConfigEntry<string> HotkeyScreenshotWebhookAvatarURL;

		public static ConfigEntry<string> HotkeyScreenshotMessage;

		private static readonly Random _random = new Random();

		private static string _localWebhookURL = "";

		private static string _localWebhookUsername = "";

		private static string _localWebhookAvatarURL = "";

		private static string _localHotkeyScreenshotWebhookURL = "";

		private static string _localHotkeyScreenshotWebhookUsername = "";

		private static string _localHotkeyScreenshotWebhookAvatarURL = "";

		public static string GetRandomDeathMessage()
		{
			string value = DeathMessage.Value;
			if (string.IsNullOrEmpty(value))
			{
				return "met their demise!";
			}
			string[] array = (from m in value.Split(new char[1] { ';' })
				select m.Trim() into m
				where !string.IsNullOrEmpty(m)
				select m).ToArray();
			if (array.Length == 0)
			{
				return "met their demise!";
			}
			return array[_random.Next(array.Length)];
		}

		public static string GetWebhookURL()
		{
			return GetSyncedValueOrLocalFallback(WebhookURL, _localWebhookURL);
		}

		public static string GetWebhookUsername()
		{
			if (ShouldUseLocalFallback(WebhookURL, _localWebhookURL))
			{
				return _localWebhookUsername;
			}
			return GetSyncedValueOrLocalFallback(WebhookUsername, _localWebhookUsername);
		}

		public static string GetWebhookAvatarURL()
		{
			if (ShouldUseLocalFallback(WebhookURL, _localWebhookURL))
			{
				return _localWebhookAvatarURL;
			}
			return GetSyncedValueOrLocalFallback(WebhookAvatarURL, _localWebhookAvatarURL);
		}

		public static string GetHotkeyScreenshotWebhookURL()
		{
			return GetSyncedValueOrLocalFallback(HotkeyScreenshotWebhookURL, _localHotkeyScreenshotWebhookURL);
		}

		public static string GetHotkeyScreenshotWebhookUsername()
		{
			if (ShouldUseLocalFallback(HotkeyScreenshotWebhookURL, _localHotkeyScreenshotWebhookURL))
			{
				return _localHotkeyScreenshotWebhookUsername;
			}
			return GetSyncedValueOrLocalFallback(HotkeyScreenshotWebhookUsername, _localHotkeyScreenshotWebhookUsername);
		}

		public static string GetHotkeyScreenshotWebhookAvatarURL()
		{
			if (ShouldUseLocalFallback(HotkeyScreenshotWebhookURL, _localHotkeyScreenshotWebhookURL))
			{
				return _localHotkeyScreenshotWebhookAvatarURL;
			}
			return GetSyncedValueOrLocalFallback(HotkeyScreenshotWebhookAvatarURL, _localHotkeyScreenshotWebhookAvatarURL);
		}

		public static void GenerateConfigs(ConfigFile configFile)
		{
			Config = configFile;
			ScreenshotHotkey = ConfigFileExtensions.BindConfig<KeyCode>(Config, "Screenshot", "Hotkey", (KeyCode)293, "The hotkey to take a screenshot and send to Discord.", false, (int?)1, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			WebhookURL = ConfigFileExtensions.BindConfig<string>(Config, "Webhook", "URL", "", "The URL of the Discord webhook to send messages to.", true, (int?)2, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			WebhookUsername = ConfigFileExtensions.BindConfig<string>(Config, "Webhook", "Username", "Valheim Death Bot", "The username of the Discord webhook to send messages to.", true, (int?)3, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			WebhookAvatarURL = ConfigFileExtensions.BindConfig<string>(Config, "Webhook", "AvatarURL", "", "The avatar URL of the Discord webhook to send messages to.", true, (int?)4, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DeathMessage = ConfigFileExtensions.BindConfig<string>(Config, "Death Screenshot", "Message", "met their demise! Final moments captured...", "The message to send with death screenshots (player name will be prepended automatically).", true, (int?)5, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			HotkeyScreenshotWebhookURL = ConfigFileExtensions.BindConfig<string>(Config, "Player Capture Webhook", "URL", "", "Optional separate webhook URL for player capture (F12) screenshots. If empty, uses the main Webhook URL.", true, (int?)6, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			HotkeyScreenshotWebhookUsername = ConfigFileExtensions.BindConfig<string>(Config, "Player Capture Webhook", "Username", "Valheim Screenshot Bot", "The username for the player capture webhook.", true, (int?)7, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			HotkeyScreenshotWebhookAvatarURL = ConfigFileExtensions.BindConfig<string>(Config, "Player Capture Webhook", "AvatarURL", "", "The avatar URL for the player capture webhook.", true, (int?)8, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			HotkeyScreenshotMessage = ConfigFileExtensions.BindConfig<string>(Config, "Player Capture Webhook", "Message", "captured this screenshot!", "The message to send with player capture screenshots (player name will be prepended automatically).", true, (int?)9, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			_localWebhookURL = Normalize(WebhookURL.Value);
			_localWebhookUsername = Normalize(WebhookUsername.Value);
			_localWebhookAvatarURL = Normalize(WebhookAvatarURL.Value);
			_localHotkeyScreenshotWebhookURL = Normalize(HotkeyScreenshotWebhookURL.Value);
			_localHotkeyScreenshotWebhookUsername = Normalize(HotkeyScreenshotWebhookUsername.Value);
			_localHotkeyScreenshotWebhookAvatarURL = Normalize(HotkeyScreenshotWebhookAvatarURL.Value);
		}

		private static string GetSyncedValueOrLocalFallback(ConfigEntry<string> entry, string localFallback)
		{
			string text = Normalize(entry.Value);
			if (text.Length <= 0)
			{
				return localFallback;
			}
			return text;
		}

		private static bool ShouldUseLocalFallback(ConfigEntry<string> entry, string localFallback)
		{
			if (Normalize(entry.Value).Length == 0)
			{
				return localFallback.Length > 0;
			}
			return false;
		}

		private static string Normalize(string value)
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				return value.Trim();
			}
			return "";
		}
	}
	public class SimpleDiscordWebhook
	{
		private const int LargeScreenshotPixelThreshold = 4000000;

		private const int LargeScreenshotJpegQuality = 90;

		private const int WebhookRequestTimeoutMilliseconds = 15000;

		private static ScreenshotEncoding _startupScreenshotEncoding = ScreenshotEncoding.Png;

		private static int _startupResolutionWidth;

		private static int _startupResolutionHeight;

		private static readonly HttpClient WebhookHttpClient = CreateHttpClient();

		private readonly Uri _webhookUri;

		private readonly string? _username;

		private readonly string? _avatarUrl;

		public SimpleDiscordWebhook(string webhookUrl, string? username = null, string? avatarUrl = null)
		{
			if (!TryNormalizeWebhookUrl(webhookUrl, out Uri webhookUri, out string reason))
			{
				throw new ArgumentException("Invalid webhook URL: " + reason, "webhookUrl");
			}
			_webhookUri = webhookUri;
			_username = NormalizeOptionalText(username);
			_avatarUrl = NormalizeOptionalText(avatarUrl);
		}

		public static void ConfigureScreenshotEncodingForStartupResolution()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			int width = Screen.width;
			int height = Screen.height;
			if (width <= 0 || height <= 0)
			{
				Resolution currentResolution = Screen.currentResolution;
				width = ((Resolution)(ref currentResolution)).width;
				height = ((Resolution)(ref currentResolution)).height;
			}
			ConfigureScreenshotEncoding(width, height);
		}

		public static void ConfigureScreenshotEncoding(int width, int height)
		{
			_startupResolutionWidth = Math.Max(0, width);
			_startupResolutionHeight = Math.Max(0, height);
			_startupScreenshotEncoding = (((long)_startupResolutionWidth * (long)_startupResolutionHeight >= 4000000) ? ScreenshotEncoding.Jpeg : ScreenshotEncoding.Png);
			Debug.Log((object)($"DiscordScreenshots: startup resolution {_startupResolutionWidth}x{_startupResolutionHeight}; " + "using " + GetScreenshotFormatName() + " for screenshot uploads."));
		}

		public static string CreateScreenshotFilename(string baseName, DateTime timestamp)
		{
			return $"{baseName}_{timestamp:yyyy-MM-dd_HH-mm-ss}.{GetScreenshotExtension()}";
		}

		public async Task SendMessageAsync(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				throw new ArgumentException("Message cannot be null or empty", "message");
			}
			string jsonPayload = JsonConvert.SerializeObject((object)new SimpleWebhookPayload
			{
				content = message,
				username = _username,
				avatar_url = _avatarUrl
			});
			await SendPayloadAsync(jsonPayload);
		}

		public void SendMessage(string message)
		{
			SendMessageAsync(message).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
		}

		public async Task SendScreenshotAsync(string? message = null, string? filename = null)
		{
			try
			{
				filename = NormalizeScreenshotFilename(filename);
				Texture2D val = ScreenCapture.CaptureScreenshotAsTexture();
				if ((Object)(object)val == (Object)null)
				{
					throw new Exception("Failed to capture screenshot - returned null texture");
				}
				ScreenshotUploadData uploadData = ProcessScreenshotForUpload(val);
				Debug.Log((object)$"Screenshot captured and encoded as {uploadData.FormatName} - {uploadData.Data.Length} bytes, uploading...");
				await Task.Run(async delegate
				{
					await SendFileAsync(uploadData.Data, filename, message, uploadData.ContentType);
				});
			}
			catch (Exception ex)
			{
				throw new Exception("Error capturing and sending screenshot: " + ex.Message, ex);
			}
		}

		public static Texture2D CaptureScreenshot()
		{
			Texture2D obj = ScreenCapture.CaptureScreenshotAsTexture();
			if ((Object)(object)obj == (Object)null)
			{
				throw new Exception("Failed to capture screenshot - returned null texture");
			}
			return obj;
		}

		public byte[] ProcessScreenshot(Texture2D screenshot)
		{
			return ProcessScreenshotForUpload(screenshot).Data;
		}

		public ScreenshotUploadData ProcessScreenshotForUpload(Texture2D screenshot)
		{
			byte[] array;
			string extension;
			string contentType;
			string text;
			if (_startupScreenshotEncoding == ScreenshotEncoding.Jpeg)
			{
				array = ImageConversion.EncodeToJPG(screenshot, 90);
				extension = "jpg";
				contentType = "image/jpeg";
				text = $"JPEG quality {90}";
			}
			else
			{
				array = ImageConversion.EncodeToPNG(screenshot);
				extension = "png";
				contentType = "image/png";
				text = "PNG";
			}
			Object.DestroyImmediate((Object)(object)screenshot);
			if (array == null || array.Length == 0)
			{
				throw new Exception("Failed to encode screenshot to " + text);
			}
			return new ScreenshotUploadData(array, extension, contentType, text);
		}

		public async Task SendFileAsync(byte[] fileData, string filename, string? message = null, string contentType = "image/png")
		{
			if (fileData == null || fileData.Length == 0)
			{
				throw new ArgumentException("File data cannot be null or empty", "fileData");
			}
			if (string.IsNullOrEmpty(filename))
			{
				throw new ArgumentException("Filename cannot be null or empty", "filename");
			}
			string boundary = "----formdata-discord-" + DateTime.Now.Ticks.ToString("x");
			using MemoryStream memoryStream = new MemoryStream();
			await WriteMultipartFormDataAsync(memoryStream, boundary, fileData, filename, message, contentType);
			byte[] formData = memoryStream.ToArray();
			await SendMultipartPayloadAsync(formData, boundary);
		}

		private async Task SendPayloadAsync(string jsonPayload)
		{
			_ = 1;
			try
			{
				StringContent content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
				try
				{
					HttpResponseMessage response = await WebhookHttpClient.PostAsync(_webhookUri, (HttpContent)(object)content);
					try
					{
						string text = await response.Content.ReadAsStringAsync();
						if (!response.IsSuccessStatusCode)
						{
							throw new Exception($"Discord webhook returned status: {(int)response.StatusCode} {response.StatusCode} - {text}");
						}
						if (!string.IsNullOrEmpty(text))
						{
							Debug.Log((object)("Discord response: " + text));
						}
					}
					finally
					{
						((IDisposable)response)?.Dispose();
					}
				}
				finally
				{
					((IDisposable)content)?.Dispose();
				}
			}
			catch (Exception ex)
			{
				throw new Exception("Error sending Discord webhook: " + ex.Message, ex);
			}
		}

		private async Task SendMultipartPayloadAsync(byte[] formData, string boundary)
		{
			_ = 1;
			try
			{
				ByteArrayContent content = new ByteArrayContent(formData);
				try
				{
					((HttpContent)content).Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data; boundary=" + boundary);
					HttpResponseMessage response = await WebhookHttpClient.PostAsync(_webhookUri, (HttpContent)(object)content);
					try
					{
						string text = await response.Content.ReadAsStringAsync();
						if (!response.IsSuccessStatusCode)
						{
							throw new Exception($"Discord webhook returned status: {(int)response.StatusCode} {response.StatusCode} - {text}");
						}
						if (!string.IsNullOrEmpty(text))
						{
							Debug.Log((object)("Discord file upload response: " + text));
						}
					}
					finally
					{
						((IDisposable)response)?.Dispose();
					}
				}
				finally
				{
					((IDisposable)content)?.Dispose();
				}
			}
			catch (Exception ex)
			{
				throw new Exception("Error sending Discord webhook file: " + ex.Message, ex);
			}
		}

		private async Task WriteMultipartFormDataAsync(Stream stream, string boundary, byte[] fileData, string filename, string? message, string contentType)
		{
			string newLine = "\r\n";
			byte[] boundaryBytes = Encoding.UTF8.GetBytes("--" + boundary + newLine);
			await stream.WriteAsync(boundaryBytes, 0, boundaryBytes.Length);
			string s = "Content-Disposition: form-data; name=\"files[0]\"; filename=\"" + filename + "\"" + newLine + "Content-Type: " + contentType + newLine + newLine;
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			await stream.WriteAsync(bytes, 0, bytes.Length);
			await stream.WriteAsync(fileData, 0, fileData.Length);
			byte[] newLineBytes = Encoding.UTF8.GetBytes(newLine);
			await stream.WriteAsync(newLineBytes, 0, newLineBytes.Length);
			if (!string.IsNullOrEmpty(message))
			{
				await stream.WriteAsync(boundaryBytes, 0, boundaryBytes.Length);
				SimpleWebhookPayload simpleWebhookPayload = new SimpleWebhookPayload
				{
					content = message,
					username = _username,
					avatar_url = _avatarUrl
				};
				string jsonPayload = JsonConvert.SerializeObject((object)simpleWebhookPayload);
				string s2 = "Content-Disposition: form-data; name=\"payload_json\"" + newLine + "Content-Type: application/json" + newLine + newLine;
				byte[] bytes2 = Encoding.UTF8.GetBytes(s2);
				await stream.WriteAsync(bytes2, 0, bytes2.Length);
				byte[] bytes3 = Encoding.UTF8.GetBytes(jsonPayload);
				await stream.WriteAsync(bytes3, 0, bytes3.Length);
				await stream.WriteAsync(newLineBytes, 0, newLineBytes.Length);
			}
			byte[] bytes4 = Encoding.UTF8.GetBytes("--" + boundary + "--" + newLine);
			await stream.WriteAsync(bytes4, 0, bytes4.Length);
		}

		private static HttpClient CreateHttpClient()
		{
			//IL_0016: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
			ServicePointManager.Expect100Continue = false;
			HttpClient val = new HttpClient
			{
				Timeout = TimeSpan.FromMilliseconds(15000.0)
			};
			val.DefaultRequestHeaders.UserAgent.ParseAdd("discord-screenshots");
			return val;
		}

		public static async Task SendQuickMessageAsync(string webhookUrl, string message, string? username = null, string? avatarUrl = null)
		{
			await new SimpleDiscordWebhook(webhookUrl, username, avatarUrl).SendMessageAsync(message);
		}

		public static void SendQuickMessage(string webhookUrl, string message, string? username = null, string? avatarUrl = null)
		{
			SendQuickMessageAsync(webhookUrl, message, username, avatarUrl).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
		}

		public static async Task SendQuickScreenshotAsync(string webhookUrl, string? message = null, string? username = null, string? avatarUrl = null, string? filename = null)
		{
			await new SimpleDiscordWebhook(webhookUrl, username, avatarUrl).SendScreenshotAsync(message, filename);
		}

		public static void SendQuickScreenshot(string webhookUrl, string? message = null, string? username = null, string? avatarUrl = null, string? filename = null)
		{
			SendQuickScreenshotAsync(webhookUrl, message, username, avatarUrl, filename).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
		}

		private static string NormalizeScreenshotFilename(string? filename)
		{
			string screenshotExtension = GetScreenshotExtension();
			if (string.IsNullOrEmpty(filename))
			{
				return $"valheim_screenshot_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.{screenshotExtension}";
			}
			string extension = Path.GetExtension(filename);
			if (string.IsNullOrEmpty(extension))
			{
				return filename + "." + screenshotExtension;
			}
			if (!extension.Equals(".png", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".jpg", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".jpeg", StringComparison.OrdinalIgnoreCase))
			{
				return filename;
			}
			return Path.ChangeExtension(filename, screenshotExtension) ?? (filename + "." + screenshotExtension);
		}

		private static bool TryNormalizeWebhookUrl(string? webhookUrl, out Uri webhookUri, out string reason)
		{
			webhookUri = null;
			if (webhookUrl == null)
			{
				reason = "URL cannot be empty";
				return false;
			}
			string text = webhookUrl.Trim();
			if (text.Length == 0)
			{
				reason = "URL cannot be empty";
				return false;
			}
			for (int i = 0; i < text.Length; i++)
			{
				if (char.IsControl(text[i]))
				{
					reason = "URL contains control characters";
					return false;
				}
			}
			if (!Uri.TryCreate(text, UriKind.Absolute, out Uri result) || result == null)
			{
				reason = "URL must be an absolute HTTPS URL";
				return false;
			}
			if (!string.Equals(result.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
			{
				reason = "URL must use HTTPS";
				return false;
			}
			if (string.IsNullOrEmpty(result.Host))
			{
				reason = "URL must include a host";
				return false;
			}
			webhookUri = result;
			reason = string.Empty;
			return true;
		}

		private static string? NormalizeOptionalText(string? value)
		{
			if (value == null)
			{
				return null;
			}
			string text = value.Trim();
			if (text.Length != 0)
			{
				return text;
			}
			return null;
		}

		private static string GetScreenshotExtension()
		{
			if (_startupScreenshotEncoding != ScreenshotEncoding.Jpeg)
			{
				return "png";
			}
			return "jpg";
		}

		private static string GetScreenshotFormatName()
		{
			if (_startupScreenshotEncoding != ScreenshotEncoding.Jpeg)
			{
				return "PNG";
			}
			return $"JPEG quality {90}";
		}
	}
	public sealed class ScreenshotUploadData
	{
		public byte[] Data { get; }

		public string Extension { get; }

		public string ContentType { get; }

		public string FormatName { get; }

		public ScreenshotUploadData(byte[] data, string extension, string contentType, string formatName)
		{
			Data = data;
			Extension = extension;
			ContentType = contentType;
			FormatName = formatName;
		}
	}
	internal enum ScreenshotEncoding
	{
		Png,
		Jpeg
	}
	internal class SimpleWebhookPayload
	{
		public string? content { get; set; }

		public string? username { get; set; }

		public string? avatar_url { get; set; }
	}
	[BepInPlugin("warpalicious.discordScreenshots", "discordScreenshots", "1.6.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class discordScreenshotsPlugin : BaseUnityPlugin
	{
		private const string ModName = "discordScreenshots";

		private const string ModVersion = "1.6.5";

		private const string Author = "warpalicious";

		private const string ModGUID = "warpalicious.discordScreenshots";

		private readonly Harmony HarmonyInstance = new Harmony("warpalicious.discordScreenshots");

		public static readonly ManualLogSource TemplateLogger = Logger.CreateLogSource("discordScreenshots");

		public static AssetBundle assetBundle;

		public void Awake()
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			HarmonyInstance.PatchAll(executingAssembly);
			BepinexConfiguration.GenerateConfigs(((BaseUnityPlugin)this).Config);
			SimpleDiscordWebhook.ConfigureScreenshotEncodingForStartupResolution();
		}

		public static void LoadAssetBundle()
		{
			assetBundle = AssetUtils.LoadAssetBundleFromResources("discordscreenshots", Assembly.GetExecutingAssembly());
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.Save();
		}

		private void OnApplicationQuit()
		{
			PlayerDeathScreenshotPatch.UploadStoredDeathScreenshot("application quit", waitForUpload: true);
		}
	}
}
namespace discordScreenshots.Patches
{
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	public static class DiscordConsoleCommandPatch
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			public static ConsoleEvent <>9__0_1;

			public static ConsoleEvent <>9__0_2;

			internal void <Postfix>b__0_0(ConsoleEventArgs args)
			{
				<>c__DisplayClass0_0 CS$<>8__locals6 = new <>c__DisplayClass0_0();
				if (args.Length < 2)
				{
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString("Usage: discord <message>");
					}
					Terminal context2 = args.Context;
					if (context2 != null)
					{
						context2.AddString("Example: discord Hello from Valheim!");
					}
					return;
				}
				CS$<>8__locals6.message = string.Join(" ", args.Args, 1, args.Args.Length - 1);
				if (string.IsNullOrWhiteSpace(CS$<>8__locals6.message))
				{
					Terminal context3 = args.Context;
					if (context3 != null)
					{
						context3.AddString("Message cannot be empty");
					}
					return;
				}
				CS$<>8__locals6.playerName = "Server";
				if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
				{
					CS$<>8__locals6.playerName = Player.m_localPlayer.GetPlayerName();
				}
				Terminal context4 = args.Context;
				if (context4 != null)
				{
					context4.AddString("Sending message to Discord...");
				}
				Task.Run(async delegate
				{
					try
					{
						string formattedMessage = "\ud83d\udcac **" + CS$<>8__locals6.playerName + "**: " + CS$<>8__locals6.message;
						await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.GetWebhookURL(), formattedMessage, "Valheim Console");
						Debug.Log((object)("Discord message sent successfully: " + formattedMessage));
					}
					catch (Exception ex)
					{
						Debug.LogError((object)("Failed to send Discord message: " + ex.Message));
					}
				});
			}

			internal void <Postfix>b__0_1(ConsoleEventArgs args)
			{
				<>c__DisplayClass0_1 CS$<>8__locals3 = new <>c__DisplayClass0_1();
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("Testing Discord webhook connection...");
				}
				CS$<>8__locals3.playerName = "Server";
				if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
				{
					CS$<>8__locals3.playerName = Player.m_localPlayer.GetPlayerName();
				}
				Task.Run(async delegate
				{
					try
					{
						await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.GetWebhookURL(), "\ud83e\uddea Discord webhook test from **" + CS$<>8__locals3.playerName + "** - Connection working!", "Valheim Test Bot");
						Debug.Log((object)"Discord webhook test message sent successfully!");
					}
					catch (Exception ex)
					{
						Debug.LogError((object)("Discord webhook test failed: " + ex.Message));
					}
				});
			}

			internal void <Postfix>b__0_2(ConsoleEventArgs args)
			{
				<>c__DisplayClass0_2 CS$<>8__locals17 = new <>c__DisplayClass0_2
				{
					playerName = "Unknown Player"
				};
				if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
				{
					CS$<>8__locals17.playerName = Player.m_localPlayer.GetPlayerName();
				}
				CS$<>8__locals17.message = null;
				if (args.Length > 1)
				{
					CS$<>8__locals17.message = string.Join(" ", args.Args, 1, args.Args.Length - 1);
				}
				if (string.IsNullOrEmpty(CS$<>8__locals17.message))
				{
					CS$<>8__locals17.message = "\ud83d\udcf8 **" + CS$<>8__locals17.playerName + "** took a screenshot!";
				}
				Terminal context = args.Context;
				if (context != null)
				{
					context.AddString("Capturing screenshot...");
				}
				try
				{
					CS$<>8__locals17.webhook = new SimpleDiscordWebhook(BepinexConfiguration.GetWebhookURL(), "Valheim Screenshots");
					CS$<>8__locals17.filename = SimpleDiscordWebhook.CreateScreenshotFilename(CS$<>8__locals17.playerName + "_screenshot", DateTime.Now);
					Texture2D val = ScreenCapture.CaptureScreenshotAsTexture();
					if ((Object)(object)val == (Object)null)
					{
						throw new Exception("Failed to capture screenshot");
					}
					CS$<>8__locals17.uploadData = CS$<>8__locals17.webhook.ProcessScreenshotForUpload(val);
					Terminal context2 = args.Context;
					if (context2 != null)
					{
						context2.AddString("Screenshot captured, uploading to Discord...");
					}
					Task.Run(async delegate
					{
						try
						{
							await CS$<>8__locals17.webhook.SendFileAsync(CS$<>8__locals17.uploadData.Data, CS$<>8__locals17.filename, CS$<>8__locals17.message, CS$<>8__locals17.uploadData.ContentType);
							Debug.Log((object)("Screenshot uploaded to Discord for " + CS$<>8__locals17.playerName));
						}
						catch (Exception ex2)
						{
							Debug.LogError((object)("Failed to upload screenshot: " + ex2.Message));
						}
					});
				}
				catch (Exception ex)
				{
					Terminal context3 = args.Context;
					if (context3 != null)
					{
						context3.AddString("Error: " + ex.Message);
					}
					Debug.LogError((object)("Failed to capture screenshot: " + ex.Message));
				}
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass0_0
		{
			public string playerName;

			public string message;

			internal async Task <Postfix>b__3()
			{
				try
				{
					string formattedMessage = "\ud83d\udcac **" + playerName + "**: " + message;
					await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.GetWebhookURL(), formattedMessage, "Valheim Console");
					Debug.Log((object)("Discord message sent successfully: " + formattedMessage));
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("Failed to send Discord message: " + ex.Message));
				}
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass0_1
		{
			public string playerName;

			internal async Task <Postfix>b__4()
			{
				try
				{
					await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.GetWebhookURL(), "\ud83e\uddea Discord webhook test from **" + playerName + "** - Connection working!", "Valheim Test Bot");
					Debug.Log((object)"Discord webhook test message sent successfully!");
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("Discord webhook test failed: " + ex.Message));
				}
			}
		}

		[CompilerGenerated]
		private sealed class <>c__DisplayClass0_2
		{
			public string message;

			public string playerName;

			public SimpleDiscordWebhook webhook;

			public ScreenshotUploadData uploadData;

			public string filename;

			internal async Task <Postfix>b__5()
			{
				try
				{
					await webhook.SendFileAsync(uploadData.Data, filename, message, uploadData.ContentType);
					Debug.Log((object)("Screenshot uploaded to Discord for " + playerName));
				}
				catch (Exception ex)
				{
					Debug.LogError((object)("Failed to upload screenshot: " + ex.Message));
				}
			}
		}

		[HarmonyPostfix]
		private static void Postfix()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//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_0029: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_0099: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					if (args.Length < 2)
					{
						Terminal context = args.Context;
						if (context != null)
						{
							context.AddString("Usage: discord <message>");
						}
						Terminal context2 = args.Context;
						if (context2 != null)
						{
							context2.AddString("Example: discord Hello from Valheim!");
						}
					}
					else
					{
						string message = string.Join(" ", args.Args, 1, args.Args.Length - 1);
						if (string.IsNullOrWhiteSpace(message))
						{
							Terminal context3 = args.Context;
							if (context3 != null)
							{
								context3.AddString("Message cannot be empty");
							}
						}
						else
						{
							string playerName = "Server";
							if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
							{
								playerName = Player.m_localPlayer.GetPlayerName();
							}
							Terminal context4 = args.Context;
							if (context4 != null)
							{
								context4.AddString("Sending message to Discord...");
							}
							Task.Run(async delegate
							{
								try
								{
									string formattedMessage = "\ud83d\udcac **" + playerName + "**: " + message;
									await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.GetWebhookURL(), formattedMessage, "Valheim Console");
									Debug.Log((object)("Discord message sent successfully: " + formattedMessage));
								}
								catch (Exception ex)
								{
									Debug.LogError((object)("Failed to send Discord message: " + ex.Message));
								}
							});
						}
					}
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("discord", "sends a message to Discord webhook. Usage: discord <message>", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>c.<>9__0_1;
			if (obj2 == null)
			{
				ConsoleEvent val2 = delegate(ConsoleEventArgs args)
				{
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString("Testing Discord webhook connection...");
					}
					string playerName = "Server";
					if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
					{
						playerName = Player.m_localPlayer.GetPlayerName();
					}
					Task.Run(async delegate
					{
						try
						{
							await SimpleDiscordWebhook.SendQuickMessageAsync(BepinexConfiguration.GetWebhookURL(), "\ud83e\uddea Discord webhook test from **" + playerName + "** - Connection working!", "Valheim Test Bot");
							Debug.Log((object)"Discord webhook test message sent successfully!");
						}
						catch (Exception ex)
						{
							Debug.LogError((object)("Discord webhook test failed: " + ex.Message));
						}
					});
				};
				<>c.<>9__0_1 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("discordtest", "tests the Discord webhook connection", (ConsoleEvent)obj2, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj3 = <>c.<>9__0_2;
			if (obj3 == null)
			{
				ConsoleEvent val3 = delegate(ConsoleEventArgs args)
				{
					string playerName = "Unknown Player";
					if ((Object)(object)Player.m_localPlayer != (Object)null && !string.IsNullOrEmpty(Player.m_localPlayer.GetPlayerName()))
					{
						playerName = Player.m_localPlayer.GetPlayerName();
					}
					string message = null;
					if (args.Length > 1)
					{
						message = string.Join(" ", args.Args, 1, args.Args.Length - 1);
					}
					if (string.IsNullOrEmpty(message))
					{
						message = "\ud83d\udcf8 **" + playerName + "** took a screenshot!";
					}
					Terminal context = args.Context;
					if (context != null)
					{
						context.AddString("Capturing screenshot...");
					}
					try
					{
						SimpleDiscordWebhook webhook = new SimpleDiscordWebhook(BepinexConfiguration.GetWebhookURL(), "Valheim Screenshots");
						string filename = SimpleDiscordWebhook.CreateScreenshotFilename(playerName + "_screenshot", DateTime.Now);
						Texture2D val4 = ScreenCapture.CaptureScreenshotAsTexture();
						if ((Object)(object)val4 == (Object)null)
						{
							throw new Exception("Failed to capture screenshot");
						}
						ScreenshotUploadData uploadData = webhook.ProcessScreenshotForUpload(val4);
						Terminal context2 = args.Context;
						if (context2 != null)
						{
							context2.AddString("Screenshot captured, uploading to Discord...");
						}
						Task.Run(async delegate
						{
							try
							{
								await webhook.SendFileAsync(uploadData.Data, filename, message, uploadData.ContentType);
								Debug.Log((object)("Screenshot uploaded to Discord for " + playerName));
							}
							catch (Exception ex2)
							{
								Debug.LogError((object)("Failed to upload screenshot: " + ex2.Message));
							}
						});
					}
					catch (Exception ex)
					{
						Terminal context3 = args.Context;
						if (context3 != null)
						{
							context3.AddString("Error: " + ex.Message);
						}
						Debug.LogError((object)("Failed to capture screenshot: " + ex.Message));
					}
				};
				<>c.<>9__0_2 = val3;
				obj3 = (object)val3;
			}
			new ConsoleCommand("discordscreenshot", "captures and sends a screenshot to Discord", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	public static class HotkeyScreenshotPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && Input.GetKeyDown(BepinexConfiguration.ScreenshotHotkey.Value))
				{
					string text = __instance.GetPlayerName();
					if (string.IsNullOrEmpty(text))
					{
						text = "Unknown Player";
					}
					Debug.Log((object)("HotkeyScreenshotPatch: " + text + " pressed screenshot hotkey"));
					string hotkeyScreenshotWebhookURL = BepinexConfiguration.GetHotkeyScreenshotWebhookURL();
					bool num = !string.IsNullOrEmpty(hotkeyScreenshotWebhookURL);
					string webhookUrl = (num ? hotkeyScreenshotWebhookURL : BepinexConfiguration.GetWebhookURL());
					string webhookUsername = (num ? BepinexConfiguration.GetHotkeyScreenshotWebhookUsername() : BepinexConfiguration.GetWebhookUsername());
					string webhookAvatarUrl = (num ? BepinexConfiguration.GetHotkeyScreenshotWebhookAvatarURL() : BepinexConfiguration.GetWebhookAvatarURL());
					string text2 = (num ? BepinexConfiguration.HotkeyScreenshotMessage.Value : "captured this screenshot!");
					string screenshotMessage = "\ud83d\udcf8 **" + text + "** " + text2;
					string filename = SimpleDiscordWebhook.CreateScreenshotFilename(text + "_screenshot", DateTime.Now);
					SendHotkeyScreenshotAsync(webhookUrl, screenshotMessage, webhookUsername, webhookAvatarUrl, filename);
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("HotkeyScreenshotPatch: Error: " + ex.Message));
			}
		}

		private static async Task SendHotkeyScreenshotAsync(string webhookUrl, string screenshotMessage, string webhookUsername, string webhookAvatarUrl, string filename)
		{
			try
			{
				await SimpleDiscordWebhook.SendQuickScreenshotAsync(webhookUrl, screenshotMessage, webhookUsername, webhookAvatarUrl, filename);
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("HotkeyScreenshotPatch: Failed to upload screenshot: " + ex.Message));
			}
		}
	}
	public static class PlayerDeathScreenshotPatch
	{
		internal static Texture2D? storedDeathScreenshot;

		internal static string? storedPlayerName;

		internal static DateTime storedDeathTime;

		internal static IEnumerator CaptureDeathScreenshotCoroutine(string playerName)
		{
			yield return (object)new WaitForSeconds(0.1f);
			storedDeathScreenshot = SimpleDiscordWebhook.CaptureScreenshot();
			storedPlayerName = playerName;
			storedDeathTime = DateTime.Now;
			Debug.Log((object)("Death screenshot captured and stored for " + playerName));
		}

		internal static void UploadStoredDeathScreenshot(string trigger, bool waitForUpload)
		{
			if ((Object)(object)storedDeathScreenshot == (Object)null)
			{
				Debug.Log((object)("Death screenshot upload skipped on " + trigger + ": no stored death screenshot found"));
				return;
			}
			Texture2D val = storedDeathScreenshot;
			string text = (string.IsNullOrEmpty(storedPlayerName) ? "Unknown Player" : storedPlayerName);
			DateTime timestamp = storedDeathTime;
			storedDeathScreenshot = null;
			storedPlayerName = null;
			try
			{
				SimpleDiscordWebhook webhook = new SimpleDiscordWebhook(BepinexConfiguration.GetWebhookURL(), BepinexConfiguration.GetWebhookUsername(), BepinexConfiguration.GetWebhookAvatarURL());
				ScreenshotUploadData uploadData = webhook.ProcessScreenshotForUpload(val);
				string deathMessage = "**" + text + "** " + BepinexConfiguration.GetRandomDeathMessage();
				string filename = SimpleDiscordWebhook.CreateScreenshotFilename(text + "_death", timestamp);
				Task task = Task.Run(async delegate
				{
					await webhook.SendFileAsync(uploadData.Data, filename, deathMessage, uploadData.ContentType);
				});
				if (waitForUpload)
				{
					task.GetAwaiter().GetResult();
					Debug.Log((object)("Death screenshot uploaded successfully for " + text + " on " + trigger));
				}
				else
				{
					LogUploadResultAsync(task, text, trigger);
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Error processing stored death screenshot on " + trigger + ": " + ex.Message));
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)val);
				}
			}
		}

		private static async Task LogUploadResultAsync(Task uploadTask, string playerName, string trigger)
		{
			try
			{
				await uploadTask;
				Debug.Log((object)("Death screenshot uploaded successfully for " + playerName + " on " + trigger));
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Error uploading death screenshot on " + trigger + ": " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "OnRagdollCreated")]
	public static class PlayerRagdollScreenshotPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Humanoid __instance)
		{
			try
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer))
				{
					string text = val.GetPlayerName();
					if (string.IsNullOrEmpty(text))
					{
						text = "Unknown Player";
					}
					Debug.Log((object)("PlayerRagdollScreenshotPatch: " + text + " died - capturing screenshot"));
					((MonoBehaviour)val).StartCoroutine(PlayerDeathScreenshotPatch.CaptureDeathScreenshotCoroutine(text));
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("PlayerRagdollScreenshotPatch: Error: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Awake")]
	public static class PlayerRespawnScreenshotPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance)
		{
			try
			{
				Debug.Log((object)"PlayerRespawnScreenshotPatch: Awake");
				Debug.Log((object)"PlayerRespawnScreenshotPatch: Local player confirmed");
				if ((Object)(object)PlayerDeathScreenshotPatch.storedDeathScreenshot != (Object)null)
				{
					Debug.Log((object)("PlayerRespawnScreenshotPatch: Processing stored death screenshot for " + PlayerDeathScreenshotPatch.storedPlayerName));
					PlayerDeathScreenshotPatch.UploadStoredDeathScreenshot("respawn", waitForUpload: false);
				}
				else
				{
					Debug.Log((object)"PlayerRespawnScreenshotPatch: No stored death screenshot found");
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("PlayerRespawnScreenshotPatch: Error: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Logout")]
	public static class GameLogoutDeathScreenshotPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			try
			{
				PlayerDeathScreenshotPatch.UploadStoredDeathScreenshot("logout", waitForUpload: false);
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("GameLogoutDeathScreenshotPatch: Error: " + ex.Message));
			}
		}
	}
}