From 6aabab679d39ba64331490d48841aa6ea4e815d9 Mon Sep 17 00:00:00 2001 From: baliasnyifeliks Date: Fri, 30 Jan 2026 18:48:54 +0200 Subject: [PATCH] Add AshdiBase to Graveyard --- AshdiBase/ApnHelper.cs | 86 +++++++++ AshdiBase/AshdiBase.csproj | 13 ++ AshdiBase/AshdiBaseInvoke.cs | 271 ++++++++++++++++++++++++++++ AshdiBase/Controller.cs | 162 +++++++++++++++++ AshdiBase/ErrorCodes.cs | 7 + AshdiBase/ModInit.cs | 51 ++++++ AshdiBase/Models/AshdiIframeInfo.cs | 8 + AshdiBase/Models/SerialStructure.cs | 15 ++ AshdiBase/Models/VoiceInfo.cs | 27 +++ AshdiBase/OnlineApi.cs | 25 +++ AshdiBase/StatsService.cs | 37 ++++ AshdiBase/TouchService.cs | 25 +++ AshdiBase/manifest.json | 6 + README.md | 1 + 14 files changed, 734 insertions(+) create mode 100644 AshdiBase/ApnHelper.cs create mode 100644 AshdiBase/AshdiBase.csproj create mode 100644 AshdiBase/AshdiBaseInvoke.cs create mode 100644 AshdiBase/Controller.cs create mode 100644 AshdiBase/ErrorCodes.cs create mode 100644 AshdiBase/ModInit.cs create mode 100644 AshdiBase/Models/AshdiIframeInfo.cs create mode 100644 AshdiBase/Models/SerialStructure.cs create mode 100644 AshdiBase/Models/VoiceInfo.cs create mode 100644 AshdiBase/OnlineApi.cs create mode 100644 AshdiBase/StatsService.cs create mode 100644 AshdiBase/TouchService.cs create mode 100644 AshdiBase/manifest.json diff --git a/AshdiBase/ApnHelper.cs b/AshdiBase/ApnHelper.cs new file mode 100644 index 0000000..394a5bc --- /dev/null +++ b/AshdiBase/ApnHelper.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json.Linq; +using Shared.Models.Base; +using System; +using System.Web; + +namespace Shared.Engine +{ + public static class ApnHelper + { + public const string DefaultHost = "https://tut.im/proxy.php?url={encodeurl}"; + + public static bool TryGetInitConf(JObject conf, out bool enabled, out string host) + { + enabled = false; + host = null; + + if (conf == null) + return false; + + if (!conf.TryGetValue("apn", out var apnToken) || apnToken?.Type != JTokenType.Boolean) + return false; + + enabled = apnToken.Value(); + host = conf.Value("apn_host"); + return true; + } + + public static void ApplyInitConf(bool enabled, string host, BaseSettings init) + { + if (init == null) + return; + + if (!enabled) + { + init.apnstream = false; + init.apn = null; + return; + } + + if (string.IsNullOrWhiteSpace(host)) + host = DefaultHost; + + if (init.apn == null) + init.apn = new ApnConf(); + + init.apn.host = host; + init.apnstream = true; + } + + public static bool IsEnabled(BaseSettings init) + { + return init?.apnstream == true && !string.IsNullOrWhiteSpace(init?.apn?.host); + } + + public static bool IsAshdiUrl(string url) + { + return !string.IsNullOrEmpty(url) && + url.IndexOf("ashdi.vip", StringComparison.OrdinalIgnoreCase) >= 0; + } + + public static string WrapUrl(BaseSettings init, string url) + { + if (!IsEnabled(init)) + return url; + + return BuildUrl(init.apn.host, url); + } + + public static string BuildUrl(string host, string url) + { + if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(url)) + return url; + + if (host.Contains("{encodeurl}")) + return host.Replace("{encodeurl}", HttpUtility.UrlEncode(url)); + + if (host.Contains("{encode_uri}")) + return host.Replace("{encode_uri}", HttpUtility.UrlEncode(url)); + + if (host.Contains("{uri}")) + return host.Replace("{uri}", url); + + return $"{host.TrimEnd('/')}/{url}"; + } + } +} diff --git a/AshdiBase/AshdiBase.csproj b/AshdiBase/AshdiBase.csproj new file mode 100644 index 0000000..d189ee5 --- /dev/null +++ b/AshdiBase/AshdiBase.csproj @@ -0,0 +1,13 @@ + + + net9.0 + library + true + + + + + ..\..\Shared.dll + + + diff --git a/AshdiBase/AshdiBaseInvoke.cs b/AshdiBase/AshdiBaseInvoke.cs new file mode 100644 index 0000000..23f331f --- /dev/null +++ b/AshdiBase/AshdiBaseInvoke.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using HtmlAgilityPack; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Shared; +using Shared.Engine; +using Shared.Models; +using Shared.Models.Online.Settings; +using AshdiBase.Models; + +namespace AshdiBase +{ + public class AshdiBaseInvoke + { + private readonly OnlinesSettings _init; + private readonly IHybridCache _hybridCache; + private readonly Action _onLog; + private readonly ProxyManager _proxyManager; + + public AshdiBaseInvoke(OnlinesSettings init, IHybridCache hybridCache, Action onLog, ProxyManager proxyManager) + { + _init = init; + _hybridCache = hybridCache; + _onLog = onLog; + _proxyManager = proxyManager; + } + + string AshdiRequestUrl(string url) + { + if (!ApnHelper.IsAshdiUrl(url)) + return url; + + return ApnHelper.WrapUrl(_init, url); + } + + public async Task GetIframeInfo(string imdb_id, long kinopoisk_id) + { + string memKey = $"AshdiBase:iframe:{imdb_id}:{kinopoisk_id}"; + if (_hybridCache.TryGetValue(memKey, out AshdiIframeInfo cached)) + return cached; + + try + { + string iframeUrl = null; + if (!string.IsNullOrEmpty(imdb_id)) + { + iframeUrl = await RequestIframeUrl($"https://base.ashdi.vip/api/product/read_api.php?imdb={imdb_id}"); + } + + if (string.IsNullOrEmpty(iframeUrl) && kinopoisk_id > 0) + { + iframeUrl = await RequestIframeUrl($"https://base.ashdi.vip/api/product/read_api.php?kinopoisk={kinopoisk_id}"); + } + + if (string.IsNullOrEmpty(iframeUrl)) + return null; + + var info = new AshdiIframeInfo + { + Url = iframeUrl, + IsSerial = iframeUrl.Contains("ashdi.vip/serial/", StringComparison.OrdinalIgnoreCase) + }; + + _hybridCache.Set(memKey, info, cacheTime(20)); + return info; + } + catch (Exception ex) + { + _onLog($"AshdiBase GetIframeInfo error: {ex.Message}"); + return null; + } + } + + async Task RequestIframeUrl(string apiUrl) + { + string requestUrl = apiUrl; + if (ApnHelper.IsEnabled(_init)) + requestUrl = ApnHelper.WrapUrl(_init, apiUrl); + + _onLog($"AshdiBase: requesting API {requestUrl}"); + string response = await Http.Get(requestUrl, headers: new List() + { + new HeadersModel("User-Agent", "Mozilla/5.0"), + new HeadersModel("Referer", "https://base.ashdi.vip/") + }, proxy: _proxyManager.Get()); + + if (string.IsNullOrEmpty(response)) + return null; + + return ExtractIframeSrc(response); + } + + string ExtractIframeSrc(string response) + { + if (string.IsNullOrEmpty(response)) + return null; + + string decoded = System.Web.HttpUtility.HtmlDecode(response); + + var match = Regex.Match(decoded, @"]+src=['""]([^'""]+)['""]", RegexOptions.IgnoreCase); + if (!match.Success) + match = Regex.Match(decoded, @"src=['""]([^'""]+)['""]", RegexOptions.IgnoreCase); + + if (!match.Success) + return null; + + string src = match.Groups[1].Value?.Trim(); + if (string.IsNullOrEmpty(src)) + return null; + + if (src.StartsWith("//")) + src = "https:" + src; + + return src; + } + + public async Task> ParseAshdiSerial(string iframeUrl) + { + var headers = new List() + { + new HeadersModel("User-Agent", "Mozilla/5.0"), + new HeadersModel("Referer", "https://ashdi.vip/") + }; + + try + { + string requestUrl = iframeUrl; + if (iframeUrl.Contains("ashdi.vip/serial/", StringComparison.OrdinalIgnoreCase)) + { + var baseUrlMatch = Regex.Match(iframeUrl, @"(https://ashdi\.vip/serial/\d+)"); + if (baseUrlMatch.Success) + requestUrl = baseUrlMatch.Groups[1].Value; + } + + string html = await Http.Get(AshdiRequestUrl(requestUrl), headers: headers, proxy: _proxyManager.Get()); + if (string.IsNullOrEmpty(html)) + return new List(); + + var match = Regex.Match(html, @"file:'(\[.+?\])'", RegexOptions.Singleline); + if (!match.Success) + return new List(); + + string jsonStr = match.Groups[1].Value + .Replace("\\'", "'") + .Replace("\\\"", "\""); + + var voicesArray = JsonConvert.DeserializeObject>(jsonStr); + var voices = new List(); + var voiceCounts = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var voiceObj in voicesArray) + { + string voiceName = voiceObj["title"]?.ToString().Trim(); + if (string.IsNullOrEmpty(voiceName)) + continue; + + if (voiceCounts.ContainsKey(voiceName)) + { + voiceCounts[voiceName]++; + voiceName = $"{voiceName} {voiceCounts[voiceName]}"; + } + else + { + voiceCounts[voiceObj["title"]?.ToString().Trim()] = 1; + } + + var voiceInfo = new VoiceInfo + { + Name = voiceObj["title"]?.ToString().Trim(), + PlayerType = "ashdi-serial", + DisplayName = voiceName + }; + + var seasons = voiceObj["folder"] as JArray; + if (seasons != null) + { + foreach (var seasonObj in seasons) + { + string seasonTitle = seasonObj["title"]?.ToString(); + var seasonMatch = Regex.Match(seasonTitle ?? string.Empty, @"Сезон\s+(\d+)", RegexOptions.IgnoreCase); + if (!seasonMatch.Success) + continue; + + int seasonNumber = int.Parse(seasonMatch.Groups[1].Value); + var episodes = new List(); + var episodesArray = seasonObj["folder"] as JArray; + + if (episodesArray != null) + { + int episodeNum = 1; + foreach (var epObj in episodesArray) + { + episodes.Add(new EpisodeInfo + { + Number = episodeNum++, + Title = epObj["title"]?.ToString(), + File = epObj["file"]?.ToString(), + Id = epObj["id"]?.ToString(), + Poster = epObj["poster"]?.ToString(), + Subtitle = epObj["subtitle"]?.ToString() + }); + } + } + + voiceInfo.Seasons[seasonNumber] = episodes; + } + } + + voices.Add(voiceInfo); + } + + return voices; + } + catch (Exception ex) + { + _onLog($"AshdiBase ParseAshdiSerial error: {ex.Message}"); + return new List(); + } + } + + public async Task> ParseAshdiSources(string iframeUrl) + { + var result = new List<(string link, string quality)>(); + string html = await Http.Get(AshdiRequestUrl(iframeUrl), headers: new List() + { + new HeadersModel("User-Agent", "Mozilla/5.0"), + new HeadersModel("Referer", "https://ashdi.vip/") + }, proxy: _proxyManager.Get()); + + if (string.IsNullOrEmpty(html)) + return result; + + var doc = new HtmlDocument(); + doc.LoadHtml(html); + + var sourceNodes = doc.DocumentNode.SelectNodes("//source[contains(@src, '.m3u8')]"); + if (sourceNodes != null) + { + foreach (var node in sourceNodes) + { + result.Add((node.GetAttributeValue("src", null), node.GetAttributeValue("label", null) ?? node.GetAttributeValue("res", null) ?? "1080p")); + } + } + + if (result.Count > 0) + return result; + + var match = Regex.Match(html, @"file\s*:\s*['""]([^'""]+\.m3u8[^'""]*)['""]", RegexOptions.IgnoreCase); + if (match.Success) + result.Add((match.Groups[1].Value, "1080p")); + + return result; + } + + public static TimeSpan cacheTime(int multiaccess, int home = 5, int mikrotik = 2, OnlinesSettings init = null, int rhub = -1) + { + if (init != null && init.rhub && rhub != -1) + return TimeSpan.FromMinutes(rhub); + + int ctime = AppInit.conf.mikrotik ? mikrotik : AppInit.conf.multiaccess ? init != null && init.cache_time > 0 ? init.cache_time : multiaccess : home; + if (ctime > multiaccess) + ctime = multiaccess; + + return TimeSpan.FromMinutes(ctime); + } + } +} diff --git a/AshdiBase/Controller.cs b/AshdiBase/Controller.cs new file mode 100644 index 0000000..d8603d4 --- /dev/null +++ b/AshdiBase/Controller.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web; +using Microsoft.AspNetCore.Mvc; +using Shared; +using Shared.Engine; +using Shared.Models.Online.Settings; +using Shared.Models.Templates; +using AshdiBase.Models; + +namespace AshdiBase.Controllers +{ + public class Controller : BaseOnlineController + { + private readonly ProxyManager proxyManager; + + public Controller() + { + proxyManager = new ProxyManager(ModInit.AshdiBase); + } + + [HttpGet] + [Route("ashdi-base")] + async public Task Index(long id, string imdb_id, long kinopoisk_id, string title, string original_title, string original_language, int year, string source, int serial, string account_email, string t, int s = -1, int e = -1, bool play = false, bool rjson = false) + { + var init = await loadKit(ModInit.AshdiBase); + if (await IsBadInitialization(init)) + return Forbid(); + + await StatsService.StatsAsync(host); + if (TouchService.Touch(host)) + return OnError(ErrorCodes.Touch, proxyManager); + + var invoke = new AshdiBaseInvoke(init, hybridCache, OnLog, proxyManager); + + var iframeInfo = await invoke.GetIframeInfo(imdb_id, kinopoisk_id); + if (iframeInfo == null || string.IsNullOrEmpty(iframeInfo.Url)) + return OnError("ashdi-base", proxyManager); + + if (play) + { + var streams = await invoke.ParseAshdiSources(iframeInfo.Url); + if (streams != null && streams.Count > 0) + return Redirect(BuildStreamUrl(init, streams.First().link)); + + return Content("AshdiBase", "text/html; charset=utf-8"); + } + + if (iframeInfo.IsSerial) + { + var voices = await invoke.ParseAshdiSerial(iframeInfo.Url); + if (voices == null || voices.Count == 0) + return OnError("ashdi-base", proxyManager); + + var structure = new SerialStructure { SerialUrl = iframeInfo.Url }; + foreach (var voice in voices) + { + if (!string.IsNullOrEmpty(voice.DisplayName)) + structure.Voices[voice.DisplayName] = voice; + } + + var allSeasons = structure.Voices + .SelectMany(v => v.Value.Seasons.Keys) + .Distinct() + .OrderBy(sn => sn) + .ToList(); + + if (s == -1) + { + var seasonsWithEpisodes = allSeasons + .Where(season => structure.Voices.Values.Any(v => + v.Seasons.ContainsKey(season) && + v.Seasons[season].Any(ep => !string.IsNullOrEmpty(ep.File)))) + .ToList(); + + if (!seasonsWithEpisodes.Any()) + return OnError("ashdi-base", proxyManager); + + var seasonTpl = new SeasonTpl(seasonsWithEpisodes.Count); + foreach (var season in seasonsWithEpisodes) + { + string link = $"{host}/ashdi-base?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s={season}"; + seasonTpl.Append($"{season}", link, season.ToString()); + } + + return rjson + ? Content(seasonTpl.ToJson(), "application/json; charset=utf-8") + : Content(seasonTpl.ToHtml(), "text/html; charset=utf-8"); + } + + var voicesForSeason = structure.Voices + .Where(v => v.Value.Seasons.ContainsKey(s)) + .Select(v => new { DisplayName = v.Key, Info = v.Value }) + .ToList(); + + if (!voicesForSeason.Any()) + return OnError("ashdi-base", proxyManager); + + if (string.IsNullOrEmpty(t)) + t = voicesForSeason[0].DisplayName; + + var voiceTpl = new VoiceTpl(); + foreach (var voice in voicesForSeason) + { + string voiceLink = $"{host}/ashdi-base?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=1&s={s}&t={HttpUtility.UrlEncode(voice.DisplayName)}"; + voiceTpl.Append(voice.DisplayName, voice.DisplayName == t, voiceLink); + } + + if (!structure.Voices.ContainsKey(t) || !structure.Voices[t].Seasons.ContainsKey(s)) + return OnError("ashdi-base", proxyManager); + + var episodes = structure.Voices[t].Seasons[s] + .Where(ep => !string.IsNullOrEmpty(ep.File)) + .ToList(); + + var episodeTpl = new EpisodeTpl(); + foreach (var ep in episodes) + { + string link = BuildStreamUrl(init, ep.File); + episodeTpl.Append( + name: ep.Title, + title: title, + s: s.ToString(), + e: ep.Number.ToString(), + link: link + ); + } + + if (rjson) + return Content(episodeTpl.ToJson(voiceTpl), "application/json; charset=utf-8"); + + return Content(voiceTpl.ToHtml() + episodeTpl.ToHtml(), "text/html; charset=utf-8"); + } + + string playLink = $"{host}/ashdi-base?imdb_id={imdb_id}&kinopoisk_id={kinopoisk_id}&title={HttpUtility.UrlEncode(title)}&original_title={HttpUtility.UrlEncode(original_title)}&year={year}&serial=0&play=true"; + var movieTpl = new MovieTpl(title, original_title, 1); + movieTpl.Append(title, accsArgs(playLink), method: "play"); + return rjson + ? Content(movieTpl.ToJson(), "application/json; charset=utf-8") + : Content(movieTpl.ToHtml(), "text/html; charset=utf-8"); + } + + string BuildStreamUrl(OnlinesSettings init, string streamLink) + { + string link = accsArgs(streamLink); + if (ApnHelper.IsEnabled(init)) + { + if (ModInit.ApnHostProvided || ApnHelper.IsAshdiUrl(link)) + return ApnHelper.WrapUrl(init, link); + + var noApn = (OnlinesSettings)init.Clone(); + noApn.apnstream = false; + noApn.apn = null; + return HostStreamProxy(noApn, link); + } + + return HostStreamProxy(init, link); + } + } +} diff --git a/AshdiBase/ErrorCodes.cs b/AshdiBase/ErrorCodes.cs new file mode 100644 index 0000000..3eaea0a --- /dev/null +++ b/AshdiBase/ErrorCodes.cs @@ -0,0 +1,7 @@ +namespace AshdiBase +{ + public static class ErrorCodes + { + public const string Touch = "50549302-F082-4507-9B80-3ADCA2F29FF1"; + } +} diff --git a/AshdiBase/ModInit.cs b/AshdiBase/ModInit.cs new file mode 100644 index 0000000..84ceb2c --- /dev/null +++ b/AshdiBase/ModInit.cs @@ -0,0 +1,51 @@ +using Newtonsoft.Json.Linq; +using Shared; +using Shared.Engine; +using Shared.Models.Module; +using Shared.Models.Online.Settings; + +namespace AshdiBase +{ + public class ModInit + { + public static OnlinesSettings AshdiBase; + public static bool ApnHostProvided; + + /// + /// модуль загружен + /// + public static void loaded(InitspaceModel initspace) + { + AshdiBase = new OnlinesSettings("AshdiBase", "https://base.ashdi.vip", streamproxy: false, useproxy: false) + { + displayname = "Ashdi Base", + displayindex = 0, + proxy = new Shared.Models.Base.ProxySettings() + { + useAuth = true, + username = "a", + password = "a", + list = new string[] { "socks5://IP:PORT" } + } + }; + + var conf = ModuleInvoke.Conf("AshdiBase", AshdiBase); + bool hasApn = ApnHelper.TryGetInitConf(conf, out bool apnEnabled, out string apnHost); + conf.Remove("apn"); + conf.Remove("apn_host"); + AshdiBase = conf.ToObject(); + if (hasApn) + ApnHelper.ApplyInitConf(apnEnabled, apnHost, AshdiBase); + ApnHostProvided = hasApn && apnEnabled && !string.IsNullOrWhiteSpace(apnHost); + if (hasApn && apnEnabled) + { + AshdiBase.streamproxy = false; + } + else if (AshdiBase.streamproxy) + { + AshdiBase.apnstream = false; + AshdiBase.apn = null; + } + } + } +} diff --git a/AshdiBase/Models/AshdiIframeInfo.cs b/AshdiBase/Models/AshdiIframeInfo.cs new file mode 100644 index 0000000..b207595 --- /dev/null +++ b/AshdiBase/Models/AshdiIframeInfo.cs @@ -0,0 +1,8 @@ +namespace AshdiBase.Models +{ + public class AshdiIframeInfo + { + public string Url { get; set; } + public bool IsSerial { get; set; } + } +} diff --git a/AshdiBase/Models/SerialStructure.cs b/AshdiBase/Models/SerialStructure.cs new file mode 100644 index 0000000..2680945 --- /dev/null +++ b/AshdiBase/Models/SerialStructure.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace AshdiBase.Models +{ + public class SerialStructure + { + public string SerialUrl { get; set; } + public Dictionary Voices { get; set; } + + public SerialStructure() + { + Voices = new Dictionary(); + } + } +} diff --git a/AshdiBase/Models/VoiceInfo.cs b/AshdiBase/Models/VoiceInfo.cs new file mode 100644 index 0000000..16b837f --- /dev/null +++ b/AshdiBase/Models/VoiceInfo.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace AshdiBase.Models +{ + public class VoiceInfo + { + public string Name { get; set; } + public string DisplayName { get; set; } + public string PlayerType { get; set; } + public Dictionary> Seasons { get; set; } + + public VoiceInfo() + { + Seasons = new Dictionary>(); + } + } + + public class EpisodeInfo + { + public int Number { get; set; } + public string Title { get; set; } + public string File { get; set; } + public string Id { get; set; } + public string Poster { get; set; } + public string Subtitle { get; set; } + } +} diff --git a/AshdiBase/OnlineApi.cs b/AshdiBase/OnlineApi.cs new file mode 100644 index 0000000..b077d2c --- /dev/null +++ b/AshdiBase/OnlineApi.cs @@ -0,0 +1,25 @@ +using Shared.Models.Base; +using System.Collections.Generic; + +namespace AshdiBase +{ + public class OnlineApi + { + public static List<(string name, string url, string plugin, int index)> Events(string host, long id, string imdb_id, long kinopoisk_id, string title, string original_title, string original_language, int year, string source, int serial, string account_email) + { + var online = new List<(string name, string url, string plugin, int index)>(); + + var init = ModInit.AshdiBase; + if (init.enable && !init.rip) + { + string url = init.overridehost; + if (string.IsNullOrEmpty(url) || TouchService.Touch(host)) + url = $"{host}/ashdi-base"; + + online.Add((init.displayname, url, "ashdi-base", init.displayindex)); + } + + return online; + } + } +} diff --git a/AshdiBase/StatsService.cs b/AshdiBase/StatsService.cs new file mode 100644 index 0000000..51f8c4e --- /dev/null +++ b/AshdiBase/StatsService.cs @@ -0,0 +1,37 @@ +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Shared.Engine; + +namespace AshdiBase +{ + public class StatsService + { + private static string _apiHost = "https://base.lampame.v6.rocks"; + + private static int _isStatsRequested = 0; + + public static async Task StatsAsync(string host) + { + if (Interlocked.CompareExchange(ref _isStatsRequested, 1, 0) == 0) + { + try + { + var jsonContent = "{\"Host\": \"" + host + "\", \"Module\": \"AshdiBase\"}"; + var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); + + var result = await Http.BasePost( + $"{_apiHost}/api/collections/Lampac_Ukraine/records", + content); + + result.response.EnsureSuccessStatusCode(); + } + catch + { + Interlocked.Exchange(ref _isStatsRequested, 0); + } + } + } + } +} diff --git a/AshdiBase/TouchService.cs b/AshdiBase/TouchService.cs new file mode 100644 index 0000000..2abd057 --- /dev/null +++ b/AshdiBase/TouchService.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AshdiBase +{ + public class TouchService + { + private static readonly HashSet EntrySet = new( + new[] + { + "c3ZpdGFubW92aWU=", + "cG9ydGFsLXR2", + "Zmxvd25ldA==" + } + .Select(base64 => Encoding.UTF8.GetString(Convert.FromBase64String(base64))), + StringComparer.OrdinalIgnoreCase); + + public static bool Touch(string host) + { + return EntrySet.Any(host.Contains); + } + } +} diff --git a/AshdiBase/manifest.json b/AshdiBase/manifest.json new file mode 100644 index 0000000..4ad75d7 --- /dev/null +++ b/AshdiBase/manifest.json @@ -0,0 +1,6 @@ +{ + "enable": true, + "version": 2, + "initspace": "AshdiBase.ModInit", + "online": "AshdiBase.OnlineApi" +} diff --git a/README.md b/README.md index 4304853..2d435c8 100644 --- a/README.md +++ b/README.md @@ -5,3 +5,4 @@ Dead modules archive for lampac-ukraine. ## Modules - AniHUB +- AshdiBase