using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Fomo;
using Fomo.Core;
using Fomo.Core.Util;
using FomoTelegram.Commands;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
[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("AndrewLin.FomoTelegram")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("FomoTelegram: A Fomo submod that relays On-Together chat messages to a Telegram chat via a Telegram Bot. Use /fomohelp and look for /fomotelegram commands")]
[assembly: AssemblyFileVersion("0.2.3.0")]
[assembly: AssemblyInformationalVersion("0.2.3+dc290028f2302c7953f8cdfe5b05908e8e411cbb")]
[assembly: AssemblyProduct("AndrewLin.FomoTelegram")]
[assembly: AssemblyTitle("AndrewLin.FomoTelegram")]
[assembly: AssemblyVersion("0.2.3.0")]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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;
}
}
}
namespace FomoTelegram
{
public static class BuildInfo
{
public const string Version = "0.2.3";
}
public sealed class FomoTelegramManager : IDisposable
{
public const string PlaceholderApiKey = "YOUR_BOT_TOKEN_HERE";
public const string PlaceholderChatId = "YOUR_CHAT_ID_HERE";
private static readonly TimeSpan MinSendInterval = TimeSpan.FromMilliseconds(50.0);
private readonly ManualLogSource _log = Logger.CreateLogSource("FomoTelegram.Manager");
private readonly HttpClient _http = new HttpClient
{
Timeout = TimeSpan.FromSeconds(40.0)
};
private readonly string _apiBase;
private readonly string _chatId;
private readonly long _chatIdLong;
private readonly ConcurrentQueue<string> _sendQueue = new ConcurrentQueue<string>();
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private readonly Task _workerTask;
public bool IsReady { get; private set; }
public FomoTelegramManager(string apiKey, string chatId)
{
_apiBase = "https://api.telegram.org/bot" + apiKey;
_chatId = chatId;
long.TryParse(chatId, out _chatIdLong);
_workerTask = Task.Run(() => WorkerLoopAsync(_cts.Token));
ValidateAndStartReceiverAsync();
}
public void Enqueue(string text)
{
if (IsReady)
{
_sendQueue.Enqueue(text);
}
}
public async Task SendDirectAsync(string text)
{
_ = 1;
try
{
string content2 = JsonConvert.SerializeObject((object)new
{
chat_id = _chatId,
text = text
});
using StringContent content = new StringContent(content2, Encoding.UTF8, "application/json");
HttpResponseMessage resp = await _http.PostAsync(_apiBase + "/sendMessage", content);
if (!resp.IsSuccessStatusCode)
{
string arg = await resp.Content.ReadAsStringAsync();
_log.LogWarning((object)$"Telegram send failed ({resp.StatusCode}): {arg}");
}
}
catch (Exception ex)
{
_log.LogWarning((object)("Telegram send failed: " + ex.Message));
}
}
private async Task ValidateAndStartReceiverAsync()
{
_ = 1;
try
{
string text = await (await _http.GetAsync(_apiBase + "/getMe")).Content.ReadAsStringAsync();
JObject val = JObject.Parse(text);
JToken obj = val["ok"];
if (obj == null || !Extensions.Value<bool>((IEnumerable<JToken>)obj))
{
_log.LogError((object)("Telegram validation failed - check your API key. Response: " + text));
return;
}
JToken obj2 = val["result"];
object obj3;
if (obj2 == null)
{
obj3 = null;
}
else
{
JToken obj4 = obj2[(object)"username"];
obj3 = ((obj4 != null) ? Extensions.Value<string>((IEnumerable<JToken>)obj4) : null);
}
if (obj3 == null)
{
obj3 = "unknown";
}
string arg = (string)obj3;
JToken obj5 = val["result"];
long? obj6;
if (obj5 == null)
{
obj6 = null;
}
else
{
JToken obj7 = obj5[(object)"id"];
obj6 = ((obj7 != null) ? new long?(Extensions.Value<long>((IEnumerable<JToken>)obj7)) : null);
}
long? num = obj6;
long valueOrDefault = num.GetValueOrDefault();
_log.LogInfo((object)$"Connected to Telegram as @{arg} (id={valueOrDefault}).");
IsReady = true;
}
catch (Exception ex)
{
_log.LogError((object)("Telegram validation failed - check your API key. Error: " + ex.Message));
return;
}
Task.Run(() => ReceiveLoopAsync(_cts.Token));
_log.LogInfo((object)"Telegram inbound receiver started.");
}
private async Task ReceiveLoopAsync(CancellationToken ct)
{
long offset = await DropPendingUpdatesAsync(ct);
while (!ct.IsCancellationRequested)
{
try
{
string requestUri = $"{_apiBase}/getUpdates?offset={offset}&timeout=30&allowed_updates=%5B%22message%22%5D";
JObject val = JObject.Parse(await (await _http.GetAsync(requestUri, ct)).Content.ReadAsStringAsync());
JToken obj = val["ok"];
if (obj == null || !Extensions.Value<bool>((IEnumerable<JToken>)obj))
{
continue;
}
JToken obj2 = val["result"];
JArray val2 = (JArray)(object)((obj2 is JArray) ? obj2 : null);
if (val2 == null)
{
continue;
}
foreach (JToken item in val2)
{
JToken obj3 = item[(object)"update_id"];
long num = ((obj3 != null) ? Extensions.Value<long>((IEnumerable<JToken>)obj3) : 0);
offset = num + 1;
JToken val3 = item[(object)"message"];
if (val3 == null)
{
continue;
}
JToken obj4 = val3[(object)"text"];
string text = ((obj4 != null) ? Extensions.Value<string>((IEnumerable<JToken>)obj4) : null);
if (string.IsNullOrEmpty(text))
{
continue;
}
JToken obj5 = val3[(object)"chat"];
long? obj6;
if (obj5 == null)
{
obj6 = null;
}
else
{
JToken obj7 = obj5[(object)"id"];
obj6 = ((obj7 != null) ? new long?(Extensions.Value<long>((IEnumerable<JToken>)obj7)) : null);
}
long? num2 = obj6;
long valueOrDefault = num2.GetValueOrDefault();
if (_chatIdLong == 0L || valueOrDefault == _chatIdLong)
{
JToken obj8 = val3[(object)"from"];
bool? obj9;
if (obj8 == null)
{
obj9 = null;
}
else
{
JToken obj10 = obj8[(object)"is_bot"];
obj9 = ((obj10 != null) ? new bool?(Extensions.Value<bool>((IEnumerable<JToken>)obj10)) : null);
}
bool? flag = obj9;
if (!flag.GetValueOrDefault())
{
ChatUtils.DispatchIncomingText(text);
}
}
}
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex2)
{
_log.LogWarning((object)("Telegram polling error: " + ex2.Message));
try
{
await Task.Delay(5000, ct);
}
catch (OperationCanceledException)
{
break;
}
}
}
}
private async Task<long> DropPendingUpdatesAsync(CancellationToken ct)
{
_ = 1;
try
{
string requestUri = _apiBase + "/getUpdates?timeout=0";
JToken obj = JObject.Parse(await (await _http.GetAsync(requestUri, ct)).Content.ReadAsStringAsync())["result"];
JArray val = (JArray)(object)((obj is JArray) ? obj : null);
if (val != null && ((JContainer)val).Count > 0)
{
JToken obj2 = val[((JContainer)val).Count - 1][(object)"update_id"];
return ((obj2 != null) ? Extensions.Value<long>((IEnumerable<JToken>)obj2) : 0) + 1;
}
}
catch
{
}
return 0L;
}
private async Task WorkerLoopAsync(CancellationToken ct)
{
_log.LogInfo((object)"Telegram send-worker started.");
while (!ct.IsCancellationRequested)
{
try
{
if (_sendQueue.TryDequeue(out string result))
{
await SendDirectAsync(result);
try
{
await Task.Delay(MinSendInterval, ct);
}
catch (OperationCanceledException)
{
break;
}
}
else
{
try
{
await Task.Delay(100, ct);
}
catch (OperationCanceledException)
{
break;
}
}
}
catch (OperationCanceledException)
{
break;
}
catch (Exception arg)
{
_log.LogError((object)$"Unexpected error in Telegram send-worker: {arg}");
try
{
await Task.Delay(1000, ct);
goto end_IL_01c2;
}
catch (OperationCanceledException)
{
}
break;
end_IL_01c2:;
}
}
_log.LogInfo((object)"Telegram send-worker stopped.");
}
public void Dispose()
{
_cts.Cancel();
try
{
_workerTask.Wait(TimeSpan.FromSeconds(2.0));
}
catch
{
}
_cts.Dispose();
_http.Dispose();
}
}
[BepInPlugin("com.andrewlin.ontogether.fomotelegram", "FomoTelegram", "0.2.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class FomoTelegramPlugin : BaseUnityPlugin
{
public const string ModGUID = "com.andrewlin.ontogether.fomotelegram";
public const string ModName = "FomoTelegram";
public const string ModVersion = "0.2.3";
internal static ManualLogSource Log = null;
public static ConfigEntry<bool>? EnableFeature { get; private set; }
public static ConfigEntry<string>? TelegramBotApiKey { get; private set; }
public static ConfigEntry<string>? TelegramChatId { get; private set; }
public static ConfigEntry<bool>? RelayGlobalChat { get; private set; }
public static ConfigEntry<bool>? RelayLocalChat { get; private set; }
public static ConfigEntry<bool>? RelayNotifications { get; private set; }
public static ConfigEntry<string>? MessageFormat { get; private set; }
public static ConfigEntry<string>? NotificationFormat { get; private set; }
public static FomoTelegramManager? TelegramManager { get; private set; }
public static string ConfigPath { get; private set; } = "BepInEx/config/com.andrewlin.ontogether.fomotelegram.cfg";
private void Awake()
{
Log = ((BaseUnityPlugin)this).Logger;
Log.LogInfo((object)"FomoTelegram v0.2.3 loading…");
InitConfig();
ConfigPath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
Log.LogInfo((object)("Config file path: " + ConfigPath));
ChatCommandManager commandManager = FomoPlugin.CommandManager;
if (commandManager != null)
{
commandManager.Register((IChatCommand)(object)new FomoTelegramToggleCommand());
}
ChatCommandManager commandManager2 = FomoPlugin.CommandManager;
if (commandManager2 != null)
{
commandManager2.Register((IChatCommand)(object)new FomoTelegramMessageFormatCommand());
}
ChatCommandManager commandManager3 = FomoPlugin.CommandManager;
if (commandManager3 != null)
{
commandManager3.Register((IChatCommand)(object)new FomoTelegramNotificationFormatCommand());
}
ChatCommandManager commandManager4 = FomoPlugin.CommandManager;
if (commandManager4 != null)
{
commandManager4.Register((IChatCommand)(object)new FomoTelegramSetupInfoCommand());
}
string text = TelegramBotApiKey?.Value ?? string.Empty;
string text2 = TelegramChatId?.Value ?? string.Empty;
if (string.IsNullOrWhiteSpace(text) || text == "YOUR_BOT_TOKEN_HERE")
{
Log.LogWarning((object)("TelegramBotApiKey is not set. Edit config and restart: " + ConfigPath));
}
else if (string.IsNullOrWhiteSpace(text2) || text2 == "YOUR_CHAT_ID_HERE")
{
Log.LogWarning((object)("TelegramChatId is not set. Edit config and restart: " + ConfigPath));
}
else
{
TelegramManager = new FomoTelegramManager(text, text2);
TelegramChatSink telegramChatSink = new TelegramChatSink(TelegramManager);
ChatSinkManager sinkManager = FomoPlugin.SinkManager;
if (sinkManager != null)
{
sinkManager.Register((IChatSink)(object)telegramChatSink);
}
Log.LogInfo((object)"TelegramChatSink registered with Fomo SinkManager.");
}
Log.LogInfo((object)"FomoTelegram loaded.");
}
private void OnDestroy()
{
TelegramManager?.Dispose();
}
private void InitConfig()
{
EnableFeature = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableFeature", true, "Master switch - disable to stop all Telegram forwarding without removing the plugin.");
TelegramBotApiKey = ((BaseUnityPlugin)this).Config.Bind<string>("Telegram", "TelegramBotApiKey", "YOUR_BOT_TOKEN_HERE", "Your Telegram Bot API token from @BotFather (e.g. 123456:ABC-DEF…).");
TelegramChatId = ((BaseUnityPlugin)this).Config.Bind<string>("Telegram", "TelegramChatId", "YOUR_CHAT_ID_HERE", "Telegram chat / group / channel ID to forward messages to. Use @userinfobot or the Telegram API to find your chat ID.");
RelayGlobalChat = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "RelayGlobalChat", true, "Forward global chat messages to Telegram.");
RelayLocalChat = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "RelayLocalChat", true, "Forward local chat messages to Telegram.");
RelayNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("Filters", "RelayNotifications", true, "Forward system notifications (joins, leaves, etc.) to Telegram.");
MessageFormat = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "MessageFormat", "[{channel:short}{distance}] {username}: {message}", "Format string for chat messages sent to Telegram.\nPlaceholders: {timestamp[:fmt]}, {channel[:short]}, {username}, {message}, {distance}, {source}, {playerid}");
NotificationFormat = ((BaseUnityPlugin)this).Config.Bind<string>("Formatting", "NotificationFormat", "{message}", "Format string for system notifications sent to Telegram.\nPlaceholders: {timestamp[:fmt]}, {channel[:short]}, {username}, {message}, {distance}, {source}, {playerid}");
}
}
public class TelegramChatSink : IChatSink
{
private static readonly ManualLogSource _log = Logger.CreateLogSource("FomoTelegram.TCS");
private readonly FomoTelegramManager _manager;
public TelegramChatSink(FomoTelegramManager manager)
{
_manager = manager;
}
public Task SendAsync(ChatEntry entry)
{
ConfigEntry<bool>? enableFeature = FomoTelegramPlugin.EnableFeature;
if (enableFeature == null || !enableFeature.Value)
{
return Task.CompletedTask;
}
if (!_manager.IsReady)
{
return Task.CompletedTask;
}
if (entry.Channel == null || entry.UserName == null)
{
ConfigEntry<bool>? relayNotifications = FomoTelegramPlugin.RelayNotifications;
if (relayNotifications == null || !relayNotifications.Value)
{
return Task.CompletedTask;
}
}
else if (entry.Channel != null && entry.Channel.StartsWith("Local"))
{
ConfigEntry<bool>? relayLocalChat = FomoTelegramPlugin.RelayLocalChat;
if (relayLocalChat == null || !relayLocalChat.Value)
{
return Task.CompletedTask;
}
}
else
{
ConfigEntry<bool>? relayGlobalChat = FomoTelegramPlugin.RelayGlobalChat;
if (relayGlobalChat == null || !relayGlobalChat.Value)
{
return Task.CompletedTask;
}
}
if (entry.IsNotification)
{
string text = ChatEntryFormatter.Format(entry, FomoTelegramPlugin.NotificationFormat?.Value);
_manager.Enqueue(text);
}
else
{
string text2 = ChatEntryFormatter.Format(entry, FomoTelegramPlugin.MessageFormat?.Value);
_manager.Enqueue(text2);
}
return Task.CompletedTask;
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "AndrewLin.FomoTelegram";
public const string PLUGIN_NAME = "AndrewLin.FomoTelegram";
public const string PLUGIN_VERSION = "0.2.3";
}
}
namespace FomoTelegram.Commands
{
public class FomoTelegramMessageFormatCommand : IChatCommand, IComparable<IChatCommand>
{
public const string CMD = "fomotelegrammessageformat";
public string Name => "fomotelegrammessageformat";
public string ShortName => "ftmf";
public string Description => "Set or get the Telegram message format.";
public void Execute(string[] args)
{
ConfigEntry<bool>? enableFeature = FomoTelegramPlugin.EnableFeature;
if (enableFeature == null || !enableFeature.Value)
{
ChatUtils.AddGlobalNotification("Telegram feature is disabled.");
}
else if (FomoTelegramPlugin.MessageFormat == null)
{
ChatUtils.AddGlobalNotification("Fomo Telegram Mod is not initialized yet.");
}
else if (args.Length == 0)
{
ChatUtils.AddGlobalNotification("Telegram message format: " + FomoTelegramPlugin.MessageFormat?.Value);
}
else if (args.Length != 0)
{
string text = string.Join(" ", args);
if (string.IsNullOrWhiteSpace(text))
{
ChatUtils.AddGlobalNotification("Please enter a valid Telegram message format.");
return;
}
FomoTelegramPlugin.MessageFormat.Value = text;
ChatUtils.AddGlobalNotification("Telegram message format is now set to: " + text + ".");
}
}
}
public class FomoTelegramNotificationFormatCommand : IChatCommand, IComparable<IChatCommand>
{
public const string CMD = "fomotelegramnotificationformat";
public string Name => "fomotelegramnotificationformat";
public string ShortName => "ftnf";
public string Description => "Set or get the Telegram notification format.";
public void Execute(string[] args)
{
ConfigEntry<bool>? enableFeature = FomoTelegramPlugin.EnableFeature;
if (enableFeature == null || !enableFeature.Value)
{
ChatUtils.AddGlobalNotification("Telegram feature is disabled.");
}
else if (FomoTelegramPlugin.NotificationFormat == null)
{
ChatUtils.AddGlobalNotification("Fomo Telegram Mod is not initialized yet.");
}
else if (args.Length == 0)
{
ChatUtils.AddGlobalNotification("Telegram notification format: " + FomoTelegramPlugin.NotificationFormat?.Value);
}
else if (args.Length != 0)
{
string text = string.Join(" ", args);
if (string.IsNullOrWhiteSpace(text))
{
ChatUtils.AddGlobalNotification("Please enter a valid Telegram notification format.");
return;
}
FomoTelegramPlugin.NotificationFormat.Value = text;
ChatUtils.AddGlobalNotification("Telegram notification format is now set to: " + text + ".");
}
}
}
public class FomoTelegramSetupInfoCommand : IChatCommand, IComparable<IChatCommand>
{
public const string CMD = "fomotelegramsetupinfo";
public string Name => "fomotelegramsetupinfo";
public string ShortName => "ftsinfo";
public string Description => "Instruction to set up the Telegram integration.";
public void Execute(string[] args)
{
ChatUtils.AddGlobalNotification("To set up Telegram integration you need:");
ChatUtils.AddGlobalNotification("1. A Telegram Bot API key from @BotFather.");
ChatUtils.AddGlobalNotification("2. Your Telegram chat ID from @userinfobot.");
ChatUtils.AddGlobalNotification("3. Update the API key and chat ID in the mod configuration.");
ChatUtils.AddGlobalNotification("Config path: " + FomoTelegramPlugin.ConfigPath);
ChatUtils.AddGlobalNotification("4. Then restart the game.");
ChatUtils.AddGlobalNotification("Inputting API key and chat ID via chat commands is not supported for security reasons. Please update the config file directly.");
}
}
public class FomoTelegramToggleCommand : IChatCommand, IComparable<IChatCommand>
{
public const string CMD = "fomotelegramtoggle";
public string Name => "fomotelegramtoggle";
public string ShortName => "ftt";
public string Description => "Toggle Telegram chat feature on/off.";
public void Execute(string[] args)
{
if (FomoTelegramPlugin.EnableFeature != null)
{
FomoTelegramPlugin.EnableFeature.Value = !FomoTelegramPlugin.EnableFeature.Value;
ChatUtils.AddGlobalNotification("Fomo Telegram is now " + (FomoTelegramPlugin.EnableFeature.Value ? "enabled" : "disabled") + ".");
}
}
}
}