mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-11-04 08:34:27 -05: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:
@@ -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(' '));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user