mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 17:58:26 -04:00
Applied codestyle to all .cs files
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
@@ -19,20 +19,15 @@ public sealed class BehaviorExecutor : IBehaviourExecutor, INService
|
||||
{
|
||||
_lateExecutors = _services.GetServices<ILateExecutor>();
|
||||
_lateBlockers = _services.GetServices<ILateBlocker>();
|
||||
_earlyBehaviors = _services.GetServices<IEarlyBehavior>()
|
||||
.OrderByDescending(x => x.Priority);
|
||||
_earlyBehaviors = _services.GetServices<IEarlyBehavior>().OrderByDescending(x => x.Priority);
|
||||
_transformers = _services.GetServices<IInputTransformer>();
|
||||
}
|
||||
|
||||
public async Task<bool> RunEarlyBehavioursAsync(SocketGuild guild, IUserMessage usrMsg)
|
||||
{
|
||||
foreach (var beh in _earlyBehaviors)
|
||||
{
|
||||
if (await beh.RunBehavior(guild, usrMsg))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -43,7 +38,7 @@ public sealed class BehaviorExecutor : IBehaviourExecutor, INService
|
||||
foreach (var exec in _transformers)
|
||||
{
|
||||
string newContent;
|
||||
if ((newContent = await exec.TransformInput(guild, usrMsg.Channel, usrMsg.Author, messageContent))
|
||||
if ((newContent = await exec.TransformInput(guild, usrMsg.Channel, usrMsg.Author, messageContent))
|
||||
!= messageContent.ToLowerInvariant())
|
||||
{
|
||||
messageContent = newContent;
|
||||
@@ -57,16 +52,14 @@ public sealed class BehaviorExecutor : IBehaviourExecutor, INService
|
||||
public async Task<bool> RunLateBlockersAsync(ICommandContext ctx, CommandInfo cmd)
|
||||
{
|
||||
foreach (var exec in _lateBlockers)
|
||||
{
|
||||
if (await exec.TryBlockLate(ctx, cmd.Module.GetTopLevelModule().Name, cmd))
|
||||
{
|
||||
Log.Information("Late blocking User [{0}] Command: [{1}] in [{2}]",
|
||||
Log.Information("Late blocking User [{0}] Command: [{1}] in [{2}]",
|
||||
ctx.User,
|
||||
cmd.Aliases[0],
|
||||
exec.GetType().Name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -74,17 +67,13 @@ public sealed class BehaviorExecutor : IBehaviourExecutor, INService
|
||||
public async Task RunLateExecutorsAsync(SocketGuild guild, IUserMessage usrMsg)
|
||||
{
|
||||
foreach (var exec in _lateExecutors)
|
||||
{
|
||||
try
|
||||
{
|
||||
await exec.LateExecute(guild, usrMsg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error in {TypeName} late executor: {ErrorMessage}",
|
||||
exec.GetType().Name,
|
||||
ex.Message);
|
||||
Log.Error(ex, "Error in {TypeName} late executor: {ErrorMessage}", exec.GetType().Name, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,63 +12,81 @@ public interface IBotCredsProvider
|
||||
public IBotCredentials GetCreds();
|
||||
public void ModifyCredsFile(Action<Creds> func);
|
||||
}
|
||||
|
||||
|
||||
public sealed class BotCredsProvider : IBotCredsProvider
|
||||
{
|
||||
private readonly int? _totalShards;
|
||||
private const string _credsFileName = "creds.yml";
|
||||
private const string _credsExampleFileName = "creds_example.yml";
|
||||
|
||||
private string CredsPath => Path.Combine(Directory.GetCurrentDirectory(), _credsFileName);
|
||||
private string CredsExamplePath => Path.Combine(Directory.GetCurrentDirectory(), _credsExampleFileName);
|
||||
private string OldCredsJsonPath => Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
|
||||
private string OldCredsJsonBackupPath => Path.Combine(Directory.GetCurrentDirectory(), "credentials.json.bak");
|
||||
|
||||
|
||||
private string CredsPath
|
||||
=> Path.Combine(Directory.GetCurrentDirectory(), _credsFileName);
|
||||
|
||||
private string CredsExamplePath
|
||||
=> Path.Combine(Directory.GetCurrentDirectory(), _credsExampleFileName);
|
||||
|
||||
private string OldCredsJsonPath
|
||||
=> Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
|
||||
|
||||
private string OldCredsJsonBackupPath
|
||||
=> Path.Combine(Directory.GetCurrentDirectory(), "credentials.json.bak");
|
||||
|
||||
private readonly int? _totalShards;
|
||||
|
||||
|
||||
private readonly Creds _creds = new();
|
||||
private readonly IConfigurationRoot _config;
|
||||
|
||||
|
||||
|
||||
private readonly object reloadLock = new();
|
||||
|
||||
public BotCredsProvider(int? totalShards = null)
|
||||
{
|
||||
_totalShards = totalShards;
|
||||
if (!File.Exists(CredsExamplePath)) File.WriteAllText(CredsExamplePath, Yaml.Serializer.Serialize(_creds));
|
||||
|
||||
MigrateCredentials();
|
||||
|
||||
if (!File.Exists(CredsPath))
|
||||
Log.Warning($"{CredsPath} is missing. "
|
||||
+ "Attempting to load creds from environment variables prefixed with 'NadekoBot_'. "
|
||||
+ $"Example is in {CredsExamplePath}");
|
||||
|
||||
_config = new ConfigurationBuilder().AddYamlFile(CredsPath, false, true)
|
||||
.AddEnvironmentVariables("NadekoBot_")
|
||||
.Build();
|
||||
|
||||
ChangeToken.OnChange(() => _config.GetReloadToken(), Reload);
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
lock (reloadLock)
|
||||
{
|
||||
_creds.OwnerIds.Clear();
|
||||
_config.Bind(_creds);
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_creds.Token))
|
||||
{
|
||||
Log.Error("Token is missing from creds.yml or Environment variables.\n" +
|
||||
"Add it and restart the program.");
|
||||
Log.Error("Token is missing from creds.yml or Environment variables.\n"
|
||||
+ "Add it and restart the program.");
|
||||
Helpers.ReadErrorAndExit(5);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_creds.RestartCommand?.Cmd)
|
||||
|| string.IsNullOrWhiteSpace(_creds.RestartCommand?.Args))
|
||||
{
|
||||
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
||||
{
|
||||
_creds.RestartCommand = new()
|
||||
{
|
||||
Args = "dotnet",
|
||||
Cmd = "NadekoBot.dll -- {0}",
|
||||
};
|
||||
}
|
||||
_creds.RestartCommand = new() { Args = "dotnet", Cmd = "NadekoBot.dll -- {0}" };
|
||||
else
|
||||
{
|
||||
_creds.RestartCommand = new()
|
||||
{
|
||||
Args = "NadekoBot.exe",
|
||||
Cmd = "{0}",
|
||||
};
|
||||
}
|
||||
_creds.RestartCommand = new() { Args = "NadekoBot.exe", Cmd = "{0}" };
|
||||
}
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_creds.RedisOptions))
|
||||
_creds.RedisOptions = "127.0.0.1,syncTimeout=3000";
|
||||
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_creds.CoinmarketcapApiKey))
|
||||
_creds.CoinmarketcapApiKey = "e79ec505-0913-439d-ae07-069e296a6079";
|
||||
|
||||
@@ -76,35 +94,6 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
||||
}
|
||||
}
|
||||
|
||||
public BotCredsProvider(int? totalShards = null)
|
||||
{
|
||||
_totalShards = totalShards;
|
||||
if (!File.Exists(CredsExamplePath))
|
||||
{
|
||||
File.WriteAllText(CredsExamplePath, Yaml.Serializer.Serialize(_creds));
|
||||
}
|
||||
|
||||
MigrateCredentials();
|
||||
|
||||
if (!File.Exists(CredsPath))
|
||||
{
|
||||
Log.Warning($"{CredsPath} is missing. " +
|
||||
$"Attempting to load creds from environment variables prefixed with 'NadekoBot_'. " +
|
||||
$"Example is in {CredsExamplePath}");
|
||||
}
|
||||
|
||||
_config = new ConfigurationBuilder()
|
||||
.AddYamlFile(CredsPath, false, true)
|
||||
.AddEnvironmentVariables("NadekoBot_")
|
||||
.Build();
|
||||
|
||||
ChangeToken.OnChange(
|
||||
() => _config.GetReloadToken(),
|
||||
Reload);
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
||||
public void ModifyCredsFile(Action<Creds> func)
|
||||
{
|
||||
var ymlData = File.ReadAllText(_credsFileName);
|
||||
@@ -114,13 +103,13 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
||||
|
||||
ymlData = Yaml.Serializer.Serialize(creds);
|
||||
File.WriteAllText(_credsFileName, ymlData);
|
||||
|
||||
|
||||
Reload();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if there's a V2 credentials file present, loads it if it exists,
|
||||
/// converts it to new model, and saves it to YAML. Also backs up old credentials to credentials.json.bak
|
||||
/// Checks if there's a V2 credentials file present, loads it if it exists,
|
||||
/// converts it to new model, and saves it to YAML. Also backs up old credentials to credentials.json.bak
|
||||
/// </summary>
|
||||
private void MigrateCredentials()
|
||||
{
|
||||
@@ -140,25 +129,20 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
||||
OsuApiKey = oldCreds.OsuApiKey,
|
||||
CleverbotApiKey = oldCreds.CleverbotApiKey,
|
||||
TotalShards = oldCreds.TotalShards <= 1 ? 1 : oldCreds.TotalShards,
|
||||
Patreon = new(oldCreds.PatreonAccessToken,
|
||||
null,
|
||||
null,
|
||||
oldCreds.PatreonCampaignId),
|
||||
Votes = new(oldCreds.VotesUrl,
|
||||
oldCreds.VotesToken,
|
||||
string.Empty,
|
||||
string.Empty),
|
||||
Patreon = new(oldCreds.PatreonAccessToken, null, null, oldCreds.PatreonCampaignId),
|
||||
Votes = new(oldCreds.VotesUrl, oldCreds.VotesToken, string.Empty, string.Empty),
|
||||
BotListToken = oldCreds.BotListToken,
|
||||
RedisOptions = oldCreds.RedisOptions,
|
||||
LocationIqApiKey = oldCreds.LocationIqApiKey,
|
||||
TimezoneDbApiKey = oldCreds.TimezoneDbApiKey,
|
||||
CoinmarketcapApiKey = oldCreds.CoinmarketcapApiKey,
|
||||
CoinmarketcapApiKey = oldCreds.CoinmarketcapApiKey
|
||||
};
|
||||
|
||||
File.Move(OldCredsJsonPath, OldCredsJsonBackupPath, true);
|
||||
File.WriteAllText(CredsPath, Yaml.Serializer.Serialize(creds));
|
||||
|
||||
Log.Warning("Data from credentials.json has been moved to creds.yml\nPlease inspect your creds.yml for correctness");
|
||||
Log.Warning(
|
||||
"Data from credentials.json has been moved to creds.yml\nPlease inspect your creds.yml for correctness");
|
||||
}
|
||||
|
||||
if (File.Exists(_credsFileName))
|
||||
@@ -170,8 +154,8 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
||||
File.WriteAllText(_credsFileName, Yaml.Serializer.Serialize(creds));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBotCredentials GetCreds() => _creds;
|
||||
}
|
||||
public IBotCredentials GetCreds()
|
||||
=> _creds;
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using NadekoBot.Services.Database;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
@@ -13,7 +13,11 @@ public class CurrencyService : ICurrencyService, INService
|
||||
private readonly IEmbedBuilderService _eb;
|
||||
private readonly IUser _bot;
|
||||
|
||||
public CurrencyService(DbService db, DiscordSocketClient c, GamblingConfigService gss, IEmbedBuilderService eb)
|
||||
public CurrencyService(
|
||||
DbService db,
|
||||
DiscordSocketClient c,
|
||||
GamblingConfigService gss,
|
||||
IEmbedBuilderService eb)
|
||||
{
|
||||
_db = db;
|
||||
_gss = gss;
|
||||
@@ -21,16 +25,18 @@ public class CurrencyService : ICurrencyService, INService
|
||||
_bot = c.CurrentUser;
|
||||
}
|
||||
|
||||
private CurrencyTransaction GetCurrencyTransaction(ulong userId, string reason, long amount) =>
|
||||
new()
|
||||
{
|
||||
Amount = amount,
|
||||
UserId = userId,
|
||||
Reason = reason ?? "-",
|
||||
};
|
||||
private CurrencyTransaction GetCurrencyTransaction(ulong userId, string reason, long amount)
|
||||
=> new() { Amount = amount, UserId = userId, Reason = reason ?? "-" };
|
||||
|
||||
private bool InternalChange(ulong userId, string userName, string discrim, string avatar,
|
||||
string reason, long amount, bool gamble, NadekoContext uow)
|
||||
private bool InternalChange(
|
||||
ulong userId,
|
||||
string userName,
|
||||
string discrim,
|
||||
string avatar,
|
||||
string reason,
|
||||
long amount,
|
||||
bool gamble,
|
||||
NadekoContext uow)
|
||||
{
|
||||
var result = uow.TryUpdateCurrencyState(userId, userName, discrim, avatar, amount);
|
||||
if (result)
|
||||
@@ -45,47 +51,64 @@ public class CurrencyService : ICurrencyService, INService
|
||||
uow.TryUpdateCurrencyState(_bot.Id, _bot.Username, _bot.Discriminator, _bot.AvatarId, -amount, true);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task InternalAddAsync(ulong userId, string userName, string discrim, string avatar, string reason, long amount, bool gamble)
|
||||
private async Task InternalAddAsync(
|
||||
ulong userId,
|
||||
string userName,
|
||||
string discrim,
|
||||
string avatar,
|
||||
string reason,
|
||||
long amount,
|
||||
bool gamble)
|
||||
{
|
||||
if (amount < 0)
|
||||
{
|
||||
throw new ArgumentException("You can't add negative amounts. Use RemoveAsync method for that.", nameof(amount));
|
||||
}
|
||||
throw new ArgumentException("You can't add negative amounts. Use RemoveAsync method for that.",
|
||||
nameof(amount));
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
InternalChange(userId, userName, discrim, avatar, reason, amount, gamble, uow);
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public Task AddAsync(ulong userId, string reason, long amount, bool gamble = false)
|
||||
public Task AddAsync(
|
||||
ulong userId,
|
||||
string reason,
|
||||
long amount,
|
||||
bool gamble = false)
|
||||
=> InternalAddAsync(userId, null, null, null, reason, amount, gamble);
|
||||
|
||||
public async Task AddAsync(IUser user, string reason, long amount, bool sendMessage = false, bool gamble = false)
|
||||
public async Task AddAsync(
|
||||
IUser user,
|
||||
string reason,
|
||||
long amount,
|
||||
bool sendMessage = false,
|
||||
bool gamble = false)
|
||||
{
|
||||
await InternalAddAsync(user.Id, user.Username, user.Discriminator, user.AvatarId, reason, amount, gamble);
|
||||
if (sendMessage)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sign = _gss.Data.Currency.Sign;
|
||||
await user
|
||||
.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle($"Received Currency")
|
||||
.AddField("Amount", amount + sign)
|
||||
.AddField("Reason", reason));
|
||||
await user.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle("Received Currency")
|
||||
.AddField("Amount", amount + sign)
|
||||
.AddField("Reason", reason));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddBulkAsync(IEnumerable<ulong> userIds, IEnumerable<string> reasons, IEnumerable<long> amounts, bool gamble = false)
|
||||
public async Task AddBulkAsync(
|
||||
IEnumerable<ulong> userIds,
|
||||
IEnumerable<string> reasons,
|
||||
IEnumerable<long> amounts,
|
||||
bool gamble = false)
|
||||
{
|
||||
var idArray = userIds as ulong[] ?? userIds.ToArray();
|
||||
var reasonArray = reasons as string[] ?? reasons.ToArray();
|
||||
@@ -97,15 +120,17 @@ public class CurrencyService : ICurrencyService, INService
|
||||
var userIdHashSet = new HashSet<ulong>(idArray.Length);
|
||||
await using var uow = _db.GetDbContext();
|
||||
for (var i = 0; i < idArray.Length; i++)
|
||||
{
|
||||
// i have to prevent same user changing more than once as it will cause db error
|
||||
if (userIdHashSet.Add(idArray[i]))
|
||||
InternalChange(idArray[i], null, null, null, reasonArray[i], amountArray[i], gamble, uow);
|
||||
}
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveBulkAsync(IEnumerable<ulong> userIds, IEnumerable<string> reasons, IEnumerable<long> amounts, bool gamble = false)
|
||||
|
||||
public async Task RemoveBulkAsync(
|
||||
IEnumerable<ulong> userIds,
|
||||
IEnumerable<string> reasons,
|
||||
IEnumerable<long> amounts,
|
||||
bool gamble = false)
|
||||
{
|
||||
var idArray = userIds as ulong[] ?? userIds.ToArray();
|
||||
var reasonArray = reasons as string[] ?? reasons.ToArray();
|
||||
@@ -117,20 +142,24 @@ public class CurrencyService : ICurrencyService, INService
|
||||
var userIdHashSet = new HashSet<ulong>(idArray.Length);
|
||||
await using var uow = _db.GetDbContext();
|
||||
for (var i = 0; i < idArray.Length; i++)
|
||||
{
|
||||
// i have to prevent same user changing more than once as it will cause db error
|
||||
if (userIdHashSet.Add(idArray[i]))
|
||||
InternalChange(idArray[i], null, null, null, reasonArray[i], -amountArray[i], gamble, uow);
|
||||
}
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private async Task<bool> InternalRemoveAsync(ulong userId, string userName, string userDiscrim, string avatar, string reason, long amount, bool gamble = false)
|
||||
private async Task<bool> InternalRemoveAsync(
|
||||
ulong userId,
|
||||
string userName,
|
||||
string userDiscrim,
|
||||
string avatar,
|
||||
string reason,
|
||||
long amount,
|
||||
bool gamble = false)
|
||||
{
|
||||
if (amount < 0)
|
||||
{
|
||||
throw new ArgumentException("You can't remove negative amounts. Use AddAsync method for that.", nameof(amount));
|
||||
}
|
||||
throw new ArgumentException("You can't remove negative amounts. Use AddAsync method for that.",
|
||||
nameof(amount));
|
||||
|
||||
bool result;
|
||||
await using var uow = _db.GetDbContext();
|
||||
@@ -139,9 +168,18 @@ public class CurrencyService : ICurrencyService, INService
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<bool> RemoveAsync(ulong userId, string reason, long amount, bool gamble = false)
|
||||
public Task<bool> RemoveAsync(
|
||||
ulong userId,
|
||||
string reason,
|
||||
long amount,
|
||||
bool gamble = false)
|
||||
=> InternalRemoveAsync(userId, null, null, null, reason, amount, gamble);
|
||||
|
||||
public Task<bool> RemoveAsync(IUser user, string reason, long amount, bool sendMessage = false, bool gamble = false)
|
||||
public Task<bool> RemoveAsync(
|
||||
IUser user,
|
||||
string reason,
|
||||
long amount,
|
||||
bool sendMessage = false,
|
||||
bool gamble = false)
|
||||
=> InternalRemoveAsync(user.Id, user.Username, user.Discriminator, user.AvatarId, reason, amount, gamble);
|
||||
}
|
||||
}
|
@@ -5,6 +5,19 @@ namespace NadekoBot.Services;
|
||||
|
||||
public class FontProvider : INService
|
||||
{
|
||||
public FontFamily DottyFont { get; }
|
||||
|
||||
public FontFamily UniSans { get; }
|
||||
|
||||
public FontFamily NotoSans { get; }
|
||||
//public FontFamily Emojis { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Font used for .rip command
|
||||
/// </summary>
|
||||
public Font RipFont { get; }
|
||||
|
||||
public List<FontFamily> FallBackFonts { get; }
|
||||
private readonly FontCollection _fonts;
|
||||
|
||||
public FontProvider()
|
||||
@@ -20,44 +33,23 @@ public class FontProvider : INService
|
||||
|
||||
// try loading some emoji and jap fonts on windows as fallback fonts
|
||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fontsfolder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts);
|
||||
var fontsfolder = Environment.GetFolderPath(Environment.SpecialFolder.Fonts);
|
||||
FallBackFonts.Add(_fonts.Install(Path.Combine(fontsfolder, "seguiemj.ttf")));
|
||||
FallBackFonts.AddRange(_fonts.InstallCollection(Path.Combine(fontsfolder, "msgothic.ttc")));
|
||||
FallBackFonts.AddRange(_fonts.InstallCollection(Path.Combine(fontsfolder, "segoe.ttc")));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// any fonts present in data/fonts should be added as fallback fonts
|
||||
// this will allow support for special characters when drawing text
|
||||
foreach (var font in Directory.GetFiles(@"data/fonts"))
|
||||
{
|
||||
if (font.EndsWith(".ttf"))
|
||||
{
|
||||
FallBackFonts.Add(_fonts.Install(font));
|
||||
}
|
||||
else if (font.EndsWith(".ttc"))
|
||||
{
|
||||
FallBackFonts.AddRange(_fonts.InstallCollection(font));
|
||||
}
|
||||
}
|
||||
else if (font.EndsWith(".ttc")) FallBackFonts.AddRange(_fonts.InstallCollection(font));
|
||||
|
||||
RipFont = NotoSans.CreateFont(20, FontStyle.Bold);
|
||||
DottyFont = FallBackFonts.First(x => x.Name == "dotty");
|
||||
}
|
||||
|
||||
public FontFamily DottyFont { get; }
|
||||
|
||||
public FontFamily UniSans { get; }
|
||||
public FontFamily NotoSans { get; }
|
||||
//public FontFamily Emojis { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Font used for .rip command
|
||||
/// </summary>
|
||||
public Font RipFont { get; }
|
||||
public List<FontFamily> FallBackFonts { get; }
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@ using Google.Apis.YouTube.v3;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
@@ -14,26 +15,162 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
{
|
||||
private const string SearchEngineId = "018084019232060951019:hs5piey28-e";
|
||||
|
||||
private static readonly Regex
|
||||
plRegex = new("(?:youtu\\.be\\/|list=)(?<id>[\\da-zA-Z\\-_]*)", RegexOptions.Compiled);
|
||||
|
||||
public IReadOnlyDictionary<string, string> Languages { get; } = new Dictionary<string, string>
|
||||
{
|
||||
{ "afrikaans", "af" },
|
||||
{ "albanian", "sq" },
|
||||
{ "arabic", "ar" },
|
||||
{ "armenian", "hy" },
|
||||
{ "azerbaijani", "az" },
|
||||
{ "basque", "eu" },
|
||||
{ "belarusian", "be" },
|
||||
{ "bengali", "bn" },
|
||||
{ "bulgarian", "bg" },
|
||||
{ "catalan", "ca" },
|
||||
{ "chinese-traditional", "zh-TW" },
|
||||
{ "chinese-simplified", "zh-CN" },
|
||||
{ "chinese", "zh-CN" },
|
||||
{ "croatian", "hr" },
|
||||
{ "czech", "cs" },
|
||||
{ "danish", "da" },
|
||||
{ "dutch", "nl" },
|
||||
{ "english", "en" },
|
||||
{ "esperanto", "eo" },
|
||||
{ "estonian", "et" },
|
||||
{ "filipino", "tl" },
|
||||
{ "finnish", "fi" },
|
||||
{ "french", "fr" },
|
||||
{ "galician", "gl" },
|
||||
{ "german", "de" },
|
||||
{ "georgian", "ka" },
|
||||
{ "greek", "el" },
|
||||
{ "haitian Creole", "ht" },
|
||||
{ "hebrew", "iw" },
|
||||
{ "hindi", "hi" },
|
||||
{ "hungarian", "hu" },
|
||||
{ "icelandic", "is" },
|
||||
{ "indonesian", "id" },
|
||||
{ "irish", "ga" },
|
||||
{ "italian", "it" },
|
||||
{ "japanese", "ja" },
|
||||
{ "korean", "ko" },
|
||||
{ "lao", "lo" },
|
||||
{ "latin", "la" },
|
||||
{ "latvian", "lv" },
|
||||
{ "lithuanian", "lt" },
|
||||
{ "macedonian", "mk" },
|
||||
{ "malay", "ms" },
|
||||
{ "maltese", "mt" },
|
||||
{ "norwegian", "no" },
|
||||
{ "persian", "fa" },
|
||||
{ "polish", "pl" },
|
||||
{ "portuguese", "pt" },
|
||||
{ "romanian", "ro" },
|
||||
{ "russian", "ru" },
|
||||
{ "serbian", "sr" },
|
||||
{ "slovak", "sk" },
|
||||
{ "slovenian", "sl" },
|
||||
{ "spanish", "es" },
|
||||
{ "swahili", "sw" },
|
||||
{ "swedish", "sv" },
|
||||
{ "tamil", "ta" },
|
||||
{ "telugu", "te" },
|
||||
{ "thai", "th" },
|
||||
{ "turkish", "tr" },
|
||||
{ "ukrainian", "uk" },
|
||||
{ "urdu", "ur" },
|
||||
{ "vietnamese", "vi" },
|
||||
{ "welsh", "cy" },
|
||||
{ "yiddish", "yi" },
|
||||
{ "af", "af" },
|
||||
{ "sq", "sq" },
|
||||
{ "ar", "ar" },
|
||||
{ "hy", "hy" },
|
||||
{ "az", "az" },
|
||||
{ "eu", "eu" },
|
||||
{ "be", "be" },
|
||||
{ "bn", "bn" },
|
||||
{ "bg", "bg" },
|
||||
{ "ca", "ca" },
|
||||
{ "zh-tw", "zh-TW" },
|
||||
{ "zh-cn", "zh-CN" },
|
||||
{ "hr", "hr" },
|
||||
{ "cs", "cs" },
|
||||
{ "da", "da" },
|
||||
{ "nl", "nl" },
|
||||
{ "en", "en" },
|
||||
{ "eo", "eo" },
|
||||
{ "et", "et" },
|
||||
{ "tl", "tl" },
|
||||
{ "fi", "fi" },
|
||||
{ "fr", "fr" },
|
||||
{ "gl", "gl" },
|
||||
{ "de", "de" },
|
||||
{ "ka", "ka" },
|
||||
{ "el", "el" },
|
||||
{ "ht", "ht" },
|
||||
{ "iw", "iw" },
|
||||
{ "hi", "hi" },
|
||||
{ "hu", "hu" },
|
||||
{ "is", "is" },
|
||||
{ "id", "id" },
|
||||
{ "ga", "ga" },
|
||||
{ "it", "it" },
|
||||
{ "ja", "ja" },
|
||||
{ "ko", "ko" },
|
||||
{ "lo", "lo" },
|
||||
{ "la", "la" },
|
||||
{ "lv", "lv" },
|
||||
{ "lt", "lt" },
|
||||
{ "mk", "mk" },
|
||||
{ "ms", "ms" },
|
||||
{ "mt", "mt" },
|
||||
{ "no", "no" },
|
||||
{ "fa", "fa" },
|
||||
{ "pl", "pl" },
|
||||
{ "pt", "pt" },
|
||||
{ "ro", "ro" },
|
||||
{ "ru", "ru" },
|
||||
{ "sr", "sr" },
|
||||
{ "sk", "sk" },
|
||||
{ "sl", "sl" },
|
||||
{ "es", "es" },
|
||||
{ "sw", "sw" },
|
||||
{ "sv", "sv" },
|
||||
{ "ta", "ta" },
|
||||
{ "te", "te" },
|
||||
{ "th", "th" },
|
||||
{ "tr", "tr" },
|
||||
{ "uk", "uk" },
|
||||
{ "ur", "ur" },
|
||||
{ "vi", "vi" },
|
||||
{ "cy", "cy" },
|
||||
{ "yi", "yi" }
|
||||
};
|
||||
|
||||
private readonly YouTubeService yt;
|
||||
private readonly UrlshortenerService sh;
|
||||
private readonly CustomsearchService cs;
|
||||
|
||||
|
||||
//private readonly Regex YtVideoIdRegex = new Regex(@"(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)(?<id>[a-zA-Z0-9_-]{6,11})", RegexOptions.Compiled);
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
|
||||
public GoogleApiService(IBotCredentials creds, IHttpClientFactory factory)
|
||||
{
|
||||
_creds = creds;
|
||||
_httpFactory = factory;
|
||||
|
||||
var bcs = new BaseClientService.Initializer
|
||||
{
|
||||
ApplicationName = "Nadeko Bot",
|
||||
ApiKey = _creds.GoogleApiKey,
|
||||
};
|
||||
|
||||
var bcs = new BaseClientService.Initializer { ApplicationName = "Nadeko Bot", ApiKey = _creds.GoogleApiKey };
|
||||
|
||||
yt = new(bcs);
|
||||
sh = new(bcs);
|
||||
cs = new(bcs);
|
||||
}
|
||||
private static readonly Regex plRegex = new("(?:youtu\\.be\\/|list=)(?<id>[\\da-zA-Z\\-_]*)", RegexOptions.Compiled);
|
||||
|
||||
public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1)
|
||||
{
|
||||
await Task.Yield();
|
||||
@@ -44,10 +181,7 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
var match = plRegex.Match(keywords);
|
||||
if (match.Length > 1)
|
||||
{
|
||||
return new[] { match.Groups["id"].Value.ToString() };
|
||||
}
|
||||
if (match.Length > 1) return new[] { match.Groups["id"].Value };
|
||||
var query = yt.Search.List("snippet");
|
||||
query.MaxResults = count;
|
||||
query.Type = "playlist";
|
||||
@@ -56,10 +190,6 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
return (await query.ExecuteAsync()).Items.Select(i => i.Id.PlaylistId);
|
||||
}
|
||||
|
||||
//private readonly Regex YtVideoIdRegex = new Regex(@"(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)(?<id>[a-zA-Z0-9_-]{6,11})", RegexOptions.Compiled);
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
|
||||
// todo future add quota users
|
||||
public async Task<IEnumerable<string>> GetRelatedVideosAsync(string id, int count = 1)
|
||||
{
|
||||
@@ -93,7 +223,9 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
return (await query.ExecuteAsync()).Items.Select(i => "http://www.youtube.com/watch?v=" + i.Id.VideoId);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<(string Name, string Id, string Url)>> GetVideoInfosByKeywordAsync(string keywords, int count = 1)
|
||||
public async Task<IEnumerable<(string Name, string Id, string Url)>> GetVideoInfosByKeywordAsync(
|
||||
string keywords,
|
||||
int count = 1)
|
||||
{
|
||||
await Task.Yield();
|
||||
if (string.IsNullOrWhiteSpace(keywords))
|
||||
@@ -106,10 +238,12 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
query.MaxResults = count;
|
||||
query.Q = keywords;
|
||||
query.Type = "video";
|
||||
return (await query.ExecuteAsync()).Items.Select(i => (i.Snippet.Title.TrimTo(50), i.Id.VideoId, "http://www.youtube.com/watch?v=" + i.Id.VideoId));
|
||||
return (await query.ExecuteAsync()).Items.Select(i
|
||||
=> (i.Snippet.Title.TrimTo(50), i.Id.VideoId, "http://www.youtube.com/watch?v=" + i.Id.VideoId));
|
||||
}
|
||||
|
||||
public Task<string> ShortenUrl(Uri url) => ShortenUrl(url.ToString());
|
||||
public Task<string> ShortenUrl(Uri url)
|
||||
=> ShortenUrl(url.ToString());
|
||||
|
||||
public async Task<string> ShortenUrl(string url)
|
||||
{
|
||||
@@ -163,8 +297,7 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
|
||||
toReturn.AddRange(data.Items.Select(i => i.ContentDetails.VideoId));
|
||||
nextPageToken = data.NextPageToken;
|
||||
}
|
||||
while (count > 0 && !string.IsNullOrWhiteSpace(nextPageToken));
|
||||
} while (count > 0 && !string.IsNullOrWhiteSpace(nextPageToken));
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
@@ -189,12 +322,8 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
q.Id = string.Join(",", videoIdsList.Take(toGet));
|
||||
videoIdsList = videoIdsList.Skip(toGet).ToList();
|
||||
var items = (await q.ExecuteAsync()).Items;
|
||||
foreach (var i in items)
|
||||
{
|
||||
toReturn.Add(i.Id, System.Xml.XmlConvert.ToTimeSpan(i.ContentDetails.Duration));
|
||||
}
|
||||
}
|
||||
while (remaining > 0);
|
||||
foreach (var i in items) toReturn.Add(i.Id, XmlConvert.ToTimeSpan(i.ContentDetails.Duration));
|
||||
} while (remaining > 0);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
@@ -218,156 +347,24 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
return new(search.Items[0].Image, search.Items[0].Link);
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, string> Languages { get; } = new Dictionary<string, string>() {
|
||||
{ "afrikaans", "af"},
|
||||
{ "albanian", "sq"},
|
||||
{ "arabic", "ar"},
|
||||
{ "armenian", "hy"},
|
||||
{ "azerbaijani", "az"},
|
||||
{ "basque", "eu"},
|
||||
{ "belarusian", "be"},
|
||||
{ "bengali", "bn"},
|
||||
{ "bulgarian", "bg"},
|
||||
{ "catalan", "ca"},
|
||||
{ "chinese-traditional", "zh-TW"},
|
||||
{ "chinese-simplified", "zh-CN"},
|
||||
{ "chinese", "zh-CN"},
|
||||
{ "croatian", "hr"},
|
||||
{ "czech", "cs"},
|
||||
{ "danish", "da"},
|
||||
{ "dutch", "nl"},
|
||||
{ "english", "en"},
|
||||
{ "esperanto", "eo"},
|
||||
{ "estonian", "et"},
|
||||
{ "filipino", "tl"},
|
||||
{ "finnish", "fi"},
|
||||
{ "french", "fr"},
|
||||
{ "galician", "gl"},
|
||||
{ "german", "de"},
|
||||
{ "georgian", "ka"},
|
||||
{ "greek", "el"},
|
||||
{ "haitian Creole", "ht"},
|
||||
{ "hebrew", "iw"},
|
||||
{ "hindi", "hi"},
|
||||
{ "hungarian", "hu"},
|
||||
{ "icelandic", "is"},
|
||||
{ "indonesian", "id"},
|
||||
{ "irish", "ga"},
|
||||
{ "italian", "it"},
|
||||
{ "japanese", "ja"},
|
||||
{ "korean", "ko"},
|
||||
{ "lao", "lo"},
|
||||
{ "latin", "la"},
|
||||
{ "latvian", "lv"},
|
||||
{ "lithuanian", "lt"},
|
||||
{ "macedonian", "mk"},
|
||||
{ "malay", "ms"},
|
||||
{ "maltese", "mt"},
|
||||
{ "norwegian", "no"},
|
||||
{ "persian", "fa"},
|
||||
{ "polish", "pl"},
|
||||
{ "portuguese", "pt"},
|
||||
{ "romanian", "ro"},
|
||||
{ "russian", "ru"},
|
||||
{ "serbian", "sr"},
|
||||
{ "slovak", "sk"},
|
||||
{ "slovenian", "sl"},
|
||||
{ "spanish", "es"},
|
||||
{ "swahili", "sw"},
|
||||
{ "swedish", "sv"},
|
||||
{ "tamil", "ta"},
|
||||
{ "telugu", "te"},
|
||||
{ "thai", "th"},
|
||||
{ "turkish", "tr"},
|
||||
{ "ukrainian", "uk"},
|
||||
{ "urdu", "ur"},
|
||||
{ "vietnamese", "vi"},
|
||||
{ "welsh", "cy"},
|
||||
{ "yiddish", "yi"},
|
||||
|
||||
{ "af", "af"},
|
||||
{ "sq", "sq"},
|
||||
{ "ar", "ar"},
|
||||
{ "hy", "hy"},
|
||||
{ "az", "az"},
|
||||
{ "eu", "eu"},
|
||||
{ "be", "be"},
|
||||
{ "bn", "bn"},
|
||||
{ "bg", "bg"},
|
||||
{ "ca", "ca"},
|
||||
{ "zh-tw", "zh-TW"},
|
||||
{ "zh-cn", "zh-CN"},
|
||||
{ "hr", "hr"},
|
||||
{ "cs", "cs"},
|
||||
{ "da", "da"},
|
||||
{ "nl", "nl"},
|
||||
{ "en", "en"},
|
||||
{ "eo", "eo"},
|
||||
{ "et", "et"},
|
||||
{ "tl", "tl"},
|
||||
{ "fi", "fi"},
|
||||
{ "fr", "fr"},
|
||||
{ "gl", "gl"},
|
||||
{ "de", "de"},
|
||||
{ "ka", "ka"},
|
||||
{ "el", "el"},
|
||||
{ "ht", "ht"},
|
||||
{ "iw", "iw"},
|
||||
{ "hi", "hi"},
|
||||
{ "hu", "hu"},
|
||||
{ "is", "is"},
|
||||
{ "id", "id"},
|
||||
{ "ga", "ga"},
|
||||
{ "it", "it"},
|
||||
{ "ja", "ja"},
|
||||
{ "ko", "ko"},
|
||||
{ "lo", "lo"},
|
||||
{ "la", "la"},
|
||||
{ "lv", "lv"},
|
||||
{ "lt", "lt"},
|
||||
{ "mk", "mk"},
|
||||
{ "ms", "ms"},
|
||||
{ "mt", "mt"},
|
||||
{ "no", "no"},
|
||||
{ "fa", "fa"},
|
||||
{ "pl", "pl"},
|
||||
{ "pt", "pt"},
|
||||
{ "ro", "ro"},
|
||||
{ "ru", "ru"},
|
||||
{ "sr", "sr"},
|
||||
{ "sk", "sk"},
|
||||
{ "sl", "sl"},
|
||||
{ "es", "es"},
|
||||
{ "sw", "sw"},
|
||||
{ "sv", "sv"},
|
||||
{ "ta", "ta"},
|
||||
{ "te", "te"},
|
||||
{ "th", "th"},
|
||||
{ "tr", "tr"},
|
||||
{ "uk", "uk"},
|
||||
{ "ur", "ur"},
|
||||
{ "vi", "vi"},
|
||||
{ "cy", "cy"},
|
||||
{ "yi", "yi"},
|
||||
};
|
||||
|
||||
public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage)
|
||||
{
|
||||
await Task.Yield();
|
||||
string text;
|
||||
|
||||
if (!Languages.ContainsKey(sourceLanguage) ||
|
||||
!Languages.ContainsKey(targetLanguage))
|
||||
if (!Languages.ContainsKey(sourceLanguage) || !Languages.ContainsKey(targetLanguage))
|
||||
throw new ArgumentException(nameof(sourceLanguage) + "/" + nameof(targetLanguage));
|
||||
|
||||
|
||||
var url = new Uri(string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
|
||||
var url = new Uri(string.Format(
|
||||
"https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
|
||||
ConvertToLanguageCode(sourceLanguage),
|
||||
ConvertToLanguageCode(targetLanguage),
|
||||
WebUtility.UrlEncode(sourceText)));
|
||||
using (var http = _httpFactory.CreateClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
|
||||
http.DefaultRequestHeaders.Add("user-agent",
|
||||
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
|
||||
text = await http.GetStringAsync(url);
|
||||
}
|
||||
|
||||
@@ -379,4 +376,4 @@ public class GoogleApiService : IGoogleApiService, INService
|
||||
Languages.TryGetValue(language, out var mode);
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,45 +1,50 @@
|
||||
#nullable disable
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
using NadekoBot.Db;
|
||||
using Newtonsoft.Json;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public class Localization : ILocalization, INService
|
||||
{
|
||||
private readonly BotConfigService _bss;
|
||||
private readonly DbService _db;
|
||||
private static readonly Dictionary<string, CommandData> _commandData =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, CommandData>>(
|
||||
File.ReadAllText("./data/strings/commands/commands.en-US.json"));
|
||||
|
||||
public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; }
|
||||
public CultureInfo DefaultCultureInfo => _bss.Data.DefaultLocale;
|
||||
|
||||
private static readonly Dictionary<string, CommandData> _commandData = JsonConvert.DeserializeObject<Dictionary<string, CommandData>>(
|
||||
File.ReadAllText("./data/strings/commands/commands.en-US.json"));
|
||||
public CultureInfo DefaultCultureInfo
|
||||
=> _bss.Data.DefaultLocale;
|
||||
|
||||
private readonly BotConfigService _bss;
|
||||
private readonly DbService _db;
|
||||
|
||||
public Localization(BotConfigService bss, Bot bot, DbService db)
|
||||
{
|
||||
_bss = bss;
|
||||
_db = db;
|
||||
|
||||
var cultureInfoNames = bot.AllGuildConfigs
|
||||
.ToDictionary(x => x.GuildId, x => x.Locale);
|
||||
|
||||
GuildCultureInfos = new(cultureInfoNames.ToDictionary(x => x.Key, x =>
|
||||
{
|
||||
CultureInfo cultureInfo = null;
|
||||
try
|
||||
{
|
||||
if (x.Value is null)
|
||||
return null;
|
||||
cultureInfo = new(x.Value);
|
||||
}
|
||||
catch { }
|
||||
return cultureInfo;
|
||||
}).Where(x => x.Value != null));
|
||||
var cultureInfoNames = bot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale);
|
||||
|
||||
GuildCultureInfos = new(cultureInfoNames.ToDictionary(x => x.Key,
|
||||
x =>
|
||||
{
|
||||
CultureInfo cultureInfo = null;
|
||||
try
|
||||
{
|
||||
if (x.Value is null)
|
||||
return null;
|
||||
cultureInfo = new(x.Value);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return cultureInfo;
|
||||
})
|
||||
.Where(x => x.Value != null));
|
||||
}
|
||||
|
||||
public void SetGuildCulture(IGuild guild, CultureInfo ci) =>
|
||||
SetGuildCulture(guild.Id, ci);
|
||||
public void SetGuildCulture(IGuild guild, CultureInfo ci)
|
||||
=> SetGuildCulture(guild.Id, ci);
|
||||
|
||||
public void SetGuildCulture(ulong guildId, CultureInfo ci)
|
||||
{
|
||||
@@ -59,12 +64,11 @@ public class Localization : ILocalization, INService
|
||||
GuildCultureInfos.AddOrUpdate(guildId, ci, (id, old) => ci);
|
||||
}
|
||||
|
||||
public void RemoveGuildCulture(IGuild guild) =>
|
||||
RemoveGuildCulture(guild.Id);
|
||||
public void RemoveGuildCulture(IGuild guild)
|
||||
=> RemoveGuildCulture(guild.Id);
|
||||
|
||||
public void RemoveGuildCulture(ulong guildId)
|
||||
{
|
||||
|
||||
if (GuildCultureInfos.TryRemove(guildId, out var _))
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
@@ -80,17 +84,17 @@ public class Localization : ILocalization, INService
|
||||
bs.DefaultLocale = ci;
|
||||
});
|
||||
|
||||
public void ResetDefaultCulture() =>
|
||||
SetDefaultCulture(CultureInfo.CurrentCulture);
|
||||
public void ResetDefaultCulture()
|
||||
=> SetDefaultCulture(CultureInfo.CurrentCulture);
|
||||
|
||||
public CultureInfo GetCultureInfo(IGuild guild) =>
|
||||
GetCultureInfo(guild?.Id);
|
||||
public CultureInfo GetCultureInfo(IGuild guild)
|
||||
=> GetCultureInfo(guild?.Id);
|
||||
|
||||
public CultureInfo GetCultureInfo(ulong? guildId)
|
||||
{
|
||||
if (guildId is null || !GuildCultureInfos.TryGetValue(guildId.Value, out var info) || info is null)
|
||||
return _bss.Data.DefaultLocale;
|
||||
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -99,13 +103,8 @@ public class Localization : ILocalization, INService
|
||||
_commandData.TryGetValue(key, out var toReturn);
|
||||
|
||||
if (toReturn is null)
|
||||
return new()
|
||||
{
|
||||
Cmd = key,
|
||||
Desc = key,
|
||||
Usage = new[] { key },
|
||||
};
|
||||
return new() { Cmd = key, Desc = key, Usage = new[] { key } };
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,8 +15,13 @@ public class RedisCache : IDataCache
|
||||
private readonly string _redisKey;
|
||||
private readonly EndPoint _redisEndpoint;
|
||||
|
||||
public RedisCache(ConnectionMultiplexer redis, IBotCredentials creds,
|
||||
IImageCache imageCache, ILocalDataCache dataCache)
|
||||
private readonly object timelyLock = new();
|
||||
|
||||
public RedisCache(
|
||||
ConnectionMultiplexer redis,
|
||||
IBotCredentials creds,
|
||||
IImageCache imageCache,
|
||||
ILocalDataCache dataCache)
|
||||
{
|
||||
Redis = redis;
|
||||
_redisEndpoint = Redis.GetEndPoints().First();
|
||||
@@ -52,7 +57,7 @@ public class RedisCache : IDataCache
|
||||
public Task SetAnimeDataAsync(string key, string data)
|
||||
{
|
||||
var _db = Redis.GetDatabase();
|
||||
return _db.StringSetAsync("anime_" + key, data, expiry: TimeSpan.FromHours(3));
|
||||
return _db.StringSetAsync("anime_" + key, data, TimeSpan.FromHours(3));
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Data)> TryGetNovelDataAsync(string key)
|
||||
@@ -65,10 +70,9 @@ public class RedisCache : IDataCache
|
||||
public Task SetNovelDataAsync(string key, string data)
|
||||
{
|
||||
var _db = Redis.GetDatabase();
|
||||
return _db.StringSetAsync("novel_" + key, data, expiry: TimeSpan.FromHours(3));
|
||||
return _db.StringSetAsync("novel_" + key, data, TimeSpan.FromHours(3));
|
||||
}
|
||||
|
||||
private readonly object timelyLock = new();
|
||||
public TimeSpan? AddTimelyClaim(ulong id, int period)
|
||||
{
|
||||
if (period == 0)
|
||||
@@ -82,6 +86,7 @@ public class RedisCache : IDataCache
|
||||
_db.StringSet($"{_redisKey}_timelyclaim_{id}", true, time);
|
||||
return null;
|
||||
}
|
||||
|
||||
return _db.KeyTimeToLive($"{_redisKey}_timelyclaim_{id}");
|
||||
}
|
||||
}
|
||||
@@ -91,9 +96,7 @@ public class RedisCache : IDataCache
|
||||
var server = Redis.GetServer(_redisEndpoint);
|
||||
var _db = Redis.GetDatabase();
|
||||
foreach (var k in server.Keys(pattern: $"{_redisKey}_timelyclaim_*"))
|
||||
{
|
||||
_db.KeyDelete(k, CommandFlags.FireAndForget);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryAddAffinityCooldown(ulong userId, out TimeSpan? time)
|
||||
@@ -106,6 +109,7 @@ public class RedisCache : IDataCache
|
||||
_db.StringSet($"{_redisKey}_affinity_{userId}", true, time);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -119,13 +123,14 @@ public class RedisCache : IDataCache
|
||||
_db.StringSet($"{_redisKey}_divorce_{userId}", true, time);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task SetStreamDataAsync(string url, string data)
|
||||
{
|
||||
var _db = Redis.GetDatabase();
|
||||
return _db.StringSetAsync($"{_redisKey}_stream_{url}", data, expiry: TimeSpan.FromHours(6));
|
||||
return _db.StringSetAsync($"{_redisKey}_stream_{url}", data, TimeSpan.FromHours(6));
|
||||
}
|
||||
|
||||
public bool TryGetStreamData(string url, out string dataStr)
|
||||
@@ -143,9 +148,7 @@ public class RedisCache : IDataCache
|
||||
0, // i don't use the value
|
||||
TimeSpan.FromSeconds(expireIn),
|
||||
When.NotExists))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return _db.KeyTimeToLive($"{_redisKey}_ratelimit_{id}_{name}");
|
||||
}
|
||||
@@ -153,10 +156,7 @@ public class RedisCache : IDataCache
|
||||
public bool TryGetEconomy(out string data)
|
||||
{
|
||||
var _db = Redis.GetDatabase();
|
||||
if ((data = _db.StringGet($"{_redisKey}_economy")) != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if ((data = _db.StringGet($"{_redisKey}_economy")) != null) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -164,12 +164,15 @@ public class RedisCache : IDataCache
|
||||
public void SetEconomy(string data)
|
||||
{
|
||||
var _db = Redis.GetDatabase();
|
||||
_db.StringSet($"{_redisKey}_economy",
|
||||
data,
|
||||
expiry: TimeSpan.FromMinutes(3));
|
||||
_db.StringSet($"{_redisKey}_economy", data, TimeSpan.FromMinutes(3));
|
||||
}
|
||||
|
||||
public async Task<TOut> GetOrAddCachedDataAsync<TParam, TOut>(string key, Func<TParam, Task<TOut>> factory, TParam param, TimeSpan expiry) where TOut : class
|
||||
public async Task<TOut> GetOrAddCachedDataAsync<TParam, TOut>(
|
||||
string key,
|
||||
Func<TParam, Task<TOut>> factory,
|
||||
TParam param,
|
||||
TimeSpan expiry)
|
||||
where TOut : class
|
||||
{
|
||||
var _db = Redis.GetDatabase();
|
||||
|
||||
@@ -179,13 +182,13 @@ public class RedisCache : IDataCache
|
||||
var obj = await factory(param);
|
||||
|
||||
if (obj is null)
|
||||
return default(TOut);
|
||||
return default;
|
||||
|
||||
await _db.StringSetAsync(key, JsonConvert.SerializeObject(obj),
|
||||
expiry: expiry);
|
||||
await _db.StringSetAsync(key, JsonConvert.SerializeObject(obj), expiry);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
return (TOut)JsonConvert.DeserializeObject(data, typeof(TOut));
|
||||
}
|
||||
|
||||
@@ -194,7 +197,7 @@ public class RedisCache : IDataCache
|
||||
var db = Redis.GetDatabase();
|
||||
|
||||
var str = (string)db.StringGet($"{_redisKey}_last_currency_decay");
|
||||
if(string.IsNullOrEmpty(str))
|
||||
if (string.IsNullOrEmpty(str))
|
||||
return DateTime.MinValue;
|
||||
|
||||
return JsonConvert.DeserializeObject<DateTime>(str);
|
||||
@@ -206,4 +209,4 @@ public class RedisCache : IDataCache
|
||||
|
||||
db.StringSet($"{_redisKey}_last_currency_decay", JsonConvert.SerializeObject(DateTime.UtcNow));
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,7 +5,7 @@ public static class RedisImageExtensions
|
||||
{
|
||||
private const string OldCdnUrl = "nadeko-pictures.nyc3.digitaloceanspaces.com";
|
||||
private const string NewCdnUrl = "cdn.nadeko.bot";
|
||||
|
||||
|
||||
public static Uri ToNewCdn(this Uri uri)
|
||||
=> new(uri.ToString().Replace(OldCdnUrl, NewCdnUrl));
|
||||
}
|
||||
}
|
@@ -1,26 +1,13 @@
|
||||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
using StackExchange.Redis;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Common.Yml;
|
||||
using Newtonsoft.Json;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
{
|
||||
private readonly ConnectionMultiplexer _con;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly HttpClient _http;
|
||||
private readonly string _imagesPath;
|
||||
|
||||
private IDatabase Db
|
||||
=> _con.GetDatabase();
|
||||
|
||||
private const string BASE_PATH = "data/";
|
||||
private const string CARDS_PATH = $"{BASE_PATH}images/cards";
|
||||
|
||||
public ImageUrls ImageUrls { get; private set; }
|
||||
|
||||
public enum ImageKeys
|
||||
{
|
||||
CoinHeads,
|
||||
@@ -36,6 +23,14 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
XpBg
|
||||
}
|
||||
|
||||
private const string BASE_PATH = "data/";
|
||||
private const string CARDS_PATH = $"{BASE_PATH}images/cards";
|
||||
|
||||
private IDatabase Db
|
||||
=> _con.GetDatabase();
|
||||
|
||||
public ImageUrls ImageUrls { get; private set; }
|
||||
|
||||
public IReadOnlyList<byte[]> Heads
|
||||
=> GetByteArrayData(ImageKeys.CoinHeads);
|
||||
|
||||
@@ -69,17 +64,10 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
public byte[] RipOverlay
|
||||
=> GetByteData(ImageKeys.RipOverlay);
|
||||
|
||||
public byte[] GetCard(string key)
|
||||
// since cards are always local for now, don't cache them
|
||||
=> File.ReadAllBytes(Path.Join(CARDS_PATH, key + ".jpg"));
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
if (await AllKeysExist())
|
||||
return;
|
||||
|
||||
await Reload();
|
||||
}
|
||||
private readonly ConnectionMultiplexer _con;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly HttpClient _http;
|
||||
private readonly string _imagesPath;
|
||||
|
||||
public RedisImagesCache(ConnectionMultiplexer con, IBotCredentials creds)
|
||||
{
|
||||
@@ -93,6 +81,18 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
ImageUrls = Yaml.Deserializer.Deserialize<ImageUrls>(File.ReadAllText(_imagesPath));
|
||||
}
|
||||
|
||||
public byte[] GetCard(string key)
|
||||
// since cards are always local for now, don't cache them
|
||||
=> File.ReadAllBytes(Path.Join(CARDS_PATH, key + ".jpg"));
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
if (await AllKeysExist())
|
||||
return;
|
||||
|
||||
await Reload();
|
||||
}
|
||||
|
||||
private void Migrate()
|
||||
{
|
||||
// migrate to yml
|
||||
@@ -105,21 +105,22 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
|
||||
if (oldData is not null)
|
||||
{
|
||||
var newData = new ImageUrls()
|
||||
var newData = new ImageUrls
|
||||
{
|
||||
Coins =
|
||||
new()
|
||||
{
|
||||
Heads = oldData.Coins.Heads.Length == 1 &&
|
||||
oldData.Coins.Heads[0].ToString() ==
|
||||
"https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/heads.png"
|
||||
? new[] { new Uri("https://cdn.nadeko.bot/coins/heads3.png") }
|
||||
: oldData.Coins.Heads,
|
||||
Tails = oldData.Coins.Tails.Length == 1 &&
|
||||
oldData.Coins.Tails[0].ToString() ==
|
||||
"https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/tails.png"
|
||||
Heads =
|
||||
oldData.Coins.Heads.Length == 1
|
||||
&& oldData.Coins.Heads[0].ToString()
|
||||
== "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/heads.png"
|
||||
? new[] { new Uri("https://cdn.nadeko.bot/coins/heads3.png") }
|
||||
: oldData.Coins.Heads,
|
||||
Tails = oldData.Coins.Tails.Length == 1
|
||||
&& oldData.Coins.Tails[0].ToString()
|
||||
== "https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/coins/tails.png"
|
||||
? new[] { new Uri("https://cdn.nadeko.bot/coins/tails3.png") }
|
||||
: oldData.Coins.Tails,
|
||||
: oldData.Coins.Tails
|
||||
},
|
||||
Dice = oldData.Dice.Map(x => x.ToNewCdn()),
|
||||
Currency = oldData.Currency.Map(x => x.ToNewCdn()),
|
||||
@@ -128,7 +129,7 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
{
|
||||
Dot = oldData.Rategirl.Dot.ToNewCdn(), Matrix = oldData.Rategirl.Matrix.ToNewCdn()
|
||||
},
|
||||
Rip = new() { Bg = oldData.Rip.Bg.ToNewCdn(), Overlay = oldData.Rip.Overlay.ToNewCdn(), },
|
||||
Rip = new() { Bg = oldData.Rip.Bg.ToNewCdn(), Overlay = oldData.Rip.Overlay.ToNewCdn() },
|
||||
Slots = new()
|
||||
{
|
||||
Bg = new("https://cdn.nadeko.bot/slots/slots_bg.png"),
|
||||
@@ -139,8 +140,8 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
"https://cdn.nadeko.bot/slots/4.png", "https://cdn.nadeko.bot/slots/5.png"
|
||||
}.Map(x => new Uri(x))
|
||||
},
|
||||
Xp = new() { Bg = oldData.Xp.Bg.ToNewCdn(), },
|
||||
Version = 2,
|
||||
Xp = new() { Bg = oldData.Xp.Bg.ToNewCdn() },
|
||||
Version = 2
|
||||
};
|
||||
|
||||
File.Move(oldFilePath, backupFilePath, true);
|
||||
@@ -161,7 +162,6 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
{
|
||||
ImageUrls = Yaml.Deserializer.Deserialize<ImageUrls>(await File.ReadAllTextAsync(_imagesPath));
|
||||
foreach (var key in GetAllKeys())
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case ImageKeys.CoinHeads:
|
||||
@@ -200,7 +200,6 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Load(ImageKeys key, Uri uri)
|
||||
@@ -221,20 +220,17 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
await Db.ListRightPushAsync(GetRedisKey(key), vals);
|
||||
|
||||
if (uris.Length != vals.Length)
|
||||
{
|
||||
Log.Information("{Loaded}/{Max} URIs for the key '{ImageKey}' have been loaded.\n" +
|
||||
"Some of the supplied URIs are either unavailable or invalid",
|
||||
Log.Information(
|
||||
"{Loaded}/{Max} URIs for the key '{ImageKey}' have been loaded.\n"
|
||||
+ "Some of the supplied URIs are either unavailable or invalid",
|
||||
vals.Length,
|
||||
uris.Length,
|
||||
key
|
||||
);
|
||||
}
|
||||
key);
|
||||
}
|
||||
|
||||
private async Task<byte[]> GetImageData(Uri uri)
|
||||
{
|
||||
if (uri.IsFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytes = await File.ReadAllBytesAsync(uri.LocalPath);
|
||||
@@ -245,7 +241,6 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
Log.Warning(ex, "Failed reading image bytes from uri: {Uri}", uri.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -260,9 +255,7 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
|
||||
private async Task<bool> AllKeysExist()
|
||||
{
|
||||
var tasks = await GetAllKeys()
|
||||
.Select(x => Db.KeyExistsAsync(GetRedisKey(x)))
|
||||
.WhenAll();
|
||||
var tasks = await GetAllKeys().Select(x => Db.KeyExistsAsync(GetRedisKey(x))).WhenAll();
|
||||
|
||||
return tasks.All(exist => exist);
|
||||
}
|
||||
@@ -278,4 +271,4 @@ public sealed class RedisImagesCache : IImageCache, IReadyExecutor
|
||||
|
||||
private RedisKey GetRedisKey(ImageKeys key)
|
||||
=> _creds.RedisKey() + "_image_" + key;
|
||||
}
|
||||
}
|
@@ -8,9 +8,6 @@ namespace NadekoBot.Services;
|
||||
|
||||
public class RedisLocalDataCache : ILocalDataCache
|
||||
{
|
||||
private readonly ConnectionMultiplexer _con;
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
private const string POKEMON_ABILITIES_FILE = "data/pokemon/pokemon_abilities.json";
|
||||
private const string POKEMON_LIST_FILE = "data/pokemon/pokemon_list.json";
|
||||
private const string POKEMON_MAP_PATH = "data/pokemon/name-id_map.json";
|
||||
@@ -40,6 +37,9 @@ public class RedisLocalDataCache : ILocalDataCache
|
||||
private init => Set("pokemon_map", value);
|
||||
}
|
||||
|
||||
private readonly ConnectionMultiplexer _con;
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
public RedisLocalDataCache(ConnectionMultiplexer con, IBotCredentials creds, DiscordSocketClient client)
|
||||
{
|
||||
_con = con;
|
||||
@@ -49,32 +49,25 @@ public class RedisLocalDataCache : ILocalDataCache
|
||||
if (shardId == 0)
|
||||
{
|
||||
if (!File.Exists(POKEMON_LIST_FILE))
|
||||
{
|
||||
Log.Warning($"{POKEMON_LIST_FILE} is missing. Pokemon abilities not loaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
Pokemons =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(POKEMON_LIST_FILE));
|
||||
}
|
||||
JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(
|
||||
File.ReadAllText(POKEMON_LIST_FILE));
|
||||
|
||||
if (!File.Exists(POKEMON_ABILITIES_FILE))
|
||||
{
|
||||
Log.Warning($"{POKEMON_ABILITIES_FILE} is missing. Pokemon abilities not loaded.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PokemonAbilities =
|
||||
JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(
|
||||
File.ReadAllText(POKEMON_ABILITIES_FILE)
|
||||
);
|
||||
}
|
||||
File.ReadAllText(POKEMON_ABILITIES_FILE));
|
||||
|
||||
try
|
||||
{
|
||||
TriviaQuestions = JsonConvert.DeserializeObject<TriviaQuestion[]>(File.ReadAllText(QUESTIONS_FILE));
|
||||
PokemonMap = JsonConvert.DeserializeObject<PokemonNameId[]>(File.ReadAllText(POKEMON_MAP_PATH))
|
||||
?.ToDictionary(x => x.Id, x => x.Name) ?? new();
|
||||
?.ToDictionary(x => x.Id, x => x.Name)
|
||||
?? new();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -90,4 +83,4 @@ public class RedisLocalDataCache : ILocalDataCache
|
||||
|
||||
private void Set(string key, object obj)
|
||||
=> _con.GetDatabase().StringSet($"{_creds.RedisKey()}_localdata_{key}", JsonConvert.SerializeObject(obj));
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Coordinator;
|
||||
|
||||
@@ -12,37 +13,26 @@ public class RemoteGrpcCoordinator : ICoordinator, IReadyExecutor
|
||||
|
||||
public RemoteGrpcCoordinator(IBotCredentials creds, DiscordSocketClient client)
|
||||
{
|
||||
var coordUrl = string.IsNullOrWhiteSpace(creds.CoordinatorUrl)
|
||||
? "http://localhost:3442"
|
||||
: creds.CoordinatorUrl;
|
||||
|
||||
var channel = Grpc.Net.Client.GrpcChannel.ForAddress(coordUrl);
|
||||
var coordUrl = string.IsNullOrWhiteSpace(creds.CoordinatorUrl) ? "http://localhost:3442" : creds.CoordinatorUrl;
|
||||
|
||||
var channel = GrpcChannel.ForAddress(coordUrl);
|
||||
_coordClient = new(channel);
|
||||
_client = client;
|
||||
}
|
||||
|
||||
|
||||
public bool RestartBot()
|
||||
{
|
||||
_coordClient.RestartAllShards(new()
|
||||
{
|
||||
|
||||
});
|
||||
_coordClient.RestartAllShards(new());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Die(bool graceful)
|
||||
=> _coordClient.Die(new()
|
||||
{
|
||||
Graceful = graceful
|
||||
});
|
||||
=> _coordClient.Die(new() { Graceful = graceful });
|
||||
|
||||
public bool RestartShard(int shardId)
|
||||
{
|
||||
_coordClient.RestartShard(new()
|
||||
{
|
||||
ShardId = shardId,
|
||||
});
|
||||
_coordClient.RestartShard(new() { ShardId = shardId });
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -51,15 +41,14 @@ public class RemoteGrpcCoordinator : ICoordinator, IReadyExecutor
|
||||
{
|
||||
var res = _coordClient.GetAllStatuses(new());
|
||||
|
||||
return res.Statuses
|
||||
.ToArray()
|
||||
.Map(s => new ShardStatus()
|
||||
{
|
||||
ConnectionState = FromCoordConnState(s.State),
|
||||
GuildCount = s.GuildCount,
|
||||
ShardId = s.ShardId,
|
||||
LastUpdate = s.LastUpdate.ToDateTime(),
|
||||
});
|
||||
return res.Statuses.ToArray()
|
||||
.Map(s => new ShardStatus
|
||||
{
|
||||
ConnectionState = FromCoordConnState(s.State),
|
||||
GuildCount = s.GuildCount,
|
||||
ShardId = s.ShardId,
|
||||
LastUpdate = s.LastUpdate.ToDateTime()
|
||||
});
|
||||
}
|
||||
|
||||
public int GetGuildCount()
|
||||
@@ -82,18 +71,21 @@ public class RemoteGrpcCoordinator : ICoordinator, IReadyExecutor
|
||||
try
|
||||
{
|
||||
var reply = await _coordClient.HeartbeatAsync(new()
|
||||
{
|
||||
State = ToCoordConnState(_client.ConnectionState),
|
||||
GuildCount = _client.ConnectionState == ConnectionState.Connected ? _client.Guilds.Count : 0,
|
||||
ShardId = _client.ShardId,
|
||||
}, deadline: DateTime.UtcNow + TimeSpan.FromSeconds(10));
|
||||
{
|
||||
State = ToCoordConnState(_client.ConnectionState),
|
||||
GuildCount =
|
||||
_client.ConnectionState == ConnectionState.Connected ? _client.Guilds.Count : 0,
|
||||
ShardId = _client.ShardId
|
||||
},
|
||||
deadline: DateTime.UtcNow + TimeSpan.FromSeconds(10));
|
||||
gracefulImminent = reply.GracefulImminent;
|
||||
}
|
||||
catch (RpcException ex)
|
||||
{
|
||||
if (!gracefulImminent)
|
||||
{
|
||||
Log.Warning(ex, "Hearbeat failed and graceful shutdown was not expected: {Message}",
|
||||
Log.Warning(ex,
|
||||
"Hearbeat failed and graceful shutdown was not expected: {Message}",
|
||||
ex.Message);
|
||||
break;
|
||||
}
|
||||
@@ -130,4 +122,4 @@ public class RemoteGrpcCoordinator : ICoordinator, IReadyExecutor
|
||||
ConnState.Connected => ConnectionState.Connected,
|
||||
_ => ConnectionState.Disconnected
|
||||
};
|
||||
}
|
||||
}
|
@@ -13,6 +13,7 @@ public class SingleProcessCoordinator : ICoordinator
|
||||
_creds = creds;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public bool RestartBot()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_creds.RestartCommand?.Cmd)
|
||||
@@ -21,7 +22,7 @@ public class SingleProcessCoordinator : ICoordinator
|
||||
Log.Error("You must set RestartCommand.Cmd and RestartCommand.Args in creds.yml");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Process.Start(_creds.RestartCommand.Cmd, _creds.RestartCommand.Args);
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
@@ -40,7 +41,7 @@ public class SingleProcessCoordinator : ICoordinator
|
||||
public IList<ShardStatus> GetAllShardStatuses()
|
||||
=> new[]
|
||||
{
|
||||
new ShardStatus()
|
||||
new ShardStatus
|
||||
{
|
||||
ConnectionState = _client.ConnectionState,
|
||||
GuildCount = _client.Guilds.Count,
|
||||
@@ -54,4 +55,4 @@ public class SingleProcessCoordinator : ICoordinator
|
||||
|
||||
public Task Reload()
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ public class SoundCloudApiService : INService
|
||||
{
|
||||
response = await http.GetStringAsync($"https://scapi.nadeko.bot/resolve?url={url}");
|
||||
}
|
||||
|
||||
|
||||
var responseObj = JsonConvert.DeserializeObject<SoundCloudVideo>(response);
|
||||
if (responseObj?.Kind != "track")
|
||||
throw new InvalidOperationException("Url is either not a track, or it doesn't exist.");
|
||||
@@ -37,12 +37,13 @@ public class SoundCloudApiService : INService
|
||||
var response = string.Empty;
|
||||
using (var http = _httpFactory.CreateClient())
|
||||
{
|
||||
response = await http.GetStringAsync(new Uri($"https://scapi.nadeko.bot/tracks?q={Uri.EscapeDataString(query)}"));
|
||||
response = await http.GetStringAsync(
|
||||
new Uri($"https://scapi.nadeko.bot/tracks?q={Uri.EscapeDataString(query)}"));
|
||||
}
|
||||
|
||||
var responseObj = JsonConvert.DeserializeObject<SoundCloudVideo[]>(response)
|
||||
.FirstOrDefault(s => s.Streamable is true);
|
||||
|
||||
.FirstOrDefault(s => s.Streamable is true);
|
||||
|
||||
if (responseObj?.Kind != "track")
|
||||
throw new InvalidOperationException("Query yielded no results.");
|
||||
|
||||
@@ -56,17 +57,22 @@ public class SoundCloudVideo
|
||||
public long Id { get; set; } = 0;
|
||||
public SoundCloudUser User { get; set; } = new();
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string FullName => User.Name + " - " + Title;
|
||||
|
||||
public string FullName
|
||||
=> User.Name + " - " + Title;
|
||||
|
||||
public bool? Streamable { get; set; } = false;
|
||||
public int Duration { get; set; }
|
||||
|
||||
[JsonProperty("permalink_url")]
|
||||
public string TrackLink { get; set; } = string.Empty;
|
||||
|
||||
[JsonProperty("artwork_url")]
|
||||
public string ArtworkUrl { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
public class SoundCloudUser
|
||||
{
|
||||
[JsonProperty("username")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
#nullable disable
|
||||
using System.Collections.Immutable;
|
||||
using System.Collections;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
@@ -9,11 +9,11 @@ public class StartingGuildsService : IEnumerable<ulong>, INService
|
||||
private readonly ImmutableList<ulong> _guilds;
|
||||
|
||||
public StartingGuildsService(DiscordSocketClient client)
|
||||
=> this._guilds = client.Guilds.Select(x => x.Id).ToImmutableList();
|
||||
=> _guilds = client.Guilds.Select(x => x.Id).ToImmutableList();
|
||||
|
||||
public IEnumerator<ulong> GetEnumerator() =>
|
||||
_guilds.GetEnumerator();
|
||||
public IEnumerator<ulong> GetEnumerator()
|
||||
=> _guilds.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() =>
|
||||
_guilds.GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> _guilds.GetEnumerator();
|
||||
}
|
@@ -6,30 +6,47 @@ namespace NadekoBot.Services;
|
||||
|
||||
public class StatsService : IStatsService, IReadyExecutor, INService, IDisposable
|
||||
{
|
||||
public const string BotVersion = "4.0.0";
|
||||
|
||||
public string Author
|
||||
=> "Kwoth#2452";
|
||||
|
||||
public string Library
|
||||
=> "Discord.Net";
|
||||
|
||||
public double MessagesPerSecond
|
||||
=> MessageCounter / GetUptime().TotalSeconds;
|
||||
|
||||
public long TextChannels
|
||||
=> Interlocked.Read(ref _textChannels);
|
||||
|
||||
public long VoiceChannels
|
||||
=> Interlocked.Read(ref _voiceChannels);
|
||||
|
||||
public long MessageCounter
|
||||
=> Interlocked.Read(ref _messageCounter);
|
||||
|
||||
public long CommandsRan
|
||||
=> Interlocked.Read(ref _commandsRan);
|
||||
|
||||
private readonly Process _currentProcess = Process.GetCurrentProcess();
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly DateTime _started;
|
||||
|
||||
public const string BotVersion = "4.0.0";
|
||||
public string Author => "Kwoth#2452";
|
||||
public string Library => "Discord.Net";
|
||||
public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds;
|
||||
|
||||
private long _textChannels;
|
||||
public long TextChannels => Interlocked.Read(ref _textChannels);
|
||||
private long _voiceChannels;
|
||||
public long VoiceChannels => Interlocked.Read(ref _voiceChannels);
|
||||
private long _messageCounter;
|
||||
public long MessageCounter => Interlocked.Read(ref _messageCounter);
|
||||
private long _commandsRan;
|
||||
public long CommandsRan => Interlocked.Read(ref _commandsRan);
|
||||
|
||||
private readonly Timer _botlistTimer;
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
|
||||
public StatsService(DiscordSocketClient client, CommandHandler cmdHandler,
|
||||
IBotCredentials creds, IHttpClientFactory factory)
|
||||
public StatsService(
|
||||
DiscordSocketClient client,
|
||||
CommandHandler cmdHandler,
|
||||
IBotCredentials creds,
|
||||
IHttpClientFactory factory)
|
||||
{
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
@@ -116,34 +133,39 @@ public class StatsService : IStatsService, IReadyExecutor, INService, IDisposabl
|
||||
};
|
||||
|
||||
_botlistTimer = new(async state =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_creds.BotListToken))
|
||||
return;
|
||||
try
|
||||
{
|
||||
using var http = _httpFactory.CreateClient();
|
||||
using var content = new FormUrlEncodedContent(
|
||||
new Dictionary<string, string> {
|
||||
{ "shard_count", _creds.TotalShards.ToString()},
|
||||
if (string.IsNullOrWhiteSpace(_creds.BotListToken))
|
||||
return;
|
||||
try
|
||||
{
|
||||
using var http = _httpFactory.CreateClient();
|
||||
using var content = new FormUrlEncodedContent(new Dictionary<string, string>
|
||||
{
|
||||
{ "shard_count", _creds.TotalShards.ToString() },
|
||||
{ "shard_id", client.ShardId.ToString() },
|
||||
{ "server_count", client.Guilds.Count().ToString() }
|
||||
});
|
||||
content.Headers.Clear();
|
||||
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
|
||||
http.DefaultRequestHeaders.Add("Authorization", _creds.BotListToken);
|
||||
content.Headers.Clear();
|
||||
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
|
||||
http.DefaultRequestHeaders.Add("Authorization", _creds.BotListToken);
|
||||
|
||||
using (await http.PostAsync(new Uri($"https://discordbots.org/api/bots/{client.CurrentUser.Id}/stats"), content)) { }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error ");
|
||||
// ignored
|
||||
}
|
||||
}, null, TimeSpan.FromMinutes(5), TimeSpan.FromHours(1));
|
||||
using (await http.PostAsync(
|
||||
new Uri($"https://discordbots.org/api/bots/{client.CurrentUser.Id}/stats"),
|
||||
content)) { }
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error ");
|
||||
// ignored
|
||||
}
|
||||
},
|
||||
null,
|
||||
TimeSpan.FromMinutes(5),
|
||||
TimeSpan.FromHours(1));
|
||||
}
|
||||
|
||||
public TimeSpan GetUptime() =>
|
||||
DateTime.UtcNow - _started;
|
||||
public TimeSpan GetUptime()
|
||||
=> DateTime.UtcNow - _started;
|
||||
|
||||
public string GetUptimeString(string separator = ", ")
|
||||
{
|
||||
@@ -170,4 +192,4 @@ public class StatsService : IStatsService, IReadyExecutor, INService, IDisposabl
|
||||
_currentProcess.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -26,11 +26,11 @@ public class YtdlOperation
|
||||
RedirectStandardOutput = true,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8,
|
||||
CreateNoWindow = true,
|
||||
},
|
||||
CreateNoWindow = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> GetDataAsync(params string[] args)
|
||||
{
|
||||
try
|
||||
@@ -49,13 +49,12 @@ public class YtdlOperation
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
Log.Error("youtube-dl is likely not installed. " +
|
||||
"Please install it before running the command again");
|
||||
Log.Error("youtube-dl is likely not installed. " + "Please install it before running the command again");
|
||||
return default;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex , "Exception running youtube-dl: {ErrorMessage}", ex.Message);
|
||||
Log.Error(ex, "Exception running youtube-dl: {ErrorMessage}", ex.Message);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -66,9 +65,9 @@ public class YtdlOperation
|
||||
|
||||
Log.Debug($"Executing {process.StartInfo.FileName} {process.StartInfo.Arguments}");
|
||||
process.Start();
|
||||
|
||||
|
||||
string line;
|
||||
while((line = await process.StandardOutput.ReadLineAsync()) != null)
|
||||
while ((line = await process.StandardOutput.ReadLineAsync()) != null)
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user