- NoPublicBotAttribute will now be properly ignored when built with GlobalNadeko configuration

- Added ILogCommandsService which will have dummy implementation on public bot, this means Logging Commands will be present on public bot to pull up help etc
- When .ve is enabled, NoPublicBot commands will show a nicer error message with link to selfhosting guide (thx ene)
- Fixed xp gain and .xp command not working on new users
- General cleanup
This commit is contained in:
Kwoth
2021-07-05 21:14:30 +02:00
parent fd35d3a836
commit a8a4c9fb44
13 changed files with 156 additions and 85 deletions

View File

@@ -18,6 +18,7 @@ using Discord.Net;
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Common.Configs; using NadekoBot.Common.Configs;
using NadekoBot.Db; using NadekoBot.Db;
using NadekoBot.Modules.Administration.Services;
using Serilog; using Serilog;
namespace NadekoBot namespace NadekoBot
@@ -116,6 +117,12 @@ namespace NadekoBot
.AddMemoryCache() .AddMemoryCache()
// music // music
.AddMusic() .AddMusic()
// admin
#if GLOBAL_NADEKO
.AddSingleton<ILogCommandService, DummyLogCommandService>()
#else
.AddSingleton<ILogCommandService, LogCommandService>()
#endif
; ;
svcs.AddHttpClient(); svcs.AddHttpClient();
@@ -145,7 +152,11 @@ namespace NadekoBot
typeof(IEarlyBehavior), typeof(IEarlyBehavior),
typeof(ILateBlocker), typeof(ILateBlocker),
typeof(IInputTransformer), typeof(IInputTransformer),
typeof(ILateExecutor))) typeof(ILateExecutor))
#if GLOBAL_NADEKO
.WithoutAttribute<NoPublicBotAttribute>()
#endif
)
.AsSelfWithInterfaces() .AsSelfWithInterfaces()
.WithSingletonLifetime() .WithSingletonLifetime()
); );

View File

@@ -10,7 +10,7 @@ namespace NadekoBot.Common
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{ {
#if GLOBAL_NADEKO #if GLOBAL_NADEKO
return Task.FromResult(PreconditionResult.FromError("Not available on the public bot")); return Task.FromResult(PreconditionResult.FromError("Not available on the public bot. To learn how to selfhost a private bot, click [here](https://nadekobot.readthedocs.io/en/latest/)."));
#else #else
return Task.FromResult(PreconditionResult.FromSuccess()); return Task.FromResult(PreconditionResult.FromSuccess());
#endif #endif

View File

@@ -3,6 +3,7 @@ using System.Linq;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Discord; using Discord;
using System.Collections.Generic; using System.Collections.Generic;
using LinqToDB;
using NadekoBot.Services.Database; using NadekoBot.Services.Database;
namespace NadekoBot.Db namespace NadekoBot.Db
@@ -11,16 +12,25 @@ namespace NadekoBot.Db
{ {
public static void EnsureUserCreated(this NadekoContext ctx, ulong userId, string username, string discrim, string avatarId) public static void EnsureUserCreated(this NadekoContext ctx, ulong userId, string username, string discrim, string avatarId)
{ {
ctx.Database.ExecuteSqlInterpolated($@" var rows = ctx.Database.ExecuteSqlInterpolated($@"
UPDATE OR IGNORE DiscordUser UPDATE OR IGNORE DiscordUser
SET Username={username}, SET Username={username},
Discriminator={discrim}, Discriminator={discrim},
AvatarId={avatarId} AvatarId={avatarId}
WHERE UserId={userId}; WHERE UserId={userId};");
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId) if (rows == 0)
VALUES ({userId}, {username}, {discrim}, {avatarId}); {
"); ctx.DiscordUser
.Add(new DiscordUser()
{
UserId = userId,
Username = username,
Discriminator = discrim,
AvatarId = avatarId,
});
ctx.SaveChanges();
}
} }
//temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown //temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown

View File

@@ -0,0 +1,21 @@
namespace NadekoBot.Modules.Administration
{
public enum LogType
{
Other,
MessageUpdated,
MessageDeleted,
UserJoined,
UserLeft,
UserBanned,
UserUnbanned,
UserUpdated,
ChannelCreated,
ChannelDestroyed,
ChannelUpdated,
UserPresence,
VoicePresence,
VoicePresenceTTS,
UserMuted
}
}

View File

@@ -1,5 +1,4 @@
#if !GLOBAL_NADEKO using Discord;
using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Common; using NadekoBot.Common;
using NadekoBot.Common.Attributes; using NadekoBot.Common.Attributes;
@@ -10,7 +9,6 @@ using NadekoBot.Modules.Administration.Services;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using static NadekoBot.Modules.Administration.Services.LogCommandService;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
{ {
@@ -18,7 +16,7 @@ namespace NadekoBot.Modules.Administration
{ {
[Group] [Group]
[NoPublicBot] [NoPublicBot]
public class LogCommands : NadekoSubmodule<LogCommandService> public class LogCommands : NadekoSubmodule<ILogCommandService>
{ {
public enum EnableDisable public enum EnableDisable
{ {
@@ -61,11 +59,11 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task LogEvents() public async Task LogEvents()
{ {
_service.GuildLogSettings.TryGetValue(ctx.Guild.Id, out LogSetting l); var logSetting = _service.GetGuildLogSettings(ctx.Guild.Id);
var str = string.Join("\n", Enum.GetNames(typeof(LogType)) var str = string.Join("\n", Enum.GetNames(typeof(LogType))
.Select(x => .Select(x =>
{ {
var val = l is null ? null : GetLogProperty(l, Enum.Parse<LogType>(x)); var val = logSetting is null ? null : GetLogProperty(logSetting, Enum.Parse<LogType>(x));
if (val != null) if (val != null)
return $"{Format.Bold(x)} <#{val}>"; return $"{Format.Bold(x)} <#{val}>";
return Format.Bold(x); return Format.Bold(x);
@@ -131,4 +129,3 @@ namespace NadekoBot.Modules.Administration
} }
} }
} }
#endif

View File

@@ -23,10 +23,10 @@ namespace NadekoBot.Modules.Administration.Services
public ConcurrentDictionary<ulong, bool> DeleteMessagesOnCommandChannels { get; } public ConcurrentDictionary<ulong, bool> DeleteMessagesOnCommandChannels { get; }
private readonly DbService _db; private readonly DbService _db;
private readonly LogCommandService _logService; private readonly ILogCommandService _logService;
public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db, public AdministrationService(Bot bot, CommandHandler cmdHandler, DbService db,
LogCommandService logService) ILogCommandService logService)
{ {
_db = db; _db = db;
_logService = logService; _logService = logService;

View File

@@ -1,5 +1,4 @@
#if !GLOBAL_NADEKO using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -18,7 +17,43 @@ using NadekoBot.Modules.Administration.Common;
namespace NadekoBot.Modules.Administration.Services namespace NadekoBot.Modules.Administration.Services
{ {
public class LogCommandService : INService public interface ILogCommandService
{
void AddDeleteIgnore(ulong xId);
Task LogServer(ulong guildId, ulong channelId, bool actionValue);
bool LogIgnore(ulong guildId, ulong channelId);
LogSetting GetGuildLogSettings(ulong guildId);
bool Log(ulong guildId, ulong? channelId, LogType type);
}
public sealed class DummyLogCommandService : ILogCommandService
{
public void AddDeleteIgnore(ulong xId)
{
}
public Task LogServer(ulong guildId, ulong channelId, bool actionValue)
{
return Task.CompletedTask;
}
public bool LogIgnore(ulong guildId, ulong channelId)
{
return false;
}
public LogSetting GetGuildLogSettings(ulong guildId)
{
return default;
}
public bool Log(ulong guildId, ulong? channelId, LogType type)
{
return false;
}
}
public sealed class LogCommandService : ILogCommandService
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
@@ -46,6 +81,8 @@ namespace NadekoBot.Modules.Administration.Services
_prot = prot; _prot = prot;
_tz = tz; _tz = tz;
#if !GLOBAL_NADEKO
using (var uow = db.GetDbContext()) using (var uow = db.GetDbContext())
{ {
var guildIds = client.Guilds.Select(x => x.Id).ToList(); var guildIds = client.Guilds.Select(x => x.Id).ToList();
@@ -92,9 +129,7 @@ namespace NadekoBot.Modules.Administration.Services
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
_client.GuildMemberUpdated += _client_GuildUserUpdated; _client.GuildMemberUpdated += _client_GuildUserUpdated;
#if !GLOBAL_NADEKO
_client.UserUpdated += _client_UserUpdated; _client.UserUpdated += _client_UserUpdated;
#endif
_client.ChannelCreated += _client_ChannelCreated; _client.ChannelCreated += _client_ChannelCreated;
_client.ChannelDestroyed += _client_ChannelDestroyed; _client.ChannelDestroyed += _client_ChannelDestroyed;
_client.ChannelUpdated += _client_ChannelUpdated; _client.ChannelUpdated += _client_ChannelUpdated;
@@ -109,12 +144,20 @@ namespace NadekoBot.Modules.Administration.Services
{ {
_ignoreMessageIds.Clear(); _ignoreMessageIds.Clear();
}, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1)); }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
#endif
} }
private readonly Timer _clearTimer; private readonly Timer _clearTimer;
private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new ConcurrentHashSet<ulong>();
private readonly IMemoryCache _memoryCache; private readonly IMemoryCache _memoryCache;
public LogSetting GetGuildLogSettings(ulong guildId)
{
GuildLogSettings.TryGetValue(guildId, out LogSetting logSetting);
return logSetting;
}
public void AddDeleteIgnore(ulong messageId) public void AddDeleteIgnore(ulong messageId)
{ {
_ignoreMessageIds.Add(messageId); _ignoreMessageIds.Add(messageId);
@@ -1129,25 +1172,6 @@ namespace NadekoBot.Modules.Administration.Services
return Task.CompletedTask; return Task.CompletedTask;
} }
public enum LogType
{
Other,
MessageUpdated,
MessageDeleted,
UserJoined,
UserLeft,
UserBanned,
UserUnbanned,
UserUpdated,
ChannelCreated,
ChannelDestroyed,
ChannelUpdated,
UserPresence,
VoicePresence,
VoicePresenceTTS,
UserMuted
}
private async Task<ITextChannel> TryGetLogChannel(IGuild guild, LogSetting logSetting, LogType logChannelType) private async Task<ITextChannel> TryGetLogChannel(IGuild guild, LogSetting logSetting, LogType logChannelType)
{ {
ulong? id = null; ulong? id = null;
@@ -1277,4 +1301,3 @@ namespace NadekoBot.Modules.Administration.Services
} }
} }
} }
#endif

View File

@@ -14,9 +14,9 @@ namespace NadekoBot.Modules.Administration.Services
//channelids where prunes are currently occuring //channelids where prunes are currently occuring
private ConcurrentHashSet<ulong> _pruningGuilds = new ConcurrentHashSet<ulong>(); private ConcurrentHashSet<ulong> _pruningGuilds = new ConcurrentHashSet<ulong>();
private readonly TimeSpan twoWeeks = TimeSpan.FromDays(14); private readonly TimeSpan twoWeeks = TimeSpan.FromDays(14);
private readonly LogCommandService _logService; private readonly ILogCommandService _logService;
public PruneService(LogCommandService logService) public PruneService(ILogCommandService logService)
{ {
this._logService = logService; this._logService = logService;
} }

View File

@@ -15,40 +15,47 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.CustomReactions.Extensions namespace NadekoBot.Modules.CustomReactions.Extensions
{ {
public static class Extensions public static class CustomReactionExtensions
{ {
private static readonly Regex imgRegex = new Regex("%(img|image):(?<tag>.*?)%", RegexOptions.Compiled); private static readonly Regex imgRegex = new Regex("%(img|image):(?<tag>.*?)%", RegexOptions.Compiled);
private static Dictionary<Regex, Func<Match, Task<string>>> regexPlaceholders { get; } = new Dictionary<Regex, Func<Match, Task<string>>>() private static Dictionary<Regex, Func<Match, Task<string>>> regexPlaceholders { get; } =
new Dictionary<Regex, Func<Match, Task<string>>>()
{
{
imgRegex, async (match) =>
{ {
{ imgRegex, async (match) => {
var tag = match.Groups["tag"].ToString(); var tag = match.Groups["tag"].ToString();
if (string.IsNullOrWhiteSpace(tag)) if (string.IsNullOrWhiteSpace(tag))
return ""; return "";
var fullQueryLink = $"http://imgur.com/search?q={tag}"; var fullQueryLink = $"http://imgur.com/search?q={tag}";
var config = Configuration.Default.WithDefaultLoader(); var config = Configuration.Default.WithDefaultLoader();
using(var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink).ConfigureAwait(false)) using (var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink)
.ConfigureAwait(false))
{ {
var elems = document.QuerySelectorAll("a.image-list-link").ToArray(); var elems = document.QuerySelectorAll("a.image-list-link").ToArray();
if (!elems.Any()) if (!elems.Any())
return ""; return "";
var img = (elems.ElementAtOrDefault(new NadekoRandom().Next(0, elems.Length))?.Children?.FirstOrDefault() as IHtmlImageElement); var img = (elems.ElementAtOrDefault(new NadekoRandom().Next(0, elems.Length))?.Children
?.FirstOrDefault() as IHtmlImageElement);
if (img?.Source is null) if (img?.Source is null)
return ""; return "";
return " " + img.Source.Replace("b.", ".", StringComparison.InvariantCulture) + " "; return " " + img.Source.Replace("b.", ".", StringComparison.InvariantCulture) + " ";
} }
} } }
}
}; };
private static string ResolveTriggerString(this string str, IUserMessage ctx, DiscordSocketClient client) private static string ResolveTriggerString(this string str, IUserMessage ctx, DiscordSocketClient client)
=> str.Replace("%bot.mention%", client.CurrentUser.Mention, StringComparison.Ordinal); => str.Replace("%bot.mention%", client.CurrentUser.Mention, StringComparison.Ordinal);
private static async Task<string> ResolveResponseStringAsync(this string str, IUserMessage ctx, DiscordSocketClient client, string resolvedTrigger, bool containsAnywhere) private static async Task<string> ResolveResponseStringAsync(this string str, IUserMessage ctx,
DiscordSocketClient client, string resolvedTrigger, bool containsAnywhere)
{ {
var substringIndex = resolvedTrigger.Length; var substringIndex = resolvedTrigger.Length;
if (containsAnywhere) if (containsAnywhere)
@@ -82,12 +89,17 @@ namespace NadekoBot.Modules.CustomReactions.Extensions
return str; return str;
} }
public static Task<string> ResponseWithContextAsync(this CustomReaction cr, IUserMessage ctx, DiscordSocketClient client, bool containsAnywhere) public static Task<string> ResponseWithContextAsync(this CustomReaction cr, IUserMessage ctx,
=> cr.Response.ResolveResponseStringAsync(ctx, client, cr.Trigger.ResolveTriggerString(ctx, client), containsAnywhere); DiscordSocketClient client, bool containsAnywhere)
=> cr.Response.ResolveResponseStringAsync(ctx, client, cr.Trigger.ResolveTriggerString(ctx, client),
containsAnywhere);
public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage ctx, DiscordSocketClient client, bool sanitize) public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage ctx,
DiscordSocketClient client, bool sanitize)
{ {
var channel = cr.DmResponse ? await ctx.Author.GetOrCreateDMChannelAsync().ConfigureAwait(false) : ctx.Channel; var channel = cr.DmResponse
? await ctx.Author.GetOrCreateDMChannelAsync().ConfigureAwait(false)
: ctx.Channel;
if (CREmbed.TryParse(cr.Response, out CREmbed crembed)) if (CREmbed.TryParse(cr.Response, out CREmbed crembed))
{ {
@@ -117,7 +129,11 @@ namespace NadekoBot.Modules.CustomReactions.Extensions
return await channel.EmbedAsync(crembed, sanitize).ConfigureAwait(false); return await channel.EmbedAsync(crembed, sanitize).ConfigureAwait(false);
} }
return await channel.SendMessageAsync((await cr.ResponseWithContextAsync(ctx, client, cr.ContainsAnywhere).ConfigureAwait(false)).SanitizeMentions(sanitize)).ConfigureAwait(false);
return await channel
.SendMessageAsync(
(await cr.ResponseWithContextAsync(ctx, client, cr.ContainsAnywhere).ConfigureAwait(false))
.SanitizeMentions(sanitize)).ConfigureAwait(false);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]

View File

@@ -16,9 +16,9 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class PlantPickCommands : GamblingSubmodule<PlantPickService> public class PlantPickCommands : GamblingSubmodule<PlantPickService>
{ {
private readonly LogCommandService logService; private readonly ILogCommandService logService;
public PlantPickCommands(LogCommandService logService, GamblingConfigService gss) : base(gss) public PlantPickCommands(ILogCommandService logService, GamblingConfigService gss) : base(gss)
{ {
this.logService = logService; this.logService = logService;
} }

View File

@@ -18,9 +18,9 @@ namespace NadekoBot.Modules.Music
[NoPublicBot] [NoPublicBot]
public sealed partial class Music : NadekoModule<IMusicService> public sealed partial class Music : NadekoModule<IMusicService>
{ {
private readonly LogCommandService _logService; private readonly ILogCommandService _logService;
public Music(LogCommandService _logService) public Music(ILogCommandService _logService)
{ {
this._logService = _logService; this._logService = _logService;
} }

View File

@@ -28,6 +28,7 @@ using Image = SixLabors.ImageSharp.Image;
namespace NadekoBot.Modules.Xp.Services namespace NadekoBot.Modules.Xp.Services
{ {
// todo improve xp with linqtodb
public class XpService : INService public class XpService : INService
{ {
private enum NotifOf private enum NotifOf

View File

@@ -1,7 +1,6 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using NadekoBot.Common; using NadekoBot.Common;
using NadekoBot.Common.Collections; using NadekoBot.Common.Collections;
using NadekoBot.Services; using NadekoBot.Services;
@@ -12,26 +11,19 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Reflection;
using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using AngleSharp.Attributes;
using NadekoBot.Common.Attributes; using NadekoBot.Common.Attributes;
using Serilog;
namespace NadekoBot.Extensions namespace NadekoBot.Extensions
{ {
@@ -216,7 +208,7 @@ namespace NadekoBot.Extensions
dict.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1"); dict.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1");
} }
public static IMessage DeleteAfter(this IUserMessage msg, int seconds, LogCommandService logService = null) public static IMessage DeleteAfter(this IUserMessage msg, int seconds, ILogCommandService logService = null)
{ {
if (msg is null) if (msg is null)
return null; return null;