Compare commits

...

7 commits
5.1.11 ... v5

Author SHA1 Message Date
8176cdbf96
Version upped to 5.1.14, updated CHANGELOG.md 2024-10-05 14:16:57 +13:00
113dc3748a
improved .xplb -c, it will now correctly work only on users who are still in the server, isntead of only top 1k
Fixed marmalade error on bot startup
2024-10-05 14:14:33 +13:00
5c72c6562f
Version upped to 5.1.13, updated CHANGELOG.md
Fixed seq comment in creds
2024-10-05 13:29:09 +13:00
dd939ce55a
Grpc api will no longer start unless it's enabled in creds 2024-10-05 13:24:40 +13:00
1a52085340
Forgot a entry in CHANGELOG.md 2024-10-05 12:53:07 +13:00
487c7865cb
Fixed greet/bye messages showing wrong message in the wrong server sometimes
Fixed the check for updates service
Version upped to 5.1.12. Updated CHANGELOG.md
2024-10-05 11:44:44 +13:00
3ba1d06fd0
expressions will no longer cause exceptions if the bot doesn't have perms to write in the target channel
Cleaned up expr code a little bit
2024-10-05 11:17:12 +13:00
44 changed files with 263 additions and 213 deletions

View file

@ -2,6 +2,34 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
## [5.1.14] - 03.10.2024
## Changed
- Improved `.xplb -c`, it will now correctly only show users who are still in the server with no count limit
## Fixed
- Fixed marmalade load error on startup
## [5.1.13] - 03.10.2024
### Fixed
- Grpc api server will no longer start unless enabled in creds
- Seq comment in creds fixed
## [5.1.12] - 03.10.2024
### Added
- Added support for `seq` for logging. If you fill in seq url and apiKey in creds.yml, bot will sends logs to it
### Fixed
- Fixed the Check for updates service not using the right URL and spitting an error in the console.
- Fixed another bug in `.greet` / `.bye` system, which caused it to show wrong message on a wrong server occasionally
## [5.1.11] - 03.10.2024 ## [5.1.11] - 03.10.2024
### Added ### Added

View file

@ -25,7 +25,7 @@ public sealed class Bot : IBot
public bool IsReady { get; private set; } public bool IsReady { get; private set; }
public int ShardId { get; set; } public int ShardId { get; set; }
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly CommandService _commandService; private readonly CommandService _commandService;
private readonly DbService _db; private readonly DbService _db;
@ -42,6 +42,9 @@ public sealed class Bot : IBot
_credsProvider = new BotCredsProvider(totalShards, credPath); _credsProvider = new BotCredsProvider(totalShards, credPath);
_creds = _credsProvider.GetCreds(); _creds = _credsProvider.GetCreds();
LogSetup.SetupLogger(shardId, _creds);
Log.Information("Pid: {ProcessId}", Environment.ProcessId);
_db = new EllieDbService(_credsProvider); _db = new EllieDbService(_credsProvider);
var messageCacheSize = var messageCacheSize =
@ -115,7 +118,7 @@ public sealed class Bot : IBot
// svcs.Components.Remove<IPlanner, Planner>(); // svcs.Components.Remove<IPlanner, Planner>();
// svcs.Components.Add<IPlanner, RemovablePlanner>(); // svcs.Components.Add<IPlanner, RemovablePlanner>();
svcs.AddSingleton<IBotCredentials>(_ => _credsProvider.GetCreds()); svcs.AddSingleton<IBotCreds>(_ => _credsProvider.GetCreds());
svcs.AddSingleton<DbService, DbService>(_db); svcs.AddSingleton<DbService, DbService>(_db);
svcs.AddSingleton<IBotCredsProvider>(_credsProvider); svcs.AddSingleton<IBotCredsProvider>(_credsProvider);
svcs.AddSingleton<DiscordSocketClient>(Client); svcs.AddSingleton<DiscordSocketClient>(Client);

View file

@ -26,17 +26,6 @@ public static class UserXpExtensions
return usr; return usr;
} }
public static async Task<IReadOnlyCollection<UserXpStats>> GetUsersFor(
this DbSet<UserXpStats> xps,
ulong guildId,
int page)
=> await xps.ToLinqToDBTable()
.Where(x => x.GuildId == guildId)
.OrderByDescending(x => x.Xp + x.AwardedXp)
.Skip(page * 9)
.Take(9)
.ToArrayAsyncLinqToDB();
public static async Task<List<UserXpStats>> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count) public static async Task<List<UserXpStats>> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
=> await xps.ToLinqToDBTable() => await xps.ToLinqToDBTable()
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)

View file

@ -4,7 +4,7 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings> <ImplicitUsings>true</ImplicitUsings>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Version>5.1.11</Version> <Version>5.1.14</Version>
<!-- Output/build --> <!-- Output/build -->
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory> <RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>

View file

@ -208,11 +208,11 @@ public class GreetService : IEService, IReadyExecutor
return Task.CompletedTask; return Task.CompletedTask;
} }
private TypedKey<GreetSettings?> GreetSettingsKey(GreetType type) private TypedKey<GreetSettings?> GreetSettingsKey(ulong gid, GreetType type)
=> new($"greet_settings:{type}"); => new($"greet_settings:{gid}:{type}");
public async Task<GreetSettings?> GetGreetSettingsAsync(ulong gid, GreetType type) public async Task<GreetSettings?> GetGreetSettingsAsync(ulong gid, GreetType type)
=> await _cache.GetOrAddAsync<GreetSettings?>(GreetSettingsKey(type), => await _cache.GetOrAddAsync<GreetSettings?>(GreetSettingsKey(gid, type),
() => InternalGetGreetSettingsAsync(gid, type), () => InternalGetGreetSettingsAsync(gid, type),
TimeSpan.FromSeconds(3)); TimeSpan.FromSeconds(3));

View file

@ -13,7 +13,7 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
{ {
private readonly DbService _db; private readonly DbService _db;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache; private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache;
private readonly object _cacheLock = new(); private readonly object _cacheLock = new();
@ -24,7 +24,7 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
DiscordSocketClient client, DiscordSocketClient client,
IPatronageService ps, IPatronageService ps,
DbService db, DbService db,
IBotCredentials creds) IBotCreds creds)
{ {
_db = db; _db = db;
_client = client; _client = client;

View file

@ -9,13 +9,13 @@ namespace EllieBot.Modules.Administration;
public sealed class StickyRolesService : IEService, IReadyExecutor public sealed class StickyRolesService : IEService, IReadyExecutor
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly DbService _db; private readonly DbService _db;
private HashSet<ulong> _stickyRoles = new(); private HashSet<ulong> _stickyRoles = new();
public StickyRolesService( public StickyRolesService(
DiscordSocketClient client, DiscordSocketClient client,
IBotCredentials creds, IBotCreds creds,
DbService db) DbService db)
{ {
_client = client; _client = client;

View file

@ -19,7 +19,7 @@ public sealed class CheckForUpdatesService : IEService, IReadyExecutor
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private const string RELEASES_URL = "https://toastielab.dev/Emotions-stuff/elliebot/releases"; private const string RELEASES_URL = "https://toastielab.dev/api/v1/repos/Emotions-stuff/elliebot/releases";
public CheckForUpdatesService( public CheckForUpdatesService(
BotConfigService bcs, BotConfigService bcs,

View file

@ -15,7 +15,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, IEService
private readonly IBotStrings _strings; private readonly IBotStrings _strings;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private ImmutableDictionary<ulong, IDMChannel> ownerChannels = private ImmutableDictionary<ulong, IDMChannel> ownerChannels =
new Dictionary<ulong, IDMChannel>().ToImmutableDictionary(); new Dictionary<ulong, IDMChannel>().ToImmutableDictionary();
@ -36,7 +36,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, IEService
CommandHandler cmdHandler, CommandHandler cmdHandler,
DbService db, DbService db,
IBotStrings strings, IBotStrings strings,
IBotCredentials creds, IBotCreds creds,
IHttpClientFactory factory, IHttpClientFactory factory,
BotConfigService bss, BotConfigService bss,
IPubSub pubSub, IPubSub pubSub,

View file

@ -6,49 +6,6 @@ namespace EllieBot.Modules.EllieExpressions;
public static class EllieExpressionExtensions public static class EllieExpressionExtensions
{ {
private static string ResolveTriggerString(this string str, DiscordSocketClient client)
=> str.Replace("%bot.mention%", client.CurrentUser.Mention, StringComparison.Ordinal);
public static async Task<IUserMessage> Send(
this EllieExpression cr,
IUserMessage ctx,
IReplacementService repSvc,
DiscordSocketClient client,
IMessageSenderService sender)
{
var channel = cr.DmResponse ? await ctx.Author.CreateDMChannelAsync() : ctx.Channel;
var trigger = cr.Trigger.ResolveTriggerString(client);
var substringIndex = trigger.Length;
if (cr.ContainsAnywhere)
{
var pos = ctx.Content.AsSpan().GetWordPosition(trigger);
if (pos == WordPosition.Start)
substringIndex += 1;
else if (pos == WordPosition.End)
substringIndex = ctx.Content.Length;
else if (pos == WordPosition.Middle)
substringIndex += ctx.Content.IndexOf(trigger, StringComparison.InvariantCulture);
}
var canMentionEveryone = (ctx.Author as IGuildUser)?.GuildPermissions.MentionEveryone ?? true;
var repCtx = new ReplacementContext(client: client,
guild: (ctx.Channel as ITextChannel)?.Guild as SocketGuild,
channel: ctx.Channel,
user: ctx.Author
)
.WithOverride("%target%",
() => canMentionEveryone
? ctx.Content[substringIndex..].Trim()
: ctx.Content[substringIndex..].Trim().SanitizeMentions(true));
var text = SmartText.CreateFrom(cr.Response);
text = await repSvc.ReplaceAsync(text, repCtx);
return await sender.Response(channel).Text(text).Sanitize(false).SendAsync();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static WordPosition GetWordPosition(this ReadOnlySpan<char> str, in ReadOnlySpan<char> word) public static WordPosition GetWordPosition(this ReadOnlySpan<char> str, in ReadOnlySpan<char> word)
{ {

View file

@ -11,10 +11,10 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
All All
} }
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly IHttpClientFactory _clientFactory; private readonly IHttpClientFactory _clientFactory;
public EllieExpressions(IBotCredentials creds, IHttpClientFactory clientFactory) public EllieExpressions(IBotCreds creds, IHttpClientFactory clientFactory)
{ {
_creds = creds; _creds = creds;
_clientFactory = clientFactory; _clientFactory = clientFactory;

View file

@ -249,46 +249,54 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
try try
{ {
if (guild is SocketGuild sg) if (guild is not SocketGuild sg)
return false;
var result = await _permChecker.CheckPermsAsync(
guild,
msg.Channel,
msg.Author,
"ACTUALEXPRESSIONS",
expr.Trigger
);
if (!result.IsAllowed)
{ {
var result = await _permChecker.CheckPermsAsync( var cache = _pc.GetCacheFor(guild.Id);
guild, if (cache.Verbose)
msg.Channel,
msg.Author,
"ACTUALEXPRESSIONS",
expr.Trigger
);
if (!result.IsAllowed)
{ {
var cache = _pc.GetCacheFor(guild.Id); if (result.TryPickT3(out var disallowed, out _))
if (cache.Verbose)
{ {
if (result.TryPickT3(out var disallowed, out _)) var permissionMessage = _strings.GetText(strs.perm_prevent(disallowed.PermIndex + 1,
Format.Bold(disallowed.PermText)),
sg.Id);
try
{
await _sender.Response(msg.Channel)
.Error(permissionMessage)
.SendAsync();
}
catch
{ {
var permissionMessage = _strings.GetText(strs.perm_prevent(disallowed.PermIndex + 1,
Format.Bold(disallowed.PermText)),
sg.Id);
try
{
await _sender.Response(msg.Channel)
.Error(permissionMessage)
.SendAsync();
}
catch
{
}
Log.Information("{PermissionMessage}", permissionMessage);
} }
}
return true; Log.Information("{PermissionMessage}", permissionMessage);
}
} }
return true;
} }
var sentMsg = await expr.Send(msg, _repSvc, _client, _sender); var cu = sg.CurrentUser;
var channel = expr.DmResponse ? await msg.Author.CreateDMChannelAsync() : msg.Channel;
// have no perms to speak in that channel
if (channel is ITextChannel tc && !cu.GetPermissions(tc).SendMessages)
return false;
var sentMsg = await Send(expr, msg, channel);
var reactions = expr.GetReactions(); var reactions = expr.GetReactions();
foreach (var reaction in reactions) foreach (var reaction in reactions)
@ -336,6 +344,47 @@ public sealed class EllieExpressionsService : IExecOnMessage, IReadyExecutor
return false; return false;
} }
public string ResolveTriggerString(string str)
=> str.Replace("%bot.mention%", _client.CurrentUser.Mention, StringComparison.Ordinal);
public async Task<IUserMessage> Send(
EllieExpression cr,
IUserMessage ctx,
IMessageChannel channel
)
{
var trigger = ResolveTriggerString(cr.Trigger);
var substringIndex = trigger.Length;
if (cr.ContainsAnywhere)
{
var pos = ctx.Content.AsSpan().GetWordPosition(trigger);
if (pos == WordPosition.Start)
substringIndex += 1;
else if (pos == WordPosition.End)
substringIndex = ctx.Content.Length;
else if (pos == WordPosition.Middle)
substringIndex += ctx.Content.IndexOf(trigger, StringComparison.InvariantCulture);
}
var canMentionEveryone = (ctx.Author as IGuildUser)?.GuildPermissions.MentionEveryone ?? true;
var repCtx = new ReplacementContext(client: _client,
guild: (ctx.Channel as ITextChannel)?.Guild as SocketGuild,
channel: ctx.Channel,
user: ctx.Author
)
.WithOverride("%target%",
() => canMentionEveryone
? ctx.Content[substringIndex..].Trim()
: ctx.Content[substringIndex..].Trim().SanitizeMentions(true));
var text = SmartText.CreateFrom(cr.Response);
text = await _repSvc.ReplaceAsync(text, repCtx);
return await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
}
public async Task ResetExprReactions(ulong? maybeGuildId, int id) public async Task ResetExprReactions(ulong? maybeGuildId, int id)
{ {
EllieExpression expr; EllieExpression expr;

View file

@ -14,13 +14,13 @@ public class VoteModel
public class VoteRewardService : IEService, IReadyExecutor public class VoteRewardService : IEService, IReadyExecutor
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly ICurrencyService _currencyService; private readonly ICurrencyService _currencyService;
private readonly GamblingConfigService _gamb; private readonly GamblingConfigService _gamb;
public VoteRewardService( public VoteRewardService(
DiscordSocketClient client, DiscordSocketClient client,
IBotCredentials creds, IBotCreds creds,
ICurrencyService currencyService, ICurrencyService currencyService,
GamblingConfigService gamb) GamblingConfigService gamb)
{ {

View file

@ -15,7 +15,7 @@ public class WaifuService : IEService, IReadyExecutor
private readonly ICurrencyService _cs; private readonly ICurrencyService _cs;
private readonly IBotCache _cache; private readonly IBotCache _cache;
private readonly GamblingConfigService _gss; private readonly GamblingConfigService _gss;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
public WaifuService( public WaifuService(
@ -23,7 +23,7 @@ public class WaifuService : IEService, IReadyExecutor
ICurrencyService cs, ICurrencyService cs,
IBotCache cache, IBotCache cache,
GamblingConfigService gss, GamblingConfigService gss,
IBotCredentials creds, IBotCreds creds,
DiscordSocketClient client) DiscordSocketClient client)
{ {
_db = db; _db = db;

View file

@ -19,7 +19,7 @@ public class ChatterBotService : IExecOnMessage
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IPermissionChecker _perms; private readonly IPermissionChecker _perms;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly GamesConfigService _gcs; private readonly GamesConfigService _gcs;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
@ -32,7 +32,7 @@ public class ChatterBotService : IExecOnMessage
IBot bot, IBot bot,
IPatronageService ps, IPatronageService ps,
IHttpClientFactory factory, IHttpClientFactory factory,
IBotCredentials creds, IBotCreds creds,
GamesConfigService gcs, GamesConfigService gcs,
IMessageSenderService sender, IMessageSenderService sender,
DbService db) DbService db)

View file

@ -12,9 +12,9 @@ public sealed partial class Music
{ {
private static readonly SemaphoreSlim _playlistLock = new(1, 1); private static readonly SemaphoreSlim _playlistLock = new(1, 1);
private readonly DbService _db; private readonly DbService _db;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
public PlaylistCommands(DbService db, IBotCredentials creds) public PlaylistCommands(DbService db, IBotCreds creds)
{ {
_db = db; _db = db;
_creds = creds; _creds = creds;

View file

@ -16,11 +16,11 @@ public class CryptoService : IEService
{ {
private readonly IBotCache _cache; private readonly IBotCache _cache;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly SemaphoreSlim _getCryptoLock = new(1, 1); private readonly SemaphoreSlim _getCryptoLock = new(1, 1);
public CryptoService(IBotCache cache, IHttpClientFactory httpFactory, IBotCredentials creds) public CryptoService(IBotCache cache, IHttpClientFactory httpFactory, IBotCreds creds)
{ {
_cache = cache; _cache = cache;
_httpFactory = httpFactory; _httpFactory = httpFactory;

View file

@ -9,10 +9,10 @@ public partial class Searches
[Group] [Group]
public partial class OsuCommands : EllieModule<OsuService> public partial class OsuCommands : EllieModule<OsuService>
{ {
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
public OsuCommands(IBotCredentials creds, IHttpClientFactory factory) public OsuCommands(IBotCreds creds, IHttpClientFactory factory)
{ {
_creds = creds; _creds = creds;
_httpFactory = factory; _httpFactory = factory;

View file

@ -7,9 +7,9 @@ namespace EllieBot.Modules.Searches;
public sealed class OsuService : IEService public sealed class OsuService : IEService
{ {
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
public OsuService(IHttpClientFactory httpFactory, IBotCredentials creds) public OsuService(IHttpClientFactory httpFactory, IBotCreds creds)
{ {
_httpFactory = httpFactory; _httpFactory = httpFactory;
_creds = creds; _creds = creds;

View file

@ -13,14 +13,14 @@ namespace EllieBot.Modules.Searches;
public partial class Searches : EllieModule<SearchesService> public partial class Searches : EllieModule<SearchesService>
{ {
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly IGoogleApiService _google; private readonly IGoogleApiService _google;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly IMemoryCache _cache; private readonly IMemoryCache _cache;
private readonly ITimezoneService _tzSvc; private readonly ITimezoneService _tzSvc;
public Searches( public Searches(
IBotCredentials creds, IBotCreds creds,
IGoogleApiService google, IGoogleApiService google,
IHttpClientFactory factory, IHttpClientFactory factory,
IMemoryCache cache, IMemoryCache cache,

View file

@ -11,7 +11,7 @@ public sealed class GiveawayService : IEService, IReadyExecutor
public static string GiveawayEmoji = "🎉"; public static string GiveawayEmoji = "🎉";
private readonly DbService _db; private readonly DbService _db;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly IBotStrings _strings; private readonly IBotStrings _strings;
@ -20,7 +20,7 @@ public sealed class GiveawayService : IEService, IReadyExecutor
private SortedSet<GiveawayModel> _giveawayCache = new SortedSet<GiveawayModel>(); private SortedSet<GiveawayModel> _giveawayCache = new SortedSet<GiveawayModel>();
private readonly EllieRandom _rng; private readonly EllieRandom _rng;
public GiveawayService(DbService db, IBotCredentials creds, DiscordSocketClient client, public GiveawayService(DbService db, IBotCreds creds, DiscordSocketClient client,
IMessageSenderService sender, IBotStrings strings, ILocalization localization, IMemoryCache cache) IMessageSenderService sender, IBotStrings strings, ILocalization localization, IMemoryCache cache)
{ {
_db = db; _db = db;

View file

@ -17,14 +17,14 @@ public class RemindService : IEService, IReadyExecutor, IRemindService
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly DbService _db; private readonly DbService _db;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly CultureInfo _culture; private readonly CultureInfo _culture;
public RemindService( public RemindService(
DiscordSocketClient client, DiscordSocketClient client,
DbService db, DbService db,
IBotCredentials creds, IBotCreds creds,
IMessageSenderService sender) IMessageSenderService sender)
{ {
_client = client; _client = client;

View file

@ -12,7 +12,7 @@ public sealed class RepeaterService : IReadyExecutor, IEService
private readonly DbService _db; private readonly DbService _db;
private readonly IReplacementService _repSvc; private readonly IReplacementService _repSvc;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly LinkedList<RunningRepeater> _repeaterQueue; private readonly LinkedList<RunningRepeater> _repeaterQueue;
private readonly ConcurrentHashSet<int> _noRedundant; private readonly ConcurrentHashSet<int> _noRedundant;
@ -25,7 +25,7 @@ public sealed class RepeaterService : IReadyExecutor, IEService
DiscordSocketClient client, DiscordSocketClient client,
DbService db, DbService db,
IReplacementService repSvc, IReplacementService repSvc,
IBotCredentials creds, IBotCreds creds,
IMessageSenderService sender) IMessageSenderService sender)
{ {
_db = db; _db = db;

View file

@ -34,7 +34,7 @@ public partial class Utility : EllieModule
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly ICoordinator _coord; private readonly ICoordinator _coord;
private readonly IStatsService _stats; private readonly IStatsService _stats;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly DownloadTracker _tracker; private readonly DownloadTracker _tracker;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly VerboseErrorsService _veService; private readonly VerboseErrorsService _veService;
@ -45,7 +45,7 @@ public partial class Utility : EllieModule
DiscordSocketClient client, DiscordSocketClient client,
ICoordinator coord, ICoordinator coord,
IStatsService stats, IStatsService stats,
IBotCredentials creds, IBotCreds creds,
DownloadTracker tracker, DownloadTracker tracker,
IHttpClientFactory httpFactory, IHttpClientFactory httpFactory,
VerboseErrorsService veService, VerboseErrorsService veService,

View file

@ -183,27 +183,26 @@ public partial class Xp : EllieModule<XpService>
await ctx.Channel.TriggerTypingAsync(); await ctx.Channel.TriggerTypingAsync();
var socketGuild = (SocketGuild)ctx.Guild;
var allCleanUsers = new List<UserXpStats>(); async Task<IReadOnlyCollection<UserXpStats>> GetPageItems(int curPage)
if (opts.Clean)
{ {
await ctx.Channel.TriggerTypingAsync(); var socketGuild = (SocketGuild)ctx.Guild;
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild); if (opts.Clean)
{
await ctx.Channel.TriggerTypingAsync();
await _tracker.EnsureUsersDownloadedAsync(ctx.Guild);
allCleanUsers = (await _service.GetTopUserXps(ctx.Guild.Id, 1000)) return await _service.GetTopUserXps(ctx.Guild.Id,
.Where(user => socketGuild.GetUser(user.UserId) is not null) socketGuild.Users.Select(x => x.Id).ToList(),
.ToList(); curPage);
}
return await _service.GetUserXps(ctx.Guild.Id, curPage);
} }
var res = opts.Clean await Response()
? Response()
.Paginated() .Paginated()
.Items(allCleanUsers) .PageItems(GetPageItems)
: Response()
.Paginated()
.PageItems((curPage) => _service.GetUserXps(ctx.Guild.Id, curPage));
await res
.PageSize(9) .PageSize(9)
.CurrentPage(page) .CurrentPage(page)
.Page((users, curPage) => .Page((users, curPage) =>

View file

@ -12,6 +12,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using System.Threading.Channels; using System.Threading.Channels;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using LinqToDB.Tools;
using EllieBot.Modules.Patronage; using EllieBot.Modules.Patronage;
using Color = SixLabors.ImageSharp.Color; using Color = SixLabors.ImageSharp.Color;
using Exception = System.Exception; using Exception = System.Exception;
@ -25,7 +26,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
private readonly IImageCache _images; private readonly IImageCache _images;
private readonly IBotStrings _strings; private readonly IBotStrings _strings;
private readonly FontProvider _fonts; private readonly FontProvider _fonts;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly ICurrencyService _cs; private readonly ICurrencyService _cs;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
private readonly XpConfigService _xpConfig; private readonly XpConfigService _xpConfig;
@ -55,7 +56,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
IImageCache images, IImageCache images,
IBotCache c, IBotCache c,
FontProvider fonts, FontProvider fonts,
IBotCredentials creds, IBotCreds creds,
ICurrencyService cs, ICurrencyService cs,
IHttpClientFactory http, IHttpClientFactory http,
XpConfigService xpConfig, XpConfigService xpConfig,
@ -191,9 +192,9 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
var items = await ctx.Set<DiscordUser>() var items = await ctx.Set<DiscordUser>()
.Where(x => group.Contains(x.UserId)) .Where(x => group.Contains(x.UserId))
.UpdateWithOutputAsync(old => new() .UpdateWithOutputAsync(old => new()
{ {
TotalXp = old.TotalXp + group.Key TotalXp = old.TotalXp + group.Key
}, },
(_, n) => n); (_, n) => n);
await ctx.Set<ClubInfo>() await ctx.Set<ClubInfo>()
@ -216,9 +217,9 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)
.Where(x => group.Contains(x.UserId)) .Where(x => group.Contains(x.UserId))
.UpdateWithOutputAsync(old => new() .UpdateWithOutputAsync(old => new()
{ {
Xp = old.Xp + group.Key Xp = old.Xp + group.Key
}, },
(_, n) => n); (_, n) => n);
gxps.AddRange(items); gxps.AddRange(items);
@ -230,14 +231,14 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
.Set<UserXpStats>() .Set<UserXpStats>()
.ToLinqToDBTable() .ToLinqToDBTable()
.InsertOrUpdateAsync(() => new UserXpStats() .InsertOrUpdateAsync(() => new UserXpStats()
{ {
UserId = userId, UserId = userId,
GuildId = guildId, GuildId = guildId,
Xp = group.Key, Xp = group.Key,
DateAdded = DateTime.UtcNow, DateAdded = DateTime.UtcNow,
AwardedXp = 0, AwardedXp = 0,
NotifyOnLevelUp = XpNotificationLocation.None NotifyOnLevelUp = XpNotificationLocation.None
}, },
_ => new() _ => new()
{ {
}, },
@ -566,13 +567,24 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
public async Task<IReadOnlyCollection<UserXpStats>> GetUserXps(ulong guildId, int page) public async Task<IReadOnlyCollection<UserXpStats>> GetUserXps(ulong guildId, int page)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
return await uow.Set<UserXpStats>().GetUsersFor(guildId, page); return await uow
.UserXpStats
.Where(x => x.GuildId == guildId)
.OrderByDescending(x => x.Xp + x.AwardedXp)
.Skip(page * 9)
.Take(9)
.ToArrayAsyncLinqToDB();
} }
public async Task<IReadOnlyCollection<UserXpStats>> GetTopUserXps(ulong guildId, int count) public async Task<IReadOnlyCollection<UserXpStats>> GetTopUserXps(ulong guildId, List<ulong> users, int curPage)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
return await uow.Set<UserXpStats>().GetTopUserXps(guildId, count); return await uow.Set<UserXpStats>()
.Where(x => x.GuildId == guildId && x.UserId.In(users))
.OrderByDescending(x => x.Xp + x.AwardedXp)
.Skip(curPage * 9)
.Take(9)
.ToArrayAsyncLinqToDB();
} }
public Task<IReadOnlyCollection<DiscordUser>> GetUserXps(int page, int perPage = 9) public Task<IReadOnlyCollection<DiscordUser>> GetUserXps(int page, int perPage = 9)
@ -1011,12 +1023,12 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
img.Mutate(x => img.Mutate(x =>
{ {
x.DrawText(new RichTextOptions(usernameFont) x.DrawText(new RichTextOptions(usernameFont)
{ {
HorizontalAlignment = HorizontalAlignment.Left, HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
FallbackFontFamilies = _fonts.FallBackFonts, FallbackFontFamilies = _fonts.FallBackFonts,
Origin = new(template.User.Name.Pos.X, template.User.Name.Pos.Y + 8) Origin = new(template.User.Name.Pos.X, template.User.Name.Pos.Y + 8)
}, },
"@" + username, "@" + username,
Brushes.Solid(template.User.Name.Color), Brushes.Solid(template.User.Name.Color),
outlinePen); outlinePen);
@ -1032,12 +1044,12 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
var clubFont = _fonts.NotoSans.CreateFont(template.Club.Name.FontSize, FontStyle.Regular); var clubFont = _fonts.NotoSans.CreateFont(template.Club.Name.FontSize, FontStyle.Regular);
img.Mutate(x => x.DrawText(new RichTextOptions(clubFont) img.Mutate(x => x.DrawText(new RichTextOptions(clubFont)
{ {
HorizontalAlignment = HorizontalAlignment.Right, HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Top, VerticalAlignment = VerticalAlignment.Top,
FallbackFontFamilies = _fonts.FallBackFonts, FallbackFontFamilies = _fonts.FallBackFonts,
Origin = new(template.Club.Name.Pos.X + 50, template.Club.Name.Pos.Y - 8) Origin = new(template.Club.Name.Pos.X + 50, template.Club.Name.Pos.Y - 8)
}, },
clubName, clubName,
Brushes.Solid(template.Club.Name.Color), Brushes.Solid(template.Club.Name.Color),
outlinePen)); outlinePen));
@ -1249,9 +1261,9 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
if (template.Club.Icon.Show) if (template.Club.Icon.Show)
await DrawClubImage(img, stats); await DrawClubImage(img, stats);
// #if GLOBAL_ELLIE // #if GLOBAL_ELLIE
await DrawFrame(img, stats.User.UserId); await DrawFrame(img, stats.User.UserId);
// #endif // #endif
var outputSize = template.OutputSize; var outputSize = template.OutputSize;
if (outputSize.X != img.Width || outputSize.Y != img.Height) if (outputSize.X != img.Width || outputSize.Y != img.Height)
@ -1309,7 +1321,7 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
if (frame is not null) if (frame is not null)
img.Mutate(x => x.DrawImage(frame, new Point(0, 0), new GraphicsOptions())); img.Mutate(x => x.DrawImage(frame, new Point(0, 0), new GraphicsOptions()));
} }
// #endif // #endif
private void DrawXpBar(float percent, XpBar info, Image<Rgba32> img) private void DrawXpBar(float percent, XpBar info, Image<Rgba32> img)
{ {

View file

@ -1,6 +1,4 @@
var pid = Environment.ProcessId; var shardId = 0;
var shardId = 0;
int? totalShards = null; // 0 to read from creds.yml int? totalShards = null; // 0 to read from creds.yml
if (args.Length > 0 && args[0] != "run") if (args.Length > 0 && args[0] != "run")
{ {
@ -22,7 +20,5 @@ if (args.Length > 0 && args[0] != "run")
} }
} }
LogSetup.SetupLogger(shardId);
Log.Information("Pid: {ProcessId}", pid);
await new Bot(shardId, totalShards, Environment.GetEnvironmentVariable("EllieBot__creds")).RunAndBlockAsync(); await new Bot(shardId, totalShards, Environment.GetEnvironmentVariable("EllieBot__creds")).RunAndBlockAsync();

View file

@ -31,7 +31,7 @@ public class GrpcApiService : IEService, IReadyExecutor
public Task OnReadyAsync() public Task OnReadyAsync()
{ {
var creds = _creds.GetCreds(); var creds = _creds.GetCreds();
if (creds.GrpcApi is null || creds.GrpcApi.Enabled) if (creds.GrpcApi is null || !creds.GrpcApi.Enabled)
return Task.CompletedTask; return Task.CompletedTask;
try try

View file

@ -6,9 +6,9 @@ namespace Ellie.Common;
public static class LogSetup public static class LogSetup
{ {
public static void SetupLogger(object source) public static void SetupLogger(object source, IBotCreds creds)
{ {
Log.Logger = new LoggerConfiguration().MinimumLevel.Override("Microsoft", LogEventLevel.Information) var config = new LoggerConfiguration().MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("System", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.FromLogContext() .Enrich.FromLogContext()
@ -16,8 +16,13 @@ public static class LogSetup
theme: GetTheme(), theme: GetTheme(),
outputTemplate: outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] | #{LogSource} | {Message:lj}{NewLine}{Exception}") "[{Timestamp:HH:mm:ss} {Level:u3}] | #{LogSource} | {Message:lj}{NewLine}{Exception}")
.Enrich.WithProperty("LogSource", source) .Enrich.WithProperty("LogSource", source);
.CreateLogger();
if (!string.IsNullOrWhiteSpace(creds.Seq.Url))
config = config.WriteTo.Seq(creds.Seq.Url, apiKey: creds.Seq.ApiKey);
Log.Logger = config
.CreateLogger();
Console.OutputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8;
} }

View file

@ -1,7 +1,7 @@
#nullable disable #nullable disable
namespace EllieBot; namespace EllieBot;
public interface IBotCredentials public interface IBotCreds
{ {
string Token { get; } string Token { get; }
string EllieAiToken { get; } string EllieAiToken { get; }
@ -30,6 +30,7 @@ public interface IBotCredentials
GoogleApiConfig Google { get; set; } GoogleApiConfig Google { get; set; }
BotCacheImplemenation BotCache { get; set; } BotCacheImplemenation BotCache { get; set; }
Creds.GrpcApiConfig GrpcApi { get; set; } Creds.GrpcApiConfig GrpcApi { get; set; }
SeqConfig Seq { get; set; }
} }
public interface IVotesSettings public interface IVotesSettings

View file

@ -3,6 +3,6 @@
public interface IBotCredsProvider public interface IBotCredsProvider
{ {
public void Reload(); public void Reload();
public IBotCredentials GetCreds(); public IBotCreds GetCreds();
public void ModifyCredsFile(Action<IBotCredentials> func); public void ModifyCredsFile(Action<IBotCreds> func);
} }

View file

@ -28,7 +28,7 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
public CultureInfo DefaultLocale { get; set; } public CultureInfo DefaultLocale { get; set; }
[Comment(""" [Comment("""
Style in which executed commands will show up in the console. Style in which executed commands will show up in the logs.
Allowed values: Simple, Normal, None Allowed values: Simple, Normal, None
""")] """)]
public ConsoleOutputType ConsoleOutputType { get; set; } public ConsoleOutputType ConsoleOutputType { get; set; }

View file

@ -3,10 +3,10 @@ using EllieBot.Common.Yml;
namespace EllieBot.Common; namespace EllieBot.Common;
public sealed class Creds : IBotCredentials public sealed class Creds : IBotCreds
{ {
[Comment("""DO NOT CHANGE""")] [Comment("""DO NOT CHANGE""")]
public int Version { get; set; } = 10; public int Version { get; set; } = 12;
[Comment("""Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/""")] [Comment("""Bot token. Do not share with anyone ever -> https://discordapp.com/developers/applications/""")]
public string Token { get; set; } public string Token { get; set; }
@ -164,6 +164,11 @@ public sealed class Creds : IBotCredentials
""")] """)]
public GrpcApiConfig GrpcApi { get; set; } public GrpcApiConfig GrpcApi { get; set; }
[Comment("""
Url and api key to a seq server. If url is set, bot will try to send logs to it.
""")]
public SeqConfig Seq { get; set; }
public Creds() public Creds()
{ {
Token = string.Empty; Token = string.Empty;
@ -189,7 +194,8 @@ public sealed class Creds : IBotCredentials
RestartCommand = new RestartConfig(); RestartCommand = new RestartConfig();
Google = new GoogleApiConfig(); Google = new GoogleApiConfig();
GrpcApi = new GrpcApiConfig(); GrpcApi = new();
Seq = new();
} }
public class DbOptions public class DbOptions
@ -294,6 +300,12 @@ public sealed class Creds : IBotCredentials
} }
} }
public sealed class SeqConfig
{
public string Url { get; init; }
public string ApiKey { get; init; }
}
public class GoogleApiConfig : IGoogleApiConfig public class GoogleApiConfig : IGoogleApiConfig
{ {
public string SearchId { get; init; } public string SearchId { get; init; }

View file

@ -119,7 +119,7 @@ public sealed class BotCredsProvider : IBotCredsProvider
} }
} }
public void ModifyCredsFile(Action<IBotCredentials> func) public void ModifyCredsFile(Action<IBotCreds> func)
{ {
var ymlData = File.ReadAllText(CREDS_FILE_NAME); var ymlData = File.ReadAllText(CREDS_FILE_NAME);
var creds = Yaml.Deserializer.Deserialize<Creds>(ymlData); var creds = Yaml.Deserializer.Deserialize<Creds>(ymlData);
@ -137,18 +137,18 @@ public sealed class BotCredsProvider : IBotCredsProvider
var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME)); var creds = Yaml.Deserializer.Deserialize<Creds>(File.ReadAllText(CREDS_FILE_NAME));
if (creds.Version <= 5) if (creds.Version <= 5)
{ {
creds.BotCache = BotCacheImplemenation.Redis; creds.BotCache = BotCacheImplemenation.Memory;
} }
if (creds.Version <= 9) if (creds.Version < 12)
{ {
creds.Version = 10; creds.Version = 12;
File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds)); File.WriteAllText(CREDS_FILE_NAME, Yaml.Serializer.Serialize(creds));
} }
} }
} }
public IBotCredentials GetCreds() public IBotCreds GetCreds()
{ {
lock (_reloadLock) lock (_reloadLock)
{ {

View file

@ -4,11 +4,11 @@ namespace EllieBot.Common;
public sealed class RedisPubSub : IPubSub public sealed class RedisPubSub : IPubSub
{ {
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly ConnectionMultiplexer _multi; private readonly ConnectionMultiplexer _multi;
private readonly ISeria _serializer; private readonly ISeria _serializer;
public RedisPubSub(ConnectionMultiplexer multi, ISeria serializer, IBotCredentials creds) public RedisPubSub(ConnectionMultiplexer multi, ISeria serializer, IBotCreds creds)
{ {
_multi = multi; _multi = multi;
_serializer = serializer; _serializer = serializer;

View file

@ -15,13 +15,13 @@ public class RedisBotStringsProvider : IBotStringsProvider
private readonly ConnectionMultiplexer _redis; private readonly ConnectionMultiplexer _redis;
private readonly IStringsSource _source; private readonly IStringsSource _source;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
public RedisBotStringsProvider( public RedisBotStringsProvider(
ConnectionMultiplexer redis, ConnectionMultiplexer redis,
DiscordSocketClient discordClient, DiscordSocketClient discordClient,
IStringsSource source, IStringsSource source,
IBotCredentials creds) IBotCreds creds)
{ {
_redis = redis; _redis = redis;
_source = source; _source = source;

View file

@ -11,7 +11,7 @@ public class RemoteGrpcCoordinator : ICoordinator, IReadyExecutor
private readonly Coordinator.Coordinator.CoordinatorClient _coordClient; private readonly Coordinator.Coordinator.CoordinatorClient _coordClient;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
public RemoteGrpcCoordinator(IBotCredentials creds, DiscordSocketClient client) public RemoteGrpcCoordinator(IBotCreds creds, DiscordSocketClient client)
{ {
var coordUrl = string.IsNullOrWhiteSpace(creds.CoordinatorUrl) ? "http://localhost:3442" : creds.CoordinatorUrl; var coordUrl = string.IsNullOrWhiteSpace(creds.CoordinatorUrl) ? "http://localhost:3442" : creds.CoordinatorUrl;

View file

@ -61,7 +61,7 @@ public static class ServiceCollectionExtensions
return svcs; return svcs;
} }
public static IContainer AddCache(this IContainer cont, IBotCredentials creds) public static IContainer AddCache(this IContainer cont, IBotCreds creds)
{ {
if (creds.BotCache == BotCacheImplemenation.Redis) if (creds.BotCache == BotCacheImplemenation.Redis)
{ {

View file

@ -14,12 +14,12 @@ public sealed class BlacklistService : IExecOnMessage
private readonly DbService _db; private readonly DbService _db;
private readonly IPubSub _pubSub; private readonly IPubSub _pubSub;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private IReadOnlyList<BlacklistEntry> blacklist; private IReadOnlyList<BlacklistEntry> blacklist;
private readonly TypedKey<BlacklistEntry[]> _blPubKey = new("blacklist.reload"); private readonly TypedKey<BlacklistEntry[]> _blPubKey = new("blacklist.reload");
public BlacklistService(DbService db, IPubSub pubSub, IBotCredentials creds) public BlacklistService(DbService db, IPubSub pubSub, IBotCreds creds)
{ {
_db = db; _db = db;
_pubSub = pubSub; _pubSub = pubSub;

View file

@ -5,10 +5,10 @@ namespace EllieBot.Services;
public class SingleProcessCoordinator : ICoordinator public class SingleProcessCoordinator : ICoordinator
{ {
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
public SingleProcessCoordinator(IBotCredentials creds, DiscordSocketClient client) public SingleProcessCoordinator(IBotCreds creds, DiscordSocketClient client)
{ {
_creds = creds; _creds = creds;
_client = client; _client = client;

View file

@ -29,7 +29,7 @@ public sealed class StatsService : IStatsService, IReadyExecutor, IEService
private readonly Process _currentProcess = Process.GetCurrentProcess(); private readonly Process _currentProcess = Process.GetCurrentProcess();
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly IBotCredentials _creds; private readonly IBotCreds _creds;
private readonly DateTime _started; private readonly DateTime _started;
private long textChannels; private long textChannels;
@ -42,7 +42,7 @@ public sealed class StatsService : IStatsService, IReadyExecutor, IEService
public StatsService( public StatsService(
DiscordSocketClient client, DiscordSocketClient client,
CommandHandler cmdHandler, CommandHandler cmdHandler,
IBotCredentials creds, IBotCreds creds,
IHttpClientFactory factory) IHttpClientFactory factory)
{ {
_client = client; _client = client;

View file

@ -2,9 +2,9 @@ namespace EllieBot.Extensions;
public static class BotCredentialsExtensions public static class BotCredentialsExtensions
{ {
public static bool IsOwner(this IBotCredentials creds, IUser user) public static bool IsOwner(this IBotCreds creds, IUser user)
=> creds.IsOwner(user.Id); => creds.IsOwner(user.Id);
public static bool IsOwner(this IBotCredentials creds, ulong userId) public static bool IsOwner(this IBotCreds creds, ulong userId)
=> creds.OwnerIds.Contains(userId); => creds.OwnerIds.Contains(userId);
} }

View file

@ -103,7 +103,7 @@ public static class Extensions
/// <summary> /// <summary>
/// First 10 characters of teh bot token. /// First 10 characters of teh bot token.
/// </summary> /// </summary>
public static string RedisKey(this IBotCredentials bc) public static string RedisKey(this IBotCreds bc)
=> bc.Token[..10]; => bc.Token[..10];
public static bool IsAuthor(this IMessage msg, IDiscordClient client) public static bool IsAuthor(this IMessage msg, IDiscordClient client)

View file

@ -1,5 +1,4 @@
# DO NOT CHANGE # DO NOT CHANGE
version: 1 version: 1
# List of marmalades automatically loaded at startup # List of marmalades automatically loaded at startup
loaded: loaded: []
- ngrpc