Cleaning up projects, project is building

This commit is contained in:
Kwoth
2023-04-06 10:57:30 +02:00
parent 1f1b01995e
commit 069f8fab9d
82 changed files with 822 additions and 706 deletions

View File

@@ -1,13 +1,13 @@
// namespace NadekoBot.Common;
//
// public readonly struct LocStr
// {
// public readonly string Key;
// public readonly object[] Params;
//
// public LocStr(string key, params object[] data)
// {
// Key = key;
// Params = data;
// }
// }
namespace NadekoBot;
public readonly struct LocStr
{
public readonly string Key;
public readonly object[] Params;
public LocStr(string key, params object[] data)
{
Key = key;
Params = data;
}
}

View File

@@ -12,7 +12,7 @@ public sealed class OnlyPublicBotAttribute : PreconditionAttribute
CommandInfo command,
IServiceProvider services)
{
#if GLOBAL_NADEKO || DEBUG
#if GLOBAL_NADEKO
return Task.FromResult(PreconditionResult.FromSuccess());
#else
return Task.FromResult(PreconditionResult.FromError("Only available on the public bot."));

View File

@@ -0,0 +1,10 @@
namespace NadekoBot.Modules.Gambling.Bank;
public interface IBankService
{
Task<bool> DepositAsync(ulong userId, long amount);
Task<bool> WithdrawAsync(ulong userId, long amount);
Task<long> GetBalanceAsync(ulong userId);
Task<bool> AwardAsync(ulong userId, long amount);
Task<bool> TakeAsync(ulong userId, long amount);
}

View File

@@ -11,5 +11,5 @@ public interface IPermissionChecker
IMessageChannel channel,
IUser author,
string module,
string cmd);
string? cmd);
}

View File

@@ -7,7 +7,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cloneable" Version="1.3.0" />
<PackageReference Include="Discord.Net" Version="3.104.0" />
</ItemGroup>
@@ -30,7 +29,8 @@
<ProjectReference Include="..\Nadeko.Medusa\Nadeko.Medusa.csproj" />
<ProjectReference Include="..\NadekoBot.Generators\NadekoBot.Generators.csproj" OutputItemType="Analyzer" />
<ProjectReference Include="..\Nadeko.Bot.Generators.Strings\Nadeko.Bot.Generators.Strings.csproj" OutputItemType="Analyzer" />
<ProjectReference Include="..\Nadeko.Bot.Generators.Cloneable\Nadeko.Bot.Generators.Cloneable.csproj" OutputItemType="Analyzer" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,6 +1,5 @@
#nullable disable
using System.Globalization;
using NadekoBot.Services;
// ReSharper disable InconsistentNaming

View File

@@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
public readonly struct FeatureLimitKey
{

View File

@@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
public readonly struct FeatureQuotaStats
{

View File

@@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility;
namespace NadekoBot.Modules.Patronage;
public interface ISubscriberData
{

View File

@@ -1,7 +1,7 @@
using NadekoBot.Db.Models;
using OneOf;
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
/// <summary>
/// Manages patrons and provides access to their data

View File

@@ -1,5 +1,5 @@
#nullable disable
namespace NadekoBot.Modules.Utility;
namespace NadekoBot.Modules.Patronage;
/// <summary>
/// Services implementing this interface are handling pledges/subscriptions/payments coming

View File

@@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
public readonly struct Patron
{

View File

@@ -1,7 +1,7 @@
using NadekoBot.Common.Yml;
using Cloneable;
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
[Cloneable]
public partial class PatronConfigData : ICloneable<PatronConfigData>

View File

@@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
public static class PatronExtensions
{

View File

@@ -1,5 +1,5 @@
// ReSharper disable InconsistentNaming
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
public enum PatronTier
{

View File

@@ -1,6 +1,6 @@
using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
/// <summary>
/// Represents information about why the user has triggered a quota limit

View File

@@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
public enum QuotaPer
{

View File

@@ -1,5 +1,5 @@
#nullable disable
namespace NadekoBot.Modules.Utility;
namespace NadekoBot.Modules.Patronage;
public enum SubscriptionChargeStatus
{

View File

@@ -1,4 +1,4 @@
namespace NadekoBot.Modules.Utility.Patronage;
namespace NadekoBot.Modules.Patronage;
public readonly struct UserQuotaStats
{

View File

@@ -0,0 +1,131 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Modules.Permissions.Services;
public sealed class BlacklistService : IExecOnMessage
{
public int Priority
=> int.MaxValue;
private readonly DbService _db;
private readonly IPubSub _pubSub;
private readonly IBotCredentials _creds;
private IReadOnlyList<BlacklistEntry> blacklist;
private readonly TypedKey<BlacklistEntry[]> _blPubKey = new("blacklist.reload");
public BlacklistService(DbService db, IPubSub pubSub, IBotCredentials creds)
{
_db = db;
_pubSub = pubSub;
_creds = creds;
Reload(false);
_pubSub.Sub(_blPubKey, OnReload);
}
private ValueTask OnReload(BlacklistEntry[] newBlacklist)
{
blacklist = newBlacklist;
return default;
}
public Task<bool> ExecOnMessageAsync(IGuild guild, IUserMessage usrMsg)
{
foreach (var bl in blacklist)
{
if (guild is not null && bl.Type == BlacklistType.Server && bl.ItemId == guild.Id)
{
Log.Information("Blocked input from blacklisted guild: {GuildName} [{GuildId}]", guild.Name, guild.Id);
return Task.FromResult(true);
}
if (bl.Type == BlacklistType.Channel && bl.ItemId == usrMsg.Channel.Id)
{
Log.Information("Blocked input from blacklisted channel: {ChannelName} [{ChannelId}]",
usrMsg.Channel.Name,
usrMsg.Channel.Id);
return Task.FromResult(true);
}
if (bl.Type == BlacklistType.User && bl.ItemId == usrMsg.Author.Id)
{
Log.Information("Blocked input from blacklisted user: {UserName} [{UserId}]",
usrMsg.Author.ToString(),
usrMsg.Author.Id);
return Task.FromResult(true);
}
}
return Task.FromResult(false);
}
public IReadOnlyList<BlacklistEntry> GetBlacklist()
=> blacklist;
public void Reload(bool publish = true)
{
using var uow = _db.GetDbContext();
var toPublish = uow.Blacklist.AsNoTracking().ToArray();
blacklist = toPublish;
if (publish)
_pubSub.Pub(_blPubKey, toPublish);
}
public void Blacklist(BlacklistType type, ulong id)
{
if (_creds.OwnerIds.Contains(id))
return;
using var uow = _db.GetDbContext();
var item = new BlacklistEntry
{
ItemId = id,
Type = type
};
uow.Blacklist.Add(item);
uow.SaveChanges();
Reload();
}
public void UnBlacklist(BlacklistType type, ulong id)
{
using var uow = _db.GetDbContext();
var toRemove = uow.Blacklist.FirstOrDefault(bi => bi.ItemId == id && bi.Type == type);
if (toRemove is not null)
uow.Blacklist.Remove(toRemove);
uow.SaveChanges();
Reload();
}
public void BlacklistUsers(IReadOnlyCollection<ulong> toBlacklist)
{
using (var uow = _db.GetDbContext())
{
var bc = uow.Blacklist;
//blacklist the users
bc.AddRange(toBlacklist.Select(x => new BlacklistEntry
{
ItemId = x,
Type = BlacklistType.User
}));
//clear their currencies
uow.DiscordUser.RemoveFromMany(toBlacklist);
uow.SaveChanges();
}
Reload();
}
}

View File

@@ -0,0 +1,172 @@
using CommandLine;
namespace NadekoBot.Common;
public sealed class CommandsUtilityService : ICommandsUtilityService, INService
{
private readonly CommandHandler _ch;
private readonly IBotStrings _strings;
private readonly DiscordPermOverrideService _dpos;
private readonly IEmbedBuilderService _eb;
private readonly ILocalization _loc;
private readonly Nadeko.Medusa.IMedusaLoaderService _medusae;
public CommandsUtilityService(
CommandHandler ch,
IBotStrings strings,
DiscordPermOverrideService dpos,
IEmbedBuilderService eb,
ILocalization loc,
Nadeko.Medusa.IMedusaLoaderService medusae)
{
_ch = ch;
_strings = strings;
_dpos = dpos;
_eb = eb;
_loc = loc;
_medusae = medusae;
}
public IEmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild)
{
var prefix = _ch.GetPrefix(guild);
var str = $"**`{prefix + com.Aliases.First()}`**";
var alias = com.Aliases.Skip(1).FirstOrDefault();
if (alias is not null)
str += $" **/ `{prefix + alias}`**";
var culture = _loc.GetCultureInfo(guild);
var em = _eb.Create()
.AddField(str, $"{com.RealSummary(_strings, _medusae, culture, prefix)}", true);
_dpos.TryGetOverrides(guild?.Id ?? 0, com.Name, out var overrides);
var reqs = GetCommandRequirements(com, (GuildPermission?)overrides);
if (reqs.Any())
em.AddField(GetText(strs.requires, guild), string.Join("\n", reqs));
em.AddField(_strings.GetText(strs.usage),
string.Join("\n", com.RealRemarksArr(_strings, _medusae, culture, prefix).Map(arg => Format.Code(arg))))
.WithFooter(GetText(strs.module(com.Module.GetTopLevelModule().Name), guild))
.WithOkColor();
var opt = GetNadekoOptionType(com.Attributes);
if (opt is not null)
{
var hs = GetCommandOptionHelp(opt);
if (!string.IsNullOrWhiteSpace(hs))
em.AddField(GetText(strs.options, guild), hs);
}
return em;
}
public static string GetCommandOptionHelp(Type opt)
{
var strs = GetCommandOptionHelpList(opt);
return string.Join("\n", strs);
}
public static List<string> GetCommandOptionHelpList(Type opt)
{
var strs = opt.GetProperties()
.Select(x => x.GetCustomAttributes(true).FirstOrDefault(a => a is OptionAttribute))
.Where(x => x is not null)
.Cast<OptionAttribute>()
.Select(x =>
{
var toReturn = $"`--{x.LongName}`";
if (!string.IsNullOrWhiteSpace(x.ShortName))
toReturn += $" (`-{x.ShortName}`)";
toReturn += $" {x.HelpText} ";
return toReturn;
})
.ToList();
return strs;
}
public static Type GetNadekoOptionType(IEnumerable<Attribute> attributes)
=> attributes
.Select(a => a.GetType())
.Where(a => a.IsGenericType
&& a.GetGenericTypeDefinition() == typeof(NadekoOptionsAttribute<>))
.Select(a => a.GenericTypeArguments[0])
.FirstOrDefault();
public static string[] GetCommandRequirements(CommandInfo cmd, GuildPerm? overrides = null)
{
var toReturn = new List<string>();
if (cmd.Preconditions.Any(x => x is OwnerOnlyAttribute))
toReturn.Add("Bot Owner Only");
if (cmd.Preconditions.Any(x => x is NoPublicBotAttribute)
|| cmd.Module
.Preconditions
.Any(x => x is NoPublicBotAttribute)
|| cmd.Module.GetTopLevelModule()
.Preconditions
.Any(x => x is NoPublicBotAttribute))
toReturn.Add("No Public Bot");
if (cmd.Preconditions
.Any(x => x is OnlyPublicBotAttribute)
|| cmd.Module
.Preconditions
.Any(x => x is OnlyPublicBotAttribute)
|| cmd.Module.GetTopLevelModule()
.Preconditions
.Any(x => x is OnlyPublicBotAttribute))
toReturn.Add("Only Public Bot");
var userPermString = cmd.Preconditions
.Where(ca => ca is UserPermAttribute)
.Cast<UserPermAttribute>()
.Select(userPerm =>
{
if (userPerm.ChannelPermission is { } cPerm)
return GetPreconditionString(cPerm);
if (userPerm.GuildPermission is { } gPerm)
return GetPreconditionString(gPerm);
return string.Empty;
})
.Where(x => !string.IsNullOrWhiteSpace(x))
.Join('\n');
if (overrides is null)
{
if (!string.IsNullOrWhiteSpace(userPermString))
toReturn.Add(userPermString);
}
else
{
if (!string.IsNullOrWhiteSpace(userPermString))
toReturn.Add(Format.Strikethrough(userPermString));
toReturn.Add(GetPreconditionString(overrides.Value));
}
return toReturn.ToArray();
}
public static string GetPreconditionString(ChannelPerm perm)
=> (perm + " Channel Permission").Replace("Guild", "Server");
public static string GetPreconditionString(GuildPerm perm)
=> (perm + " Server Permission").Replace("Guild", "Server");
public string GetText(LocStr str, IGuild? guild)
=> _strings.GetText(str, guild?.Id);
}
public interface ICommandsUtilityService
{
IEmbedBuilder GetCommandHelp(CommandInfo com, IGuild guild);
}

View File

@@ -0,0 +1,136 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Nadeko.Common;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Services.Database.Models;
namespace NadekoBot.Services;
public class DiscordPermOverrideService : INService, IExecPreCommand, IDiscordPermOverrideService
{
public int Priority { get; } = int.MaxValue;
private readonly DbService _db;
private readonly IServiceProvider _services;
private readonly ConcurrentDictionary<(ulong, string), DiscordPermOverride> _overrides;
public DiscordPermOverrideService(DbService db, IServiceProvider services)
{
_db = db;
_services = services;
using var uow = _db.GetDbContext();
_overrides = uow.DiscordPermOverrides.AsNoTracking()
.AsEnumerable()
.ToDictionary(o => (o.GuildId ?? 0, o.Command), o => o)
.ToConcurrent();
}
public bool TryGetOverrides(ulong guildId, string commandName, out Nadeko.Bot.Db.GuildPerm? perm)
{
commandName = commandName.ToLowerInvariant();
if (_overrides.TryGetValue((guildId, commandName), out var dpo))
{
perm = dpo.Perm;
return true;
}
perm = null;
return false;
}
public Task<PreconditionResult> ExecuteOverrides(
ICommandContext ctx,
CommandInfo command,
GuildPerm perms,
IServiceProvider services)
{
var rupa = new RequireUserPermissionAttribute(perms);
return rupa.CheckPermissionsAsync(ctx, command, services);
}
public async Task AddOverride(ulong guildId, string commandName, GuildPerm perm)
{
commandName = commandName.ToLowerInvariant();
await using var uow = _db.GetDbContext();
var over = await uow.Set<DiscordPermOverride>()
.AsQueryable()
.FirstOrDefaultAsync(x => x.GuildId == guildId && commandName == x.Command);
if (over is null)
{
uow.Set<DiscordPermOverride>()
.Add(over = new()
{
Command = commandName,
Perm = (Nadeko.Bot.Db.GuildPerm)perm,
GuildId = guildId
});
}
else
over.Perm = (Nadeko.Bot.Db.GuildPerm)perm;
_overrides[(guildId, commandName)] = over;
await uow.SaveChangesAsync();
}
public async Task ClearAllOverrides(ulong guildId)
{
await using var uow = _db.GetDbContext();
var overrides = await uow.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.ToListAsync();
uow.RemoveRange(overrides);
await uow.SaveChangesAsync();
foreach (var over in overrides)
_overrides.TryRemove((guildId, over.Command), out _);
}
public async Task RemoveOverride(ulong guildId, string commandName)
{
commandName = commandName.ToLowerInvariant();
await using var uow = _db.GetDbContext();
var over = await uow.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.FirstOrDefaultAsync(x => x.GuildId == guildId && x.Command == commandName);
if (over is null)
return;
uow.Remove(over);
await uow.SaveChangesAsync();
_overrides.TryRemove((guildId, commandName), out _);
}
public async Task<List<DiscordPermOverride>> GetAllOverrides(ulong guildId)
{
await using var uow = _db.GetDbContext();
return await uow.Set<DiscordPermOverride>()
.AsQueryable()
.AsNoTracking()
.Where(x => x.GuildId == guildId)
.ToListAsync();
}
public async Task<bool> ExecPreCommandAsync(ICommandContext context, string moduleName, CommandInfo command)
{
if (TryGetOverrides(context.Guild?.Id ?? 0, command.Name, out var perm) && perm is not null)
{
var result =
await new RequireUserPermissionAttribute((GuildPermission)perm).CheckPermissionsAsync(context,
command,
_services);
return !result.IsSuccess;
}
return false;
}
}