mirror of
https://github.com/Jetsparrow/antiantiswearingbot.git
synced 2026-01-20 23:16:08 +03:00
Switch to aspnet WebApplication
This commit is contained in:
parent
354876985d
commit
4090adef78
5
.gitignore
vendored
5
.gitignore
vendored
@ -41,6 +41,5 @@ Thumbs.db
|
|||||||
*.dotCover
|
*.dotCover
|
||||||
|
|
||||||
#secret config
|
#secret config
|
||||||
karma.cfg.json
|
secrets.json
|
||||||
*secrets.ini
|
secrets.*.json
|
||||||
*.secret.json
|
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace AntiAntiSwearingBot.Tests
|
namespace AntiAntiSwearingBot.Tests
|
||||||
@ -8,13 +11,12 @@ namespace AntiAntiSwearingBot.Tests
|
|||||||
public class DetectTests
|
public class DetectTests
|
||||||
{
|
{
|
||||||
Unbleeper ubl { get; }
|
Unbleeper ubl { get; }
|
||||||
Config cfg { get; }
|
|
||||||
SearchDictionary dict { get; }
|
SearchDictionary dict { get; }
|
||||||
|
|
||||||
public DetectTests()
|
public DetectTests()
|
||||||
{
|
{
|
||||||
cfg = Config.Load<Config>("aasb.cfg.json");
|
|
||||||
dict = new SearchDictionary(cfg);
|
dict = new SearchDictionary(OptionsMonitor );
|
||||||
ubl = new Unbleeper(dict, cfg.Unbleeper);
|
ubl = new Unbleeper(dict, cfg.Unbleeper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,42 +1,43 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Threading;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Exceptions;
|
|
||||||
using Telegram.Bot.Extensions.Polling;
|
using Telegram.Bot.Extensions.Polling;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using AntiAntiSwearingBot.Commands;
|
using AntiAntiSwearingBot.Commands;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace AntiAntiSwearingBot;
|
namespace AntiAntiSwearingBot;
|
||||||
|
|
||||||
public class AntiAntiSwearingBot
|
public class Aasb : IHostedService
|
||||||
{
|
{
|
||||||
Config Config { get; }
|
|
||||||
SearchDictionary Dict { get; }
|
SearchDictionary Dict { get; }
|
||||||
Unbleeper Unbleeper { get; }
|
Unbleeper Unbleeper { get; }
|
||||||
|
ILogger Log { get; }
|
||||||
|
public bool Started { get; private set; } = false;
|
||||||
|
|
||||||
public AntiAntiSwearingBot(Config cfg, SearchDictionary dict)
|
public Aasb(ILogger<Aasb> log, IOptions<TelegramSettings> tgCfg, Unbleeper unbp)
|
||||||
{
|
{
|
||||||
Config = cfg;
|
Log = log;
|
||||||
Dict = dict;
|
TelegramSettings = tgCfg.Value;
|
||||||
Unbleeper = new Unbleeper(dict, cfg.Unbleeper);
|
Unbleeper = unbp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TelegramSettings TelegramSettings { get; }
|
||||||
TelegramBotClient TelegramBot { get; set; }
|
TelegramBotClient TelegramBot { get; set; }
|
||||||
ChatCommandRouter Router { get; set; }
|
ChatCommandRouter Router { get; set; }
|
||||||
public User Me { get; private set; }
|
public User Me { get; private set; }
|
||||||
|
|
||||||
public async Task Init()
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Config.ApiKey))
|
if (string.IsNullOrWhiteSpace(TelegramSettings.ApiKey))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TelegramBot = new TelegramBotClient(Config.ApiKey);
|
TelegramBot = new TelegramBotClient(TelegramSettings.ApiKey);
|
||||||
|
|
||||||
Me = await TelegramBot.GetMeAsync();
|
Me = await TelegramBot.GetMeAsync();
|
||||||
|
Log.LogInformation("Connected to Telegram as @{Username}", Me.Username);
|
||||||
|
|
||||||
Router = new ChatCommandRouter(Me.Username);
|
Router = new ChatCommandRouter(Me.Username);
|
||||||
Router.Add(new LearnCommand(Dict), "learn");
|
Router.Add(new LearnCommand(Dict), "learn");
|
||||||
Router.Add(new UnlearnCommand(Dict), "unlearn");
|
Router.Add(new UnlearnCommand(Dict), "unlearn");
|
||||||
@ -46,16 +47,19 @@ public class AntiAntiSwearingBot
|
|||||||
HandleUpdateAsync,
|
HandleUpdateAsync,
|
||||||
HandleErrorAsync,
|
HandleErrorAsync,
|
||||||
receiverOptions);
|
receiverOptions);
|
||||||
|
|
||||||
|
Log.LogInformation("AntiAntiSwearBot started!");
|
||||||
|
Started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await TelegramBot.CloseAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
|
Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var ErrorMessage = exception switch
|
Log.LogError(exception, "Exception while handling API message");
|
||||||
{
|
|
||||||
ApiRequestException apiRequestException => $"Telegram API Error:\n[{apiRequestException.ErrorCode}]\n{apiRequestException.Message}",
|
|
||||||
_ => exception.ToString()
|
|
||||||
};
|
|
||||||
Console.WriteLine(ErrorMessage);
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +80,8 @@ public class AntiAntiSwearingBot
|
|||||||
await TelegramBot.SendTextMessageAsync(
|
await TelegramBot.SendTextMessageAsync(
|
||||||
msg.Chat.Id,
|
msg.Chat.Id,
|
||||||
commandResponse,
|
commandResponse,
|
||||||
replyToMessageId: msg.MessageId);
|
replyToMessageId: msg.MessageId,
|
||||||
|
disableNotification: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -85,13 +90,13 @@ public class AntiAntiSwearingBot
|
|||||||
await TelegramBot.SendTextMessageAsync(
|
await TelegramBot.SendTextMessageAsync(
|
||||||
msg.Chat.Id,
|
msg.Chat.Id,
|
||||||
unbleepResponse,
|
unbleepResponse,
|
||||||
replyToMessageId: msg.MessageId);
|
replyToMessageId: msg.MessageId,
|
||||||
|
disableNotification: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Console.WriteLine(e);
|
Log.LogError(e, "Exception while handling message {0}", msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -10,10 +10,18 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
|
||||||
<PackageReference Include="Telegram.Bot.Extensions.Polling" Version="2.0.0-alpha.1" />
|
<PackageReference Include="Telegram.Bot.Extensions.Polling" Version="2.0.0-alpha.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="secrets.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Update="aasb.cfg.json">
|
<None Update="aasb.cfg.json">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
|||||||
@ -1,29 +1,28 @@
|
|||||||
namespace AntiAntiSwearingBot;
|
namespace AntiAntiSwearingBot;
|
||||||
|
|
||||||
public class Config : ConfigBase
|
public class ServiceSettings
|
||||||
{
|
{
|
||||||
public string ApiKey { get; private set; }
|
public string Urls { get; set; }
|
||||||
public ProxySettings Proxy { get; private set; }
|
|
||||||
public SearchDictionarySettings SearchDictionary { get; private set; }
|
|
||||||
public UnbleeperSettings Unbleeper { get; private set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UnbleeperSettings
|
public class UnbleeperSettings
|
||||||
{
|
{
|
||||||
public string BleepedSwearsRegex { get; private set; }
|
public string BleepedSwearsRegex { get; set; }
|
||||||
public int MinAmbiguousWordLength { get; private set; }
|
public int MinAmbiguousWordLength { get; set; }
|
||||||
public int MinWordLength { get; private set; }
|
public int MinWordLength { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct SearchDictionarySettings
|
public class SearchDictionarySettings
|
||||||
{
|
{
|
||||||
public string DictionaryPath { get; private set; }
|
public string DictionaryPath { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ProxySettings
|
public class TelegramSettings
|
||||||
{
|
{
|
||||||
public string Url { get; private set; }
|
public string ApiKey { get; set; }
|
||||||
public int Port { get; private set; }
|
public bool UseProxy { get; set; }
|
||||||
public string Login { get; private set; }
|
public string Url { get; set; }
|
||||||
public string Password { get; private set; }
|
public int Port { get; set; }
|
||||||
|
public string Login { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,75 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
|
|
||||||
namespace AntiAntiSwearingBot;
|
|
||||||
|
|
||||||
public abstract class ConfigBase
|
|
||||||
{
|
|
||||||
public static T Load<T>(params string[] paths) where T : ConfigBase, new()
|
|
||||||
{
|
|
||||||
var result = new T();
|
|
||||||
var configJson = new JObject();
|
|
||||||
var mergeSettings = new JsonMergeSettings
|
|
||||||
{
|
|
||||||
MergeArrayHandling = MergeArrayHandling.Union
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var path in paths)
|
|
||||||
{
|
|
||||||
try { configJson.Merge(JObject.Parse(File.ReadAllText(path)), mergeSettings);}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var sr = configJson.CreateReader())
|
|
||||||
{
|
|
||||||
var settings = new JsonSerializerSettings
|
|
||||||
{
|
|
||||||
ContractResolver = new PrivateSetterContractResolver()
|
|
||||||
};
|
|
||||||
JsonSerializer.CreateDefault(settings).Populate(sr, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PrivateSetterContractResolver : DefaultContractResolver
|
|
||||||
{
|
|
||||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
|
||||||
{
|
|
||||||
var jProperty = base.CreateProperty(member, memberSerialization);
|
|
||||||
if (jProperty.Writable)
|
|
||||||
return jProperty;
|
|
||||||
|
|
||||||
jProperty.Writable = member.IsPropertyWithSetter();
|
|
||||||
|
|
||||||
return jProperty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PrivateSetterCamelCasePropertyNamesContractResolver : CamelCasePropertyNamesContractResolver
|
|
||||||
{
|
|
||||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
|
||||||
{
|
|
||||||
var jProperty = base.CreateProperty(member, memberSerialization);
|
|
||||||
if (jProperty.Writable)
|
|
||||||
return jProperty;
|
|
||||||
|
|
||||||
jProperty.Writable = member.IsPropertyWithSetter();
|
|
||||||
|
|
||||||
return jProperty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class MemberInfoExtensions
|
|
||||||
{
|
|
||||||
internal static bool IsPropertyWithSetter(this MemberInfo member)
|
|
||||||
{
|
|
||||||
var property = member as PropertyInfo;
|
|
||||||
|
|
||||||
return property?.GetSetMethod(true) != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace AntiAntiSwearingBot;
|
||||||
|
public static class IServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static IServiceCollection AddHostedSingleton<TService>(this IServiceCollection isc) where TService : class, IHostedService
|
||||||
|
{
|
||||||
|
return isc.AddSingleton<TService>().AddHostedService(svc => svc.GetRequiredService<TService>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace AntiAntiSwearingBot;
|
||||||
|
|
||||||
|
public readonly ref struct ReadLockToken
|
||||||
|
{
|
||||||
|
ReaderWriterLockSlim Lock { get; }
|
||||||
|
public ReadLockToken(ReaderWriterLockSlim l) => (Lock = l).EnterReadLock();
|
||||||
|
public void Dispose() => Lock.ExitReadLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly ref struct WriteLockToken
|
||||||
|
{
|
||||||
|
ReaderWriterLockSlim Lock { get; }
|
||||||
|
public WriteLockToken(ReaderWriterLockSlim l) => (Lock = l).EnterWriteLock();
|
||||||
|
public void Dispose() => Lock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ReaderWriterLockSlimExtensions
|
||||||
|
{
|
||||||
|
public static ReadLockToken GetReadLockToken(this ReaderWriterLockSlim l) => new ReadLockToken(l);
|
||||||
|
public static WriteLockToken GetWriteLockToken(this ReaderWriterLockSlim l) => new WriteLockToken(l);
|
||||||
|
}
|
||||||
@ -3,3 +3,4 @@ global using System.Text;
|
|||||||
global using System.Linq;
|
global using System.Linq;
|
||||||
global using System.Collections.Generic;
|
global using System.Collections.Generic;
|
||||||
|
|
||||||
|
global using Microsoft.Extensions.Options;
|
||||||
@ -1,31 +1,34 @@
|
|||||||
using System.Threading;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using AntiAntiSwearingBot;
|
using AntiAntiSwearingBot;
|
||||||
|
|
||||||
static void Log(string m) => Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}|{m}");
|
var builder = WebApplication.CreateBuilder();
|
||||||
|
|
||||||
Log("AntiAntiSwearBot starting....");
|
builder.WebHost.ConfigureAppConfiguration((hostingContext, config) =>
|
||||||
|
|
||||||
var cfg = Config.Load<Config>("aasb.cfg.json", "aasb.cfg.secret.json");
|
|
||||||
var dict = new SearchDictionary(cfg);
|
|
||||||
Log($"{dict.Count} words loaded.");
|
|
||||||
var bot = new AntiAntiSwearingBot.AntiAntiSwearingBot(cfg, dict);
|
|
||||||
bot.Init().Wait();
|
|
||||||
Log($"Connected to Telegram as @{bot.Me.Username}");
|
|
||||||
Log("AntiAntiSwearBot started! Press Ctrl-C to exit.");
|
|
||||||
|
|
||||||
ManualResetEvent quitEvent = new ManualResetEvent(false);
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
Console.CancelKeyPress += (sender, eArgs) => // ctrl-c
|
var env = hostingContext.HostingEnvironment.EnvironmentName;
|
||||||
{
|
config.AddJsonFile("secrets.json", optional: true, reloadOnChange: true);
|
||||||
eArgs.Cancel = true;
|
config.AddJsonFile($"secrets.{env}.json", optional: true, reloadOnChange: true);
|
||||||
quitEvent.Set();
|
});
|
||||||
};
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
quitEvent.WaitOne(Timeout.Infinite);
|
var cfg = builder.Configuration;
|
||||||
|
var svc = builder.Services;
|
||||||
|
|
||||||
Console.WriteLine("Waiting for exit...");
|
svc.Configure<SearchDictionarySettings>(cfg.GetSection("SearchDictionary"));
|
||||||
dict.Save();
|
svc.Configure<TelegramSettings>(cfg.GetSection("Telegram"));
|
||||||
|
svc.Configure<UnbleeperSettings>(cfg.GetSection("Unbleeper"));
|
||||||
|
|
||||||
|
svc.AddHealthChecks().AddCheck<StartupHealthCheck>("Startup");
|
||||||
|
svc.AddHostedSingleton<SearchDictionary>();
|
||||||
|
svc.AddSingleton<Unbleeper>();
|
||||||
|
svc.AddHostedSingleton<Aasb>();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
app.UseRouting();
|
||||||
|
app.UseEndpoints(cfg =>
|
||||||
|
{
|
||||||
|
cfg.MapHealthChecks("/health");
|
||||||
|
});
|
||||||
|
app.Run();
|
||||||
|
|||||||
@ -7,9 +7,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
|
|||||||
<PublishProtocol>FileSystem</PublishProtocol>
|
<PublishProtocol>FileSystem</PublishProtocol>
|
||||||
<Configuration>Release</Configuration>
|
<Configuration>Release</Configuration>
|
||||||
<Platform>Any CPU</Platform>
|
<Platform>Any CPU</Platform>
|
||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<PublishDir>i:\aasb</PublishDir>
|
<PublishDir>C:\Prog\releases\aasb</PublishDir>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
|
||||||
<SelfContained>false</SelfContained>
|
<SelfContained>false</SelfContained>
|
||||||
<_IsPortable>true</_IsPortable>
|
<_IsPortable>true</_IsPortable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
27
AntiAntiSwearingBot/Properties/launchSettings.json
Normal file
27
AntiAntiSwearingBot/Properties/launchSettings.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:50671/",
|
||||||
|
"sslPort": 44398
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AntiAntiSwearingBot": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": false,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,55 +1,73 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
namespace AntiAntiSwearingBot;
|
namespace AntiAntiSwearingBot;
|
||||||
public class SearchDictionary
|
public class SearchDictionary : BackgroundService
|
||||||
{
|
{
|
||||||
public SearchDictionary(Config cfg)
|
public SearchDictionary(IOptionsMonitor<SearchDictionarySettings> cfg)
|
||||||
{
|
{
|
||||||
var s = cfg.SearchDictionary;
|
Cfg = cfg;
|
||||||
path = s.DictionaryPath;
|
var path = cfg.CurrentValue.DictionaryPath;
|
||||||
tmppath = path + ".tmp";
|
|
||||||
|
|
||||||
words = File.ReadAllLines(path).ToList();
|
words = File.ReadAllLines(path).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IOptionsMonitor<SearchDictionarySettings> Cfg { get; }
|
||||||
|
ReaderWriterLockSlim DictLock = new();
|
||||||
|
List<string> words;
|
||||||
|
bool Changed = false;
|
||||||
|
|
||||||
public int Count => words.Count;
|
public int Count => words.Count;
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
|
||||||
|
if (Changed) Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
{
|
{
|
||||||
if (File.Exists(tmppath))
|
using var guard = DictLock.GetWriteLockToken();
|
||||||
File.Delete(tmppath);
|
Changed = false;
|
||||||
|
|
||||||
|
var path = Cfg.CurrentValue.DictionaryPath;
|
||||||
|
var tmppath = path + ".tmp";
|
||||||
|
|
||||||
File.WriteAllLines(tmppath, words);
|
File.WriteAllLines(tmppath, words);
|
||||||
if (File.Exists(path))
|
File.Move(tmppath, path, overwrite: true);
|
||||||
File.Delete(path);
|
|
||||||
File.Move(tmppath, path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct WordMatch
|
public record struct WordMatch (string Word, int Distance, int Rating);
|
||||||
{
|
|
||||||
public string Word;
|
|
||||||
public int Distance;
|
|
||||||
public int Rating;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WordMatch Match(string pattern)
|
public WordMatch Match(string pattern)
|
||||||
=> AllMatches(pattern).First();
|
=> AllMatches(pattern).First();
|
||||||
|
|
||||||
public IEnumerable<WordMatch> AllMatches(string pattern)
|
public IEnumerable<WordMatch> AllMatches(string pattern)
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
using var guard = DictLock.GetReadLockToken();
|
||||||
{
|
|
||||||
pattern = pattern.ToLowerInvariant();
|
|
||||||
return words
|
return words
|
||||||
.Select((w, i) => new WordMatch { Word = w, Distance = Language.LevenshteinDistance(pattern, w), Rating = i })
|
.Select((w, i) => new WordMatch(w, Language.LevenshteinDistance(pattern.ToLowerInvariant(), w), i))
|
||||||
.OrderBy(m => m.Distance)
|
.OrderBy(m => m.Distance)
|
||||||
.ThenBy(m => m.Rating);
|
.ThenBy(m => m.Rating);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public bool Learn(string word)
|
public bool Learn(string word)
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
using var guard = DictLock.GetWriteLockToken();
|
||||||
{
|
Changed = true;
|
||||||
|
|
||||||
int index = words.IndexOf(word);
|
int index = words.IndexOf(word);
|
||||||
if (index > 0)
|
if (index > 0)
|
||||||
{
|
{
|
||||||
@ -62,20 +80,12 @@ public class SearchDictionary
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public bool Unlearn(string word)
|
public bool Unlearn(string word)
|
||||||
{
|
{
|
||||||
lock (SyncRoot)
|
using var guard = DictLock.GetWriteLockToken();
|
||||||
|
Changed = true;
|
||||||
return words.Remove(word);
|
return words.Remove(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region service
|
|
||||||
|
|
||||||
readonly string path, tmppath;
|
|
||||||
|
|
||||||
object SyncRoot = new object();
|
|
||||||
List<string> words;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|||||||
24
AntiAntiSwearingBot/StartupHealthCheck.cs
Normal file
24
AntiAntiSwearingBot/StartupHealthCheck.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
|
|
||||||
|
namespace AntiAntiSwearingBot;
|
||||||
|
public class StartupHealthCheck : IHealthCheck
|
||||||
|
{
|
||||||
|
Aasb Bot { get; }
|
||||||
|
|
||||||
|
public StartupHealthCheck(Aasb bot)
|
||||||
|
{
|
||||||
|
Bot = bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<HealthCheckResult> CheckHealthAsync(
|
||||||
|
HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (Bot.Started)
|
||||||
|
return Task.FromResult(HealthCheckResult.Healthy("The startup task has completed."));
|
||||||
|
else
|
||||||
|
return Task.FromResult(HealthCheckResult.Unhealthy("That startup task is still running."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,10 +7,10 @@ public class Unbleeper
|
|||||||
SearchDictionary Dict { get; }
|
SearchDictionary Dict { get; }
|
||||||
UnbleeperSettings Cfg { get; }
|
UnbleeperSettings Cfg { get; }
|
||||||
|
|
||||||
public Unbleeper(SearchDictionary dict, UnbleeperSettings cfg)
|
public Unbleeper(SearchDictionary dict, IOptions<UnbleeperSettings> cfg)
|
||||||
{
|
{
|
||||||
Dict = dict;
|
Dict = dict;
|
||||||
Cfg = cfg;
|
Cfg = cfg.Value;
|
||||||
BleepedSwearsRegex = new Regex("^" + Cfg.BleepedSwearsRegex + "$", RegexOptions.Compiled);
|
BleepedSwearsRegex = new Regex("^" + Cfg.BleepedSwearsRegex + "$", RegexOptions.Compiled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +41,9 @@ public class Unbleeper
|
|||||||
)
|
)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
if (candidates.Any())
|
if (!candidates.Any())
|
||||||
{
|
return null;
|
||||||
|
|
||||||
var response = new StringBuilder();
|
var response = new StringBuilder();
|
||||||
for (int i = 0; i < candidates.Length; ++i)
|
for (int i = 0; i < candidates.Length; ++i)
|
||||||
{
|
{
|
||||||
@ -51,7 +52,4 @@ public class Unbleeper
|
|||||||
}
|
}
|
||||||
return response.ToString();
|
return response.ToString();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
{
|
{
|
||||||
|
"Kestrel": {
|
||||||
|
"Endpoints": {
|
||||||
|
"Http": {
|
||||||
|
"Url": "http://localhost:9999"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Unbleeper": {
|
"Unbleeper": {
|
||||||
"BleepedSwearsRegex": "[а-яА-ЯёЁ@\\*#]+",
|
"BleepedSwearsRegex": "[а-яА-ЯёЁ@\\*#]+",
|
||||||
"MinWordLength": 3,
|
"MinWordLength": 3,
|
||||||
@ -6,5 +13,8 @@
|
|||||||
},
|
},
|
||||||
"SearchDictionary": {
|
"SearchDictionary": {
|
||||||
"DictionaryPath": "dict/ObsceneDictionaryRu.txt"
|
"DictionaryPath": "dict/ObsceneDictionaryRu.txt"
|
||||||
|
},
|
||||||
|
"Telegram": {
|
||||||
|
"UseProxy": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user