lampac-ukraine/Mikai/ModInit.cs
Felix 6aece92fd0 refactor!: migrate all modules to new module interface architecture
BREAKING CHANGE: All module routes changed from /{module} to /lite/{module}

- Implement IModuleLoaded and IModuleOnline interfaces across all modules
- Add HttpHydra support to all Invoke classes for HTTP request handling
- Replace ModuleInvoke.Conf() with ModuleInvoke.Init() in all ModInit classes
- Convert loadKit() from async to synchronous calls in all controllers
- Replace direct AppInit.conf.online.with_search.Add() with reflection-based
  RegisterWithSearch() method for decoupled module registration
- Simplify cacheTime() logic by removing mikrotik/multiaccess conditionals
- Add GlobalUsings.cs to all modules for shared namespace imports
- Update OnlineApi to use ModuleOnlineItem instead of value tuples
- Bump all module versions to new major versions
2026-03-28 10:18:21 +02:00

239 lines
7.7 KiB
C#

using Newtonsoft.Json.Linq;
using Shared;
using Shared.Engine;
using Shared.Models.Online.Settings;
using Shared.Models.Module;
using Shared.Models.Module.Interfaces;
using Microsoft.AspNetCore.Mvc;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using Shared.Models;
using Shared.Models.Events;
using System;
using System.Net.Http;
using System.Net.Mime;
using System.Net.Security;
using System.Security.Authentication;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace Mikai
{
public class ModInit : IModuleLoaded
{
public static double Version => 4.0;
public static OnlinesSettings Mikai;
public static bool ApnHostProvided;
public static OnlinesSettings Settings
{
get => Mikai;
set => Mikai = value;
}
/// <summary>
/// модуль загружен
/// </summary>
public void Loaded(InitspaceModel initspace)
{
Mikai = new OnlinesSettings("Mikai", "https://mikai.me", streamproxy: false, useproxy: false)
{
displayname = "Mikai",
displayindex = 0,
apihost = "https://api.mikai.me/v1",
proxy = new Shared.Models.Base.ProxySettings()
{
useAuth = true,
username = "",
password = "",
list = new string[] { "socks5://ip:port" }
}
};
var conf = ModuleInvoke.Init("Mikai", JObject.FromObject(Mikai));
bool hasApn = ApnHelper.TryGetInitConf(conf, out bool apnEnabled, out string apnHost);
conf.Remove("apn");
conf.Remove("apn_host");
Mikai = conf.ToObject<OnlinesSettings>();
if (hasApn)
ApnHelper.ApplyInitConf(apnEnabled, apnHost, Mikai);
ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost);
if (hasApn && apnEnabled)
{
Mikai.streamproxy = false;
}
else if (Mikai.streamproxy)
{
Mikai.apnstream = false;
Mikai.apn = null;
}
// Виводити "уточнити пошук"
RegisterWithSearch("mikai");
}
private static void RegisterWithSearch(string plugin)
{
try
{
var onlineType = Type.GetType("Online.ModInit");
if (onlineType == null)
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
onlineType = asm.GetType("Online.ModInit");
if (onlineType != null)
break;
}
}
var confField = onlineType?.GetField("conf", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
var conf = confField?.GetValue(null);
var withSearchProp = conf?.GetType().GetProperty("with_search", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
if (withSearchProp?.GetValue(conf) is System.Collections.IList list)
{
foreach (var item in list)
{
if (string.Equals(item?.ToString(), plugin, StringComparison.OrdinalIgnoreCase))
return;
}
list.Add(plugin);
}
}
catch
{
}
}
public void Dispose()
{
}
}
public static class UpdateService
{
private static readonly string _connectUrl = "https://lmcuk.lme.isroot.in/stats";
private static ConnectResponse? Connect = null;
private static DateTime? _connectTime = null;
private static DateTime? _disconnectTime = null;
private static readonly TimeSpan _resetInterval = TimeSpan.FromHours(4);
private static Timer? _resetTimer = null;
private static readonly object _lock = new();
public static async Task ConnectAsync(string host, CancellationToken cancellationToken = default)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
lock (_lock)
{
if (_connectTime is not null || Connect?.IsUpdateUnavailable == true)
{
return;
}
_connectTime = DateTime.UtcNow;
}
try
{
using var handler = new SocketsHttpHandler
{
SslOptions = new SslClientAuthenticationOptions
{
RemoteCertificateValidationCallback = (_, _, _, _) => true,
EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13
}
};
using var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromSeconds(15);
var request = new
{
Host = host,
Module = ModInit.Settings.plugin,
Version = ModInit.Version,
};
var requestJson = JsonConvert.SerializeObject(request, Formatting.None);
var requestContent = new StringContent(requestJson, Encoding.UTF8, MediaTypeNames.Application.Json);
var response = await client
.PostAsync(_connectUrl, requestContent, cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
if (response.Content.Headers.ContentLength > 0)
{
var responseText = await response.Content
.ReadAsStringAsync(cancellationToken)
.ConfigureAwait(false);
Connect = JsonConvert.DeserializeObject<ConnectResponse>(responseText);
}
lock (_lock)
{
_resetTimer?.Dispose();
_resetTimer = null;
if (Connect?.IsUpdateUnavailable != true)
{
_resetTimer = new Timer(ResetConnectTime, null, _resetInterval, Timeout.InfiniteTimeSpan);
}
else
{
_disconnectTime = Connect?.IsNoiseEnabled == true
? DateTime.UtcNow.AddHours(Random.Shared.Next(1, 4))
: DateTime.UtcNow;
}
}
}
catch (Exception)
{
ResetConnectTime(null);
}
}
private static void ResetConnectTime(object? state)
{
lock (_lock)
{
_connectTime = null;
Connect = null;
_resetTimer?.Dispose();
_resetTimer = null;
}
}
public static bool IsDisconnected()
{
return _disconnectTime is not null
&& DateTime.UtcNow >= _disconnectTime;
}
public static ActionResult Validate(ActionResult result)
{
return IsDisconnected()
? throw new JsonReaderException($"Disconnect error: {Guid.CreateVersion7()}")
: result;
}
}
public record ConnectResponse(bool IsUpdateUnavailable, bool IsNoiseEnabled);
}