mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 09:18:27 -04:00
Added many more braces for multiline if's, Improved .crypto command quite a bit and applied locale-specific format
This commit is contained in:
@@ -349,7 +349,7 @@ resharper_csharp_place_type_constraints_on_same_line = false
|
||||
resharper_csharp_wrap_before_extends_colon = true
|
||||
resharper_csharp_place_constructor_initializer_on_same_line = false
|
||||
resharper_force_attribute_style = separate
|
||||
resharper_csharp_braces_for_ifelse = required_for_complex
|
||||
resharper_csharp_braces_for_ifelse = required_for_multiline_statement
|
||||
resharper_csharp_braces_for_foreach = required_for_multiline
|
||||
resharper_csharp_braces_for_while = required_for_multiline
|
||||
resharper_csharp_braces_for_for = required_for_multiline
|
||||
|
@@ -123,9 +123,11 @@ public sealed class Bot
|
||||
if (Environment.GetEnvironmentVariable("NADEKOBOT_IS_COORDINATED") != "1")
|
||||
svcs.AddSingleton<ICoordinator, SingleProcessCoordinator>();
|
||||
else
|
||||
{
|
||||
svcs.AddSingleton<RemoteGrpcCoordinator>()
|
||||
.AddSingleton<ICoordinator>(x => x.GetRequiredService<RemoteGrpcCoordinator>())
|
||||
.AddSingleton<IReadyExecutor>(x => x.GetRequiredService<RemoteGrpcCoordinator>());
|
||||
}
|
||||
|
||||
svcs.AddSingleton<RedisLocalDataCache>()
|
||||
.AddSingleton<ILocalDataCache>(x => x.GetRequiredService<RedisLocalDataCache>())
|
||||
|
@@ -385,8 +385,10 @@ public sealed class ConcurrentHashSet<T> : IReadOnlyCollection<T>, ICollection<T
|
||||
count += tables.CountPerLock[i];
|
||||
|
||||
if (array.Length - count < arrayIndex || count < 0) //"count" itself or "count + arrayIndex" can overflow
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"The index is equal to or greater than the length of the array, or the number of elements in the set is greater than the available space from index to the end of the destination array.");
|
||||
}
|
||||
|
||||
CopyToItems(array, arrayIndex);
|
||||
}
|
||||
|
@@ -53,6 +53,7 @@ public class EventPubSub : IPubSub
|
||||
// get subscriptions which have the same action hash code
|
||||
// note: having this as a list allows for multiple subscriptions of
|
||||
// the same insance's/static method
|
||||
{
|
||||
if (actions.TryGetValue(action, out var sameActions))
|
||||
{
|
||||
// remove last subscription
|
||||
@@ -71,6 +72,7 @@ public class EventPubSub : IPubSub
|
||||
_actions.Remove(key.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
@@ -36,8 +36,10 @@ public sealed class RedisPubSub : IPubSub
|
||||
if (dataObj is not null)
|
||||
await action(dataObj);
|
||||
else
|
||||
{
|
||||
Log.Warning("Publishing event {EventName} with a null value. This is not allowed",
|
||||
eventName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -63,8 +63,10 @@ public class ReplacementBuilder
|
||||
{
|
||||
var to = TimeZoneInfo.Local;
|
||||
if (g is not null)
|
||||
{
|
||||
if (GuildTimezoneService.AllServices.TryGetValue(client.CurrentUser.Id, out var tz))
|
||||
to = tz.GetTimeZoneOrDefault(g.Id) ?? TimeZoneInfo.Local;
|
||||
}
|
||||
|
||||
return TimeZoneInfo.ConvertTime(DateTime.UtcNow, TimeZoneInfo.Utc, to).ToString("HH:mm ")
|
||||
+ to.StandardName.GetInitials();
|
||||
|
@@ -54,11 +54,13 @@ public class Replacer
|
||||
Url = Replace(embedData.Url)
|
||||
};
|
||||
if (embedData.Author is not null)
|
||||
{
|
||||
newEmbedData.Author = new()
|
||||
{
|
||||
Name = Replace(embedData.Author.Name),
|
||||
IconUrl = Replace(embedData.Author.IconUrl)
|
||||
};
|
||||
}
|
||||
|
||||
if (embedData.Fields is not null)
|
||||
{
|
||||
@@ -78,11 +80,13 @@ public class Replacer
|
||||
}
|
||||
|
||||
if (embedData.Footer is not null)
|
||||
{
|
||||
newEmbedData.Footer = new()
|
||||
{
|
||||
Text = Replace(embedData.Footer.Text),
|
||||
IconUrl = Replace(embedData.Footer.IconUrl)
|
||||
};
|
||||
}
|
||||
|
||||
newEmbedData.Color = embedData.Color;
|
||||
|
||||
|
@@ -54,6 +54,7 @@ public sealed record SmartEmbedText : SmartText
|
||||
};
|
||||
|
||||
if (eb.Fields.Length > 0)
|
||||
{
|
||||
set.Fields = eb.Fields.Select(field
|
||||
=> new SmartTextEmbedField
|
||||
{
|
||||
@@ -62,6 +63,7 @@ public sealed record SmartEmbedText : SmartText
|
||||
Value = field.Value
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
set.Color = eb.Color?.RawValue ?? 0;
|
||||
return set;
|
||||
@@ -81,12 +83,14 @@ public sealed record SmartEmbedText : SmartText
|
||||
embed.WithUrl(Url);
|
||||
|
||||
if (Footer is not null)
|
||||
{
|
||||
embed.WithFooter(efb =>
|
||||
{
|
||||
efb.WithText(Footer.Text);
|
||||
if (Uri.IsWellFormedUriString(Footer.IconUrl, UriKind.Absolute))
|
||||
efb.WithIconUrl(Footer.IconUrl);
|
||||
});
|
||||
}
|
||||
|
||||
if (Thumbnail is not null && Uri.IsWellFormedUriString(Thumbnail, UriKind.Absolute))
|
||||
embed.WithThumbnailUrl(Thumbnail);
|
||||
@@ -105,11 +109,13 @@ public sealed record SmartEmbedText : SmartText
|
||||
}
|
||||
|
||||
if (Fields is not null)
|
||||
{
|
||||
foreach (var f in Fields)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(f.Name) && !string.IsNullOrWhiteSpace(f.Value))
|
||||
embed.AddField(f.Name, f.Value, f.Inline);
|
||||
}
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
@@ -117,10 +123,12 @@ public sealed record SmartEmbedText : SmartText
|
||||
public void NormalizeFields()
|
||||
{
|
||||
if (Fields is { Length: > 0 })
|
||||
{
|
||||
foreach (var f in Fields)
|
||||
{
|
||||
f.Name = f.Name.TrimTo(256);
|
||||
f.Value = f.Value.TrimTo(1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -53,8 +53,10 @@ public sealed class CommandOrCrTypeReader : NadekoTypeReader<CommandOrCrInfo>
|
||||
|
||||
var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(ctx, input);
|
||||
if (cmd.IsSuccess)
|
||||
{
|
||||
return TypeReaderResult.FromSuccess(new CommandOrCrInfo(((CommandInfo)cmd.Values.First().Value).Name,
|
||||
CommandOrCrInfo.Type.Normal));
|
||||
}
|
||||
|
||||
return TypeReaderResult.FromError<CommandOrCrInfo>(CommandError.ParseFailed, "No such command or cr found.");
|
||||
}
|
||||
|
@@ -14,8 +14,10 @@ public sealed class GuildDateTimeTypeReader : NadekoTypeReader<GuildDateTime>
|
||||
{
|
||||
var gdt = Parse(context.Guild.Id, input);
|
||||
if (gdt is null)
|
||||
{
|
||||
return new(TypeReaderResult.FromError<GuildDateTime>(CommandError.ParseFailed,
|
||||
"Input string is in an incorrect format."));
|
||||
}
|
||||
|
||||
return new(TypeReaderResult.FromSuccess(gdt));
|
||||
}
|
||||
|
@@ -19,13 +19,11 @@ public class CommentsObjectGraphVisitor : ChainedObjectGraphVisitor
|
||||
&& !string.IsNullOrWhiteSpace(commentsDescriptor.Comment))
|
||||
{
|
||||
var parts = commentsDescriptor.Comment.Split('\n');
|
||||
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
context.Emit(new Comment(part.Trim(), false));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return base.EnterMapping(key, value, context);
|
||||
}
|
||||
}
|
@@ -21,10 +21,12 @@ public class MultilineScalarFlowStyleEmitter : ChainedEventEmitter
|
||||
{
|
||||
var isMultiLine = value.IndexOfAny(new[] { '\r', '\n', '\x85', '\x2028', '\x2029' }) >= 0;
|
||||
if (isMultiLine)
|
||||
{
|
||||
eventInfo = new(eventInfo.Source)
|
||||
{
|
||||
Style = ScalarStyle.Literal
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -127,6 +127,7 @@ WHERE UserId={userId};");
|
||||
|
||||
// just update the amount, there is no new user data
|
||||
if (!updatedUserData)
|
||||
{
|
||||
ctx.Database.ExecuteSqlInterpolated($@"
|
||||
UPDATE OR IGNORE DiscordUser
|
||||
SET CurrencyAmount=CurrencyAmount+{amount}
|
||||
@@ -135,7 +136,9 @@ WHERE UserId={userId};
|
||||
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
||||
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
||||
");
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx.Database.ExecuteSqlInterpolated($@"
|
||||
UPDATE OR IGNORE DiscordUser
|
||||
SET CurrencyAmount=CurrencyAmount+{amount},
|
||||
@@ -147,6 +150,7 @@ WHERE UserId={userId};
|
||||
INSERT OR IGNORE INTO DiscordUser (UserId, Username, Discriminator, AvatarId, CurrencyAmount, TotalXp)
|
||||
VALUES ({userId}, {name}, {discrim}, {avatarId}, {amount}, 0);
|
||||
");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -74,9 +74,7 @@ public static class GuildConfigExtensions
|
||||
GuildConfig config;
|
||||
|
||||
if (includes is null)
|
||||
{
|
||||
config = ctx.GuildConfigs.IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
|
||||
}
|
||||
else
|
||||
{
|
||||
var set = includes(ctx.GuildConfigs);
|
||||
|
@@ -13,6 +13,7 @@ public static class UserXpExtensions
|
||||
var usr = ctx.UserXpStats.FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
||||
|
||||
if (usr is null)
|
||||
{
|
||||
ctx.Add(usr = new()
|
||||
{
|
||||
Xp = 0,
|
||||
@@ -20,6 +21,7 @@ public static class UserXpExtensions
|
||||
NotifyOnLevelUp = XpNotificationLocation.None,
|
||||
GuildId = guildId
|
||||
});
|
||||
}
|
||||
|
||||
return usr;
|
||||
}
|
||||
@@ -42,12 +44,6 @@ public static class UserXpExtensions
|
||||
.ToList();
|
||||
|
||||
public static int GetUserGuildRanking(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
||||
// @"SELECT COUNT(*) + 1
|
||||
//FROM UserXpStats
|
||||
//WHERE GuildId = @p1 AND ((Xp + AwardedXp) > (SELECT Xp + AwardedXp
|
||||
// FROM UserXpStats
|
||||
// WHERE UserId = @p2 AND GuildId = @p1
|
||||
// LIMIT 1));";
|
||||
=> xps.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId
|
||||
|
@@ -28,11 +28,13 @@ public static class WaifuExtensions
|
||||
Func<DbSet<WaifuInfo>, IQueryable<WaifuInfo>> includes = null)
|
||||
{
|
||||
if (includes is null)
|
||||
{
|
||||
return waifus.Include(wi => wi.Waifu)
|
||||
.Include(wi => wi.Affinity)
|
||||
.Include(wi => wi.Claimer)
|
||||
.Include(wi => wi.Items)
|
||||
.FirstOrDefault(wi => wi.Waifu.UserId == userId);
|
||||
}
|
||||
|
||||
return includes(waifus).AsQueryable().FirstOrDefault(wi => wi.Waifu.UserId == userId);
|
||||
}
|
||||
|
@@ -32,8 +32,11 @@ public class WaifuInfo : DbEntity
|
||||
else if (AffinityId == ClaimerId)
|
||||
status = $"... and {waifuUsername} likes {claimerUsername} too <3";
|
||||
else
|
||||
{
|
||||
status =
|
||||
$"... but {waifuUsername}'s heart belongs to {Affinity.Username.TrimTo(20)}#{Affinity.Discriminator}";
|
||||
}
|
||||
|
||||
return $"**{waifuUsername}#{Waifu.Discriminator}** - claimed by **{claimer}**\n\t{status}";
|
||||
}
|
||||
}
|
||||
|
@@ -314,9 +314,7 @@ public partial class Administration : NadekoModule<AdministrationService>
|
||||
}
|
||||
|
||||
if (time is null)
|
||||
{
|
||||
await msg.DeleteAsync();
|
||||
}
|
||||
else if (time.Time <= TimeSpan.FromDays(7))
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
|
@@ -115,13 +115,9 @@ public class AdministrationService : INService
|
||||
{
|
||||
}
|
||||
else if (newState == Administration.State.Enable)
|
||||
{
|
||||
DeleteMessagesOnCommandChannels[chId] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteMessagesOnCommandChannels.TryRemove(chId, out _);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DeafenUsers(bool value, params IGuildUser[] users)
|
||||
|
@@ -25,9 +25,7 @@ public partial class Administration
|
||||
var id = _service.ToggleGameVoiceChannel(ctx.Guild.Id, vch.Id);
|
||||
|
||||
if (id is null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.gvc_disabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
_service.GameVoiceChannels.Add(vch.Id);
|
||||
|
@@ -42,8 +42,10 @@ public class GameVoiceChannelService : INService
|
||||
{
|
||||
if (activity is { Type: ActivityType.Playing })
|
||||
//trigger gvc
|
||||
{
|
||||
if (await TriggerGvc(newUser, activity.Name))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@@ -141,9 +141,7 @@ public class GreetService : INService, IReadyExecutor
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await ByeUsers(conf, channel, new[] { user });
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -255,10 +253,12 @@ public class GreetService : INService, IReadyExecutor
|
||||
text = rep.Replace(text);
|
||||
|
||||
if (text is SmartPlainText pt)
|
||||
{
|
||||
text = new SmartEmbedText()
|
||||
{
|
||||
PlainText = pt.Text
|
||||
};
|
||||
}
|
||||
|
||||
((SmartEmbedText)text).Footer = new()
|
||||
{
|
||||
@@ -308,9 +308,7 @@ public class GreetService : INService, IReadyExecutor
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await GreetUsers(conf, channel, new[] { user });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -75,9 +75,7 @@ public sealed class ImageOnlyChannelService : IEarlyBehavior
|
||||
var newState = false;
|
||||
using var uow = _db.GetDbContext();
|
||||
if (forceDisable || (_enabledOn.TryGetValue(guildId, out var channels) && channels.TryRemove(channelId)))
|
||||
{
|
||||
uow.ImageOnlyChannels.Delete(x => x.ChannelId == channelId);
|
||||
}
|
||||
else
|
||||
{
|
||||
uow.ImageOnlyChannels.Add(new()
|
||||
|
@@ -66,9 +66,7 @@ public class MuteService : INService
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unmute = x.UnmuteAt - DateTime.UtcNow;
|
||||
@@ -82,9 +80,7 @@ public class MuteService : INService
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unban = x.UnbanAt - DateTime.UtcNow;
|
||||
@@ -98,9 +94,7 @@ public class MuteService : INService
|
||||
{
|
||||
TimeSpan after;
|
||||
if (x.UnbanAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow)
|
||||
{
|
||||
after = TimeSpan.FromMinutes(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unban = x.UnbanAt - DateTime.UtcNow;
|
||||
@@ -306,6 +300,7 @@ public class MuteService : INService
|
||||
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
|
||||
if (muteRole is null)
|
||||
//if it doesn't exist, create it
|
||||
{
|
||||
try { muteRole = await guild.CreateRoleAsync(muteRoleName, isMentionable: false); }
|
||||
catch
|
||||
{
|
||||
@@ -313,6 +308,7 @@ public class MuteService : INService
|
||||
muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName)
|
||||
?? await guild.CreateRoleAsync(defaultMuteRoleName, isMentionable: false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var toOverwrite in await guild.GetTextChannelsAsync())
|
||||
{
|
||||
@@ -414,6 +410,7 @@ public class MuteService : INService
|
||||
var toAdd = new Timer(async _ =>
|
||||
{
|
||||
if (type == TimerType.Ban)
|
||||
{
|
||||
try
|
||||
{
|
||||
RemoveTimerFromDb(guildId, userId, type);
|
||||
@@ -426,7 +423,9 @@ public class MuteService : INService
|
||||
{
|
||||
Log.Warning(ex, "Couldn't unban user {UserId} in guild {GuildId}", userId, guildId);
|
||||
}
|
||||
}
|
||||
else if (type == TimerType.AddRole)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (roleId is null)
|
||||
@@ -444,7 +443,9 @@ public class MuteService : INService
|
||||
{
|
||||
Log.Warning(ex, "Couldn't remove role from user {UserId} in guild {GuildId}", userId, guildId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// unmute the user, this will also remove the timer from the db
|
||||
@@ -455,6 +456,7 @@ public class MuteService : INService
|
||||
RemoveTimerFromDb(guildId, userId, type); // if unmute errored, just remove unmute from db
|
||||
Log.Warning(ex, "Couldn't unmute user {UserId} in guild {GuildId}", userId, guildId);
|
||||
}
|
||||
}
|
||||
},
|
||||
null,
|
||||
after,
|
||||
|
@@ -67,8 +67,10 @@ public partial class Administration
|
||||
if (thisPageOverrides.Count == 0)
|
||||
eb.WithDescription(GetText(strs.perm_override_page_none));
|
||||
else
|
||||
{
|
||||
eb.WithDescription(thisPageOverrides.Select(ov => $"{ov.Command} => {ov.Perm.ToString()}")
|
||||
.Join("\n"));
|
||||
}
|
||||
|
||||
return eb;
|
||||
},
|
||||
|
@@ -56,6 +56,7 @@ public class DiscordPermOverrideService : INService, ILateBlocker
|
||||
.FirstOrDefaultAsync(x => x.GuildId == guildId && commandName == x.Command);
|
||||
|
||||
if (over is null)
|
||||
{
|
||||
uow.Set<DiscordPermOverride>()
|
||||
.Add(over = new()
|
||||
{
|
||||
@@ -63,6 +64,7 @@ public class DiscordPermOverrideService : INService, ILateBlocker
|
||||
Perm = perm,
|
||||
GuildId = guildId
|
||||
});
|
||||
}
|
||||
else
|
||||
over.Perm = perm;
|
||||
|
||||
|
@@ -34,9 +34,7 @@ public partial class Administration
|
||||
var statuses = _service.GetRotatingStatuses();
|
||||
|
||||
if (!statuses.Any())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.ropl_not_set);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = 1;
|
||||
|
@@ -114,8 +114,10 @@ public partial class Administration
|
||||
}
|
||||
|
||||
if (punishTime is not null)
|
||||
{
|
||||
if (!_service.IsDurationAllowed(action))
|
||||
await ReplyErrorLocalizedAsync(strs.prot_cant_use_time);
|
||||
}
|
||||
|
||||
var time = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
||||
if (time is < 0 or > 60 * 24)
|
||||
@@ -176,8 +178,10 @@ public partial class Administration
|
||||
return;
|
||||
|
||||
if (timeData is not null)
|
||||
{
|
||||
if (!_service.IsDurationAllowed(action))
|
||||
await ReplyErrorLocalizedAsync(strs.prot_cant_use_time);
|
||||
}
|
||||
|
||||
var time = (int?)timeData?.Time.TotalMinutes ?? 0;
|
||||
if (time is < 0 or > 60 * 24)
|
||||
|
@@ -136,10 +136,12 @@ public class ProtectionService : INService
|
||||
}
|
||||
|
||||
if (spam is not null)
|
||||
{
|
||||
_antiSpamGuilds[gc.GuildId] = new()
|
||||
{
|
||||
AntiSpamSettings = spam
|
||||
};
|
||||
}
|
||||
|
||||
var alt = gc.AntiAltSetting;
|
||||
if (alt is not null)
|
||||
@@ -160,6 +162,7 @@ public class ProtectionService : INService
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (maybeAlts is { } alts)
|
||||
{
|
||||
if (user.CreatedAt != default)
|
||||
{
|
||||
var diff = DateTime.UtcNow - user.CreatedAt.UtcDateTime;
|
||||
@@ -176,6 +179,7 @@ public class ProtectionService : INService
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -234,6 +238,7 @@ public class ProtectionService : INService
|
||||
});
|
||||
|
||||
if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold)
|
||||
{
|
||||
if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats))
|
||||
{
|
||||
var settings = spamSettings.AntiSpamSettings;
|
||||
@@ -243,6 +248,7 @@ public class ProtectionService : INService
|
||||
settings.RoleId,
|
||||
(IGuildUser)msg.Author);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -392,9 +398,7 @@ public class ProtectionService : INService
|
||||
gc.AntiSpamSetting.RoleId = stats.AntiSpamSettings.RoleId;
|
||||
}
|
||||
else
|
||||
{
|
||||
gc.AntiSpamSetting = stats.AntiSpamSettings;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
return stats;
|
||||
|
@@ -71,13 +71,17 @@ public partial class Administration
|
||||
count = 1000;
|
||||
|
||||
if (parameter is "-s" or "--safe")
|
||||
{
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel,
|
||||
count,
|
||||
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks && !m.IsPinned);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _service.PruneWhere((ITextChannel)ctx.Channel,
|
||||
count,
|
||||
m => m.Author.Id == userId && DateTime.UtcNow - m.CreatedAt < _twoWeeks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -52,10 +52,12 @@ public class PruneService : INService
|
||||
//100 messages, Maybe this needs to be reduced by msgs.Length instead of 100
|
||||
amount -= 50;
|
||||
if (amount > 0)
|
||||
{
|
||||
msgs = (await channel.GetMessagesAsync(lastMessage, Direction.Before, 50).FlattenAsync())
|
||||
.Where(predicate)
|
||||
.Take(amount)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
@@ -143,9 +143,7 @@ public partial class Administration
|
||||
{
|
||||
var embed = _eb.Create().WithOkColor();
|
||||
if (!_service.Get(ctx.Guild.Id, out var rrs) || !rrs.Any())
|
||||
{
|
||||
embed.WithDescription(GetText(strs.no_reaction_roles));
|
||||
}
|
||||
else
|
||||
{
|
||||
var g = (SocketGuild)ctx.Guild;
|
||||
|
@@ -101,9 +101,7 @@ public partial class Administration
|
||||
var scmds = _service.GetStartupCommands().Skip(page * 5).Take(5).ToList();
|
||||
|
||||
if (scmds.Count == 0)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.startcmdlist_none);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = 0;
|
||||
@@ -128,9 +126,7 @@ public partial class Administration
|
||||
|
||||
var scmds = _service.GetAutoCommands().Skip(page * 5).Take(5).ToList();
|
||||
if (!scmds.Any())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.autocmdlist_none);
|
||||
}
|
||||
else
|
||||
{
|
||||
var i = 0;
|
||||
|
@@ -190,12 +190,16 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
|
||||
.ToImmutableDictionary();
|
||||
|
||||
if (!ownerChannels.Any())
|
||||
{
|
||||
Log.Warning(
|
||||
"No owner channels created! Make sure you've specified the correct OwnerId in the creds.yml file and invited the bot to a Discord server");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Information("Created {OwnerChannelCount} out of {TotalOwnerChannelCount} owner message channels",
|
||||
ownerChannels.Count,
|
||||
_creds.OwnerIds.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public Task LeaveGuild(string guildStr)
|
||||
@@ -214,8 +218,10 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
|
||||
var toSend = msg.Content;
|
||||
|
||||
if (msg.Attachments.Count > 0)
|
||||
{
|
||||
toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n"
|
||||
+ string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl));
|
||||
}
|
||||
|
||||
if (bs.ForwardToAllOwners)
|
||||
{
|
||||
@@ -237,6 +243,7 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
|
||||
{
|
||||
var firstOwnerChannel = ownerChannels.Values.First();
|
||||
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await firstOwnerChannel.SendConfirmAsync(_eb, title, toSend);
|
||||
@@ -245,6 +252,7 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,8 +281,11 @@ public sealed class SelfService : ILateExecutor, IReadyExecutor, INService
|
||||
{
|
||||
uow.Remove(cmd);
|
||||
if (autoCommands.TryGetValue(cmd.GuildId, out var autos))
|
||||
{
|
||||
if (autos.TryRemove(cmd.Id, out var timer))
|
||||
timer.Change(Timeout.Infinite, Timeout.Infinite);
|
||||
}
|
||||
|
||||
uow.SaveChanges();
|
||||
return true;
|
||||
}
|
||||
|
@@ -45,8 +45,10 @@ public partial class Administration
|
||||
var succ = _service.AddNew(ctx.Guild.Id, role, group);
|
||||
|
||||
if (succ)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.role_added(Format.Bold(role.Name),
|
||||
Format.Bold(group.ToString())));
|
||||
Format.Bold(@group.ToString())));
|
||||
}
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.role_in_list(Format.Bold(role.Name)));
|
||||
}
|
||||
@@ -61,8 +63,10 @@ public partial class Administration
|
||||
var set = await _service.SetNameAsync(ctx.Guild.Id, group, name);
|
||||
|
||||
if (set)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(
|
||||
strs.group_name_added(Format.Bold(group.ToString()), Format.Bold(name)));
|
||||
strs.group_name_added(Format.Bold(@group.ToString()), Format.Bold(name)));
|
||||
}
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.group_name_removed(Format.Bold(group.ToString())));
|
||||
}
|
||||
|
@@ -85,6 +85,7 @@ public class SelfAssignedRolesService : INService
|
||||
{
|
||||
var sameRole = guildUser.Guild.GetRole(roleId);
|
||||
if (sameRole is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await guildUser.RemoveRoleAsync(sameRole);
|
||||
@@ -94,6 +95,7 @@ public class SelfAssignedRolesService : INService
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,9 +194,7 @@ public class SelfAssignedRolesService : INService
|
||||
uow.SaveChanges();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
private readonly GuildTimezoneService _tz;
|
||||
private readonly IEmbedBuilderService _eb;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
|
||||
|
||||
private readonly ConcurrentHashSet<ulong> _ignoreMessageIds = new();
|
||||
|
||||
public LogCommandService(
|
||||
@@ -89,9 +89,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
{
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromHours(1));
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
{
|
||||
_ignoreMessageIds.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PresenceUpdateTask()
|
||||
@@ -108,7 +106,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
{
|
||||
if (!((SocketGuild)key.Guild).CurrentUser.GetPermissions(key).SendMessages)
|
||||
return Task.CompletedTask;
|
||||
|
||||
|
||||
if (PresenceUpdates.TryRemove(key, out var msgs))
|
||||
{
|
||||
var title = GetText(key.Guild, strs.presence_updates);
|
||||
@@ -234,9 +232,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
embed.WithImageUrl(aav.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await logChannel.EmbedAsync(embed);
|
||||
}
|
||||
@@ -656,14 +652,18 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
var afterTextChannel = cafter as ITextChannel;
|
||||
|
||||
if (before.Name != after.Name)
|
||||
{
|
||||
embed.WithTitle("ℹ️ " + GetText(logChannel.Guild, strs.ch_name_change))
|
||||
.WithDescription($"{after} | {after.Id}")
|
||||
.AddField(GetText(logChannel.Guild, strs.ch_old_name), before.Name);
|
||||
}
|
||||
else if (beforeTextChannel?.Topic != afterTextChannel?.Topic)
|
||||
{
|
||||
embed.WithTitle("ℹ️ " + GetText(logChannel.Guild, strs.ch_topic_change))
|
||||
.WithDescription($"{after} | {after.Id}")
|
||||
.AddField(GetText(logChannel.Guild, strs.old_topic), beforeTextChannel?.Topic ?? "-")
|
||||
.AddField(GetText(logChannel.Guild, strs.new_topic), afterTextChannel?.Topic ?? "-");
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
@@ -778,26 +778,33 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
|
||||
var str = string.Empty;
|
||||
if (beforeVch?.Guild == afterVch?.Guild)
|
||||
{
|
||||
str = "🎙"
|
||||
+ Format.Code(PrettyCurrentTime(usr.Guild))
|
||||
+ GetText(logChannel.Guild,
|
||||
strs.user_vmoved("👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
|
||||
Format.Bold(beforeVch?.Name ?? ""),
|
||||
Format.Bold(afterVch?.Name ?? "")));
|
||||
}
|
||||
else if (beforeVch is null)
|
||||
{
|
||||
str = "🎙"
|
||||
+ Format.Code(PrettyCurrentTime(usr.Guild))
|
||||
+ GetText(logChannel.Guild,
|
||||
strs.user_vjoined("👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
|
||||
Format.Bold(afterVch?.Name ?? "")));
|
||||
}
|
||||
else if (afterVch is null)
|
||||
{
|
||||
str = "🎙"
|
||||
+ Format.Code(PrettyCurrentTime(usr.Guild))
|
||||
+ GetText(logChannel.Guild,
|
||||
strs.user_vleft("👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
|
||||
Format.Bold(beforeVch.Name ?? "")));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
PresenceUpdates.AddOrUpdate(logChannel,
|
||||
new List<string>
|
||||
{
|
||||
@@ -808,6 +815,7 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
list.Add(str);
|
||||
return list;
|
||||
});
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -1001,8 +1009,10 @@ public sealed class LogCommandService : ILogCommandService, IReadyExecutor
|
||||
.AddField("Id", msg.Id.ToString())
|
||||
.WithFooter(CurrentTime(channel.Guild));
|
||||
if (msg.Attachments.Any())
|
||||
{
|
||||
embed.AddField(GetText(logChannel.Guild, strs.attachments),
|
||||
string.Join(", ", msg.Attachments.Select(a => a.Url)));
|
||||
}
|
||||
|
||||
await logChannel.EmbedAsync(embed);
|
||||
}
|
||||
|
@@ -58,11 +58,15 @@ public partial class Administration
|
||||
var removed = _service.LogIgnore(ctx.Guild.Id, target.Id, IgnoredItemType.Channel);
|
||||
|
||||
if (!removed)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(
|
||||
strs.log_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")")));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(
|
||||
strs.log_not_ignore_chan(Format.Bold(target.Mention + "(" + target.Id + ")")));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -74,11 +78,15 @@ public partial class Administration
|
||||
var removed = _service.LogIgnore(ctx.Guild.Id, target.Id, IgnoredItemType.User);
|
||||
|
||||
if (!removed)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(
|
||||
strs.log_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")")));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(
|
||||
strs.log_not_ignore_user(Format.Bold(target.Mention + "(" + target.Id + ")")));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
|
@@ -100,8 +100,10 @@ public partial class Administration
|
||||
if (punishment is null)
|
||||
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
|
||||
else
|
||||
{
|
||||
embed.WithDescription(GetText(strs.user_warned_and_punished(Format.Bold(user.ToString()),
|
||||
Format.Bold(punishment.Punishment.ToString()))));
|
||||
}
|
||||
|
||||
if (dmFailed)
|
||||
embed.WithFooter("⚠️ " + GetText(strs.unable_to_dm_user));
|
||||
@@ -204,9 +206,7 @@ public partial class Administration
|
||||
var embed = _eb.Create().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
|
||||
|
||||
if (!warnings.Any())
|
||||
{
|
||||
embed.WithDescription(GetText(strs.warnings_none));
|
||||
}
|
||||
else
|
||||
{
|
||||
var descText = GetText(strs.warn_count(
|
||||
@@ -288,9 +288,7 @@ public partial class Administration
|
||||
var success = await _service.WarnClearAsync(ctx.Guild.Id, userId, index, ctx.User.ToString());
|
||||
var userStr = Format.Bold((ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString());
|
||||
if (index == 0)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.warnings_cleared(userStr));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (success)
|
||||
@@ -325,12 +323,16 @@ public partial class Administration
|
||||
return;
|
||||
|
||||
if (time is null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString()),
|
||||
Format.Bold(time.Input)));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -348,12 +350,16 @@ public partial class Administration
|
||||
return;
|
||||
|
||||
if (time is null)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.warn_punish_set(Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.warn_punish_set_timed(Format.Bold(punish.ToString()),
|
||||
Format.Bold(number.ToString()),
|
||||
Format.Bold(time.Input)));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -375,11 +381,14 @@ public partial class Administration
|
||||
|
||||
string list;
|
||||
if (ps.Any())
|
||||
{
|
||||
list = string.Join("\n",
|
||||
ps.Select(x
|
||||
=> $"{x.Count} -> {x.Punishment} {(x.Punishment == PunishmentAction.AddRole ? $"<@&{x.RoleId}>" : "")} {(x.Time <= 0 ? "" : x.Time + "m")} "));
|
||||
}
|
||||
else
|
||||
list = GetText(strs.warnpl_none);
|
||||
|
||||
await SendConfirmAsync(GetText(strs.warn_punish_list), list);
|
||||
}
|
||||
|
||||
@@ -401,6 +410,7 @@ public partial class Administration
|
||||
var dmFailed = false;
|
||||
|
||||
if (guildUser is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var defaultMessage = GetText(strs.bandm(Format.Bold(ctx.Guild.Name), msg));
|
||||
@@ -412,6 +422,7 @@ public partial class Administration
|
||||
{
|
||||
dmFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
await _mute.TimedBan(ctx.Guild, user, time.Time, (ctx.User + " | " + msg).TrimTo(512));
|
||||
var toSend = _eb.Create()
|
||||
@@ -447,9 +458,7 @@ public partial class Administration
|
||||
.AddField("ID", userId.ToString(), true));
|
||||
}
|
||||
else
|
||||
{
|
||||
await Ban(user, msg);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -545,9 +554,7 @@ public partial class Administration
|
||||
var embed = _service.GetBanUserDmEmbed(Context, (IGuildUser)ctx.User, defaultMessage, reason, duration);
|
||||
|
||||
if (embed is null)
|
||||
{
|
||||
await ConfirmLocalizedAsync(strs.banmsg_disabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
@@ -755,9 +762,7 @@ public partial class Administration
|
||||
banning.Add(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
missing.Add(userStr);
|
||||
}
|
||||
}
|
||||
|
||||
var missStr = string.Join("\n", missing);
|
||||
|
@@ -233,9 +233,11 @@ WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND
|
||||
AND DateAdded < datetime('now', (SELECT '-' || WarnExpireHours || ' hours' FROM GuildConfigs as gc WHERE gc.GuildId = Warnings.GuildId));");
|
||||
|
||||
if (cleared > 0 || deleted > 0)
|
||||
{
|
||||
Log.Information("Cleared {ClearedWarnings} warnings and deleted {DeletedWarnings} warnings due to expiry",
|
||||
cleared,
|
||||
deleted);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CheckWarnExpiresAsync(ulong guildId)
|
||||
@@ -248,16 +250,20 @@ WHERE GuildId in (SELECT GuildId FROM GuildConfigs WHERE WarnExpireHours > 0 AND
|
||||
|
||||
var hours = $"{-config.WarnExpireHours} hours";
|
||||
if (config.WarnExpireAction == WarnExpireAction.Clear)
|
||||
{
|
||||
await uow.Database.ExecuteSqlInterpolatedAsync($@"UPDATE warnings
|
||||
SET Forgiven = 1,
|
||||
ForgivenBy = 'Expiry'
|
||||
WHERE GuildId={guildId}
|
||||
AND Forgiven = 0
|
||||
AND DateAdded < datetime('now', {hours})");
|
||||
}
|
||||
else if (config.WarnExpireAction == WarnExpireAction.Delete)
|
||||
{
|
||||
await uow.Database.ExecuteSqlInterpolatedAsync($@"DELETE FROM warnings
|
||||
WHERE GuildId={guildId}
|
||||
AND DateAdded < datetime('now', {hours})");
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
@@ -436,9 +442,7 @@ WHERE GuildId={guildId}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
template.Text = text;
|
||||
}
|
||||
|
||||
uow.SaveChanges();
|
||||
}
|
||||
@@ -487,22 +491,26 @@ WHERE GuildId={guildId}
|
||||
|
||||
// if template isn't set, use the old message style
|
||||
if (string.IsNullOrWhiteSpace(template))
|
||||
{
|
||||
template = JsonConvert.SerializeObject(new
|
||||
{
|
||||
color = _bcs.Data.Color.Error.PackedValue >> 8,
|
||||
description = defaultMessage
|
||||
});
|
||||
}
|
||||
// if template is set to "-" do not dm the user
|
||||
else if (template == "-")
|
||||
return default;
|
||||
// if template is an embed, send that embed with replacements
|
||||
// otherwise, treat template as a regular string with replacements
|
||||
else if (!SmartText.CreateFrom(template).IsEmbed)
|
||||
{
|
||||
template = JsonConvert.SerializeObject(new
|
||||
{
|
||||
color = _bcs.Data.Color.Error.PackedValue >> 8,
|
||||
description = template
|
||||
});
|
||||
}
|
||||
|
||||
var output = SmartText.CreateFrom(template);
|
||||
return replacer.Replace(output);
|
||||
|
@@ -59,14 +59,14 @@ public partial class Administration
|
||||
if (!roles.Any())
|
||||
text = GetText(strs.no_vcroles);
|
||||
else
|
||||
{
|
||||
text = string.Join("\n",
|
||||
roles.Select(x
|
||||
=> $"{Format.Bold(guild.GetVoiceChannel(x.Key)?.Name ?? x.Key.ToString())} => {x.Value}"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
text = GetText(strs.no_vcroles);
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
|
@@ -64,9 +64,7 @@ public static class NadekoExpressionExtensions
|
||||
return WordPosition.End;
|
||||
}
|
||||
else if (str.IsValidWordDivider(wordIndex - 1) && str.IsValidWordDivider(wordIndex + word.Length))
|
||||
{
|
||||
return WordPosition.Middle;
|
||||
}
|
||||
|
||||
return WordPosition.None;
|
||||
}
|
||||
|
@@ -249,11 +249,15 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
||||
}
|
||||
|
||||
if (newVal)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.option_enabled(Format.Code(option.ToString()),
|
||||
Format.Code(id.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.option_disabled(Format.Code(option.ToString()),
|
||||
Format.Code(id.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
|
@@ -290,6 +290,7 @@ public sealed class NadekoExpressionsService : IEarlyBehavior, IReadyExecutor
|
||||
}
|
||||
|
||||
if (expr.AutoDeleteTrigger)
|
||||
{
|
||||
try
|
||||
{
|
||||
await msg.DeleteAsync();
|
||||
@@ -297,6 +298,7 @@ public sealed class NadekoExpressionsService : IEarlyBehavior, IReadyExecutor
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Log.Information("s: {GuildId} c: {ChannelId} u: {UserId} | {UserName} executed expression {Expr}",
|
||||
guild.Id,
|
||||
@@ -341,6 +343,7 @@ public sealed class NadekoExpressionsService : IEarlyBehavior, IReadyExecutor
|
||||
private void UpdateInternal(ulong? maybeGuildId, NadekoExpression expr)
|
||||
{
|
||||
if (maybeGuildId is { } guildId)
|
||||
{
|
||||
newGuildReactions.AddOrUpdate(guildId,
|
||||
new[] { expr },
|
||||
(_, old) =>
|
||||
@@ -354,7 +357,9 @@ public sealed class NadekoExpressionsService : IEarlyBehavior, IReadyExecutor
|
||||
|
||||
return newArray;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (_gexprWriteLock)
|
||||
{
|
||||
var exprs = globalReactions;
|
||||
@@ -364,6 +369,7 @@ public sealed class NadekoExpressionsService : IEarlyBehavior, IReadyExecutor
|
||||
exprs[i] = expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task AddInternalAsync(ulong? maybeGuildId, NadekoExpression expr)
|
||||
|
@@ -131,9 +131,11 @@ public sealed class AnimalRace : IDisposable
|
||||
}
|
||||
|
||||
if (FinishedUsers[0].Bet > 0)
|
||||
{
|
||||
await _currency.AddAsync(FinishedUsers[0].UserId,
|
||||
FinishedUsers[0].Bet * (_users.Count - 1),
|
||||
new("animalrace", "win"));
|
||||
}
|
||||
|
||||
_ = OnEnded?.Invoke(this);
|
||||
});
|
||||
|
@@ -53,8 +53,10 @@ public partial class Gambling
|
||||
try
|
||||
{
|
||||
if (arg.Channel.Id == ctx.Channel.Id)
|
||||
{
|
||||
if (ar.CurrentPhase == AnimalRace.Phase.Running && ++count % 9 == 0)
|
||||
raceMessage = null;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
@@ -67,10 +69,13 @@ public partial class Gambling
|
||||
_service.AnimalRaces.TryRemove(ctx.Guild.Id, out _);
|
||||
var winner = race.FinishedUsers[0];
|
||||
if (race.FinishedUsers[0].Bet > 0)
|
||||
{
|
||||
return SendConfirmAsync(GetText(strs.animal_race),
|
||||
GetText(strs.animal_race_won_money(Format.Bold(winner.Username),
|
||||
winner.Animal.Icon,
|
||||
(race.FinishedUsers[0].Bet * (race.Users.Count - 1)) + CurrencySign)));
|
||||
}
|
||||
|
||||
return SendConfirmAsync(GetText(strs.animal_race),
|
||||
GetText(strs.animal_race_won(Format.Bold(winner.Username), winner.Animal.Icon)));
|
||||
}
|
||||
@@ -110,11 +115,13 @@ public partial class Gambling
|
||||
if (msg is null)
|
||||
raceMessage = await SendConfirmAsync(text);
|
||||
else
|
||||
{
|
||||
await msg.ModifyAsync(x => x.Embed = _eb.Create()
|
||||
.WithTitle(GetText(strs.animal_race))
|
||||
.WithDescription(text)
|
||||
.WithOkColor()
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
|
||||
private Task Ar_OnStartingFailed(AnimalRace race)
|
||||
@@ -140,9 +147,11 @@ public partial class Gambling
|
||||
{
|
||||
var user = await ar.JoinRace(ctx.User.Id, ctx.User.ToString(), amount);
|
||||
if (amount > 0)
|
||||
{
|
||||
await SendConfirmAsync(GetText(strs.animal_race_join_bet(ctx.User.Mention,
|
||||
user.Animal.Icon,
|
||||
amount + CurrencySign)));
|
||||
}
|
||||
else
|
||||
await SendConfirmAsync(GetText(strs.animal_race_join(ctx.User.Mention, user.Animal.Icon)));
|
||||
}
|
||||
|
@@ -56,9 +56,11 @@ public partial class Gambling
|
||||
if (await bj.Join(ctx.User, amount))
|
||||
await ReplyConfirmLocalizedAsync(strs.bj_joined);
|
||||
else
|
||||
{
|
||||
Log.Information("{User} can't join a blackjack game as it's in {BlackjackState} state already",
|
||||
ctx.User,
|
||||
bj.State);
|
||||
}
|
||||
}
|
||||
|
||||
await ctx.Message.DeleteAsync();
|
||||
@@ -114,21 +116,13 @@ public partial class Gambling
|
||||
full = "✅ " + full;
|
||||
}
|
||||
else if (p == bj.CurrentUser)
|
||||
{
|
||||
full = "▶ " + full;
|
||||
}
|
||||
else if (p.State == User.UserState.Stand)
|
||||
{
|
||||
full = "⏹ " + full;
|
||||
}
|
||||
else if (p.State == User.UserState.Bust)
|
||||
{
|
||||
full = "💥 " + full;
|
||||
}
|
||||
else if (p.State == User.UserState.Blackjack)
|
||||
{
|
||||
full = "💰 " + full;
|
||||
}
|
||||
|
||||
embed.AddField(full, cStr);
|
||||
}
|
||||
@@ -177,8 +171,10 @@ public partial class Gambling
|
||||
else if (a == BjAction.Stand)
|
||||
await bj.Stand(ctx.User);
|
||||
else if (a == BjAction.Double)
|
||||
{
|
||||
if (!await bj.Double(ctx.User))
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
}
|
||||
|
||||
await ctx.Message.DeleteAsync();
|
||||
}
|
||||
|
@@ -202,6 +202,7 @@ public class Blackjack
|
||||
}
|
||||
|
||||
if (hw > 21)
|
||||
{
|
||||
foreach (var usr in Players)
|
||||
{
|
||||
if (usr.State is User.UserState.Stand or User.UserState.Blackjack)
|
||||
@@ -209,7 +210,9 @@ public class Blackjack
|
||||
else
|
||||
usr.State = User.UserState.Lost;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var usr in Players)
|
||||
{
|
||||
if (usr.State == User.UserState.Blackjack)
|
||||
@@ -219,6 +222,7 @@ public class Blackjack
|
||||
else
|
||||
usr.State = User.UserState.Lost;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var usr in Players)
|
||||
{
|
||||
|
@@ -129,9 +129,7 @@ public sealed class Connect4Game : IDisposable
|
||||
_players[0] = (userId, userName);
|
||||
}
|
||||
else //else join as a second player
|
||||
{
|
||||
_players[1] = (userId, userName);
|
||||
}
|
||||
|
||||
CurrentPhase = Phase.P1Move; //start the game
|
||||
playerTimeoutTimer = new(async _ =>
|
||||
@@ -197,6 +195,7 @@ public sealed class Connect4Game : IDisposable
|
||||
|
||||
var first = _gameState[i + (j * NUMBER_OF_ROWS)];
|
||||
if (first != Field.Empty)
|
||||
{
|
||||
for (var k = 1; k < 4; k++)
|
||||
{
|
||||
var next = _gameState[i + k + (j * NUMBER_OF_ROWS)];
|
||||
@@ -208,10 +207,9 @@ public sealed class Connect4Game : IDisposable
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,17 +226,21 @@ public sealed class Connect4Game : IDisposable
|
||||
|
||||
var first = _gameState[j + (i * NUMBER_OF_ROWS)];
|
||||
if (first != Field.Empty)
|
||||
{
|
||||
for (var k = 1; k < 4; k++)
|
||||
{
|
||||
var next = _gameState[j + ((i + k) * NUMBER_OF_ROWS)];
|
||||
if (next == first)
|
||||
{
|
||||
if (k == 3)
|
||||
EndGame(Result.CurrentPlayerWon, CurrentPlayer.UserId);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -65,6 +65,7 @@ public partial class Gambling
|
||||
}
|
||||
|
||||
if (options.Bet > 0)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(ctx.User.Id, options.Bet, new("connect4", "bet")))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
@@ -72,6 +73,7 @@ public partial class Gambling
|
||||
game.Dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
game.OnGameStateUpdated += Game_OnGameStateUpdated;
|
||||
game.OnGameFailedToStart += GameOnGameFailedToStart;
|
||||
@@ -106,8 +108,10 @@ public partial class Gambling
|
||||
return;
|
||||
RepostCounter++;
|
||||
if (RepostCounter == 0)
|
||||
{
|
||||
try { msg = await ctx.Channel.SendMessageAsync("", embed: (Embed)msg.Embeds.First()); }
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
@@ -134,11 +138,15 @@ public partial class Gambling
|
||||
|
||||
string title;
|
||||
if (result == Connect4Game.Result.CurrentPlayerWon)
|
||||
{
|
||||
title = GetText(strs.connect4_won(Format.Bold(arg.CurrentPlayer.Username),
|
||||
Format.Bold(arg.OtherPlayer.Username)));
|
||||
}
|
||||
else if (result == Connect4Game.Result.OtherPlayerWon)
|
||||
{
|
||||
title = GetText(strs.connect4_won(Format.Bold(arg.OtherPlayer.Username),
|
||||
Format.Bold(arg.CurrentPlayer.Username)));
|
||||
}
|
||||
else
|
||||
title = GetText(strs.connect4_draw);
|
||||
|
||||
|
@@ -82,6 +82,7 @@ public partial class Gambling
|
||||
if (randomNumber == 6 || dice.Count == 0)
|
||||
toInsert = 0;
|
||||
else if (randomNumber != 1)
|
||||
{
|
||||
for (var j = 0; j < dice.Count; j++)
|
||||
{
|
||||
if (values[j] < randomNumber)
|
||||
@@ -90,11 +91,10 @@ public partial class Gambling
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
toInsert = dice.Count;
|
||||
}
|
||||
|
||||
dice.Insert(toInsert, GetDice(randomNumber));
|
||||
values.Insert(toInsert, randomNumber);
|
||||
@@ -190,9 +190,7 @@ public partial class Gambling
|
||||
rolled = new NadekoRandom().Next(arr[0], arr[1] + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
rolled = new NadekoRandom().Next(0, int.Parse(range) + 1);
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.dice_rolled(Format.Bold(rolled.ToString())));
|
||||
}
|
||||
|
@@ -43,6 +43,7 @@ public class CurrencyEventsService : INService
|
||||
|
||||
var added = _events.TryAdd(guildId, ce);
|
||||
if (added)
|
||||
{
|
||||
try
|
||||
{
|
||||
ce.OnEnded += OnEventEnded;
|
||||
@@ -54,6 +55,7 @@ public class CurrencyEventsService : INService
|
||||
_events.TryRemove(guildId, out ce);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
@@ -82,6 +82,7 @@ public class GameStatusEvent : ICurrencyEvent
|
||||
);
|
||||
|
||||
if (_isPotLimited)
|
||||
{
|
||||
await msg.ModifyAsync(m =>
|
||||
{
|
||||
m.Embed = GetEmbed(PotSize).Build();
|
||||
@@ -90,6 +91,7 @@ public class GameStatusEvent : ICurrencyEvent
|
||||
{
|
||||
RetryMode = RetryMode.AlwaysRetry
|
||||
});
|
||||
}
|
||||
|
||||
Log.Information("Awarded {Count} users {Amount} currency.{Remaining}",
|
||||
toAward.Count,
|
||||
@@ -179,6 +181,7 @@ public class GameStatusEvent : ICurrencyEvent
|
||||
private bool TryTakeFromPot()
|
||||
{
|
||||
if (_isPotLimited)
|
||||
{
|
||||
lock (_potLock)
|
||||
{
|
||||
if (PotSize < _amount)
|
||||
@@ -187,6 +190,7 @@ public class GameStatusEvent : ICurrencyEvent
|
||||
PotSize -= _amount;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -75,6 +75,7 @@ public class ReactionEvent : ICurrencyEvent
|
||||
await _cs.AddBulkAsync(toAward, _amount, new("event", "reaction"));
|
||||
|
||||
if (_isPotLimited)
|
||||
{
|
||||
await msg.ModifyAsync(m =>
|
||||
{
|
||||
m.Embed = GetEmbed(PotSize).Build();
|
||||
@@ -83,6 +84,7 @@ public class ReactionEvent : ICurrencyEvent
|
||||
{
|
||||
RetryMode = RetryMode.AlwaysRetry
|
||||
});
|
||||
}
|
||||
|
||||
Log.Information("Awarded {Count} users {Amount} currency.{Remaining}",
|
||||
toAward.Count,
|
||||
@@ -178,6 +180,7 @@ public class ReactionEvent : ICurrencyEvent
|
||||
private bool TryTakeFromPot()
|
||||
{
|
||||
if (_isPotLimited)
|
||||
{
|
||||
lock (_potLock)
|
||||
{
|
||||
if (PotSize < _amount)
|
||||
@@ -186,6 +189,7 @@ public class ReactionEvent : ICurrencyEvent
|
||||
PotSize -= _amount;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ public partial class Gambling
|
||||
+ GetText(strs.flipped(headCount > 0
|
||||
? Format.Bold(GetText(strs.heads))
|
||||
: Format.Bold(GetText(strs.tails))));
|
||||
|
||||
|
||||
await ctx.Channel.SendFileAsync(stream, $"{count} coins.{format.FileExtensions.First()}", msg);
|
||||
}
|
||||
|
||||
@@ -114,9 +114,7 @@ public partial class Gambling
|
||||
await _cs.AddAsync(ctx.User, toWin, new("betflip", "win"));
|
||||
}
|
||||
else
|
||||
{
|
||||
str = Format.Bold(ctx.User.ToString()) + " " + GetText(strs.better_luck);
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithDescription(str)
|
||||
|
@@ -8,7 +8,6 @@ using NadekoBot.Modules.Gambling.Services;
|
||||
using NadekoBot.Services.Currency;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling;
|
||||
@@ -79,13 +78,15 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
|
||||
// This stops the top 1% from owning more than 100% of the money
|
||||
if (ec.Cash > 0)
|
||||
{
|
||||
onePercent = ec.OnePercent / (ec.Cash - ec.Bot);
|
||||
}
|
||||
|
||||
// [21:03] Bob Page: Kinda remids me of US economy
|
||||
var embed = _eb.Create()
|
||||
.WithTitle(GetText(strs.economy_state))
|
||||
.AddField(GetText(strs.currency_owned),
|
||||
N((ec.Cash - ec.Bot)))
|
||||
N(ec.Cash - ec.Bot))
|
||||
.AddField(GetText(strs.currency_one_percent), (onePercent * 100).ToString("F2") + "%")
|
||||
.AddField(GetText(strs.currency_planted), N(ec.Planted))
|
||||
.AddField(GetText(strs.owned_waifus_total), N(ec.Waifus))
|
||||
@@ -132,7 +133,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task TimelySet(int amount, int period = 24)
|
||||
{
|
||||
if (amount < 0 || period < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_configService.ModifyConfig(gs =>
|
||||
{
|
||||
@@ -141,9 +144,13 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
});
|
||||
|
||||
if (amount == 0)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.timely_set_none);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.timely_set(Format.Bold(N(amount)), Format.Bold(period.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -155,7 +162,10 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
var members = (await role.GetMembersAsync()).Where(u => u.Status != UserStatus.Offline);
|
||||
var membersArray = members as IUser[] ?? members.ToArray();
|
||||
if (membersArray.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
|
||||
await SendConfirmAsync("🎟 " + GetText(strs.raffled_user),
|
||||
$"**{usr.Username}#{usr.Discriminator}**",
|
||||
@@ -171,7 +181,10 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
var members = await role.GetMembersAsync();
|
||||
var membersArray = members as IUser[] ?? members.ToArray();
|
||||
if (membersArray.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
|
||||
await SendConfirmAsync("🎟 " + GetText(strs.raffled_user),
|
||||
$"**{usr.Username}#{usr.Discriminator}**",
|
||||
@@ -198,7 +211,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
private async Task InternalCurrencyTransactions(ulong userId, int page)
|
||||
{
|
||||
if (--page < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<CurrencyTransaction> trs;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
@@ -222,10 +237,14 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
sb.AppendLine($"\\{change} {date} {Format.Bold(N(tr.Amount))}");
|
||||
var transactionString = GetHumanReadableTransaction(tr.Type, tr.Extra, tr.OtherId);
|
||||
if (transactionString is not null)
|
||||
{
|
||||
sb.AppendLine(transactionString);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tr.Note))
|
||||
{
|
||||
sb.AppendLine($"\t`Note:` {tr.Note.TrimTo(50)}");
|
||||
}
|
||||
}
|
||||
|
||||
embed.WithDescription(sb.ToString());
|
||||
@@ -264,10 +283,14 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
eb.AddField("Extra", tr.Extra, true);
|
||||
|
||||
if (tr.OtherId is ulong other)
|
||||
{
|
||||
eb.AddField("From Id", other);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tr.Note))
|
||||
{
|
||||
eb.AddField("Note", tr.Note);
|
||||
}
|
||||
|
||||
|
||||
eb.WithFooter(GetFormattedCurtrDate(tr));
|
||||
@@ -313,7 +336,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task Give(ShmartNumber amount, IGuildUser receiver, [Leftover] string msg)
|
||||
{
|
||||
if (amount <= 0 || ctx.User.Id == receiver.Id || receiver.IsBot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await _cs.TransferAsync(ctx.User.Id, receiver.Id, amount, ctx.User.ToString(), msg))
|
||||
{
|
||||
@@ -350,7 +375,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task Award(long amount, ulong usrId, [Leftover] string msg = null)
|
||||
{
|
||||
if (amount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var usr = await ((DiscordSocketClient)Context.Client).Rest.GetUserAsync(usrId);
|
||||
|
||||
@@ -414,7 +441,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task Take(long amount, [Leftover] IGuildUser user)
|
||||
{
|
||||
if (amount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var extra = new TxData("take",
|
||||
ctx.User.ToString()!,
|
||||
@@ -422,9 +451,13 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
ctx.User.Id);
|
||||
|
||||
if (await _cs.RemoveAsync(user.Id, amount, extra))
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.take(N(amount), Format.Bold(user.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount), Format.Bold(user.ToString()), CurrencySign));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -433,7 +466,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task Take(long amount, [Leftover] ulong usrId)
|
||||
{
|
||||
if (amount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var extra = new TxData("take",
|
||||
ctx.User.ToString()!,
|
||||
@@ -441,9 +476,13 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
ctx.User.Id);
|
||||
|
||||
if (await _cs.RemoveAsync(usrId, amount, extra))
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.take(N(amount), $"<@{usrId}>"));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount), Format.Code(usrId.ToString()), CurrencySign));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -451,12 +490,16 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task RollDuel(IUser u)
|
||||
{
|
||||
if (ctx.User.Id == u.Id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//since the challenge is created by another user, we need to reverse the ids
|
||||
//if it gets removed, means challenge is accepted
|
||||
if (_service.Duels.TryRemove((ctx.User.Id, u.Id), out var game))
|
||||
{
|
||||
await game.StartGame();
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -464,10 +507,14 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task RollDuel(ShmartNumber amount, IUser u)
|
||||
{
|
||||
if (ctx.User.Id == u.Id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (amount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var embed = _eb.Create().WithOkColor().WithTitle(GetText(strs.roll_duel));
|
||||
|
||||
@@ -478,9 +525,14 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
if (_service.Duels.TryGetValue((ctx.User.Id, u.Id), out var other))
|
||||
{
|
||||
if (other.Amount != amount)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.roll_duel_already_challenged);
|
||||
}
|
||||
else
|
||||
{
|
||||
await RollDuel(u);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -504,12 +556,16 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
embed = embed.WithDescription(description);
|
||||
|
||||
if (rdMsg is null)
|
||||
{
|
||||
rdMsg = await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
else
|
||||
{
|
||||
await rdMsg.ModifyAsync(x =>
|
||||
{
|
||||
x.Embed = embed.Build();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async Task GameOnEnded(RollDuelGame rdGame, RollDuelGame.Reason reason)
|
||||
@@ -544,7 +600,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
private async Task InternallBetroll(long amount)
|
||||
{
|
||||
if (!await CheckBetMandatory(amount))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await _cs.RemoveAsync(ctx.User, amount, new("betroll", "bet")))
|
||||
{
|
||||
@@ -588,14 +646,18 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task Leaderboard(int page = 1, params string[] args)
|
||||
{
|
||||
if (--page < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var (opts, _) = OptionsParser.ParseFrom(new LbOpts(), args);
|
||||
|
||||
List<DiscordUser> cleanRichest;
|
||||
// it's pointless to have clean on dm context
|
||||
if (ctx.Guild is null)
|
||||
{
|
||||
opts.Clean = false;
|
||||
}
|
||||
|
||||
if (opts.Clean)
|
||||
{
|
||||
@@ -658,7 +720,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public async partial Task Rps(RpsPick pick, ShmartNumber amount = default)
|
||||
{
|
||||
if (!await CheckBetOptional(amount) || amount == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string GetRpsPick(RpsPick p)
|
||||
{
|
||||
@@ -678,6 +742,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
var nadekoPick = (RpsPick)new NadekoRandom().Next(0, 3);
|
||||
|
||||
if (amount > 0)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(ctx.User.Id,
|
||||
amount,
|
||||
new("rps", "bet", "")))
|
||||
@@ -685,6 +750,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string msg;
|
||||
if (pick == nadekoPick)
|
||||
|
@@ -14,11 +14,10 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
|
||||
private readonly IEnumerable<WaifuItemModel> _antiGiftSeed = new[]
|
||||
{
|
||||
new WaifuItemModel("🥀", 100, "WiltedRose", true),
|
||||
new WaifuItemModel("✂️", 1000, "Haircut", true),
|
||||
new WaifuItemModel("🥀", 100, "WiltedRose", true), new WaifuItemModel("✂️", 1000, "Haircut", true),
|
||||
new WaifuItemModel("🧻", 10000, "ToiletPaper", true)
|
||||
};
|
||||
|
||||
|
||||
public GamblingConfigService(IConfigSeria serializer, IPubSub pubSub)
|
||||
: base(FILE_PATH, serializer, pubSub, _changeKey)
|
||||
{
|
||||
@@ -26,7 +25,7 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
gs => gs.Currency.Name,
|
||||
ConfigParsers.String,
|
||||
ConfigPrinters.ToString);
|
||||
|
||||
|
||||
AddParsedProp("currency.sign",
|
||||
gs => gs.Currency.Sign,
|
||||
ConfigParsers.String,
|
||||
@@ -37,7 +36,7 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 0);
|
||||
|
||||
|
||||
AddParsedProp("maxbet",
|
||||
gs => gs.MaxBet,
|
||||
int.TryParse,
|
||||
@@ -49,19 +48,19 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 1);
|
||||
|
||||
|
||||
AddParsedProp("gen.max",
|
||||
gs => gs.Generation.MaxAmount,
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 1);
|
||||
|
||||
|
||||
AddParsedProp("gen.cd",
|
||||
gs => gs.Generation.GenCooldown,
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val > 0);
|
||||
|
||||
|
||||
AddParsedProp("gen.chance",
|
||||
gs => gs.Generation.Chance,
|
||||
decimal.TryParse,
|
||||
@@ -72,73 +71,73 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
gs => gs.Generation.HasPassword,
|
||||
bool.TryParse,
|
||||
ConfigPrinters.ToString);
|
||||
|
||||
|
||||
AddParsedProp("bf.multi",
|
||||
gs => gs.BetFlip.Multiplier,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 1);
|
||||
|
||||
|
||||
AddParsedProp("waifu.min_price",
|
||||
gs => gs.Waifu.MinPrice,
|
||||
long.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 0);
|
||||
|
||||
|
||||
AddParsedProp("waifu.multi.reset",
|
||||
gs => gs.Waifu.Multipliers.WaifuReset,
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 0);
|
||||
|
||||
|
||||
AddParsedProp("waifu.multi.crush_claim",
|
||||
gs => gs.Waifu.Multipliers.CrushClaim,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 0);
|
||||
|
||||
|
||||
AddParsedProp("waifu.multi.normal_claim",
|
||||
gs => gs.Waifu.Multipliers.NormalClaim,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val > 0);
|
||||
|
||||
|
||||
AddParsedProp("waifu.multi.divorce_value",
|
||||
gs => gs.Waifu.Multipliers.DivorceNewValue,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val > 0);
|
||||
|
||||
|
||||
AddParsedProp("waifu.multi.all_gifts",
|
||||
gs => gs.Waifu.Multipliers.AllGiftPrices,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val > 0);
|
||||
|
||||
|
||||
AddParsedProp("waifu.multi.gift_effect",
|
||||
gs => gs.Waifu.Multipliers.GiftEffect,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 0);
|
||||
|
||||
|
||||
AddParsedProp("waifu.multi.negative_gift_effect",
|
||||
gs => gs.Waifu.Multipliers.NegativeGiftEffect,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 0);
|
||||
|
||||
|
||||
AddParsedProp("decay.percent",
|
||||
gs => gs.Decay.Percent,
|
||||
decimal.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val is >= 0 and <= 1);
|
||||
|
||||
|
||||
AddParsedProp("decay.maxdecay",
|
||||
gs => gs.Decay.MaxDecay,
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val >= 0);
|
||||
|
||||
|
||||
AddParsedProp("decay.threshold",
|
||||
gs => gs.Decay.MinThreshold,
|
||||
int.TryParse,
|
||||
@@ -151,23 +150,29 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
public void Migrate()
|
||||
{
|
||||
if (data.Version < 2)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Waifu.Items = c.Waifu.Items.Concat(_antiGiftSeed).ToList();
|
||||
c.Version = 2;
|
||||
});
|
||||
}
|
||||
|
||||
if (data.Version < 3)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 3;
|
||||
c.VoteReward = 100;
|
||||
});
|
||||
|
||||
if(data.Version < 5)
|
||||
}
|
||||
|
||||
if (data.Version < 5)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 5;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -70,12 +70,12 @@ public class GamblingService : INService, IReadyExecutor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task CurrencyDecayLoopAsync()
|
||||
{
|
||||
if (_bot.Client.ShardId != 0)
|
||||
return;
|
||||
|
||||
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
{
|
||||
@@ -125,16 +125,18 @@ WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentU
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<SlotResponse> SlotAsync(ulong userId, long amount)
|
||||
{
|
||||
var takeRes = await _cs.RemoveAsync(userId, amount, new("slot", "bet"));
|
||||
|
||||
if (!takeRes)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Error = GamblingError.NotEnough
|
||||
};
|
||||
}
|
||||
|
||||
var game = new SlotGame();
|
||||
var result = game.Spin();
|
||||
@@ -161,11 +163,13 @@ WHERE CurrencyAmount > {config.Decay.MinThreshold} AND UserId!={_client.CurrentU
|
||||
public EconomyResult GetEconomy()
|
||||
{
|
||||
if (_cache.TryGetEconomy(out var data))
|
||||
{
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<EconomyResult>(data);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
decimal cash;
|
||||
decimal onePercent;
|
||||
|
@@ -1,7 +1,6 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using System.Globalization;
|
||||
using System.Runtime;
|
||||
|
||||
namespace NadekoBot.Modules.Gambling.Common;
|
||||
|
||||
@@ -39,8 +38,9 @@ public abstract class GamblingModule<TService> : NadekoModule<TService>
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected string N<T>(T cur) where T : INumber<T>
|
||||
|
||||
protected string N<T>(T cur)
|
||||
where T : INumber<T>
|
||||
{
|
||||
var flowersCi = (CultureInfo)Culture.Clone();
|
||||
flowersCi.NumberFormat.CurrencySymbol = CurrencySign;
|
||||
|
@@ -31,12 +31,14 @@ public partial class Gambling
|
||||
}
|
||||
|
||||
if (((SocketGuild)ctx.Guild).CurrentUser.GuildPermissions.ManageMessages)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logService.AddDeleteIgnore(ctx.Message.Id);
|
||||
await ctx.Message.DeleteAsync();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -61,7 +63,7 @@ public partial class Gambling
|
||||
ctx.User.ToString(),
|
||||
amount,
|
||||
pass);
|
||||
|
||||
|
||||
if (!success)
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
}
|
||||
|
@@ -52,6 +52,7 @@ public class CurrencyRaffleService : INService
|
||||
}
|
||||
|
||||
if (newGame)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(60000);
|
||||
@@ -68,6 +69,7 @@ public class CurrencyRaffleService : INService
|
||||
catch { }
|
||||
finally { _locker.Release(); }
|
||||
});
|
||||
}
|
||||
|
||||
return (crg, null);
|
||||
}
|
||||
|
@@ -182,8 +182,10 @@ public partial class Gambling
|
||||
.ShopEntries);
|
||||
entry = entries.ElementAtOrDefault(index);
|
||||
if (entry is not null)
|
||||
{
|
||||
if (entry.Items.Add(item))
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
await ReplyErrorLocalizedAsync(strs.shop_buy_error);
|
||||
@@ -193,9 +195,7 @@ public partial class Gambling
|
||||
await ReplyConfirmLocalizedAsync(strs.shop_item_purchase);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,11 +291,13 @@ public partial class Gambling
|
||||
.ShopEntries);
|
||||
entry = entries.ElementAtOrDefault(index);
|
||||
if (entry is not null && (rightType = entry.Type == ShopEntryType.List))
|
||||
{
|
||||
if (entry.Items.Add(item))
|
||||
{
|
||||
added = true;
|
||||
uow.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entry is null)
|
||||
@@ -353,9 +355,7 @@ public partial class Gambling
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.ErrorAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -373,9 +373,7 @@ public partial class Gambling
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.ErrorAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -393,9 +391,7 @@ public partial class Gambling
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.ErrorAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -413,9 +409,7 @@ public partial class Gambling
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.ErrorAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public IEmbedBuilder EntryToEmbed(ShopEntry entry)
|
||||
@@ -423,6 +417,7 @@ public partial class Gambling
|
||||
var embed = _eb.Create().WithOkColor();
|
||||
|
||||
if (entry.Type == ShopEntryType.Role)
|
||||
{
|
||||
return embed
|
||||
.AddField(GetText(strs.name),
|
||||
GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name
|
||||
@@ -430,11 +425,14 @@ public partial class Gambling
|
||||
true)
|
||||
.AddField(GetText(strs.price), N(entry.Price), true)
|
||||
.AddField(GetText(strs.type), entry.Type.ToString(), true);
|
||||
}
|
||||
|
||||
if (entry.Type == ShopEntryType.List)
|
||||
{
|
||||
return embed.AddField(GetText(strs.name), entry.Name, true)
|
||||
.AddField(GetText(strs.price), N(entry.Price), true)
|
||||
.AddField(GetText(strs.type), GetText(strs.random_unique_item), true);
|
||||
}
|
||||
|
||||
//else if (entry.Type == ShopEntryType.Infinite_List)
|
||||
// return embed.AddField(GetText(strs.name), GetText(strs.shop_role(Format.Bold(entry.RoleName)), true))
|
||||
|
@@ -135,16 +135,20 @@ public partial class Gambling
|
||||
var (w, result, amount, remaining) = await _service.DivorceWaifuAsync(ctx.User, targetId);
|
||||
|
||||
if (result == DivorceResult.SucessWithPenalty)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_like(Format.Bold(w.Waifu.ToString()),
|
||||
N(amount)));
|
||||
}
|
||||
else if (result == DivorceResult.Success)
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_divorced_notlike(N(amount)));
|
||||
else if (result == DivorceResult.NotYourWife)
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_not_yours);
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_recent_divorce(
|
||||
Format.Bold(((int)remaining?.TotalHours).ToString()),
|
||||
Format.Bold(remaining?.Minutes.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -161,11 +165,14 @@ public partial class Gambling
|
||||
if (!sucess)
|
||||
{
|
||||
if (remaining is not null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_affinity_cooldown(
|
||||
Format.Bold(((int)remaining?.TotalHours).ToString()),
|
||||
Format.Bold(remaining?.Minutes.ToString())));
|
||||
}
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.waifu_affinity_already);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -174,8 +181,10 @@ public partial class Gambling
|
||||
else if (oldAff is null)
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_affinity_set(Format.Bold(user.ToString())));
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_affinity_changed(Format.Bold(oldAff.ToString()),
|
||||
Format.Bold(user.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -323,8 +332,10 @@ public partial class Gambling
|
||||
var sucess = await _service.GiftWaifuAsync(ctx.User, waifu, item);
|
||||
|
||||
if (sucess)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.waifu_gift(Format.Bold(item + " " + item.ItemEmoji),
|
||||
Format.Bold(waifu.ToString())));
|
||||
}
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
}
|
||||
|
@@ -99,7 +99,8 @@ public class WaifuService : INService, IReadyExecutor
|
||||
.GroupBy(x => x.New)
|
||||
.Count();
|
||||
|
||||
return (long)Math.Ceiling(waifu.Price * 1.25f) + ((divorces + affs + 2) * settings.Waifu.Multipliers.WaifuReset);
|
||||
return (long)Math.Ceiling(waifu.Price * 1.25f)
|
||||
+ ((divorces + affs + 2) * settings.Waifu.Multipliers.WaifuReset);
|
||||
}
|
||||
|
||||
public async Task<bool> TryReset(IUser user)
|
||||
@@ -154,9 +155,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
var claimer = uow.GetOrCreateUser(user);
|
||||
var waifu = uow.GetOrCreateUser(target);
|
||||
if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim")))
|
||||
{
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
uow.WaifuInfo.Add(w = new()
|
||||
@@ -179,9 +178,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
else if (isAffinity && amount > w.Price * settings.Waifu.Multipliers.CrushClaim)
|
||||
{
|
||||
if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim")))
|
||||
{
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
@@ -201,9 +198,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
else if (amount >= w.Price * settings.Waifu.Multipliers.NormalClaim) // if no affinity
|
||||
{
|
||||
if (!await _cs.RemoveAsync(user.Id, amount, new("waifu", "claim")))
|
||||
{
|
||||
result = WaifuClaimResult.NotEnoughFunds;
|
||||
}
|
||||
else
|
||||
{
|
||||
var oldClaimer = w.Claimer;
|
||||
@@ -221,9 +216,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = WaifuClaimResult.InsufficientAmount;
|
||||
}
|
||||
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
@@ -311,13 +304,9 @@ public class WaifuService : INService, IReadyExecutor
|
||||
{
|
||||
w = uow.WaifuInfo.ByWaifuUserId(targetId);
|
||||
if (w?.Claimer is null || w.Claimer.UserId != user.Id)
|
||||
{
|
||||
result = DivorceResult.NotYourWife;
|
||||
}
|
||||
else if (!_cache.TryAddDivorceCooldown(user.Id, out remaining))
|
||||
{
|
||||
result = DivorceResult.Cooldown;
|
||||
}
|
||||
else
|
||||
{
|
||||
amount = w.Price / 2;
|
||||
@@ -361,6 +350,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
await using var uow = _db.GetDbContext();
|
||||
var w = uow.WaifuInfo.ByWaifuUserId(giftedWaifu.Id, set => set.Include(x => x.Items).Include(x => x.Claimer));
|
||||
if (w is null)
|
||||
{
|
||||
uow.WaifuInfo.Add(w = new()
|
||||
{
|
||||
Affinity = null,
|
||||
@@ -368,6 +358,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
Price = 1,
|
||||
Waifu = uow.GetOrCreateUser(giftedWaifu)
|
||||
});
|
||||
}
|
||||
|
||||
if (!itemObj.Negative)
|
||||
{
|
||||
@@ -399,6 +390,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
using var uow = _db.GetDbContext();
|
||||
var wi = uow.GetWaifuInfo(targetId);
|
||||
if (wi is null)
|
||||
{
|
||||
wi = new()
|
||||
{
|
||||
AffinityCount = 0,
|
||||
@@ -412,6 +404,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
Items = new(),
|
||||
Price = 1
|
||||
};
|
||||
}
|
||||
|
||||
return wi;
|
||||
}
|
||||
@@ -498,7 +491,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
// only decay waifu values from shard 0
|
||||
if (_client.ShardId != 0)
|
||||
return;
|
||||
|
||||
|
||||
var redisKey = $"{_creds.RedisKey()}_last_waifu_decay";
|
||||
while (true)
|
||||
{
|
||||
@@ -509,9 +502,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
var decayInterval = _gss.Data.Waifu.Decay.HourInterval;
|
||||
|
||||
if (multi is < 0f or > 1f || decayInterval < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var val = await _cache.Redis.GetDatabase().StringGetAsync(redisKey);
|
||||
if (val != default)
|
||||
@@ -520,9 +511,7 @@ public class WaifuService : INService, IReadyExecutor
|
||||
var toWait = decayInterval.Hours() - (DateTime.UtcNow - lastDecay);
|
||||
|
||||
if (toWait > 0.Hours())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await _cache.Redis.GetDatabase().StringSetAsync(redisKey, DateTime.UtcNow.ToBinary());
|
||||
|
@@ -18,11 +18,13 @@ public class Betroll
|
||||
|
||||
var pair = _thresholdPairs.FirstOrDefault(x => x.WhenAbove < roll);
|
||||
if (pair is null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Multiplier = 0,
|
||||
Roll = roll
|
||||
};
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
|
@@ -25,6 +25,7 @@ public partial class Games
|
||||
|
||||
var game = new AcrophobiaGame(options);
|
||||
if (_service.AcrophobiaGames.TryAdd(channel.Id, game))
|
||||
{
|
||||
try
|
||||
{
|
||||
game.OnStarted += Game_OnStarted;
|
||||
@@ -40,6 +41,7 @@ public partial class Games
|
||||
_service.AcrophobiaGames.TryRemove(channel.Id, out game);
|
||||
game?.Dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.acro_running);
|
||||
|
||||
|
@@ -35,6 +35,7 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
|
||||
private void Migrate()
|
||||
{
|
||||
if (data.Version < 1)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 1;
|
||||
@@ -43,5 +44,6 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
|
||||
CurrencyReward = 0
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -62,9 +62,7 @@ public class GamesService : INService, IReadyExecutor
|
||||
// reset rating once a day
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromDays(1));
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
{
|
||||
GirlRatings.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<RatingTexts> GetRatingTexts()
|
||||
|
@@ -24,18 +24,23 @@ public partial class Games
|
||||
public static IEmbedBuilder GetEmbed(IEmbedBuilderService eb, HangmanGame.State state)
|
||||
{
|
||||
if (state.Phase == HangmanGame.Phase.Running)
|
||||
{
|
||||
return eb.Create()
|
||||
.WithOkColor()
|
||||
.AddField("Hangman", Draw(state))
|
||||
.AddField("Guess", Format.Code(state.Word))
|
||||
.WithFooter(state.MissedLetters.Join(' '));
|
||||
}
|
||||
|
||||
if (state.Phase == HangmanGame.Phase.Ended && state.Failed)
|
||||
{
|
||||
return eb.Create()
|
||||
.WithErrorColor()
|
||||
.AddField("Hangman", Draw(state))
|
||||
.AddField("Guess", Format.Code(state.Word))
|
||||
.WithFooter(state.MissedLetters.Join(' '));
|
||||
}
|
||||
|
||||
return eb.Create()
|
||||
.WithOkColor()
|
||||
.AddField("Hangman", Draw(state))
|
||||
|
@@ -87,16 +87,20 @@ public sealed class HangmanService : IHangmanService, ILateExecutor
|
||||
return;
|
||||
|
||||
if (state.GuessResult is HangmanGame.GuessResult.Incorrect or HangmanGame.GuessResult.AlreadyTried)
|
||||
{
|
||||
_cdCache.Set(msg.Author.Id,
|
||||
string.Empty,
|
||||
new MemoryCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3)
|
||||
});
|
||||
}
|
||||
|
||||
if (state.Phase == HangmanGame.Phase.Ended)
|
||||
{
|
||||
if (_hangmanGames.TryRemove(msg.Channel.Id, out _))
|
||||
rew = _gcs.Data.Hangman.CurrencyReward;
|
||||
}
|
||||
}
|
||||
|
||||
if (rew > 0)
|
||||
|
@@ -31,6 +31,7 @@ public partial class Games
|
||||
}
|
||||
|
||||
if (_service.StartPoll(poll))
|
||||
{
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.poll_created(ctx.User.ToString())))
|
||||
@@ -39,6 +40,7 @@ public partial class Games
|
||||
+ string.Join("\n",
|
||||
poll.Answers.Select(x
|
||||
=> $"`{x.Index + 1}.` {Format.Bold(x.Text)}"))));
|
||||
}
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.poll_already_running);
|
||||
}
|
||||
|
@@ -116,11 +116,13 @@ public class PollService : IEarlyBehavior
|
||||
var voted = await poll.TryVote(msg);
|
||||
|
||||
if (voted)
|
||||
{
|
||||
Log.Information("User {UserName} [{UserId}] voted in a poll on {GuildName} [{GuildId}] server",
|
||||
msg.Author.ToString(),
|
||||
msg.Author.Id,
|
||||
guild.Name,
|
||||
guild.Id);
|
||||
}
|
||||
|
||||
return voted;
|
||||
}
|
||||
|
@@ -151,9 +151,11 @@ public class TypingGame
|
||||
.AddField("Errors", distance.ToString(), true));
|
||||
|
||||
if (_finishedUserIds.Count % 4 == 0)
|
||||
{
|
||||
await Channel.SendConfirmAsync(_eb,
|
||||
":exclamation: A lot of people finished, here is the text for those still typing:"
|
||||
+ $"\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B", StringComparison.InvariantCulture)).SanitizeMentions(true)}**");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@@ -89,9 +89,7 @@ public class TicTacToe
|
||||
embed.WithFooter(GetText(strs.ttt_users_move(_users[curUserIndex])));
|
||||
}
|
||||
else
|
||||
{
|
||||
embed.WithFooter(GetText(strs.ttt_has_won(winner)));
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
@@ -123,6 +123,7 @@ public class TriviaGame
|
||||
//hint
|
||||
await Task.Delay(_options.QuestionTimer * 1000 / 2, triviaCancelSource.Token);
|
||||
if (!_options.NoHint)
|
||||
{
|
||||
try
|
||||
{
|
||||
await questionMessage.ModifyAsync(m
|
||||
@@ -134,6 +135,7 @@ public class TriviaGame
|
||||
break;
|
||||
}
|
||||
catch (Exception ex) { Log.Warning(ex, "Error editing triva message"); }
|
||||
}
|
||||
|
||||
//timeout
|
||||
await Task.Delay(_options.QuestionTimer * 1000 / 2, triviaCancelSource.Token);
|
||||
@@ -147,6 +149,7 @@ public class TriviaGame
|
||||
}
|
||||
|
||||
if (!triviaCancelSource.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var embed = _eb.Create()
|
||||
@@ -165,6 +168,7 @@ public class TriviaGame
|
||||
{
|
||||
Log.Warning(ex, "Error sending trivia time's up message");
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
@@ -186,6 +190,7 @@ public class TriviaGame
|
||||
var old = ShouldStopGame;
|
||||
ShouldStopGame = true;
|
||||
if (!old)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Channel.SendConfirmAsync(_eb, GetText(strs.trivia_game), GetText(strs.trivia_stopping));
|
||||
@@ -194,6 +199,7 @@ public class TriviaGame
|
||||
{
|
||||
Log.Warning(ex, "Error sending trivia stopping message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task PotentialGuess(SocketMessage imsg)
|
||||
|
@@ -232,13 +232,17 @@ public partial class Help : NadekoModule<HelpService>
|
||||
{
|
||||
//if cross is specified, and the command doesn't satisfy the requirements, cross it out
|
||||
if (opts.View == CommandsOptions.ViewType.Cross)
|
||||
{
|
||||
return
|
||||
$"{(succ.Contains(x) ? "✅" : "❌")}{prefix + x.Aliases.First(),-15} {"[" + x.Aliases.Skip(1).FirstOrDefault() + "]",-8}";
|
||||
}
|
||||
|
||||
return
|
||||
$"{prefix + x.Aliases.First(),-15} {"[" + x.Aliases.Skip(1).FirstOrDefault() + "]",-8}";
|
||||
});
|
||||
|
||||
if (i == last - 1 && (i + 1) % 2 != 0)
|
||||
{
|
||||
transformed = transformed.Chunk(2)
|
||||
.Select(x =>
|
||||
{
|
||||
@@ -246,6 +250,8 @@ public partial class Help : NadekoModule<HelpService>
|
||||
return $"{x.First()}";
|
||||
return string.Concat(x);
|
||||
});
|
||||
}
|
||||
|
||||
embed.AddField(g.ElementAt(i).Key, "```css\n" + string.Join("\n", transformed) + "\n```", true);
|
||||
}
|
||||
}
|
||||
|
@@ -288,9 +288,7 @@ public sealed partial class Music : NadekoModule<IMusicService>
|
||||
// if (mps > 0)
|
||||
// add += Format.Bold(GetText(strs.song_skips_after(TimeSpan.FromSeconds(mps).ToString("HH\\:mm\\:ss")))) + "\n";
|
||||
if (repeatType == PlayerRepeatType.Track)
|
||||
{
|
||||
add += "🔂 " + GetText(strs.repeating_track) + "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (mp.Autoplay)
|
||||
|
@@ -69,12 +69,14 @@ public sealed partial class Music
|
||||
var pl = uow.MusicPlaylists.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (pl is not null)
|
||||
{
|
||||
if (_creds.IsOwner(ctx.User) || pl.AuthorId == ctx.User.Id)
|
||||
{
|
||||
uow.MusicPlaylists.Remove(pl);
|
||||
await uow.SaveChangesAsync();
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -219,8 +219,10 @@ public sealed class MusicService : IMusicService
|
||||
=> _ =>
|
||||
{
|
||||
if (_settings.TryGetValue(guildId, out var settings))
|
||||
{
|
||||
if (settings.AutoDisconnect)
|
||||
return LeaveVoiceChannelAsync(guildId);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
@@ -232,9 +234,11 @@ public sealed class MusicService : IMusicService
|
||||
return false;
|
||||
|
||||
if (mp.IsStopped)
|
||||
{
|
||||
if (!_voiceStateService.TryGetProxy(guildId, out var proxy)
|
||||
|| proxy.State == VoiceProxy.VoiceProxyState.Stopped)
|
||||
await JoinVoiceChannelAsync(guildId, voiceChannelId);
|
||||
}
|
||||
|
||||
mp.Next();
|
||||
return true;
|
||||
|
@@ -44,6 +44,7 @@ public class RadioResolver : IRadioResolver
|
||||
if (query.Contains(".pls"))
|
||||
//File1=http://armitunes.com:8000/
|
||||
//Regex.Match(query)
|
||||
{
|
||||
try
|
||||
{
|
||||
var m = _plsRegex.Match(file);
|
||||
@@ -55,6 +56,7 @@ public class RadioResolver : IRadioResolver
|
||||
Log.Warning("Failed reading .pls:\n{PlsFile}", file);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.Contains(".m3u"))
|
||||
/*
|
||||
@@ -62,6 +64,7 @@ public class RadioResolver : IRadioResolver
|
||||
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
|
||||
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
|
||||
*/
|
||||
{
|
||||
try
|
||||
{
|
||||
var m = _m3URegex.Match(file);
|
||||
@@ -73,9 +76,11 @@ public class RadioResolver : IRadioResolver
|
||||
Log.Warning("Failed reading .m3u:\n{M3uFile}", file);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.Contains(".asx"))
|
||||
//<ref href="http://armitunes.com:8000"/>
|
||||
{
|
||||
try
|
||||
{
|
||||
var m = _asxRegex.Match(file);
|
||||
@@ -87,6 +92,7 @@ public class RadioResolver : IRadioResolver
|
||||
Log.Warning("Failed reading .asx:\n{AsxFile}", file);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (query.Contains(".xspf"))
|
||||
/*
|
||||
@@ -95,6 +101,7 @@ public class RadioResolver : IRadioResolver
|
||||
<trackList>
|
||||
<track><location>file:///mp3s/song_1.mp3</location></track>
|
||||
*/
|
||||
{
|
||||
try
|
||||
{
|
||||
var m = _xspfRegex.Match(file);
|
||||
@@ -106,6 +113,7 @@ public class RadioResolver : IRadioResolver
|
||||
Log.Warning("Failed reading .xspf:\n{XspfFile}", file);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
@@ -205,9 +205,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
|
||||
yield return info;
|
||||
}
|
||||
else
|
||||
{
|
||||
data += Environment.NewLine;
|
||||
}
|
||||
}
|
||||
|
||||
await _trackCacher.CachePlaylistTrackIdsAsync(playlistId, MusicPlatform.Youtube, trackIds);
|
||||
|
@@ -81,9 +81,7 @@ public partial class NSFW : NadekoModule<ISearchImagesService>
|
||||
try
|
||||
{
|
||||
if (tags is null || tags.Length == 0)
|
||||
{
|
||||
await InternalDapiCommand(null, true, _service.Hentai);
|
||||
}
|
||||
else
|
||||
{
|
||||
var groups = tags.Split('|');
|
||||
|
@@ -245,9 +245,7 @@ public class SearchImageCacher : INService
|
||||
page = _rng.Next(0, maxPage);
|
||||
}
|
||||
else
|
||||
{
|
||||
page = _rng.Next(0, 11);
|
||||
}
|
||||
|
||||
var result = await DownloadImagesAsync(tags, isExplicit, type, page, cancel);
|
||||
|
||||
|
@@ -75,11 +75,13 @@ public class SearchImagesService : ISearchImagesService, INService
|
||||
CancellationToken cancel)
|
||||
{
|
||||
if (!tags.All(x => IsValidTag(x)))
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Error = "One or more tags are invalid.",
|
||||
Url = ""
|
||||
};
|
||||
}
|
||||
|
||||
Log.Information("Getting {V} image for Guild: {GuildId}...", dapi.ToString(), guildId);
|
||||
try
|
||||
@@ -87,27 +89,33 @@ public class SearchImagesService : ISearchImagesService, INService
|
||||
BlacklistedTags.TryGetValue(guildId, out var blTags);
|
||||
|
||||
if (dapi == Booru.E621)
|
||||
{
|
||||
for (var i = 0; i < tags.Length; ++i)
|
||||
{
|
||||
if (tags[i] == "yuri")
|
||||
tags[i] = "female/female";
|
||||
}
|
||||
}
|
||||
|
||||
if (dapi == Booru.Derpibooru)
|
||||
{
|
||||
for (var i = 0; i < tags.Length; ++i)
|
||||
{
|
||||
if (tags[i] == "yuri")
|
||||
tags[i] = "lesbian";
|
||||
}
|
||||
}
|
||||
|
||||
var result = await _cache.GetImageNew(tags, forceExplicit, dapi, blTags ?? new HashSet<string>(), cancel);
|
||||
|
||||
if (result is null)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Error = "Image not found.",
|
||||
Url = ""
|
||||
};
|
||||
}
|
||||
|
||||
var reply = new UrlReply
|
||||
{
|
||||
|
@@ -129,11 +129,15 @@ public partial class Permissions
|
||||
_service.UnBlacklist(type, id);
|
||||
|
||||
if (action == AddRemove.Add)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.blacklisted(Format.Code(type.ToString()),
|
||||
Format.Code(id.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.unblacklisted(Format.Code(type.ToString()),
|
||||
Format.Code(id.ToString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -69,9 +69,7 @@ public partial class Permissions
|
||||
await ReplyConfirmLocalizedAsync(strs.cmdcd_cleared(Format.Bold(name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.cmdcd_add(Format.Bold(name), Format.Bold(secs.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -84,10 +82,12 @@ public partial class Permissions
|
||||
if (!localSet.Any())
|
||||
await ReplyConfirmLocalizedAsync(strs.cmdcd_none);
|
||||
else
|
||||
{
|
||||
await channel.SendTableAsync("",
|
||||
localSet.Select(c => c.CommandName + ": " + c.Seconds + GetText(strs.sec)),
|
||||
s => $"{s,-30}",
|
||||
2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -230,10 +230,12 @@ public partial class Permissions
|
||||
removed = config.FilteredWords.FirstOrDefault(fw => fw.Word.Trim().ToLowerInvariant() == word);
|
||||
|
||||
if (removed is null)
|
||||
{
|
||||
config.FilteredWords.Add(new()
|
||||
{
|
||||
Word = word
|
||||
});
|
||||
}
|
||||
else
|
||||
uow.Remove(removed);
|
||||
|
||||
|
@@ -132,6 +132,7 @@ public sealed class FilterService : IEarlyBehavior
|
||||
var filteredServerWords = FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
|
||||
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
|
||||
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
|
||||
{
|
||||
foreach (var word in wordsInMessage)
|
||||
{
|
||||
if (filteredChannelWords.Contains(word) || filteredServerWords.Contains(word))
|
||||
@@ -155,6 +156,7 @@ public sealed class FilterService : IEarlyBehavior
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@@ -43,9 +43,7 @@ public class GlobalPermissionService : ILateBlocker, INService
|
||||
_bss.ModifyConfig(bs =>
|
||||
{
|
||||
if (bs.Blocked.Modules.Add(moduleName))
|
||||
{
|
||||
added = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bs.Blocked.Modules.Remove(moduleName);
|
||||
@@ -67,9 +65,7 @@ public class GlobalPermissionService : ILateBlocker, INService
|
||||
_bss.ModifyConfig(bs =>
|
||||
{
|
||||
if (bs.Blocked.Commands.Add(commandName))
|
||||
{
|
||||
added = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bs.Blocked.Commands.Remove(commandName);
|
||||
|
@@ -155,6 +155,7 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
from -= 1;
|
||||
to -= 1;
|
||||
if (!(from == to || from < 0 || to < 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
Permissionv2 fromPerm;
|
||||
@@ -163,12 +164,12 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
var config = uow.GcWithPermissionsFor(ctx.Guild.Id);
|
||||
var permsCol = new PermissionsCollection<Permissionv2>(config.Permissions);
|
||||
|
||||
var fromFound = from < permsCol.Count;
|
||||
var fromFound = @from < permsCol.Count;
|
||||
var toFound = to < permsCol.Count;
|
||||
|
||||
if (!fromFound)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.perm_not_found(++from));
|
||||
await ReplyErrorLocalizedAsync(strs.perm_not_found(++@from));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -178,9 +179,9 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
return;
|
||||
}
|
||||
|
||||
fromPerm = permsCol[from];
|
||||
fromPerm = permsCol[@from];
|
||||
|
||||
permsCol.RemoveAt(from);
|
||||
permsCol.RemoveAt(@from);
|
||||
permsCol.Insert(to, fromPerm);
|
||||
await uow.SaveChangesAsync();
|
||||
_service.UpdateCache(config);
|
||||
@@ -188,7 +189,7 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.moved_permission(
|
||||
Format.Code(fromPerm.GetCommand(prefix, (SocketGuild)ctx.Guild)),
|
||||
++from,
|
||||
++@from,
|
||||
++to));
|
||||
|
||||
return;
|
||||
@@ -196,6 +197,7 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
catch (Exception e) when (e is ArgumentOutOfRangeException or IndexOutOfRangeException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.perm_out_of_range);
|
||||
}
|
||||
@@ -257,13 +259,17 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
});
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.ux_enable(Format.Code(command.Name),
|
||||
GetText(strs.of_command),
|
||||
Format.Code(user.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.ux_disable(Format.Code(command.Name),
|
||||
GetText(strs.of_command),
|
||||
Format.Code(user.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -281,13 +287,17 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
});
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.ux_enable(Format.Code(module.Name),
|
||||
GetText(strs.of_module),
|
||||
Format.Code(user.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.ux_disable(Format.Code(module.Name),
|
||||
GetText(strs.of_module),
|
||||
Format.Code(user.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -309,13 +319,17 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
});
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.rx_enable(Format.Code(command.Name),
|
||||
GetText(strs.of_command),
|
||||
Format.Code(role.Name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.rx_disable(Format.Code(command.Name),
|
||||
GetText(strs.of_command),
|
||||
Format.Code(role.Name)));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -337,13 +351,17 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.rx_enable(Format.Code(module.Name),
|
||||
GetText(strs.of_module),
|
||||
Format.Code(role.Name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.rx_disable(Format.Code(module.Name),
|
||||
GetText(strs.of_module),
|
||||
Format.Code(role.Name)));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -362,13 +380,17 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
});
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.cx_enable(Format.Code(command.Name),
|
||||
GetText(strs.of_command),
|
||||
Format.Code(chnl.Name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.cx_disable(Format.Code(command.Name),
|
||||
GetText(strs.of_command),
|
||||
Format.Code(chnl.Name)));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -386,13 +408,17 @@ public partial class Permissions : NadekoModule<PermissionService>
|
||||
});
|
||||
|
||||
if (action.Value)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.cx_enable(Format.Code(module.Name),
|
||||
GetText(strs.of_module),
|
||||
Format.Code(chnl.Name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.cx_disable(Format.Code(module.Name),
|
||||
GetText(strs.of_module),
|
||||
Format.Code(chnl.Name)));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
|
@@ -112,6 +112,7 @@ public class PermissionService : ILateBlocker, INService
|
||||
if (!resetCommand && !pc.Permissions.CheckPermissions(msg, commandName, moduleName, out var index))
|
||||
{
|
||||
if (pc.Verbose)
|
||||
{
|
||||
try
|
||||
{
|
||||
await channel.SendErrorAsync(_eb,
|
||||
@@ -123,6 +124,7 @@ public class PermissionService : ILateBlocker, INService
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -145,8 +147,10 @@ public class PermissionService : ILateBlocker, INService
|
||||
{
|
||||
returnMsg = "You need Admin permissions in order to use permission commands.";
|
||||
if (pc.Verbose)
|
||||
{
|
||||
try { await channel.SendErrorAsync(_eb, returnMsg); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -155,8 +159,10 @@ public class PermissionService : ILateBlocker, INService
|
||||
{
|
||||
returnMsg = $"You need the {Format.Bold(role.Name)} role in order to use permission commands.";
|
||||
if (pc.Verbose)
|
||||
{
|
||||
try { await channel.SendErrorAsync(_eb, returnMsg); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -65,6 +65,7 @@ public partial class Searches
|
||||
|
||||
var favAnime = GetText(strs.anime_no_fav);
|
||||
if (favorites.Length > 0 && favorites[0].QuerySelector("p") is null)
|
||||
{
|
||||
favAnime = string.Join("\n",
|
||||
favorites[0]
|
||||
.QuerySelectorAll("ul > li > div.di-tc.va-t > a")
|
||||
@@ -75,6 +76,7 @@ public partial class Searches
|
||||
var elem = (IHtmlAnchorElement)x;
|
||||
return $"[{elem.InnerHtml}]({elem.Href})";
|
||||
}));
|
||||
}
|
||||
|
||||
var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li.clearfix")
|
||||
.Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml))
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.Searches.Services;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
@@ -36,30 +37,50 @@ public partial class Searches
|
||||
|
||||
var usd = crypto.Quote["USD"];
|
||||
|
||||
var sevenDay = usd.PercentChange7d.ToString("F2", Culture);
|
||||
var lastDay = usd.PercentChange24h.ToString("F2", Culture);
|
||||
var localCulture = (CultureInfo)Culture.Clone();
|
||||
localCulture.NumberFormat.CurrencySymbol = "$";
|
||||
|
||||
var sevenDay = (usd.PercentChange7d / 100).ToString("P2", localCulture);
|
||||
var lastDay = (usd.PercentChange24h / 100).ToString("P2", localCulture);
|
||||
var price = usd.Price < 0.01
|
||||
? usd.Price.ToString(Culture)
|
||||
: usd.Price.ToString("F2", Culture);
|
||||
? usd.Price.ToString(localCulture)
|
||||
: usd.Price.ToString("C2", localCulture);
|
||||
|
||||
var volume = usd.Volume24h.ToString("n0", Culture);
|
||||
var marketCap = usd.MarketCap.ToString("n0", Culture);
|
||||
var volume = usd.Volume24h.ToString("C0", localCulture);
|
||||
var marketCap = usd.MarketCap.ToString("C0", localCulture);
|
||||
var dominance = (usd.MarketCapDominance / 100).ToString("P2", localCulture);
|
||||
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithAuthor($"#{crypto.CmcRank}")
|
||||
.WithTitle($"{crypto.Name} ({crypto.Symbol})")
|
||||
.WithUrl($"https://coinmarketcap.com/currencies/{crypto.Slug}/")
|
||||
.WithThumbnailUrl(
|
||||
$"https://s3.coinmarketcap.com/static/img/coins/128x128/{crypto.Id}.png")
|
||||
.AddField(GetText(strs.market_cap),
|
||||
$"${marketCap}",
|
||||
true)
|
||||
.AddField(GetText(strs.price), $"${price}", true)
|
||||
.AddField(GetText(strs.volume_24h), $"${volume}", true)
|
||||
.AddField(GetText(strs.change_7d_24h), $"{sevenDay}% / {lastDay}%", true)
|
||||
.WithImageUrl(
|
||||
$"https://s3.coinmarketcap.com/generated/sparklines/web/7d/usd/{crypto.Id}.png"));
|
||||
var toSend = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithAuthor($"#{crypto.CmcRank}")
|
||||
.WithTitle($"{crypto.Name} ({crypto.Symbol})")
|
||||
.WithUrl($"https://coinmarketcap.com/currencies/{crypto.Slug}/")
|
||||
.WithThumbnailUrl( $"https://s3.coinmarketcap.com/static/img/coins/128x128/{crypto.Id}.png")
|
||||
.AddField(GetText(strs.market_cap), marketCap, true)
|
||||
.AddField(GetText(strs.price), price, true)
|
||||
.AddField(GetText(strs.volume_24h), volume, true)
|
||||
.AddField(GetText(strs.change_7d_24h), $"{sevenDay} / {lastDay}", true)
|
||||
.AddField(GetText(strs.market_cap_dominance), dominance, true)
|
||||
.WithImageUrl($"https://s3.coinmarketcap.com/generated/sparklines/web/7d/usd/{crypto.Id}.png");
|
||||
|
||||
if (crypto.CirculatingSupply is double cs)
|
||||
{
|
||||
var csStr = cs.ToString("N0", localCulture);
|
||||
|
||||
if (crypto.MaxSupply is double ms)
|
||||
{
|
||||
var perc = (cs / ms).ToString("P1", localCulture);
|
||||
|
||||
toSend.AddField(GetText(strs.circulating_supply), $"{csStr} ({perc})", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
toSend.AddField(GetText(strs.circulating_supply), csStr, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await ctx.Channel.EmbedAsync(toSend);
|
||||
}
|
||||
}
|
||||
}
|
@@ -69,8 +69,10 @@ public class FeedsService : INService
|
||||
.ToList();
|
||||
|
||||
if (!_lastPosts.TryGetValue(kvp.Key, out var lastFeedUpdate))
|
||||
{
|
||||
lastFeedUpdate = _lastPosts[kvp.Key] =
|
||||
items.Any() ? items[items.Count - 1].LastUpdate : DateTime.UtcNow;
|
||||
}
|
||||
|
||||
foreach (var (feedItem, itemUpdateDate) in items)
|
||||
{
|
||||
@@ -106,8 +108,10 @@ public class FeedsService : INService
|
||||
.FirstOrDefault(x => x.Name.LocalName == "preview");
|
||||
|
||||
if (previewElement is null)
|
||||
{
|
||||
previewElement = afi.Element.Elements()
|
||||
.FirstOrDefault(x => x.Name.LocalName == "thumbnail");
|
||||
}
|
||||
|
||||
if (previewElement is not null)
|
||||
{
|
||||
|
@@ -251,9 +251,7 @@ public partial class Searches
|
||||
|
||||
// poe.ninja API does not include a "chaosEquivalent" property for Chaos Orbs.
|
||||
if (cleanCurrency == "Chaos Orb")
|
||||
{
|
||||
chaosEquivalent = 1.0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
var currencyInput = obj["lines"]
|
||||
@@ -265,9 +263,7 @@ public partial class Searches
|
||||
}
|
||||
|
||||
if (cleanConvert == "Chaos Orb")
|
||||
{
|
||||
conversionEquivalent = 1.0F;
|
||||
}
|
||||
else
|
||||
{
|
||||
var currencyOutput = obj["lines"]
|
||||
|
@@ -60,9 +60,7 @@ public partial class Searches : NadekoModule<SearchesService>
|
||||
var data = await _service.GetWeatherDataAsync(query);
|
||||
|
||||
if (data is null)
|
||||
{
|
||||
embed.WithDescription(GetText(strs.city_not_found)).WithErrorColor();
|
||||
}
|
||||
else
|
||||
{
|
||||
var f = StandardConversions.CelsiusToFahrenheit;
|
||||
@@ -284,6 +282,7 @@ public partial class Searches : NadekoModule<SearchesService>
|
||||
|
||||
query = query.Trim();
|
||||
if (!_cachedShortenedLinks.TryGetValue(query, out var shortLink))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var http = _httpFactory.CreateClient();
|
||||
@@ -310,6 +309,7 @@ public partial class Searches : NadekoModule<SearchesService>
|
||||
Log.Error(ex, "Error shortening a link: {Message}", ex.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
@@ -693,9 +693,7 @@ public partial class Searches : NadekoModule<SearchesService>
|
||||
}
|
||||
|
||||
if (obj.Error is not null || obj.Verses is null || obj.Verses.Length == 0)
|
||||
{
|
||||
await SendErrorAsync(obj.Error ?? "No verse found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var v = obj.Verses[0];
|
||||
|
@@ -76,9 +76,7 @@ public class SearchesService : INService
|
||||
Log.Warning("data/magicitems.json is missing. Magic items are not loaded");
|
||||
|
||||
if (File.Exists("data/yomama.txt"))
|
||||
{
|
||||
_yomamaJokes = File.ReadAllLines("data/yomama.txt").Shuffle().ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_yomamaJokes = new();
|
||||
|
@@ -172,8 +172,10 @@ public partial class Searches
|
||||
}
|
||||
|
||||
if (data.IsLive)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.streamer_online(Format.Bold(data.Name),
|
||||
Format.Bold(data.Viewers.ToString())));
|
||||
}
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.streamer_offline(data.Name));
|
||||
}
|
||||
|
@@ -127,7 +127,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
||||
{
|
||||
if (_client.ShardId != 0)
|
||||
return;
|
||||
|
||||
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(30));
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
{
|
||||
@@ -185,10 +185,12 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
||||
if (_trackCounter.ContainsKey(key))
|
||||
_trackCounter[key].Add(info.GuildId);
|
||||
else
|
||||
{
|
||||
_trackCounter[key] = new()
|
||||
{
|
||||
info.GuildId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return default;
|
||||
@@ -230,6 +232,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
||||
{
|
||||
var key = stream.CreateKey();
|
||||
if (_shardTrackedStreams.TryGetValue(key, out var fss))
|
||||
{
|
||||
await fss
|
||||
// send offline stream notifications only to guilds which enable it with .stoff
|
||||
.SelectMany(x => x.Value)
|
||||
@@ -238,6 +241,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
||||
?.GetTextChannel(fs.ChannelId)
|
||||
?.EmbedAsync(GetEmbed(fs.GuildId, stream)))
|
||||
.WhenAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,6 +251,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
||||
{
|
||||
var key = stream.CreateKey();
|
||||
if (_shardTrackedStreams.TryGetValue(key, out var fss))
|
||||
{
|
||||
await fss.SelectMany(x => x.Value)
|
||||
.Select(fs =>
|
||||
{
|
||||
@@ -264,6 +269,7 @@ public sealed class StreamNotificationService : INService, IReadyExecutor
|
||||
return textChannel.EmbedAsync(GetEmbed(fs.GuildId, stream), message);
|
||||
})
|
||||
.WhenAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -15,9 +15,12 @@ public class ImageCacherObject : IComparable<ImageCacherObject>
|
||||
if (type == Booru.Danbooru && !Uri.IsWellFormedUriString(obj.FileUrl, UriKind.Absolute))
|
||||
FileUrl = "https://danbooru.donmai.us" + obj.FileUrl;
|
||||
else
|
||||
{
|
||||
FileUrl = obj.FileUrl.StartsWith("http", StringComparison.InvariantCulture)
|
||||
? obj.FileUrl
|
||||
: "https:" + obj.FileUrl;
|
||||
}
|
||||
|
||||
SearchType = type;
|
||||
Rating = obj.Rating;
|
||||
Tags = new((obj.Tags ?? obj.TagString).Split(' '));
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user