forked from EllieBotDevs/elliebot
v6 #3
10 changed files with 199 additions and 144 deletions
CHANGELOG.md
src/EllieBot
EllieBot.csproj
Modules
Administration
Permissions/Filter
Searches
StreamNotification
_common/StreamNotifications
Utility
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -2,6 +2,34 @@
|
|||
|
||||
*a,c,f,r,o*
|
||||
|
||||
## [6.0.12] - 20.03.2025
|
||||
|
||||
### Fixed
|
||||
- `.antispamignore` fixed for the last time hopefully
|
||||
- protection commands are some of the oldest commands, and they might get overhauled in future updates
|
||||
- please report if you find any other weird issue with them
|
||||
|
||||
## [6.0.11] - 20.03.2025
|
||||
|
||||
### Changed
|
||||
- wordfilter, invitefilter and linkfilter will now properly detect forwarded messages, as forwards were used to circumvent filtering.
|
||||
|
||||
### Fixed
|
||||
- `.dmc` fixed
|
||||
- Fixed .streamremove - now showing proper youtube name when removing instead of channel id
|
||||
|
||||
## [6.0.10] - 20.03.2025
|
||||
|
||||
### Changed
|
||||
|
||||
- Live channels `.lcha` is limited to 1 for now. It will be reverted back to 5 in a couple of days at most as some things need to be implemented.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `.antispam` won't break if you have thread channels in the server anymore
|
||||
- `.ve` now works properly
|
||||
- selfhosters: `.yml` parsing errors will now tell you which .yml file is causing the issue and why.
|
||||
|
||||
## [6.0.9] - 19.03.2025
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>true</ImplicitUsings>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
<Version>6.0.9</Version>
|
||||
<Version>6.0.12</Version>
|
||||
|
||||
<!-- Output/build -->
|
||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||
|
|
|
@ -107,12 +107,12 @@ public class AdministrationService : IEService, IReadyExecutor
|
|||
.UpdateWithOutputAsync(x => new()
|
||||
{
|
||||
DeleteMessageOnCommand = !x.DeleteMessageOnCommand
|
||||
});
|
||||
}, (old, newVal) => newVal);
|
||||
|
||||
if (conf.Length == 0)
|
||||
return false;
|
||||
|
||||
var val = conf[0].Inserted.DeleteMessageOnCommand;
|
||||
var val = conf[0].DeleteMessageOnCommand;
|
||||
|
||||
if (val)
|
||||
deleteMessagesOnCommand.Add(guildId);
|
||||
|
|
|
@ -27,6 +27,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
private readonly UserPunishService _punishService;
|
||||
private readonly INotifySubscriber _notifySub;
|
||||
private readonly ShardData _shardData;
|
||||
|
||||
private readonly Channel<PunishQueueItem> _punishUserQueue =
|
||||
Channel.CreateUnbounded<PunishQueueItem>(new()
|
||||
{
|
||||
|
@ -176,10 +177,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
try
|
||||
{
|
||||
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings)
|
||||
|| spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new()
|
||||
{
|
||||
ChannelId = channel.Id
|
||||
}))
|
||||
|| spamSettings.AntiSpamSettings.IgnoredChannels.Any(x => x.ChannelId == channel.Id))
|
||||
return;
|
||||
|
||||
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id,
|
||||
|
@ -275,23 +273,25 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
await using var uow = _db.GetDbContext();
|
||||
|
||||
await uow.GetTable<AntiRaidSetting>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = action,
|
||||
Seconds = seconds,
|
||||
UserThreshold = userThreshold,
|
||||
PunishDuration = minutesDuration
|
||||
}, _ => new()
|
||||
{
|
||||
Action = action,
|
||||
Seconds = seconds,
|
||||
UserThreshold = userThreshold,
|
||||
PunishDuration = minutesDuration
|
||||
}, () => new()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = action,
|
||||
Seconds = seconds,
|
||||
UserThreshold = userThreshold,
|
||||
PunishDuration = minutesDuration
|
||||
},
|
||||
_ => new()
|
||||
{
|
||||
Action = action,
|
||||
Seconds = seconds,
|
||||
UserThreshold = userThreshold,
|
||||
PunishDuration = minutesDuration
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
|
||||
|
||||
return stats;
|
||||
|
@ -364,23 +364,25 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
await using var uow = _db.GetDbContext();
|
||||
await uow.GetTable<AntiSpamSetting>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = stats.AntiSpamSettings.Action,
|
||||
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
||||
MuteTime = stats.AntiSpamSettings.MuteTime,
|
||||
RoleId = stats.AntiSpamSettings.RoleId
|
||||
}, (old) => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = stats.AntiSpamSettings.Action,
|
||||
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
||||
MuteTime = stats.AntiSpamSettings.MuteTime,
|
||||
RoleId = stats.AntiSpamSettings.RoleId
|
||||
}, () => new()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = stats.AntiSpamSettings.Action,
|
||||
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
||||
MuteTime = stats.AntiSpamSettings.MuteTime,
|
||||
RoleId = stats.AntiSpamSettings.RoleId
|
||||
},
|
||||
(old) => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = stats.AntiSpamSettings.Action,
|
||||
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
||||
MuteTime = stats.AntiSpamSettings.MuteTime,
|
||||
RoleId = stats.AntiSpamSettings.RoleId
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
@ -405,7 +407,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
if (spam.IgnoredChannels.All(x => x.ChannelId != channelId))
|
||||
{
|
||||
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
|
||||
temp.AntiSpamSettings.IgnoredChannels.Add(obj); // add to local cache
|
||||
temp.AntiSpamSettings.IgnoredChannels.Add(obj);
|
||||
|
||||
spam.IgnoredChannels.Add(obj);
|
||||
added = true;
|
||||
|
@ -417,7 +419,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
uow.Set<AntiSpamIgnore>().Remove(toRemove);
|
||||
|
||||
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
|
||||
temp.AntiSpamSettings.IgnoredChannels.Remove(toRemove); // remove from local cache
|
||||
temp.AntiSpamSettings.IgnoredChannels.RemoveAll(x => x.ChannelId == channelId);
|
||||
|
||||
added = false;
|
||||
}
|
||||
|
@ -462,22 +464,24 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
|
||||
await uow.GetTable<AntiAltSetting>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = action,
|
||||
ActionDurationMinutes = actionDurationMinutes,
|
||||
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
||||
RoleId = roleId
|
||||
}, _ => new()
|
||||
{
|
||||
Action = action,
|
||||
ActionDurationMinutes = actionDurationMinutes,
|
||||
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
||||
RoleId = roleId
|
||||
}, () => new()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
{
|
||||
GuildId = guildId,
|
||||
Action = action,
|
||||
ActionDurationMinutes = actionDurationMinutes,
|
||||
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
||||
RoleId = roleId
|
||||
},
|
||||
_ => new()
|
||||
{
|
||||
Action = action,
|
||||
ActionDurationMinutes = actionDurationMinutes,
|
||||
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
||||
RoleId = roleId
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
|
||||
_antiAltGuilds[guildId] = new(new()
|
||||
{
|
||||
|
|
|
@ -52,12 +52,12 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
|||
await using var uow = _db.GetDbContext();
|
||||
|
||||
var confs = await uow.GetTable<GuildFilterConfig>()
|
||||
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
|
||||
.LoadWith(x => x.FilterInvitesChannelIds)
|
||||
.LoadWith(x => x.FilterWordsChannelIds)
|
||||
.LoadWith(x => x.FilterLinksChannelIds)
|
||||
.LoadWith(x => x.FilteredWords)
|
||||
.ToListAsyncLinqToDB();
|
||||
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
|
||||
.LoadWith(x => x.FilterInvitesChannelIds)
|
||||
.LoadWith(x => x.FilterWordsChannelIds)
|
||||
.LoadWith(x => x.FilterLinksChannelIds)
|
||||
.LoadWith(x => x.FilteredWords)
|
||||
.ToListAsyncLinqToDB();
|
||||
|
||||
foreach (var conf in confs)
|
||||
{
|
||||
|
@ -97,7 +97,7 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
|||
await using var uow = _db.GetDbContext();
|
||||
var fc = uow.FilterConfigForId(guildId,
|
||||
set => set.Include(x => x.FilteredWords)
|
||||
.Include(x => x.FilterWordsChannelIds));
|
||||
.Include(x => x.FilterWordsChannelIds));
|
||||
|
||||
WordFilteringServers.TryRemove(guildId);
|
||||
ServerFilteredWords.TryRemove(guildId, out _);
|
||||
|
@ -140,7 +140,8 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
|||
var filteredChannelWords =
|
||||
FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
|
||||
var filteredServerWords = FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
|
||||
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
|
||||
var wordsInMessage = (usrMsg.Content + " " + usrMsg.ForwardedMessages.FirstOrDefault().Message?.Content)
|
||||
.ToLowerInvariant().Split(' ');
|
||||
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
|
||||
{
|
||||
foreach (var word in wordsInMessage)
|
||||
|
@ -183,7 +184,8 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
|||
return false;
|
||||
|
||||
if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id) || InviteFilteringServers.Contains(guild.Id))
|
||||
&& usrMsg.Content.IsDiscordInvite())
|
||||
&& (usrMsg.Content.IsDiscordInvite() ||
|
||||
usrMsg.ForwardedMessages.Any(x => x.Message?.Content.IsDiscordInvite() ?? false)))
|
||||
{
|
||||
Log.Information("User {UserName} [{UserId}] sent a filtered invite to {ChannelId} channel",
|
||||
usrMsg.Author.ToString(),
|
||||
|
@ -219,7 +221,8 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
|||
return false;
|
||||
|
||||
if ((LinkFilteringChannels.Contains(usrMsg.Channel.Id) || LinkFilteringServers.Contains(guild.Id))
|
||||
&& usrMsg.Content.TryGetUrlPath(out _))
|
||||
&& (usrMsg.Content.TryGetUrlPath(out _) ||
|
||||
usrMsg.ForwardedMessages.Any(x => x.Message?.Content.TryGetUrlPath(out _) ?? false)))
|
||||
{
|
||||
Log.Information("User {UserName} [{UserId}] sent a filtered link to {ChannelId} channel",
|
||||
usrMsg.Author.ToString(),
|
||||
|
@ -246,10 +249,10 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
|||
await using var uow = _db.GetDbContext();
|
||||
|
||||
var conf = await uow.GetTable<GuildFilterConfig>()
|
||||
.Where(fi => fi.GuildId == guildId)
|
||||
.LoadWith(x => x.FilterInvitesChannelIds)
|
||||
.LoadWith(x => x.FilterLinksChannelIds)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
.Where(fi => fi.GuildId == guildId)
|
||||
.LoadWith(x => x.FilterInvitesChannelIds)
|
||||
.LoadWith(x => x.FilterLinksChannelIds)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
return new()
|
||||
{
|
||||
|
|
|
@ -48,7 +48,10 @@ public partial class Searches
|
|||
return;
|
||||
}
|
||||
|
||||
await Response().Confirm(strs.stream_removed(Format.Bold(fs.Username), fs.Type)).SendAsync();
|
||||
await Response()
|
||||
.Confirm(strs.stream_removed(
|
||||
Format.Bold(string.IsNullOrWhiteSpace(fs.PrettyName) ? fs.Username : fs.PrettyName),
|
||||
fs.Type)).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
|
|
|
@ -63,20 +63,19 @@ public class NotifChecker
|
|||
.ToDictionary(x => x.Key.Name, x => x.Value));
|
||||
|
||||
var newStreamData = await oldStreamDataDict
|
||||
.Select(x =>
|
||||
.Select(async x =>
|
||||
{
|
||||
// get all stream data for the streams of this type
|
||||
if (_streamProviders.TryGetValue(x.Key,
|
||||
out var provider))
|
||||
{
|
||||
return provider.GetStreamDataAsync(x.Value
|
||||
return await 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>>(
|
||||
new List<StreamData>());
|
||||
return [];
|
||||
})
|
||||
.WhenAll();
|
||||
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
using System.Net;
|
||||
using EllieBot.Db.Models;
|
||||
using EllieBot.Services;
|
||||
using EllieBot.Db.Models;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Xml.Linq;
|
||||
using AngleSharp.Browser;
|
||||
|
||||
namespace EllieBot.Modules.Searches.Common.StreamNotifications.Providers;
|
||||
|
||||
|
@ -113,56 +108,65 @@ public sealed partial class YouTubeProvider : Provider
|
|||
/// <returns><see cref="StreamData"/> of the channel. Null if none found</returns>
|
||||
public override async Task<StreamData?> GetStreamDataAsync(string channelId)
|
||||
{
|
||||
var instances = _scs.Data.InvidiousInstances;
|
||||
|
||||
if (instances is not { Count: > 0 })
|
||||
return null;
|
||||
|
||||
var invInstance = instances[_rng.Next(0, instances.Count)];
|
||||
var client = _httpFactory.CreateClient();
|
||||
client.BaseAddress = new Uri(invInstance);
|
||||
|
||||
var channel = await client.GetFromJsonAsync<InvidiousChannelResponse>($"/api/v1/channels/{channelId}");
|
||||
if (channel is null)
|
||||
return null;
|
||||
|
||||
var response =
|
||||
await client.GetFromJsonAsync<InvChannelStreamsResponse>($"/api/v1/channels/{channelId}/streams");
|
||||
if (response is null)
|
||||
return null;
|
||||
|
||||
var vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming && x.LengthSeconds == 0);
|
||||
var isLive = false;
|
||||
if (vid is null)
|
||||
try
|
||||
{
|
||||
vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming);
|
||||
var instances = _scs.Data.InvidiousInstances;
|
||||
|
||||
if (instances is not { Count: > 0 })
|
||||
return null;
|
||||
|
||||
var invInstance = instances[_rng.Next(0, instances.Count)];
|
||||
var client = _httpFactory.CreateClient();
|
||||
client.BaseAddress = new Uri(invInstance);
|
||||
|
||||
var channel = await client.GetFromJsonAsync<InvidiousChannelResponse>($"/api/v1/channels/{channelId}");
|
||||
if (channel is null)
|
||||
return null;
|
||||
|
||||
var response =
|
||||
await client.GetFromJsonAsync<InvChannelStreamsResponse>($"/api/v1/channels/{channelId}/streams");
|
||||
if (response is null)
|
||||
return null;
|
||||
|
||||
var vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming && x.LengthSeconds == 0);
|
||||
var isLive = false;
|
||||
if (vid is null)
|
||||
{
|
||||
vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming);
|
||||
}
|
||||
else
|
||||
{
|
||||
isLive = true;
|
||||
}
|
||||
|
||||
if (vid is null)
|
||||
return null;
|
||||
|
||||
var avatarUrl = channel?.AuthorThumbnails?.Select(x => x.Url).LastOrDefault();
|
||||
|
||||
return new StreamData()
|
||||
{
|
||||
Game = "Livestream",
|
||||
Name = vid.Author,
|
||||
Preview = vid.Thumbnails
|
||||
.Skip(1)
|
||||
.Select(x => "https://i.ytimg.com/" + x.Url)
|
||||
.FirstOrDefault(),
|
||||
Title = vid.Title,
|
||||
Viewers = vid.ViewCount,
|
||||
AvatarUrl = avatarUrl,
|
||||
IsLive = isLive,
|
||||
StreamType = FollowedStream.FType.Youtube,
|
||||
StreamUrl = "https://youtube.com/watch?v=" + vid.VideoId,
|
||||
UniqueName = vid.AuthorId,
|
||||
};
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
isLive = true;
|
||||
}
|
||||
|
||||
if (vid is null)
|
||||
Log.Warning(ex, "Unable to get stream data for a youtube channel {ChannelId}", channelId);
|
||||
_failingStreams.TryAdd(channelId, DateTime.UtcNow);
|
||||
return null;
|
||||
|
||||
var avatarUrl = channel?.AuthorThumbnails?.Select(x => x.Url).LastOrDefault();
|
||||
|
||||
return new StreamData()
|
||||
{
|
||||
Game = "Livestream",
|
||||
Name = vid.Author,
|
||||
Preview = vid.Thumbnails
|
||||
.Skip(1)
|
||||
.Select(x => "https://i.ytimg.com/" + x.Url)
|
||||
.FirstOrDefault(),
|
||||
Title = vid.Title,
|
||||
Viewers = vid.ViewCount,
|
||||
AvatarUrl = avatarUrl,
|
||||
IsLive = isLive,
|
||||
StreamType = FollowedStream.FType.Youtube,
|
||||
StreamUrl = "https://youtube.com/watch?v=" + vid.VideoId,
|
||||
UniqueName = vid.AuthorId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -17,7 +17,7 @@ public class LiveChannelService(
|
|||
IReplacementService repSvc,
|
||||
ShardData shardData) : IReadyExecutor, IEService
|
||||
{
|
||||
public const int MAX_LIVECHANNELS = 5;
|
||||
public const int MAX_LIVECHANNELS = 1;
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, LiveChannelConfig>> _liveChannels = new();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using EllieBot.Common.ModuleBehaviors;
|
||||
using EllieBot.Db.Models;
|
||||
|
@ -36,10 +37,10 @@ public class VerboseErrorsService : IReadyExecutor, IEService
|
|||
try
|
||||
{
|
||||
var embed = _hs.GetCommandHelp(cmd, channel.Guild)
|
||||
.WithTitle("Command Error")
|
||||
.WithDescription(reason)
|
||||
.WithFooter("Admin may disable verbose errors via `.ve` command")
|
||||
.WithErrorColor();
|
||||
.WithTitle("Command Error")
|
||||
.WithDescription(reason)
|
||||
.WithFooter("Admin may disable verbose errors via `.ve` command")
|
||||
.WithErrorColor();
|
||||
|
||||
await _sender.Response(channel).Embed(embed).SendAsync();
|
||||
}
|
||||
|
@ -50,16 +51,29 @@ public class VerboseErrorsService : IReadyExecutor, IEService
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles or sets verbose errors for the specified guild.
|
||||
/// </summary>
|
||||
/// <param name="guildId">The ID of the guild to toggle verbose errors for.</param>
|
||||
/// <param name="maybeEnabled">If specified, sets to this value; otherwise toggles current value.</param>
|
||||
/// <returns>Returns the new state of verbose errors (true = enabled, false = disabled).</returns>
|
||||
public async Task<bool> ToggleVerboseErrors(ulong guildId, bool? maybeEnabled = null)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var isEnabled = ctx.GetTable<GuildConfig>()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.Select(x => x.VerboseErrors)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (isEnabled) // This doesn't need to be duplicated inside the using block
|
||||
var current = await ctx.GetTable<GuildConfig>()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.Select(x => x.VerboseErrors)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var newState = maybeEnabled ?? !current;
|
||||
|
||||
await ctx.GetTable<GuildConfig>()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.Set(x => x.VerboseErrors, newState)
|
||||
.UpdateAsync();
|
||||
|
||||
if (newState)
|
||||
{
|
||||
_guildsDisabled.TryRemove(guildId);
|
||||
}
|
||||
|
@ -68,15 +82,15 @@ public class VerboseErrorsService : IReadyExecutor, IEService
|
|||
_guildsDisabled.Add(guildId);
|
||||
}
|
||||
|
||||
return isEnabled;
|
||||
return newState;
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
var disabledOn = ctx.GetTable<GuildConfig>()
|
||||
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && !x.VerboseErrors)
|
||||
.Select(x => x.GuildId);
|
||||
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && !x.VerboseErrors)
|
||||
.Select(x => x.GuildId);
|
||||
|
||||
foreach (var guildId in disabledOn)
|
||||
_guildsDisabled.Add(guildId);
|
||||
|
|
Loading…
Add table
Reference in a new issue