Added many more braces for multiline if's, Improved .crypto command quite a bit and applied locale-specific format

This commit is contained in:
Kwoth
2022-02-04 06:00:17 +01:00
parent f77f2f433f
commit eda38e64d1
129 changed files with 635 additions and 233 deletions

View File

@@ -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 () =>

View File

@@ -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)

View File

@@ -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);

View File

@@ -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)

View File

@@ -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 });
}
}
}

View File

@@ -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()

View File

@@ -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,

View File

@@ -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;
},

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);
}
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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())));
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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]

View File

@@ -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);

View File

@@ -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);

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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]

View File

@@ -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)

View File

@@ -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);
});

View File

@@ -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)));
}

View File

@@ -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();
}

View File

@@ -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)
{

View File

@@ -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;
}
}
}
}

View File

@@ -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);

View File

@@ -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())));
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
});
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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));
}

View File

@@ -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);
}

View File

@@ -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))

View File

@@ -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));
}

View File

@@ -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());

View File

@@ -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()
{

View File

@@ -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);

View File

@@ -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
};
});
}
}
}

View File

@@ -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()

View File

@@ -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))

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -205,9 +205,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
yield return info;
}
else
{
data += Environment.NewLine;
}
}
await _trackCacher.CachePlaylistTrackIdsAsync(playlistId, MusicPlatform.Youtube, trackIds);

View File

@@ -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('|');

View File

@@ -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);

View File

@@ -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
{

View File

@@ -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())));
}
}
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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]

View File

@@ -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;
}

View File

@@ -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))

View File

@@ -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);
}
}
}

View File

@@ -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)
{

View File

@@ -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"]

View File

@@ -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];

View File

@@ -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();

View File

@@ -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));
}

View File

@@ -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();
}
}
}

View File

@@ -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(' '));

View File

@@ -46,8 +46,10 @@ public class NotifChecker
.ToList();
if (remove)
{
foreach (var toBeRemoved in toReturn)
_streamProviders[toBeRemoved.Type].ClearErrorsFor(toBeRemoved.Name);
}
return toReturn;
}
@@ -74,9 +76,11 @@ public class NotifChecker
// get all stream data for the streams of this type
if (_streamProviders.TryGetValue(x.Key,
out var provider))
{
return provider.GetStreamDataAsync(x.Value
.Select(entry => entry.Key)
.ToList());
}
// this means there's no provider for this stream data, (and there was before?)
return Task.FromResult<IReadOnlyCollection<StreamData>>(
@@ -111,9 +115,7 @@ public class NotifChecker
// before it sends an offline notification to the subscribers.
var streamId = (key.Type, key.Name);
if (!newData.IsLive && _offlineBuffer.Remove(streamId))
{
newlyOffline.Add(newData);
}
else if (newData.IsLive != oldData.IsLive)
{
if (newData.IsLive)

View File

@@ -57,6 +57,7 @@ public class CommandMapService : IInputTransformer, INService
return input;
if (guild is not null)
{
if (AliasMaps.TryGetValue(guild.Id, out var maps))
{
var keys = maps.Keys.OrderByDescending(x => x.Length);
@@ -88,6 +89,7 @@ public class CommandMapService : IInputTransformer, INService
return newInput;
}
}
}
return input;
}

View File

@@ -67,8 +67,11 @@ public partial class Utility
embed.WithThumbnailUrl(guild.IconUrl);
if (guild.Emotes.Any())
{
embed.AddField(GetText(strs.custom_emojis) + $"({guild.Emotes.Count})",
string.Join(" ", guild.Emotes.Shuffle().Take(20).Select(e => $"{e.Name} {e}")).TrimTo(1020));
}
await ctx.Channel.EmbedAsync(embed);
}

View File

@@ -62,10 +62,12 @@ public partial class Utility
}
if (quotes.Any())
{
await SendConfirmAsync(GetText(strs.quotes_page(page + 1)),
string.Join("\n",
quotes.Select(q
=> $"`#{q.Id}` {Format.Bold(q.Keyword.SanitizeAllMentions()),-20} by {q.AuthorName.SanitizeAllMentions()}")));
}
else
await ReplyErrorLocalizedAsync(strs.quotes_page_none);
}
@@ -227,9 +229,7 @@ public partial class Utility
var q = uow.Quotes.GetById(id);
if (q?.GuildId != ctx.Guild.Id || (!hasManageMessages && q.AuthorId != ctx.Message.Author.Id))
{
response = GetText(strs.quotes_remove_none);
}
else
{
uow.Quotes.Remove(q);

View File

@@ -124,9 +124,7 @@ public partial class Utility
}
}
else
{
embed.WithDescription(GetText(strs.reminders_none));
}
embed.AddPaginatedFooter(page + 1, null);
await ctx.Channel.EmbedAsync(embed);

View File

@@ -150,9 +150,7 @@ public class RemindService : INService
ch = await user.CreateDMChannelAsync();
}
else
{
ch = _client.GetGuild(r.ServerId)?.GetTextChannel(r.ChannelId);
}
if (ch is null)
return;

View File

@@ -193,8 +193,10 @@ public sealed class RepeaterService : IReadyExecutor, INService
var channel = _client.GetChannel(repeater.ChannelId) as ITextChannel;
if (channel is null)
{
try { channel = await _client.Rest.GetChannelAsync(repeater.ChannelId) as ITextChannel; }
catch { }
}
if (channel is null)
{
@@ -210,6 +212,7 @@ public sealed class RepeaterService : IReadyExecutor, INService
}
if (_noRedundant.Contains(repeater.Id))
{
try
{
var lastMsgInChannel = await channel.GetMessagesAsync(2).Flatten().FirstAsync();
@@ -224,8 +227,10 @@ public sealed class RepeaterService : IReadyExecutor, INService
guild.Id,
channel.Id);
}
}
if (repeater.LastMessageId is { } lastMessageId)
{
try
{
var oldMsg = await channel.GetMessageAsync(lastMessageId);
@@ -239,6 +244,7 @@ public sealed class RepeaterService : IReadyExecutor, INService
guild.Id,
channel.Id);
}
}
var rep = new ReplacementBuilder().WithDefault(guild.CurrentUser, channel, guild, _client).Build();

View File

@@ -57,10 +57,12 @@ public partial class Utility
user.ToString());
if (action == AddRemove.Add)
{
if (success)
await ReplyConfirmLocalizedAsync(strs.stream_role_bl_add(Format.Bold(user.ToString())));
else
await ReplyConfirmLocalizedAsync(strs.stream_role_bl_add_fail(Format.Bold(user.ToString())));
}
else if (success)
await ReplyConfirmLocalizedAsync(strs.stream_role_bl_rem(Format.Bold(user.ToString())));
else
@@ -80,10 +82,12 @@ public partial class Utility
user.ToString());
if (action == AddRemove.Add)
{
if (success)
await ReplyConfirmLocalizedAsync(strs.stream_role_wl_add(Format.Bold(user.ToString())));
else
await ReplyConfirmLocalizedAsync(strs.stream_role_wl_add_fail(Format.Bold(user.ToString())));
}
else if (success)
await ReplyConfirmLocalizedAsync(strs.stream_role_wl_rem(Format.Bold(user.ToString())));
else

View File

@@ -90,9 +90,7 @@ public class StreamRoleService : INService
}
}
else
{
success = streamRoleSettings.Whitelist.Add(userObj);
}
}
else
{
@@ -109,9 +107,7 @@ public class StreamRoleService : INService
success = streamRoleSettings.Blacklist.Remove(toRemove);
}
else
{
success = streamRoleSettings.Blacklist.Add(userObj);
}
}
await uow.SaveChangesAsync();
@@ -270,6 +266,7 @@ public class StreamRoleService : INService
{
//check if user is in the addrole
if (user.RoleIds.Contains(setting.AddRoleId))
{
try
{
addRole ??= user.Guild.GetRole(setting.AddRoleId);
@@ -287,6 +284,7 @@ public class StreamRoleService : INService
Log.Warning(ex, "Error removing stream role(s). Forcibly disabling stream role feature");
throw new StreamRolePermissionException();
}
}
}
}

View File

@@ -48,9 +48,7 @@ public partial class Utility
decimal res;
if (originUnit.Triggers == targetUnit.Triggers)
{
res = value;
}
else if (originUnit.UnitType == "temperature")
{
//don't really care too much about efficiency, so just convert to Kelvin, then to target

View File

@@ -98,11 +98,13 @@ public partial class Utility : NadekoModule
if (arr.Length == 0)
await ReplyErrorLocalizedAsync(strs.nobody_playing_game);
else
{
await SendConfirmAsync("```css\n"
+ string.Join("\n",
arr.GroupBy(_ => i++ / 2)
.Select(ig => string.Concat(ig.Select(el => $"• {el,-27}"))))
+ "\n```");
}
}
[Cmd]
@@ -215,8 +217,10 @@ public partial class Utility : NadekoModule
if (!roles.Any())
await ReplyErrorLocalizedAsync(strs.no_roles_on_page);
else
{
await SendConfirmAsync(GetText(strs.roles_page(page, Format.Bold(target.ToString()))),
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles).SanitizeMentions(true));
}
}
else
{
@@ -228,8 +232,10 @@ public partial class Utility : NadekoModule
if (!roles.Any())
await ReplyErrorLocalizedAsync(strs.no_roles_on_page);
else
{
await SendConfirmAsync(GetText(strs.roles_all_page(page)),
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles).SanitizeMentions(true));
}
}
}
@@ -436,18 +442,20 @@ public partial class Utility : NadekoModule
if (string.IsNullOrWhiteSpace(s.ToString()))
{
if (s.Attachments.Any())
{
msg += "FILES_UPLOADED: "
+ string.Join("\n", s.Attachments.Select(x => x.Url));
}
else if (s.Embeds.Any())
{
msg += "EMBEDS: "
+ string.Join("\n--------\n",
s.Embeds.Select(x
=> $"Description: {x.Description}"));
}
}
else
{
msg += s.ToString();
}
return msg;
})

View File

@@ -20,8 +20,10 @@ public partial class Xp
var club = _service.TransferClub(ctx.User, newOwner);
if (club is not null)
{
await ReplyConfirmLocalizedAsync(strs.club_transfered(Format.Bold(club.Name),
Format.Bold(newOwner.ToString())));
}
else
await ReplyErrorLocalizedAsync(strs.club_transfer_failed);
}
@@ -264,8 +266,11 @@ public partial class Xp
public partial Task ClubKick([Leftover] string userName)
{
if (_service.Kick(ctx.User.Id, userName, out var club))
{
return ReplyConfirmLocalizedAsync(strs.club_user_kick(Format.Bold(userName),
Format.Bold(club.ToString())));
}
return ReplyErrorLocalizedAsync(strs.club_user_kick_fail);
}
@@ -279,8 +284,11 @@ public partial class Xp
public partial Task ClubBan([Leftover] string userName)
{
if (_service.Ban(ctx.User.Id, userName, out var club))
{
return ReplyConfirmLocalizedAsync(strs.club_user_banned(Format.Bold(userName),
Format.Bold(club.ToString())));
}
return ReplyErrorLocalizedAsync(strs.club_user_ban_fail);
}
@@ -294,8 +302,11 @@ public partial class Xp
public partial Task ClubUnBan([Leftover] string userName)
{
if (_service.UnBan(ctx.User.Id, userName, out var club))
{
return ReplyConfirmLocalizedAsync(strs.club_user_unbanned(Format.Bold(userName),
Format.Bold(club.ToString())));
}
return ReplyErrorLocalizedAsync(strs.club_user_unban_fail);
}

View File

@@ -38,9 +38,7 @@ public class ClubService : INService
uow.SaveChanges();
}
else
{
return false;
}
uow.Set<ClubApplicants>().RemoveRange(uow.Set<ClubApplicants>().AsQueryable().Where(x => x.UserId == du.Id));
club = du.Club;

View File

@@ -76,9 +76,7 @@ public partial class Xp : NadekoModule<XpService>
var str = ctx.Guild.GetRole(x.RoleId)?.ToString();
if (str is null)
{
str = GetText(strs.role_not_found(Format.Code(x.RoleId.ToString())));
}
else
{
if (!x.Remove)
@@ -141,8 +139,10 @@ public partial class Xp : NadekoModule<XpService>
if (action == AddRemove.Add)
await ReplyConfirmLocalizedAsync(strs.xp_role_reward_add_role(level, Format.Bold(role.ToString())));
else
{
await ReplyConfirmLocalizedAsync(strs.xp_role_reward_remove_role(Format.Bold(level.ToString()),
Format.Bold(role.ToString())));
}
}
[Cmd]
@@ -365,12 +365,14 @@ public partial class Xp : NadekoModule<XpService>
if (!users.Any())
embed.WithDescription("-");
else
{
for (var i = 0; i < users.Length; i++)
{
var user = users[i];
embed.AddField($"#{i + 1 + (page * 9)} {user.ToString()}",
$"{GetText(strs.level_x(new LevelStats(users[i].TotalXp).Level))} - {users[i].TotalXp}xp");
}
}
await ctx.Channel.EmbedAsync(embed);
}

View File

@@ -39,10 +39,12 @@ public sealed class XpConfigService : ConfigServiceBase<XpConfig>
private void Migrate()
{
if (data.Version < 2)
{
ModifyConfig(c =>
{
c.Version = 2;
c.XpFromImage = 0;
});
}
}
}

View File

@@ -1,5 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Db.Models;
using NadekoBot.Services.Database.Models;
@@ -17,7 +18,7 @@ using Image = SixLabors.ImageSharp.Image;
namespace NadekoBot.Modules.Xp.Services;
// todo improve xp with linqtodb
public class XpService : INService
public class XpService : INService, IReadyExecutor
{
public const int XP_REQUIRED_LVL_1 = 36;
@@ -29,7 +30,6 @@ public class XpService : INService
private readonly FontProvider _fonts;
private readonly IBotCredentials _creds;
private readonly ICurrencyService _cs;
private readonly Task _updateXpTask;
private readonly IHttpClientFactory _httpFactory;
private readonly XpConfigService _xpConfig;
private readonly IPubSub _pubSub;
@@ -82,12 +82,14 @@ public class XpService : INService
InternalReloadXpTemplate();
if (client.ShardId == 0)
{
_pubSub.Sub(_xpTemplateReloadKey,
_ =>
{
InternalReloadXpTemplate();
return default;
});
}
//load settings
var allGuildConfigs = bot.AllGuildConfigs.Where(x => x.XpSettings is not null).ToList();
@@ -119,14 +121,16 @@ public class XpService : INService
foreach (var guild in _client.Guilds)
Client_OnGuildAvailable(guild);
#endif
_updateXpTask = Task.Run(UpdateLoop);
}
public Task OnReadyAsync()
=> UpdateLoop();
private async Task UpdateLoop()
{
while (true)
using var timer = new PeriodicTimer(5.Seconds());
while (await timer.WaitForNextTickAsync())
{
await Task.Delay(TimeSpan.FromSeconds(5));
try
{
var toNotify =
@@ -168,8 +172,10 @@ public class XpService : INService
du.LastLevelUp = DateTime.UtcNow;
var first = item.First();
if (du.NotifyOnLevelUp != XpNotificationLocation.None)
{
toNotify.Add((first.Guild, first.Channel, first.User, newGlobalLevelData.Level,
du.NotifyOnLevelUp, NotifOf.Global));
}
}
if (oldGuildLevelData.Level < newGuildLevelData.Level)
@@ -178,8 +184,10 @@ public class XpService : INService
//send level up notification
var first = item.First();
if (usr.NotifyOnLevelUp != XpNotificationLocation.None)
{
toNotify.Add((first.Guild, first.Channel, first.User, newGuildLevelData.Level,
usr.NotifyOnLevelUp, NotifOf.Server));
}
//give role
if (!roleRewards.TryGetValue(usr.GuildId, out var rrews))
@@ -227,16 +235,20 @@ public class XpService : INService
if (x.NotifOf == NotifOf.Server)
{
if (x.NotifyType == XpNotificationLocation.Dm)
{
await x.User.SendConfirmAsync(_eb,
_strings.GetText(strs.level_up_dm(x.User.Mention,
Format.Bold(x.Level.ToString()),
Format.Bold(x.Guild.ToString() ?? "-")),
x.Guild.Id));
}
else if (x.MessageChannel is not null) // channel
{
await x.MessageChannel.SendConfirmAsync(_eb,
_strings.GetText(strs.level_up_channel(x.User.Mention,
Format.Bold(x.Level.ToString())),
x.Guild.Id));
}
}
else
{
@@ -306,11 +318,13 @@ public class XpService : INService
if (rew is not null)
rew.Amount = amount;
else
{
settings.CurrencyRewards.Add(new()
{
Level = level,
Amount = amount
});
}
}
uow.SaveChanges();
@@ -455,11 +469,15 @@ public class XpService : INService
private void ScanChannelForVoiceXp(SocketVoiceChannel channel)
{
if (ShouldTrackVoiceChannel(channel))
{
foreach (var user in channel.Users)
ScanUserForVoiceXp(user, channel);
}
else
{
foreach (var user in channel.Users)
UserLeftVoiceChannel(user, channel);
}
}
/// <summary>
@@ -510,12 +528,14 @@ public class XpService : INService
var actualXp = (int)Math.Floor(xp);
if (actualXp > 0)
{
_addMessageXp.Enqueue(new()
{
Guild = channel.Guild,
User = user,
XpAmount = actualXp
});
}
}
private bool ShouldTrackXp(SocketGuildUser user, ulong channelId)
@@ -779,6 +799,7 @@ public class XpService : INService
}
if (template.User.GlobalLevel.Show)
{
img.Mutate(x =>
{
x.DrawText(stats.Global.Level.ToString(),
@@ -786,8 +807,10 @@ public class XpService : INService
template.User.GlobalLevel.Color,
new(template.User.GlobalLevel.Pos.X, template.User.GlobalLevel.Pos.Y)); //level
});
}
if (template.User.GuildLevel.Show)
{
img.Mutate(x =>
{
x.DrawText(stats.Guild.Level.ToString(),
@@ -795,6 +818,7 @@ public class XpService : INService
template.User.GuildLevel.Color,
new(template.User.GuildLevel.Pos.X, template.User.GuildLevel.Pos.Y));
});
}
var pen = new Pen(Color.Black, 1);
@@ -812,18 +836,22 @@ public class XpService : INService
}
if (template.User.Xp.Global.Show)
{
img.Mutate(x => x.DrawText($"{global.LevelXp}/{global.RequiredXp}",
_fonts.NotoSans.CreateFont(template.User.Xp.Global.FontSize, FontStyle.Bold),
Brushes.Solid(template.User.Xp.Global.Color),
pen,
new(template.User.Xp.Global.Pos.X, template.User.Xp.Global.Pos.Y)));
}
if (template.User.Xp.Guild.Show)
{
img.Mutate(x => x.DrawText($"{guild.LevelXp}/{guild.RequiredXp}",
_fonts.NotoSans.CreateFont(template.User.Xp.Guild.FontSize, FontStyle.Bold),
Brushes.Solid(template.User.Xp.Guild.Color),
pen,
new(template.User.Xp.Guild.Pos.X, template.User.Xp.Guild.Pos.Y)));
}
if (stats.FullGuildStats.AwardedXp != 0 && template.User.Xp.Awarded.Show)
{
@@ -840,16 +868,20 @@ public class XpService : INService
//ranking
if (template.User.GlobalRank.Show)
{
img.Mutate(x => x.DrawText(stats.GlobalRanking.ToString(),
_fonts.UniSans.CreateFont(template.User.GlobalRank.FontSize, FontStyle.Bold),
template.User.GlobalRank.Color,
new(template.User.GlobalRank.Pos.X, template.User.GlobalRank.Pos.Y)));
}
if (template.User.GuildRank.Show)
{
img.Mutate(x => x.DrawText(stats.GuildRanking.ToString(),
_fonts.UniSans.CreateFont(template.User.GuildRank.FontSize, FontStyle.Bold),
template.User.GuildRank.Color,
new(template.User.GuildRank.Pos.X, template.User.GuildRank.Pos.Y)));
}
//time on this level
@@ -860,20 +892,25 @@ public class XpService : INService
}
if (template.User.TimeOnLevel.Global.Show)
{
img.Mutate(x => x.DrawText(GetTimeSpent(stats.User.LastLevelUp, template.User.TimeOnLevel.Format),
_fonts.NotoSans.CreateFont(template.User.TimeOnLevel.Global.FontSize, FontStyle.Bold),
template.User.TimeOnLevel.Global.Color,
new(template.User.TimeOnLevel.Global.Pos.X, template.User.TimeOnLevel.Global.Pos.Y)));
}
if (template.User.TimeOnLevel.Guild.Show)
{
img.Mutate(x
=> x.DrawText(GetTimeSpent(stats.FullGuildStats.LastLevelUp, template.User.TimeOnLevel.Format),
_fonts.NotoSans.CreateFont(template.User.TimeOnLevel.Guild.FontSize, FontStyle.Bold),
template.User.TimeOnLevel.Guild.Color,
new(template.User.TimeOnLevel.Guild.Pos.X, template.User.TimeOnLevel.Guild.Pos.Y)));
}
//avatar
if (stats.User.AvatarId is not null && template.User.Icon.Show)
{
try
{
var avatarUrl = stats.User.RealAvatarUrl();
@@ -913,6 +950,7 @@ public class XpService : INService
{
Log.Warning(ex, "Error drawing avatar image");
}
}
//club image
if (template.Club.Icon.Show)
@@ -973,6 +1011,7 @@ public class XpService : INService
private async Task DrawClubImage(Image<Rgba32> img, FullUserStats stats)
{
if (!string.IsNullOrWhiteSpace(stats.User.Club?.ImageUrl))
{
try
{
var imgUrl = new Uri(stats.User.Club.ImageUrl);
@@ -1015,6 +1054,7 @@ public class XpService : INService
{
Log.Warning(ex, "Error drawing club image");
}
}
}
public void XpReset(ulong guildId, ulong userId)