add DictionaryDB

This commit is contained in:
sim1222 2023-08-05 23:14:16 +09:00
parent 842a1550dc
commit 21bc3bd775
10 changed files with 271 additions and 41 deletions

View File

@ -4,6 +4,7 @@ using System.Text.RegularExpressions;
using Discord; using Discord;
using Discord.Audio; using Discord.Audio;
using Discord.WebSocket; using Discord.WebSocket;
using Elementary.Dictionary;
using ManagedBass; using ManagedBass;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NAudio.Wave; using NAudio.Wave;
@ -24,6 +25,8 @@ public class AudioManager
private AudioConverter _audioConverter; private AudioConverter _audioConverter;
private PlaybackQueue _playbackQueue; private PlaybackQueue _playbackQueue;
private EmojiDictionary _emojiDictionary;
private DictionaryDB _dictionaryDB;
// private AudioMixer _audioMixer; // private AudioMixer _audioMixer;
private ILogger _logger; private ILogger _logger;
@ -31,13 +34,15 @@ public class AudioManager
public bool isConnected; public bool isConnected;
public AudioManager(IServiceProvider services, DiscordSocketClient client, SozaiAPI sozaiApi, public AudioManager(IServiceProvider services, DiscordSocketClient client, SozaiAPI sozaiApi,
VoicevoxAPI voicevoxApi) VoicevoxAPI voicevoxApi, EmojiDictionary emojiDictionary, DictionaryDB dictionaryDB)
{ {
_services = services; _services = services;
_client = client; _client = client;
_sozaiAPI = sozaiApi; _sozaiAPI = sozaiApi;
_voicevoxAPI = voicevoxApi; _voicevoxAPI = voicevoxApi;
_audioConverter = new(); _audioConverter = new();
_emojiDictionary = emojiDictionary;
_dictionaryDB = dictionaryDB;
_logger = LogManager.GetCurrentClassLogger(); _logger = LogManager.GetCurrentClassLogger();
} }
@ -72,9 +77,11 @@ public class AudioManager
_audioClient.Dispose(); _audioClient.Dispose();
} }
public async Task PlayAudio(string path) public async Task PlayAudio(string path, float volume = 1.0f)
{ {
await using var wave = _audioConverter.CreateStreamFromFilePath(path, 0.15f); float maxVolume = 0.15f;
await using var wave = _audioConverter.CreateStreamFromFilePath(path, volume * maxVolume);
_logger.Log(LogLevel.Info, $"Playing {path} {wave.Length} bytes"); _logger.Log(LogLevel.Info, $"Playing {path} {wave.Length} bytes");
@ -88,7 +95,13 @@ public class AudioManager
if (text.Contains("```")) text = "コードブロック"; if (text.Contains("```")) text = "コードブロック";
if (text.StartsWith("!") || text.StartsWith(".")) return; if (text.StartsWith("!") || text.StartsWith(".")) return;
text = Regex.Replace(text, @"https?://[\w/:%#\$&\?\(\)~\.=\+\-]+", "URL"); text = Regex.Replace(text, @"https?://[\w/:%#\$&\?\(\)~\.=\+\-]+", "URL");
text = Regex.Replace(text, @"<:[\w]+:[\d]+>", m => m.Value.Split(":")[1]);
text = Regex.Replace(text, @"<:[\w]+:[\d]+>", m => m.Value.Split(":")[1]); // <:emoji:123456789> -> emoji
text = Regex.Replace(text, @"<a:[\w]+:[\d]+>", m => m.Value.Split(":")[1]); // <:emoji:123456789> -> emoji
text = _dictionaryDB.Replace(text);
text = _emojiDictionary.Replace(text);
float volume = 0.12f; float volume = 0.12f;

View File

@ -7,7 +7,6 @@ public class SozaiAPI
{ {
private ILogger _logger; private ILogger _logger;
private Uri AssetsInfoUrl = new("https://r2.sim1222.com/sozai/index.json");
private Asset[] Assets; private Asset[] Assets;
private class Asset private class Asset
@ -32,9 +31,9 @@ public class SozaiAPI
_logger = LogManager.GetCurrentClassLogger(); _logger = LogManager.GetCurrentClassLogger();
} }
public async Task Setup() public async Task Setup(string url)
{ {
var response = await _client.GetAsync(AssetsInfoUrl); var response = await _client.GetAsync(url);
var stream = await response.Content.ReadAsStreamAsync(); var stream = await response.Content.ReadAsStreamAsync();
Assets = await JsonSerializer.DeserializeAsync<Asset[]>(stream); Assets = await JsonSerializer.DeserializeAsync<Asset[]>(stream);
@ -47,10 +46,10 @@ public class SozaiAPI
var asset = Assets.FirstOrDefault(asset => asset.names.Contains(name)); var asset = Assets.FirstOrDefault(asset => asset.names.Contains(name));
if (asset == null) return null; if (asset == null) return null;
Console.WriteLine($"Requested {asset.names[0]}"); _logger.Info($"Requested {asset.names[0]}");
var response = await _client.GetAsync(asset.url); var response = await _client.GetAsync(asset.url);
Console.WriteLine($"Got response {response.StatusCode}"); _logger.Info($"Got response {response.StatusCode}");
var stream = await response.Content.ReadAsStreamAsync(); var stream = await response.Content.ReadAsStreamAsync();
return stream; return stream;
} }

View File

@ -1,6 +1,7 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Elementary.Audio; using Elementary.Audio;
using Elementary.Dictionary;
namespace Elementary.Commands; namespace Elementary.Commands;
@ -9,12 +10,15 @@ public class MessageCommands : ModuleBase<SocketCommandContext>
private AudioManager _audioManager; private AudioManager _audioManager;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly CommandService _commandService; private readonly CommandService _commandService;
private DictionaryDB _dictionaryDB;
public MessageCommands(AudioManager audioManager, CommandService commandService, IServiceProvider serviceProvider) public MessageCommands(AudioManager audioManager, CommandService commandService, IServiceProvider serviceProvider,
DictionaryDB dictionaryDB)
{ {
_audioManager = audioManager; _audioManager = audioManager;
_commandService = commandService; _commandService = commandService;
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_dictionaryDB = dictionaryDB;
} }
[Command("help")] [Command("help")]
@ -68,13 +72,44 @@ public class MessageCommands : ModuleBase<SocketCommandContext>
[Summary("Plays audio from a file.")] [Summary("Plays audio from a file.")]
public async Task PlayAsync([Remainder] [Summary("The path to the file")] string path) public async Task PlayAsync([Remainder] [Summary("The path to the file")] string path)
{ {
path = path.Trim('"');
await _audioManager.PlayAudio(path); await _audioManager.PlayAudio(path);
} }
[Command("stop", RunMode = RunMode.Async)] [Command("stop", RunMode = RunMode.Async)]
[Summary("Stops playing audio.")] [Summary("Stops playing audio.")]
public async Task StopAsync() public async Task StopAsync()
{ {
await _audioManager.StopAudio(); await _audioManager.StopAudio();
} }
[Command("add", RunMode = RunMode.Async)]
[Summary("Adds a word to the dictionary.")]
public Task AddAsync( [Summary("From Surface")] string surface, [Summary("To Pronunciation")]string pronunciation)
{
_dictionaryDB.Add(surface, pronunciation);
return ReplyAsync($"Added {surface} -> {pronunciation}");
}
[Command("del", RunMode = RunMode.Async)]
[Summary("Deletes a word from the dictionary.")]
public Task DeleteAsync([Summary("From Surface")] string surface)
{
_dictionaryDB.Remove(surface);
return ReplyAsync($"Removed {surface}");
}
[Command("list", RunMode = RunMode.Async)]
[Summary("Lists all words in the dictionary.")]
public Task ListAsync()
{
var dict = _dictionaryDB.GetAll();
string result = "```\n";
foreach (var item in dict)
{
result += $"{item.Key} -> {item.Value}\n";
}
result += "```";
return ReplyAsync(result);
}
} }

View File

@ -7,6 +7,24 @@ namespace Elementary.Config;
public class AppSettings public class AppSettings
{ {
public DiscordSettings DiscordSettings { get; set; } public DiscordSettings DiscordSettings { get; set; }
public SozaiSettings SozaiSettings { get; set; }
public EmojiSettings EmojiSettings { get; set; }
public VoicevoxSettings VoicevoxSettings { get; set; }
}
public class VoicevoxSettings
{
public string Url { get; set; }
}
public class EmojiSettings
{
public string DictionaryPath { get; set; }
}
public class SozaiSettings
{
public string Url { get; set; }
} }
public class DiscordSettings public class DiscordSettings

View File

@ -0,0 +1,84 @@
using System.Text.RegularExpressions;
using LiteDB;
using NLog;
namespace Elementary.Dictionary;
public class DictionaryDB
{
private LiteDatabase _db;
private ILogger _logger;
private class DictionaryItem
{
public string Surface { get; set; }
public string Pronunciation { get; set; }
public int Priority { get; set; }
}
public DictionaryDB()
{
_logger = LogManager.GetCurrentClassLogger();
}
public async Task Setup()
{
_db = new LiteDatabase("dictionary.db");
}
public void Add(string surface, string pronunciation, int priority = 5)
{
surface = Regex.Replace(surface, @"<:[\w]+:[\d]+>", m => m.Value.Split(":")[1]); // <:emoji:123456789> -> emoji
surface = Regex.Replace(surface, @"<a:[\w]+:[\d]+>", m => m.Value.Split(":")[1]); // <:emoji:123456789> -> emoji
var collection = _db.GetCollection<DictionaryItem>("dictionary");
// Remove existing items
collection.DeleteMany(item => item.Surface == surface);
collection.Insert(new DictionaryItem
{
Surface = surface,
Pronunciation = pronunciation,
Priority = priority
});
_logger.Info($"Added {surface} -> {pronunciation}");
}
public void Remove(string surface)
{
surface = Regex.Replace(surface, @"<:[\w]+:[\d]+>", m => m.Value.Split(":")[1]); // <:emoji:123456789> -> emoji
surface = Regex.Replace(surface, @"<a:[\w]+:[\d]+>", m => m.Value.Split(":")[1]); // <:emoji:123456789> -> emoji
var collection = _db.GetCollection<DictionaryItem>("dictionary");
collection.DeleteMany(item => item.Surface == surface);
_logger.Info($"Removed {surface}");
}
public string Replace(string text)
{
var collection = _db.GetCollection<DictionaryItem>("dictionary");
var items = collection.FindAll().OrderByDescending(item => item.Priority);
foreach (var item in items)
{
text = text.Replace(item.Surface, item.Pronunciation);
}
return text;
}
public Dictionary<string, string> GetAll()
{
var collection = _db.GetCollection<DictionaryItem>("dictionary");
var items = collection.FindAll();
var dict = new Dictionary<string, string>();
foreach (var item in items)
{
dict.Add(item.Surface, item.Pronunciation);
}
return dict;
}
}

View File

@ -0,0 +1,46 @@

using System.Text.Json;
using Microsoft.Extensions.Logging;
using NLog;
using ILogger = NLog.ILogger;
namespace Elementary.Dictionary;
public class EmojiDictionary
{
private Dictionary<string, DictionaryItem> _dict;
private readonly ILogger _logger;
private class DictionaryItem
{
public string[] keywords { get; set; }
public string short_name { get; set; }
public string group { get; set; }
public string subgroup { get; set; }
}
public EmojiDictionary()
{
_logger = LogManager.GetCurrentClassLogger();
}
public async Task Setup(string Path)
{
using StreamReader file = new(Path);
_dict = await JsonSerializer.DeserializeAsync<Dictionary<string, DictionaryItem>>(file.BaseStream);
_logger.Info($"Loaded {_dict.Count} emoji");
}
public string Replace(string text)
{
string[] ignoreKeys = {"、", "。"};
foreach (var (key, value) in _dict)
{
if (ignoreKeys.Contains(key)) continue;
text = text.Replace(key, value.short_name);
}
return text;
}
}

View File

@ -17,20 +17,21 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CSCore" Version="1.2.1.2" /> <PackageReference Include="CSCore" Version="1.2.1.2"/>
<PackageReference Include="Discord.Net" Version="3.11.0" /> <PackageReference Include="Discord.Net" Version="3.11.0"/>
<PackageReference Include="Discord.Net.Core" Version="3.11.0" /> <PackageReference Include="Discord.Net.Core" Version="3.11.0"/>
<PackageReference Include="ManagedBass" Version="3.1.1" /> <PackageReference Include="LiteDB" Version="5.0.17" />
<PackageReference Include="ManagedBass.Mix" Version="3.1.1" /> <PackageReference Include="ManagedBass" Version="3.1.1"/>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" /> <PackageReference Include="ManagedBass.Mix" Version="3.1.1"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4"/>
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0"/>
<PackageReference Include="NAudio" Version="2.1.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0"/>
<PackageReference Include="NLog" Version="5.2.2" /> <PackageReference Include="NAudio" Version="2.1.0"/>
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.2" /> <PackageReference Include="NLog" Version="5.2.2"/>
<PackageReference Include="System.Speech" Version="7.0.0" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.3.2"/>
<PackageReference Include="System.Speech" Version="7.0.0"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -44,14 +45,18 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="bass.dll"> <None Update="bass.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="bassmix.dll"> <None Update="bassmix.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="nlog.config"> <None Update="nlog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="emoji-ja\data\emoji_ja.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -6,6 +6,7 @@ using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using Elementary.Audio; using Elementary.Audio;
using Elementary.Commands; using Elementary.Commands;
using Elementary.Dictionary;
using ManagedBass; using ManagedBass;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -27,10 +28,23 @@ public class Program
private Handler _handler; private Handler _handler;
private SozaiAPI _sozaiAPI; private SozaiAPI _sozaiAPI;
private VoicevoxAPI _voicevoxAPI; private VoicevoxAPI _voicevoxAPI;
private EmojiDictionary _emojiDictionary;
private DictionaryDB _dictionaryDB;
public static void Main(string[] args) public static async Task Main(string[] args)
{ {
new Program().Setup().GetAwaiter().GetResult(); while (true)
{
try
{
await new Program().Setup();
await Task.Delay(-1);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
} }
private async Task Setup() private async Task Setup()
@ -57,6 +71,8 @@ public class Program
.AddSingleton<PlaybackQueue>() .AddSingleton<PlaybackQueue>()
.AddTransient<AudioConverter>() .AddTransient<AudioConverter>()
.AddSingleton<MessageHandler>() .AddSingleton<MessageHandler>()
.AddSingleton<EmojiDictionary>()
.AddSingleton<DictionaryDB>()
.BuildServiceProvider(); .BuildServiceProvider();
_logger = LogManager.GetCurrentClassLogger(); _logger = LogManager.GetCurrentClassLogger();
@ -64,7 +80,10 @@ public class Program
_sozaiAPI = _services.GetRequiredService<SozaiAPI>(); _sozaiAPI = _services.GetRequiredService<SozaiAPI>();
_voicevoxAPI = _services.GetRequiredService<VoicevoxAPI>(); _voicevoxAPI = _services.GetRequiredService<VoicevoxAPI>();
_handler = new Handler(_client, _commands, _services, _services.GetRequiredService<AudioManager>(), _emojiDictionary = _services.GetRequiredService<EmojiDictionary>();
_dictionaryDB = _services.GetRequiredService<DictionaryDB>();
_handler = new(_client, _commands, _services, _services.GetRequiredService<AudioManager>(),
_services.GetRequiredService<MessageHandler>()); _services.GetRequiredService<MessageHandler>());
_client.Log += Log; _client.Log += Log;
@ -73,8 +92,10 @@ public class Program
// Bass.Init(-1, 44100, DeviceInitFlags.NoSpeakerAssignment, IntPtr.Zero); // Bass.Init(-1, 44100, DeviceInitFlags.NoSpeakerAssignment, IntPtr.Zero);
await _handler.Setup(); await _handler.Setup();
await _sozaiAPI.Setup(); await _sozaiAPI.Setup(configuration.AppSettings.SozaiSettings.Url);
await _voicevoxAPI.Setup("http://localhost:50021"); await _voicevoxAPI.Setup(configuration.AppSettings.VoicevoxSettings.Url);
await _emojiDictionary.Setup(configuration.AppSettings.EmojiSettings.DictionaryPath);
await _dictionaryDB.Setup();
await _client.LoginAsync(TokenType.Bot, configuration.AppSettings.DiscordSettings.Token); await _client.LoginAsync(TokenType.Bot, configuration.AppSettings.DiscordSettings.Token);
await _client.StartAsync(); await _client.StartAsync();
await _client.SetActivityAsync(new Game("!join")); await _client.SetActivityAsync(new Game("!join"));
@ -84,7 +105,7 @@ public class Program
// await _client.StopAsync(); // await _client.StopAsync();
// await _client.StartAsync(); // await _client.StartAsync();
// }; // };
_client.UserVoiceStateUpdated += async (user, before, after) => _client.UserVoiceStateUpdated += async (user, before, after) =>
{ {
if (user.Id != _client.CurrentUser.Id) return; if (user.Id != _client.CurrentUser.Id) return;

View File

@ -9,6 +9,15 @@
"AppSettings": { "AppSettings": {
"DiscordSettings": { "DiscordSettings": {
"Token": "MTEzMDAzMjkxMTQzMzg1OTE5Mw.G8yVqt.HrC65f0t4dvZQIZ7iER4CV70pLvhAl8PIyknSs" "Token": "MTEzMDAzMjkxMTQzMzg1OTE5Mw.G8yVqt.HrC65f0t4dvZQIZ7iER4CV70pLvhAl8PIyknSs"
},
"VoicevoxSettings": {
"Url": "http://localhost:50021"
},
"SozaiSettings": {
"Url": "https://r2.sim1222.com/sozai/index.json"
},
"EmojiSettings": {
"DictionaryPath": "emoji-ja\\data\\emoji_ja.json"
} }
} }
} }

View File

@ -4,7 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true" autoReload="true"
internalLogFile="c:\temp\console-example-internal.log" internalLogFile="c:\temp\console-example-internal.log"
internalLogLevel="Info" > internalLogLevel="Info">
<!-- the targets to write to --> <!-- the targets to write to -->
<targets> <targets>
@ -14,17 +14,17 @@
<!-- <target xsi:type="Console" name="logconsole" --> <!-- <target xsi:type="Console" name="logconsole" -->
<!-- layout="${longdate} | ${level} |${message} |${all-event-properties} ${exception:format=tostring}" /> --> <!-- layout="${longdate} | ${level} |${message} |${all-event-properties} ${exception:format=tostring}" /> -->
<target name="coloredConsole" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false" <target name="coloredConsole" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false"
layout="${longdate} [${level:uppercase=true:padding=-4}] ${logger} | ${message} ${all-event-properties} ${exception:format=tostring}" > layout="${longdate} [${level:uppercase=true:padding=-4}] ${logger} | ${message} ${all-event-properties} ${exception:format=tostring}">
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> <highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray"/>
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> <highlight-row condition="level == LogLevel.Info" foregroundColor="Gray"/>
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> <highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow"/>
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> <highlight-row condition="level == LogLevel.Error" foregroundColor="Red"/>
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> <highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White"/>
</target> </target>
</targets> </targets>
<!-- rules to map from logger name to target --> <!-- rules to map from logger name to target -->
<rules> <rules>
<logger name="*" minlevel="Trace" writeTo="logfile,coloredConsole" /> <logger name="*" minlevel="Trace" writeTo="logfile,coloredConsole"/>
</rules> </rules>
</nlog> </nlog>