using AutoMapper;
using Hangfire;
using HtmlAgilityPack;
using IP2Region;
using Masuit.MyBlogs.Core.Common.Mails;
using Masuit.Tools;
using Masuit.Tools.Media;
using MaxMind.GeoIP2;
using MaxMind.GeoIP2.Exceptions;
using MaxMind.GeoIP2.Responses;
using Microsoft.Extensions.DependencyInjection;
using Polly;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using TimeZoneConverter;
namespace Masuit.MyBlogs.Core.Common
{
///
/// 公共类库
///
public static class CommonHelper
{
static CommonHelper()
{
ThreadPool.QueueUserWorkItem(_ =>
{
while (true)
{
BanRegex = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "ban.txt"), Encoding.UTF8);
ModRegex = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "mod.txt"), Encoding.UTF8);
DenyIP = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "denyip.txt"), Encoding.UTF8);
string[] lines = File.ReadAllLines(Path.Combine(AppContext.BaseDirectory + "App_Data", "DenyIPRange.txt"), Encoding.UTF8);
DenyIPRange = new Dictionary();
foreach (string line in lines)
{
try
{
var strs = line.Split(' ');
DenyIPRange[strs[0]] = strs[1];
}
catch (IndexOutOfRangeException)
{
}
}
IPWhiteList = File.ReadAllText(Path.Combine(AppContext.BaseDirectory + "App_Data", "whitelist.txt")).Split(',', ',').ToList();
Console.WriteLine("刷新公共数据...");
Thread.Sleep(TimeSpan.FromMinutes(10));
}
});
}
///
/// 敏感词
///
public static string BanRegex { get; set; }
///
/// 审核词
///
public static string ModRegex { get; set; }
///
/// 全局禁止IP
///
public static string DenyIP { get; set; }
///
/// ip白名单
///
public static List IPWhiteList { get; set; }
///
/// 每IP错误的次数统计
///
public static ConcurrentDictionary IPErrorTimes { get; set; } = new();
///
/// 系统设定
///
public static ConcurrentDictionary SystemSettings { get; set; } = new();
///
/// 网站启动时间
///
public static DateTime StartupTime { get; set; } = DateTime.Now;
///
/// IP黑名单地址段
///
public static Dictionary DenyIPRange { get; set; }
///
/// 判断IP地址是否被黑名单
///
///
///
public static bool IsDenyIpAddress(this string ip)
{
if (IPWhiteList.Contains(ip))
{
return false;
}
return DenyIP.Contains(ip) ||
DenyIPRange.AsParallel().Any(kv => kv.Key.StartsWith(ip.Split('.')[0]) && ip.IpAddressInRange(kv.Key, kv.Value));
}
///
/// 是否是禁区
///
///
///
public static bool IsInDenyArea(this string ips)
{
var denyAreas = SystemSettings.GetOrAdd("DenyArea", "").Split(new[] { ',', ',' }, StringSplitOptions.RemoveEmptyEntries);
if (denyAreas.Any())
{
foreach (var item in ips.Split(','))
{
var pos = GetIPLocation(item);
return pos.Contains(denyAreas) || denyAreas.Intersect(pos.Split("|")).Any();
}
}
return false;
}
private static readonly DbSearcher IPSearcher = new(Path.Combine(AppContext.BaseDirectory + "App_Data", "ip2region.db"));
public static readonly DatabaseReader MaxmindReader = new(Path.Combine(AppContext.BaseDirectory + "App_Data", "GeoLite2-City.mmdb"));
private static readonly DatabaseReader MaxmindAsnReader = new(Path.Combine(AppContext.BaseDirectory + "App_Data", "GeoLite2-ASN.mmdb"));
public static AsnResponse GetIPAsn(this string ip)
{
if (ip.IsPrivateIP())
{
return new AsnResponse();
}
return Policy.Handle().Fallback(new AsnResponse()).Execute(() => MaxmindAsnReader.Asn(ip));
}
public static AsnResponse GetIPAsn(this IPAddress ip)
{
return Policy.Handle().Fallback(new AsnResponse()).Execute(() => MaxmindAsnReader.Asn(ip));
}
public static string GetIPLocation(this string ips)
{
var (location, network) = GetIPLocation(IPAddress.Parse(ips));
return location + "|" + network;
}
public static (string location, string network) GetIPLocation(this IPAddress ip)
{
switch (ip.AddressFamily)
{
case AddressFamily.InterNetwork when ip.IsPrivateIP():
case AddressFamily.InterNetworkV6 when ip.IsPrivateIP():
return ("内网", "内网IP");
case AddressFamily.InterNetworkV6 when ip.IsIPv4MappedToIPv6:
ip = ip.MapToIPv4();
goto case AddressFamily.InterNetwork;
case AddressFamily.InterNetwork:
var parts = IPSearcher.MemorySearch(ip.ToString())?.Region.Split('|');
if (parts != null)
{
var asn = GetIPAsn(ip);
var network = parts[^1] == "0" ? asn.AutonomousSystemOrganization : parts[^1];
var location = parts[..^1].Where(s => s != "0").Distinct().Join("");
return (location, network + $"(AS{asn.AutonomousSystemNumber})");
}
goto default;
default:
var cityResp = Policy.Handle().Fallback(new CityResponse()).Execute(() => MaxmindReader.City(ip));
var asnResp = GetIPAsn(ip);
return (cityResp.Country.Names.GetValueOrDefault("zh-CN") + cityResp.City.Names.GetValueOrDefault("zh-CN"), asnResp.AutonomousSystemOrganization + $"(AS{asnResp.AutonomousSystemNumber})");
}
}
///
/// 获取ip所在时区
///
///
///
public static string GetClientTimeZone(this IPAddress ip)
{
switch (ip.AddressFamily)
{
case AddressFamily.InterNetwork when ip.IsPrivateIP():
case AddressFamily.InterNetworkV6 when ip.IsPrivateIP():
return "Asia/Shanghai";
default:
var resp = Policy.Handle().Fallback(new CityResponse()).Execute(() => MaxmindReader.City(ip));
return resp.Location.TimeZone ?? "Asia/Shanghai";
}
}
///
/// 类型映射
///
///
///
///
public static T Mapper(this object source) where T : class => Startup.ServiceProvider.GetRequiredService().Map(source);
///
/// 发送邮件
///
/// 标题
/// 内容
/// 收件人
///
[AutomaticRetry(Attempts = 1, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public static void SendMail(string title, string content, string tos, string clientip)
{
Startup.ServiceProvider.GetRequiredService().Send(title, content, tos);
RedisHelper.SAdd($"Email:{DateTime.Now:yyyyMMdd}", new { title, content, tos, time = DateTime.Now, clientip });
RedisHelper.Expire($"Email:{DateTime.Now:yyyyMMdd}", 86400);
}
///
/// 清理html的img标签的除src之外的其他属性
///
///
///
public static string ClearImgAttributes(this string html)
{
var doc = new HtmlDocument();
doc.LoadHtml(html);
var nodes = doc.DocumentNode.Descendants("img");
foreach (var node in nodes)
{
node.Attributes.RemoveWhere(a => !new[] { "src", "data-original", "width", "style", "class" }.Contains(a.Name));
}
return doc.DocumentNode.OuterHtml;
}
///
/// 将html的img标签的src属性名替换成data-original
///
///
///
///
public static string ReplaceImgAttribute(this string html, string title)
{
var doc = new HtmlDocument();
doc.LoadHtml(html);
var nodes = doc.DocumentNode.Descendants("img");
foreach (var node in nodes)
{
if (node.Attributes.Contains("src"))
{
string src = node.Attributes["src"].Value;
node.Attributes.Remove("src");
node.Attributes.Add("data-original", src);
node.Attributes.Add("alt", SystemSettings["Title"]);
node.Attributes.Add("title", title);
}
}
return doc.DocumentNode.OuterHtml;
}
///
/// 获取文章摘要
///
///
/// 截取长度
/// 摘要最少字数
///
public static string GetSummary(this string html, int length = 150, int min = 10)
{
var doc = new HtmlDocument();
doc.LoadHtml(html);
var summary = doc.DocumentNode.Descendants("p").FirstOrDefault(n => n.InnerText.Length > min)?.InnerText ?? "没有摘要";
if (summary.Length > length)
{
return summary.Substring(0, length) + "...";
}
return summary;
}
public static string TrimQuery(this string path)
{
return path.Split('&').Where(s => s.Split('=', StringSplitOptions.RemoveEmptyEntries).Length == 2).Join("&");
}
///
/// 添加水印
///
///
///
public static Stream AddWatermark(this Stream stream)
{
if (!string.IsNullOrEmpty(SystemSettings.GetOrAdd("Watermark", string.Empty)))
{
try
{
var watermarker = new ImageWatermarker(stream)
{
SkipWatermarkForSmallImages = true,
SmallImagePixelsThreshold = 90000
};
return watermarker.AddWatermark(SystemSettings["Watermark"], Color.LightGray, WatermarkPosition.BottomRight, 30);
}
catch
{
//
}
}
return stream;
}
///
/// 转换时区
///
/// UTC时间
/// 时区id
///
public static DateTime ToTimeZone(this in DateTime time, string zone)
{
return TimeZoneInfo.ConvertTime(time, TZConvert.GetTimeZoneInfo(zone));
}
///
/// 转换时区
///
/// UTC时间
/// 时区id
/// 时间格式字符串
///
public static string ToTimeZoneF(this in DateTime time, string zone, string format = "yyyy-MM-dd HH:mm:ss")
{
return ToTimeZone(time, zone).ToString(format);
}
}
}