mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 09:48:26 -04:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9d3e80eb32 | ||
|
42cbb7f626 | ||
|
4d175477f5 | ||
|
643987c41f | ||
|
03396642a4 | ||
|
3fd5f0c97a | ||
|
5d78f29329 | ||
|
2a98aceae6 | ||
|
5c933b676d | ||
|
2e4de7723e |
24
CHANGELOG.md
24
CHANGELOG.md
@@ -2,6 +2,30 @@
|
|||||||
|
|
||||||
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
|
## [4.2.14] - 03.07.2022
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added `.log userwarned` (Logging user warnings)
|
||||||
|
- Claiming `.timely` will now show a button which you can click to set a reminder
|
||||||
|
- Added `%server.icon%` placeholder
|
||||||
|
- Added `warn` punishment action for protection commands (it won't work with `.warnp`)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `.log userbanned` will now have a ban reason
|
||||||
|
- When `.die` is used, bot will try to update it's status to `Invisible`
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed elipsis character issue with aliases/quotes. You should now be able to set an elipsis to be an alias of `.quoteprint`
|
||||||
|
|
||||||
|
## [4.2.13] - 30.06.2022
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed `.cash` bank interaction not being ephemeral anymore
|
||||||
|
|
||||||
## [4.2.12] - 30.06.2022
|
## [4.2.12] - 30.06.2022
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
@@ -70,6 +70,7 @@ public sealed class Bot
|
|||||||
: GatewayIntents.AllUnprivileged,
|
: GatewayIntents.AllUnprivileged,
|
||||||
LogGatewayIntentWarnings = false,
|
LogGatewayIntentWarnings = false,
|
||||||
FormatUsersInBidirectionalUnicode = false,
|
FormatUsersInBidirectionalUnicode = false,
|
||||||
|
DefaultRetryMode = RetryMode.AlwaysRetry ^ RetryMode.RetryRatelimit
|
||||||
});
|
});
|
||||||
|
|
||||||
_commandService = new(new()
|
_commandService = new(new()
|
||||||
@@ -260,6 +261,7 @@ public sealed class Bot
|
|||||||
Client.JoinedGuild += Client_JoinedGuild;
|
Client.JoinedGuild += Client_JoinedGuild;
|
||||||
Client.LeftGuild += Client_LeftGuild;
|
Client.LeftGuild += Client_LeftGuild;
|
||||||
|
|
||||||
|
// _ = Client.SetStatusAsync(UserStatus.Online);
|
||||||
Log.Information("Shard {ShardId} logged in", Client.ShardId);
|
Log.Information("Shard {ShardId} logged in", Client.ShardId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,5 +27,6 @@ public enum LogType
|
|||||||
UserPresence,
|
UserPresence,
|
||||||
VoicePresence,
|
VoicePresence,
|
||||||
VoicePresenceTts,
|
VoicePresenceTts,
|
||||||
UserMuted
|
UserMuted,
|
||||||
|
UserWarned,
|
||||||
}
|
}
|
@@ -67,13 +67,13 @@ public abstract class NadekoModule : ModuleBase
|
|||||||
|
|
||||||
// localized replies
|
// localized replies
|
||||||
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||||
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
|
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||||
|
|
||||||
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||||
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
|
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||||
|
|
||||||
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||||
=> SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
|
=> SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||||
|
|
||||||
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
||||||
{
|
{
|
||||||
|
@@ -55,6 +55,7 @@ public class ReplacementBuilder
|
|||||||
_reps.TryAdd("%server%", () => g is null ? "DM" : g.Name);
|
_reps.TryAdd("%server%", () => g is null ? "DM" : g.Name);
|
||||||
_reps.TryAdd("%server.id%", () => g is null ? "DM" : g.Id.ToString());
|
_reps.TryAdd("%server.id%", () => g is null ? "DM" : g.Id.ToString());
|
||||||
_reps.TryAdd("%server.name%", () => g is null ? "DM" : g.Name);
|
_reps.TryAdd("%server.name%", () => g is null ? "DM" : g.Name);
|
||||||
|
_reps.TryAdd("%server.icon%", () => g is null ? null : g.IconUrl);
|
||||||
_reps.TryAdd("%server.members%", () => g is { } sg ? sg.MemberCount.ToString() : "?");
|
_reps.TryAdd("%server.members%", () => g is { } sg ? sg.MemberCount.ToString() : "?");
|
||||||
_reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
|
_reps.TryAdd("%server.boosters%", () => g.PremiumSubscriptionCount.ToString());
|
||||||
_reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
|
_reps.TryAdd("%server.boost_level%", () => ((int)g.PremiumTier).ToString());
|
||||||
|
@@ -44,11 +44,11 @@ public static class QuoteExtensions
|
|||||||
var rngk = new NadekoRandom();
|
var rngk = new NadekoRandom();
|
||||||
return (await quotes.AsQueryable()
|
return (await quotes.AsQueryable()
|
||||||
.Where(q => q.GuildId == guildId
|
.Where(q => q.GuildId == guildId
|
||||||
&& q.Keyword == keyword
|
&& (keyword == null || q.Keyword == keyword)
|
||||||
&& EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
&& (EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
||||||
// && q.Text.Contains(text, StringComparison.OrdinalIgnoreCase)
|
|| EF.Functions.Like(q.AuthorName, text)))
|
||||||
)
|
.ToListAsync())
|
||||||
.ToListAsync()).OrderBy(_ => rngk.Next())
|
.OrderBy(_ => rngk.Next())
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -48,7 +48,8 @@ public enum PunishmentAction
|
|||||||
RemoveRoles,
|
RemoveRoles,
|
||||||
ChatMute,
|
ChatMute,
|
||||||
VoiceMute,
|
VoiceMute,
|
||||||
AddRole
|
AddRole,
|
||||||
|
Warn
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AntiSpamIgnore : DbEntity
|
public class AntiSpamIgnore : DbEntity
|
||||||
|
@@ -29,4 +29,5 @@ public class LogSetting : DbEntity
|
|||||||
|
|
||||||
public ulong? LogVoicePresenceId { get; set; }
|
public ulong? LogVoicePresenceId { get; set; }
|
||||||
public ulong? LogVoicePresenceTTSId { get; set; }
|
public ulong? LogVoicePresenceTTSId { get; set; }
|
||||||
|
public ulong? LogWarnsId { get; set; }
|
||||||
}
|
}
|
3518
src/NadekoBot/Migrations/MySql/20220703194400_logwarns.Designer.cs
generated
Normal file
3518
src/NadekoBot/Migrations/MySql/20220703194400_logwarns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
src/NadekoBot/Migrations/MySql/20220703194400_logwarns.cs
Normal file
25
src/NadekoBot/Migrations/MySql/20220703194400_logwarns.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.Mysql
|
||||||
|
{
|
||||||
|
public partial class logwarns : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "logwarnsid",
|
||||||
|
table: "logsettings",
|
||||||
|
type: "bigint unsigned",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "logwarnsid",
|
||||||
|
table: "logsettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1406,6 +1406,10 @@ namespace NadekoBot.Migrations.Mysql
|
|||||||
.HasColumnType("bigint unsigned")
|
.HasColumnType("bigint unsigned")
|
||||||
.HasColumnName("logvoicepresencettsid");
|
.HasColumnName("logvoicepresencettsid");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogWarnsId")
|
||||||
|
.HasColumnType("bigint unsigned")
|
||||||
|
.HasColumnName("logwarnsid");
|
||||||
|
|
||||||
b.Property<ulong?>("MessageDeletedId")
|
b.Property<ulong?>("MessageDeletedId")
|
||||||
.HasColumnType("bigint unsigned")
|
.HasColumnType("bigint unsigned")
|
||||||
.HasColumnName("messagedeletedid");
|
.HasColumnName("messagedeletedid");
|
||||||
|
3660
src/NadekoBot/Migrations/Postgresql/20220703194412_logwarns.Designer.cs
generated
Normal file
3660
src/NadekoBot/Migrations/Postgresql/20220703194412_logwarns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
public partial class logwarns : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<decimal>(
|
||||||
|
name: "logwarnsid",
|
||||||
|
table: "logsettings",
|
||||||
|
type: "numeric(20,0)",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "logwarnsid",
|
||||||
|
table: "logsettings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1472,6 +1472,10 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("logvoicepresencettsid");
|
.HasColumnName("logvoicepresencettsid");
|
||||||
|
|
||||||
|
b.Property<decimal?>("LogWarnsId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("logwarnsid");
|
||||||
|
|
||||||
b.Property<decimal?>("MessageDeletedId")
|
b.Property<decimal?>("MessageDeletedId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("messagedeletedid");
|
.HasColumnName("messagedeletedid");
|
||||||
|
2825
src/NadekoBot/Migrations/Sqlite/20220703194348_logwarns.Designer.cs
generated
Normal file
2825
src/NadekoBot/Migrations/Sqlite/20220703194348_logwarns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
src/NadekoBot/Migrations/Sqlite/20220703194348_logwarns.cs
Normal file
43
src/NadekoBot/Migrations/Sqlite/20220703194348_logwarns.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
public partial class logwarns : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "Name",
|
||||||
|
table: "StreamOnlineMessages",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "TEXT");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<ulong>(
|
||||||
|
name: "LogWarnsId",
|
||||||
|
table: "LogSettings",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LogWarnsId",
|
||||||
|
table: "LogSettings");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<string>(
|
||||||
|
name: "Name",
|
||||||
|
table: "StreamOnlineMessages",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "",
|
||||||
|
oldClrType: typeof(string),
|
||||||
|
oldType: "TEXT",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -278,7 +278,6 @@ namespace NadekoBot.Migrations
|
|||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("Type")
|
b.Property<int>("Type")
|
||||||
@@ -1100,6 +1099,9 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<ulong?>("LogVoicePresenceTTSId")
|
b.Property<ulong?>("LogVoicePresenceTTSId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong?>("LogWarnsId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<ulong?>("MessageDeletedId")
|
b.Property<ulong?>("MessageDeletedId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
@@ -335,6 +335,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await _client.SetStatusAsync(UserStatus.DoNotDisturb);
|
||||||
await ReplyConfirmLocalizedAsync(strs.shutting_down);
|
await ReplyConfirmLocalizedAsync(strs.shutting_down);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@@ -26,6 +26,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
private readonly IMemoryCache _memoryCache;
|
private readonly IMemoryCache _memoryCache;
|
||||||
|
|
||||||
private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new();
|
private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new();
|
||||||
|
private readonly UserPunishService _punishService;
|
||||||
|
|
||||||
public LogCommandService(
|
public LogCommandService(
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
@@ -35,7 +36,8 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
ProtectionService prot,
|
ProtectionService prot,
|
||||||
GuildTimezoneService tz,
|
GuildTimezoneService tz,
|
||||||
IMemoryCache memoryCache,
|
IMemoryCache memoryCache,
|
||||||
IEmbedBuilderService eb)
|
IEmbedBuilderService eb,
|
||||||
|
UserPunishService punishService)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_memoryCache = memoryCache;
|
_memoryCache = memoryCache;
|
||||||
@@ -45,6 +47,8 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
_mute = mute;
|
_mute = mute;
|
||||||
_prot = prot;
|
_prot = prot;
|
||||||
_tz = tz;
|
_tz = tz;
|
||||||
|
_punishService = punishService;
|
||||||
|
|
||||||
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();
|
||||||
@@ -78,6 +82,8 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
_mute.UserUnmuted += MuteCommands_UserUnmuted;
|
_mute.UserUnmuted += MuteCommands_UserUnmuted;
|
||||||
|
|
||||||
_prot.OnAntiProtectionTriggered += TriggeredAntiProtection;
|
_prot.OnAntiProtectionTriggered += TriggeredAntiProtection;
|
||||||
|
|
||||||
|
_punishService.OnUserWarned += PunishServiceOnOnUserWarned;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
@@ -183,6 +189,30 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
GuildLogSettings.AddOrUpdate(guildId, _ => logSetting, (_, _) => logSetting);
|
GuildLogSettings.AddOrUpdate(guildId, _ => logSetting, (_, _) => logSetting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private async Task PunishServiceOnOnUserWarned(Warning arg)
|
||||||
|
{
|
||||||
|
if (!GuildLogSettings.TryGetValue(arg.GuildId, out var logSetting) || logSetting.LogWarnsId is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var g = _client.GetGuild(arg.GuildId);
|
||||||
|
|
||||||
|
ITextChannel? logChannel;
|
||||||
|
if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserWarned)) is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var embed = _eb.Create()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle($"⚠️ User Warned")
|
||||||
|
.WithDescription($"<@{arg.UserId}> | {arg.UserId}")
|
||||||
|
.AddField("Mod", arg.Moderator)
|
||||||
|
.AddField("Reason", string.IsNullOrWhiteSpace(arg.Reason) ? "-" : arg.Reason, true)
|
||||||
|
.WithFooter(CurrentTime(g));
|
||||||
|
|
||||||
|
await logChannel.EmbedAsync(embed);
|
||||||
|
}
|
||||||
|
|
||||||
private Task _client_UserUpdated(SocketUser before, SocketUser uAfter)
|
private Task _client_UserUpdated(SocketUser before, SocketUser uAfter)
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
@@ -296,6 +326,9 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
channelId = logSetting.LogVoicePresenceTTSId =
|
channelId = logSetting.LogVoicePresenceTTSId =
|
||||||
logSetting.LogVoicePresenceTTSId is null ? cid : default;
|
logSetting.LogVoicePresenceTTSId is null ? cid : default;
|
||||||
break;
|
break;
|
||||||
|
case LogType.UserWarned:
|
||||||
|
channelId = logSetting.LogWarnsId = logSetting.LogWarnsId is null ? cid : default;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uow.SaveChanges();
|
uow.SaveChanges();
|
||||||
@@ -945,11 +978,25 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
ITextChannel? logChannel;
|
ITextChannel? logChannel;
|
||||||
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null)
|
if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
string? reason = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var ban = await guild.GetBanAsync(usr);
|
||||||
|
reason = ban?.Reason;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var embed = _eb.Create()
|
var embed = _eb.Create()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned))
|
.WithTitle("🚫 " + GetText(logChannel.Guild, strs.user_banned))
|
||||||
.WithDescription(usr.ToString()!)
|
.WithDescription(usr.ToString()!)
|
||||||
.AddField("Id", usr.Id.ToString())
|
.AddField("Id", usr.Id.ToString())
|
||||||
|
.AddField("Reason", string.IsNullOrWhiteSpace(reason) ? "-" : reason)
|
||||||
.WithFooter(CurrentTime(guild));
|
.WithFooter(CurrentTime(guild));
|
||||||
|
|
||||||
var avatarUrl = usr.GetAvatarUrl();
|
var avatarUrl = usr.GetAvatarUrl();
|
||||||
@@ -1130,6 +1177,9 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
case LogType.UserMuted:
|
case LogType.UserMuted:
|
||||||
id = logSetting.UserMutedId;
|
id = logSetting.UserMutedId;
|
||||||
break;
|
break;
|
||||||
|
case LogType.UserWarned:
|
||||||
|
id = logSetting.LogWarnsId;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id is null or 0)
|
if (id is null or 0)
|
||||||
@@ -1200,6 +1250,9 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
|||||||
case LogType.VoicePresenceTts:
|
case LogType.VoicePresenceTts:
|
||||||
newLogSetting.LogVoicePresenceTTSId = null;
|
newLogSetting.LogVoicePresenceTTSId = null;
|
||||||
break;
|
break;
|
||||||
|
case LogType.UserWarned:
|
||||||
|
newLogSetting.LogWarnsId = null;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (_, _) => newLogSetting);
|
GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (_, _) => newLogSetting);
|
||||||
|
@@ -341,7 +341,8 @@ public partial class Administration
|
|||||||
public async partial Task WarnPunish(int number, PunishmentAction punish, StoopidTime time = null)
|
public async partial Task WarnPunish(int number, PunishmentAction punish, StoopidTime time = null)
|
||||||
{
|
{
|
||||||
// this should never happen. Addrole has its own method with higher priority
|
// this should never happen. Addrole has its own method with higher priority
|
||||||
if (punish == PunishmentAction.AddRole)
|
// also disallow warn punishment for getting warned
|
||||||
|
if (punish is PunishmentAction.AddRole or PunishmentAction.Warn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
||||||
|
@@ -18,6 +18,8 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
private readonly BotConfigService _bcs;
|
private readonly BotConfigService _bcs;
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
|
|
||||||
|
public event Func<Warning, Task> OnUserWarned = static delegate { return Task.CompletedTask; };
|
||||||
|
|
||||||
public UserPunishService(
|
public UserPunishService(
|
||||||
MuteService mute,
|
MuteService mute,
|
||||||
DbService db,
|
DbService db,
|
||||||
@@ -93,6 +95,8 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = OnUserWarned(warn);
|
||||||
|
|
||||||
var totalCount = previousCount + weight;
|
var totalCount = previousCount + weight;
|
||||||
|
|
||||||
var p = ps.Where(x => x.Count > previousCount && x.Count <= totalCount)
|
var p = ps.Where(x => x.Count > previousCount && x.Count <= totalCount)
|
||||||
@@ -185,6 +189,9 @@ public class UserPunishService : INService, IReadyExecutor
|
|||||||
guild.Id);
|
guild.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case PunishmentAction.Warn:
|
||||||
|
await Warn(guild, user.Id, mod, 1, reason);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ using NadekoBot.Modules.Utility.Patronage;
|
|||||||
using NadekoBot.Modules.Gambling.Bank;
|
using NadekoBot.Modules.Gambling.Bank;
|
||||||
using NadekoBot.Modules.Gambling.Common;
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
using NadekoBot.Modules.Gambling.Services;
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
using NadekoBot.Modules.Utility.Services;
|
||||||
using NadekoBot.Services.Currency;
|
using NadekoBot.Services.Currency;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
@@ -44,6 +45,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
private readonly GamblingConfigService _configService;
|
private readonly GamblingConfigService _configService;
|
||||||
private readonly IBankService _bank;
|
private readonly IBankService _bank;
|
||||||
private readonly IPatronageService _ps;
|
private readonly IPatronageService _ps;
|
||||||
|
private readonly RemindService _remind;
|
||||||
|
|
||||||
private IUserMessage rdMsg;
|
private IUserMessage rdMsg;
|
||||||
|
|
||||||
@@ -54,7 +56,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
DownloadTracker tracker,
|
DownloadTracker tracker,
|
||||||
GamblingConfigService configService,
|
GamblingConfigService configService,
|
||||||
IBankService bank,
|
IBankService bank,
|
||||||
IPatronageService ps)
|
IPatronageService ps,
|
||||||
|
RemindService remind)
|
||||||
: base(configService)
|
: base(configService)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
@@ -62,6 +65,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
_client = client;
|
_client = client;
|
||||||
_bank = bank;
|
_bank = bank;
|
||||||
_ps = ps;
|
_ps = ps;
|
||||||
|
_remind = remind;
|
||||||
|
|
||||||
_enUsCulture = new CultureInfo("en-US", false).NumberFormat;
|
_enUsCulture = new CultureInfo("en-US", false).NumberFormat;
|
||||||
_enUsCulture.NumberDecimalDigits = 0;
|
_enUsCulture.NumberDecimalDigits = 0;
|
||||||
@@ -110,6 +114,39 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
PrettyName = "Timely"
|
PrettyName = "Timely"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public class RemindMeInteraction : NInteraction
|
||||||
|
{
|
||||||
|
public RemindMeInteraction(
|
||||||
|
[NotNull] DiscordSocketClient client,
|
||||||
|
ulong userId,
|
||||||
|
[NotNull] Func<SocketMessageComponent, Task> action)
|
||||||
|
: base(client, userId, action)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override NadekoInteractionData Data
|
||||||
|
=> new NadekoInteractionData(
|
||||||
|
Emote: Emoji.Parse("⏰"),
|
||||||
|
CustomId: "timely:remind_me",
|
||||||
|
Text: "Remind me"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Func<SocketMessageComponent, Task> RemindTimelyAction(DateTime when)
|
||||||
|
=> async smc =>
|
||||||
|
{
|
||||||
|
var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative);
|
||||||
|
|
||||||
|
await _remind.AddReminderAsync(ctx.User.Id,
|
||||||
|
ctx.Channel.Id,
|
||||||
|
ctx.Guild.Id,
|
||||||
|
true,
|
||||||
|
when,
|
||||||
|
GetText(strs.timely_time));
|
||||||
|
|
||||||
|
await smc.RespondConfirmAsync(_eb, GetText(strs.remind_timely(tt)), ephemeral: true);
|
||||||
|
};
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async partial Task Timely()
|
public async partial Task Timely()
|
||||||
{
|
{
|
||||||
@@ -135,7 +172,11 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
|
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
|
||||||
|
|
||||||
await ReplyConfirmLocalizedAsync(strs.timely(N(val), period));
|
var inter = new RemindMeInteraction(_client,
|
||||||
|
ctx.User.Id,
|
||||||
|
RemindTimelyAction(DateTime.UtcNow.Add(TimeSpan.FromHours(period))));
|
||||||
|
|
||||||
|
await ReplyConfirmLocalizedAsync(strs.timely(N(val), period), inter.GetInteraction());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
@@ -27,7 +27,8 @@ public class CommandMapService : IInputTransformer, INService
|
|||||||
|
|
||||||
AliasMaps = new(configs.ToDictionary(x => x.GuildId,
|
AliasMaps = new(configs.ToDictionary(x => x.GuildId,
|
||||||
x => new ConcurrentDictionary<string, string>(x.CommandAliases.DistinctBy(ca => ca.Trigger)
|
x => new ConcurrentDictionary<string, string>(x.CommandAliases.DistinctBy(ca => ca.Trigger)
|
||||||
.ToDictionary(ca => ca.Trigger, ca => ca.Mapping))));
|
.ToDictionary(ca => ca.Trigger, ca => ca.Mapping),
|
||||||
|
StringComparer.OrdinalIgnoreCase)));
|
||||||
|
|
||||||
_db = db;
|
_db = db;
|
||||||
}
|
}
|
||||||
@@ -56,17 +57,29 @@ public class CommandMapService : IInputTransformer, INService
|
|||||||
|
|
||||||
if (AliasMaps.TryGetValue(guild.Id, out var maps))
|
if (AliasMaps.TryGetValue(guild.Id, out var maps))
|
||||||
{
|
{
|
||||||
var keys = maps.Keys.OrderByDescending(x => x.Length);
|
string word;
|
||||||
|
var index = input.IndexOf(' ', StringComparison.InvariantCulture);
|
||||||
foreach (var k in keys)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
string newInput;
|
word = input;
|
||||||
if (input.StartsWith(k + " ", StringComparison.InvariantCultureIgnoreCase))
|
}
|
||||||
newInput = maps[k] + input.Substring(k.Length, input.Length - k.Length);
|
|
||||||
else if (input.Equals(k, StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
newInput = maps[k];
|
|
||||||
else
|
else
|
||||||
continue;
|
{
|
||||||
|
word = input[..index];
|
||||||
|
}
|
||||||
|
|
||||||
|
string newInput;
|
||||||
|
if (maps.TryGetValue(word, out var alias))
|
||||||
|
{
|
||||||
|
if (index == -1)
|
||||||
|
newInput = alias;
|
||||||
|
else
|
||||||
|
newInput = alias + ' ' + input[index..];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -83,7 +96,34 @@ public class CommandMapService : IInputTransformer, INService
|
|||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
return newInput;
|
return newInput;
|
||||||
}
|
|
||||||
|
// var keys = maps.Keys.OrderByDescending(x => x.Length);
|
||||||
|
// foreach (var k in keys)
|
||||||
|
// {
|
||||||
|
// string newInput;
|
||||||
|
// if (input.StartsWith(k + " ", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
// newInput = maps[k] + input.Substring(k.Length, input.Length - k.Length);
|
||||||
|
// else if (input.Equals(k, StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
// newInput = maps[k];
|
||||||
|
// else
|
||||||
|
// continue;
|
||||||
|
//
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// var toDelete = await channel.SendConfirmAsync(_eb, $"{input} => {newInput}");
|
||||||
|
// _ = Task.Run(async () =>
|
||||||
|
// {
|
||||||
|
// await Task.Delay(1500);
|
||||||
|
// await toDelete.DeleteAsync(new()
|
||||||
|
// {
|
||||||
|
// RetryMode = RetryMode.AlwaysRetry
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// catch { }
|
||||||
|
//
|
||||||
|
// return newInput;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#nullable disable
|
#nullable disable warnings
|
||||||
using NadekoBot.Common.Yml;
|
using NadekoBot.Common.Yml;
|
||||||
using NadekoBot.Db;
|
using NadekoBot.Db;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
@@ -134,30 +134,40 @@ public partial class Utility
|
|||||||
.WithFooter(
|
.WithFooter(
|
||||||
GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})"))));
|
GetText(strs.created_by($"{data.AuthorName} ({data.AuthorId})"))));
|
||||||
|
|
||||||
[Cmd]
|
private async Task QuoteSearchinternalAsync(string? keyword, string textOrAuthor)
|
||||||
[RequireContext(ContextType.Guild)]
|
|
||||||
public async partial Task QuoteSearch(string keyword, [Leftover] string text)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text))
|
if (string.IsNullOrWhiteSpace(textOrAuthor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
keyword = keyword.ToUpperInvariant();
|
keyword = keyword?.ToUpperInvariant();
|
||||||
|
|
||||||
Quote keywordquote;
|
Quote quote;
|
||||||
await using (var uow = _db.GetDbContext())
|
await using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
keywordquote = await uow.Quotes.SearchQuoteKeywordTextAsync(ctx.Guild.Id, keyword, text);
|
quote = await uow.Quotes.SearchQuoteKeywordTextAsync(ctx.Guild.Id, keyword, textOrAuthor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keywordquote is null)
|
if (quote is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await ctx.Channel.SendMessageAsync($"`#{keywordquote.Id}` 💬 "
|
await ctx.Channel.SendMessageAsync($"`#{quote.Id}` 💬 "
|
||||||
+ keyword.ToLowerInvariant()
|
+ quote.Keyword.ToLowerInvariant()
|
||||||
+ ": "
|
+ ": "
|
||||||
+ keywordquote.Text.SanitizeAllMentions());
|
+ quote.Text.SanitizeAllMentions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[Priority(0)]
|
||||||
|
public partial Task QuoteSearch(string textOrAuthor)
|
||||||
|
=> QuoteSearchinternalAsync(null, textOrAuthor);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[Priority(1)]
|
||||||
|
public partial Task QuoteSearch(string keyword, [Leftover] string textOrAuthor)
|
||||||
|
=> QuoteSearchinternalAsync(keyword, textOrAuthor);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async partial Task QuoteId(int id)
|
public async partial Task QuoteId(int id)
|
||||||
|
@@ -178,4 +178,26 @@ public class RemindService : INService, IReadyExecutor
|
|||||||
public string What { get; set; }
|
public string What { get; set; }
|
||||||
public TimeSpan Time { get; set; }
|
public TimeSpan Time { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task AddReminderAsync(ulong userId,
|
||||||
|
ulong channelId,
|
||||||
|
ulong? guildId,
|
||||||
|
bool isPrivate,
|
||||||
|
DateTime time,
|
||||||
|
string message)
|
||||||
|
{
|
||||||
|
var rem = new Reminder
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
ServerId = guildId ?? 0,
|
||||||
|
IsPrivate = isPrivate,
|
||||||
|
When = time,
|
||||||
|
Message = message,
|
||||||
|
};
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
await ctx.Reminders
|
||||||
|
.AddAsync(rem);
|
||||||
|
}
|
||||||
}
|
}
|
@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
|
|||||||
|
|
||||||
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
||||||
{
|
{
|
||||||
public const string BOT_VERSION = "4.2.12";
|
public const string BOT_VERSION = "4.2.14";
|
||||||
|
|
||||||
public string Author
|
public string Author
|
||||||
=> "Kwoth#2452";
|
=> "Kwoth#2452";
|
||||||
|
@@ -58,7 +58,8 @@ public static class SocketMessageComponentExtensions
|
|||||||
bool ephemeral = false)
|
bool ephemeral = false)
|
||||||
=> smc.RespondAsync(plainText,
|
=> smc.RespondAsync(plainText,
|
||||||
embed: embed?.Build(),
|
embed: embed?.Build(),
|
||||||
embeds: embeds?.Map(x => x.Build()));
|
embeds: embeds?.Map(x => x.Build()),
|
||||||
|
ephemeral: ephemeral);
|
||||||
|
|
||||||
public static Task RespondAsync(
|
public static Task RespondAsync(
|
||||||
this SocketMessageComponent ch,
|
this SocketMessageComponent ch,
|
||||||
|
@@ -601,9 +601,12 @@ quoteshow:
|
|||||||
args:
|
args:
|
||||||
- "123"
|
- "123"
|
||||||
quotesearch:
|
quotesearch:
|
||||||
desc: "Shows a random quote for a keyword that contains any text specified in the search."
|
desc: "Shows a random quote given a search query. Partially matches in several ways: 1) Only content of any quote, 2) only by author, 3) keyword and content, 3) or keyword and author"
|
||||||
args:
|
args:
|
||||||
- "keyword text"
|
- "\"find this long text\""
|
||||||
|
- "AuthorName"
|
||||||
|
- "keyword some text"
|
||||||
|
- "keyword AuthorName"
|
||||||
quoteid:
|
quoteid:
|
||||||
desc: "Displays the quote with the specified ID number. Quote ID numbers can be found by typing `{0}liqu [num]` where `[num]` is a number of a page which contains 15 quotes."
|
desc: "Displays the quote with the specified ID number. Quote ID numbers can be found by typing `{0}liqu [num]` where `[num]` is a number of a page which contains 15 quotes."
|
||||||
args:
|
args:
|
||||||
|
@@ -596,6 +596,7 @@
|
|||||||
"quote_deleted": "Quote #{0} deleted.",
|
"quote_deleted": "Quote #{0} deleted.",
|
||||||
"region": "Region",
|
"region": "Region",
|
||||||
"remind": "I will remind {0} to {1} in {2} `({3:d.M.yyyy.} at {4:HH:mm})`",
|
"remind": "I will remind {0} to {1} in {2} `({3:d.M.yyyy.} at {4:HH:mm})`",
|
||||||
|
"remind_timely": "I will remind you about your timely reward {0}",
|
||||||
"remind_invalid": "Not a valid remind format. Remind must have a target, timer and a reason. Check the command list.",
|
"remind_invalid": "Not a valid remind format. Remind must have a target, timer and a reason. Check the command list.",
|
||||||
"remind_too_long": "Remind time has exceeded maximum.",
|
"remind_too_long": "Remind time has exceeded maximum.",
|
||||||
"repeater_redundant_no": "Repeater **#{0}** won't post redundant messages anymore.",
|
"repeater_redundant_no": "Repeater **#{0}** won't post redundant messages anymore.",
|
||||||
@@ -883,6 +884,7 @@
|
|||||||
"timely_set": "Users will be able to claim {0} every {1}h",
|
"timely_set": "Users will be able to claim {0} every {1}h",
|
||||||
"timely_set_none": "Users will not be able to claim any timely currency.",
|
"timely_set_none": "Users will not be able to claim any timely currency.",
|
||||||
"timely_reset": "All users will be able to claim timely currency again.",
|
"timely_reset": "All users will be able to claim timely currency again.",
|
||||||
|
"timely_time": "It's time for your timely reward.",
|
||||||
"price": "Price",
|
"price": "Price",
|
||||||
"market_cap": "Market Cap",
|
"market_cap": "Market Cap",
|
||||||
"market_cap_dominance": "Dominance",
|
"market_cap_dominance": "Dominance",
|
||||||
|
Reference in New Issue
Block a user