diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b56fd..700a754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o +## Unreleased + +### Added + +- `'serverlist` is now paginated + +### Changed + +- `'listservers` renamed to `'serverlist` + +### Removed +- Removed old bloat / semi broken / dumb commands + - `'memelist` / `'memegen` (too inconvenient to use) + - `'activity` (useless owner-only command) + - `'rafflecur` (Just use raffle and then award manually instead) + - `'rollduel` (we had this command?) +- You can no longer bet on `'connect4` +- `'economy` Removed. + - Was buggy and didn't really show the real state of the economy. + - It might come back improved in the future +- `'mal` Removed. Useless information / semi broken + ## [5.1.5] - 01.08.2024 ### Added diff --git a/src/EllieBot/Bot.cs b/src/EllieBot/Bot.cs index a8c5fd3..3ea8ffa 100644 --- a/src/EllieBot/Bot.cs +++ b/src/EllieBot/Bot.cs @@ -88,7 +88,7 @@ public sealed class Bot : IBot public IReadOnlyList GetCurrentGuildIds() - => Client.Guilds.Select(x => x.Id).ToList(); + => Client.Guilds.Select(x => x.Id).ToList().ToList(); private void AddServices() { diff --git a/src/EllieBot/Modules/Gambling/Connect4/Connect4.cs b/src/EllieBot/Modules/Gambling/Connect4/Connect4.cs index 45d2e89..02537fd 100644 --- a/src/EllieBot/Modules/Gambling/Connect4/Connect4.cs +++ b/src/EllieBot/Modules/Gambling/Connect4/Connect4.cs @@ -38,11 +38,11 @@ public sealed class Connect4Game : IDisposable public Phase CurrentPhase { get; private set; } = Phase.Joining; - public ImmutableArray GameState - => _gameState.ToImmutableArray(); + public IReadOnlyList GameState + => _gameState.AsReadOnly(); - public ImmutableArray<(ulong UserId, string Username)?> Players - => _players.ToImmutableArray(); + public IReadOnlyCollection<(ulong UserId, string Username)?> Players + => _players.AsReadOnly(); public (ulong UserId, string Username) CurrentPlayer => CurrentPhase == Phase.P1Move ? _players[0].Value : _players[1].Value; @@ -56,7 +56,6 @@ public sealed class Connect4Game : IDisposable private readonly SemaphoreSlim _locker = new(1, 1); private readonly Options _options; - private readonly ICurrencyService _cs; private readonly EllieRandom _rng; private Timer playerTimeoutTimer; @@ -73,12 +72,11 @@ public sealed class Connect4Game : IDisposable public Connect4Game( ulong userId, string userName, - Options options, - ICurrencyService cs) + Options options + ) { _players[0] = (userId, userName); _options = options; - _cs = cs; _rng = new(); for (var i = 0; i < NUMBER_OF_COLUMNS * NUMBER_OF_ROWS; i++) @@ -99,14 +97,13 @@ public sealed class Connect4Game : IDisposable { _ = OnGameFailedToStart?.Invoke(this); CurrentPhase = Phase.Ended; - await _cs.AddAsync(_players[0].Value.UserId, _options.Bet, new("connect4", "refund")); } } finally { _locker.Release(); } }); } - public async Task Join(ulong userId, string userName, int bet) + public async Task Join(ulong userId, string userName) { await _locker.WaitAsync(); try @@ -117,11 +114,6 @@ public sealed class Connect4Game : IDisposable if (_players[0].Value.UserId == userId) // same user can't join own game return false; - if (bet != _options.Bet) // can't join if bet amount is not the same - return false; - - if (!await _cs.RemoveAsync(userId, bet, new("connect4", "bet"))) // user doesn't have enough money to gamble - return false; if (_rng.Next(0, 2) == 0) //rolling from 0-1, if number is 0, join as first player { @@ -133,14 +125,14 @@ public sealed class Connect4Game : IDisposable CurrentPhase = Phase.P1Move; //start the game playerTimeoutTimer = new(async _ => + { + await _locker.WaitAsync(); + try { - await _locker.WaitAsync(); - try - { - EndGame(Result.OtherPlayerWon, OtherPlayer.UserId); - } - finally { _locker.Release(); } - }, + EndGame(Result.OtherPlayerWon, OtherPlayer.UserId); + } + finally { _locker.Release(); } + }, null, TimeSpan.FromSeconds(_options.TurnTimer), TimeSpan.FromSeconds(_options.TurnTimer)); @@ -351,13 +343,8 @@ public sealed class Connect4Game : IDisposable if (result == Result.Draw) { - _cs.AddAsync(CurrentPlayer.UserId, _options.Bet, new("connect4", "draw")); - _cs.AddAsync(OtherPlayer.UserId, _options.Bet, new("connect4", "draw")); return; } - - if (winId is not null) - _cs.AddAsync(winId.Value, (long)(_options.Bet * 1.98), new("connect4", "win")); } private Field GetPlayerPiece(ulong userId) @@ -394,16 +381,10 @@ public sealed class Connect4Game : IDisposable HelpText = "Turn time in seconds. It has to be between 5 and 60. Default 15.")] public int TurnTimer { get; set; } = 15; - [Option('b', "bet", Required = false, Default = 0, HelpText = "Amount you bet. Default 0.")] - public int Bet { get; set; } - public void NormalizeOptions() { if (TurnTimer is < 5 or > 60) TurnTimer = 15; - - if (Bet < 0) - Bet = 0; } } } \ No newline at end of file diff --git a/src/EllieBot/Modules/Gambling/Connect4/Connect4Commands.cs b/src/EllieBot/Modules/Gambling/Connect4/Connect4Commands.cs index 8210ad4..3b3aad1 100644 --- a/src/EllieBot/Modules/Gambling/Connect4/Connect4Commands.cs +++ b/src/EllieBot/Modules/Gambling/Connect4/Connect4Commands.cs @@ -29,17 +29,15 @@ public partial class Gambling } private readonly DiscordSocketClient _client; - private readonly ICurrencyService _cs; private IUserMessage msg; private int repostCounter; - public Connect4Commands(DiscordSocketClient client, ICurrencyService cs, GamblingConfigService gamb) + public Connect4Commands(DiscordSocketClient client, GamblingConfigService gamb) : base(gamb) { _client = client; - _cs = cs; } [Cmd] @@ -48,10 +46,8 @@ public partial class Gambling public async Task Connect4(params string[] args) { var (options, _) = OptionsParser.ParseFrom(new Connect4Game.Options(), args); - if (!await CheckBetOptional(options.Bet)) - return; - var newGame = new Connect4Game(ctx.User.Id, ctx.User.ToString(), options, _cs); + var newGame = new Connect4Game(ctx.User.Id, ctx.User.ToString(), options); Connect4Game game; if ((game = _service.Connect4Games.GetOrAdd(ctx.Channel.Id, newGame)) != newGame) { @@ -60,31 +56,17 @@ public partial class Gambling newGame.Dispose(); //means game already exists, try to join - await game.Join(ctx.User.Id, ctx.User.ToString(), options.Bet); + await game.Join(ctx.User.Id, ctx.User.ToString()); return; } - if (options.Bet > 0) - { - if (!await _cs.RemoveAsync(ctx.User.Id, options.Bet, new("connect4", "bet"))) - { - await Response().Error(strs.not_enough(CurrencySign)).SendAsync(); - _service.Connect4Games.TryRemove(ctx.Channel.Id, out _); - game.Dispose(); - return; - } - } - game.OnGameStateUpdated += Game_OnGameStateUpdated; game.OnGameFailedToStart += GameOnGameFailedToStart; game.OnGameEnded += GameOnGameEnded; _client.MessageReceived += ClientMessageReceived; game.Initialize(); - if (options.Bet == 0) - await Response().Confirm(strs.connect4_created).SendAsync(); - else - await Response().Error(strs.connect4_created_bet(N(options.Bet))).SendAsync(); + await Response().Confirm(strs.connect4_created).SendAsync(); Task ClientMessageReceived(SocketMessage arg) { @@ -99,7 +81,8 @@ public partial class Gambling if (success) { - try { await arg.DeleteAsync(); } + try + { await arg.DeleteAsync(); } catch { } } else @@ -109,7 +92,8 @@ public partial class Gambling RepostCounter++; if (RepostCounter == 0) { - try { msg = await Response().Embed(msg.Embeds.First().ToEmbedBuilder()).SendAsync(); } + try + { msg = await Response().Embed(msg.Embeds.First().ToEmbedBuilder()).SendAsync(); } catch { } } } @@ -151,19 +135,19 @@ public partial class Gambling title = GetText(strs.connect4_draw); return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed() - .WithTitle(title) - .WithDescription(GetGameStateText(game)) - .WithOkColor() - .Build()); + .WithTitle(title) + .WithDescription(GetGameStateText(game)) + .WithOkColor() + .Build()); } } private async Task Game_OnGameStateUpdated(Connect4Game game) { var embed = _sender.CreateEmbed() - .WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}") - .WithDescription(GetGameStateText(game)) - .WithOkColor(); + .WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}") + .WithDescription(GetGameStateText(game)) + .WithOkColor(); if (msg is null) @@ -198,7 +182,7 @@ public partial class Gambling for (var i = 0; i < Connect4Game.NUMBER_OF_COLUMNS; i++) sb.Append(_numbers[i]); - + return sb.ToString(); } } diff --git a/src/EllieBot/Modules/Gambling/Gambling.cs b/src/EllieBot/Modules/Gambling/Gambling.cs index 53cb07e..64fedf5 100644 --- a/src/EllieBot/Modules/Gambling/Gambling.cs +++ b/src/EllieBot/Modules/Gambling/Gambling.cs @@ -30,8 +30,6 @@ public partial class Gambling : GamblingModule private readonly GamblingTxTracker _gamblingTxTracker; private readonly IPatronageService _ps; - private IUserMessage rdMsg; - public Gambling( IGamblingService gs, DbService db, @@ -104,34 +102,6 @@ public partial class Gambling : GamblingModule await Response().Embed(eb).SendAsync(); } - [Cmd] - public async Task Economy() - { - var ec = await _service.GetEconomyAsync(); - decimal onePercent = 0; - - // 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 = _sender.CreateEmbed() - .WithTitle(GetText(strs.economy_state)) - .AddField(GetText(strs.currency_owned), 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)) - .AddField(GetText(strs.bot_currency), N(ec.Bot)) - .AddField(GetText(strs.bank_accounts), N(ec.Bank)) - .AddField(GetText(strs.total), N(ec.Cash + ec.Planted + ec.Waifus + ec.Bank)) - .WithOkColor(); - - // ec.Cash already contains ec.Bot as it's the total of all values in the CurrencyAmount column of the DiscordUser table - await Response().Embed(embed).SendAsync(); - } - private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when) { var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative); @@ -601,117 +571,6 @@ public partial class Gambling : GamblingModule } } - [Cmd] - [RequireContext(ContextType.Guild)] - public async 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] - [RequireContext(ContextType.Guild)] - public async Task RollDuel([OverrideTypeReader(typeof(BalanceTypeReader))] long amount, IUser u) - { - if (ctx.User.Id == u.Id) - { - return; - } - - if (amount <= 0) - { - return; - } - - var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.roll_duel)); - - var description = string.Empty; - - var game = new RollDuelGame(_cs, _client.CurrentUser.Id, ctx.User.Id, u.Id, amount); - //means challenge is just created - if (_service.Duels.TryGetValue((ctx.User.Id, u.Id), out var other)) - { - if (other.Amount != amount) - { - await Response().Error(strs.roll_duel_already_challenged).SendAsync(); - } - else - { - await RollDuel(u); - } - - return; - } - - if (_service.Duels.TryAdd((u.Id, ctx.User.Id), game)) - { - game.OnGameTick += GameOnGameTick; - game.OnEnded += GameOnEnded; - - await Response() - .Confirm(strs.roll_duel_challenge(Format.Bold(ctx.User.ToString()), - Format.Bold(u.ToString()), - Format.Bold(N(amount)))) - .SendAsync(); - } - - async Task GameOnGameTick(RollDuelGame arg) - { - var rolls = arg.Rolls.Last(); - description += $@"{Format.Bold(ctx.User.ToString())} rolled **{rolls.Item1}** -{Format.Bold(u.ToString())} rolled **{rolls.Item2}** --- -"; - embed = embed.WithDescription(description); - - if (rdMsg is null) - { - rdMsg = await Response().Embed(embed).SendAsync(); - } - else - { - await rdMsg.ModifyAsync(x => { x.Embed = embed.Build(); }); - } - } - - async Task GameOnEnded(RollDuelGame rdGame, RollDuelGame.Reason reason) - { - try - { - if (reason == RollDuelGame.Reason.Normal) - { - var winner = rdGame.Winner == rdGame.P1 ? ctx.User : u; - description += $"\n**{winner}** Won {N((long)(rdGame.Amount * 2 * 0.98))}"; - - embed = embed.WithDescription(description); - - await rdMsg.ModifyAsync(x => x.Embed = embed.Build()); - } - else if (reason == RollDuelGame.Reason.Timeout) - { - await Response().Error(strs.roll_duel_timeout).SendAsync(); - } - else if (reason == RollDuelGame.Reason.NoFunds) - { - await Response().Error(strs.roll_duel_no_funds).SendAsync(); - } - } - finally - { - _service.Duels.TryRemove((u.Id, ctx.User.Id), out _); - } - } - } - [Cmd] public async Task BetRoll([OverrideTypeReader(typeof(BalanceTypeReader))] long amount) { diff --git a/src/EllieBot/Modules/Gambling/GamblingService.cs b/src/EllieBot/Modules/Gambling/GamblingService.cs index e7ed369..f9a55bc 100644 --- a/src/EllieBot/Modules/Gambling/GamblingService.cs +++ b/src/EllieBot/Modules/Gambling/GamblingService.cs @@ -128,38 +128,6 @@ public class GamblingService : IEService, IReadyExecutor private static readonly TypedKey _ecoKey = new("ellie:economy"); - public async Task GetEconomyAsync() - { - var data = await _cache.GetOrAddAsync(_ecoKey, - async () => - { - await using var uow = _db.GetDbContext(); - var cash = uow.Set().GetTotalCurrency(); - var onePercent = uow.Set().GetTopOnePercentCurrency(_client.CurrentUser.Id); - decimal planted = uow.Set().AsQueryable().Sum(x => x.Amount); - var waifus = uow.Set().GetTotalValue(); - var bot = await uow.Set().GetUserCurrencyAsync(_client.CurrentUser.Id); - decimal bank = await uow.GetTable() - .SumAsyncLinqToDB(x => x.Balance); - - var result = new EconomyResult - { - Cash = cash, - Planted = planted, - Bot = bot, - Waifus = waifus, - OnePercent = onePercent, - Bank = bank - }; - - return result; - }, - TimeSpan.FromMinutes(3)); - - return data; - } - - private static readonly SemaphoreSlim _timelyLock = new(1, 1); private static TypedKey> _timelyKey diff --git a/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleCommands.cs b/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleCommands.cs deleted file mode 100644 index 513aa59..0000000 --- a/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleCommands.cs +++ /dev/null @@ -1,61 +0,0 @@ -ο»Ώ#nullable disable -using EllieBot.Common.TypeReaders; -using EllieBot.Modules.Gambling.Common; -using EllieBot.Modules.Gambling.Services; - -namespace EllieBot.Modules.Gambling; - -public partial class Gambling -{ - public partial class CurrencyRaffleCommands : GamblingSubmodule - { - public enum Mixed { Mixed } - - public CurrencyRaffleCommands(GamblingConfigService gamblingConfService) - : base(gamblingConfService) - { - } - - [Cmd] - [RequireContext(ContextType.Guild)] - [Priority(0)] - public Task RaffleCur(Mixed _, [OverrideTypeReader(typeof(BalanceTypeReader))] long amount) - => RaffleCur(amount, true); - - [Cmd] - [RequireContext(ContextType.Guild)] - [Priority(1)] - public async Task RaffleCur([OverrideTypeReader(typeof(BalanceTypeReader))] long amount, bool mixed = false) - { - if (!await CheckBetMandatory(amount)) - return; - - async Task OnEnded(IUser arg, long won) - { - await Response() - .Confirm(GetText(strs.rafflecur_ended(CurrencyName, - Format.Bold(arg.ToString()), - won + CurrencySign))) - .SendAsync(); - } - - var res = await _service.JoinOrCreateGame(ctx.Channel.Id, ctx.User, amount, mixed, OnEnded); - - if (res.Item1 is not null) - { - await Response() - .Confirm(GetText(strs.rafflecur(res.Item1.GameType.ToString())), - string.Join("\n", res.Item1.Users.Select(x => $"{x.DiscordUser} ({N(x.Amount)})")), - footer: GetText(strs.rafflecur_joined(ctx.User.ToString()))) - .SendAsync(); - } - else - { - if (res.Item2 == CurrencyRaffleService.JoinErrorType.AlreadyJoinedOrInvalidAmount) - await Response().Error(strs.rafflecur_already_joined).SendAsync(); - else if (res.Item2 == CurrencyRaffleService.JoinErrorType.NotEnoughCurrency) - await Response().Error(strs.not_enough(CurrencySign)).SendAsync(); - } - } - } -} \ No newline at end of file diff --git a/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleGame.cs b/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleGame.cs deleted file mode 100644 index d6f5770..0000000 --- a/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleGame.cs +++ /dev/null @@ -1,69 +0,0 @@ -#nullable disable -namespace EllieBot.Modules.Gambling.Common; - -public class CurrencyRaffleGame -{ - public enum Type - { - Mixed, - Normal - } - - public IEnumerable Users - => _users; - - public Type GameType { get; } - - private readonly HashSet _users = new(); - - public CurrencyRaffleGame(Type type) - => GameType = type; - - public bool AddUser(IUser usr, long amount) - { - // if game type is normal, and someone already joined the game - // (that's the user who created it) - if (GameType == Type.Normal && _users.Count > 0 && _users.First().Amount != amount) - return false; - - if (!_users.Add(new() - { - DiscordUser = usr, - Amount = amount - })) - return false; - - return true; - } - - public User GetWinner() - { - var rng = new EllieRandom(); - if (GameType == Type.Mixed) - { - var num = rng.NextLong(0L, Users.Sum(x => x.Amount)); - var sum = 0L; - foreach (var u in Users) - { - sum += u.Amount; - if (sum > num) - return u; - } - } - - var usrs = _users.ToArray(); - return usrs[rng.Next(0, usrs.Length)]; - } - - public class User - { - public IUser DiscordUser { get; set; } - public long Amount { get; set; } - - public override int GetHashCode() - => DiscordUser.GetHashCode(); - - public override bool Equals(object obj) - => obj is User u ? u.DiscordUser == DiscordUser : false; - } -} \ No newline at end of file diff --git a/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleService.cs b/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleService.cs deleted file mode 100644 index 743549e..0000000 --- a/src/EllieBot/Modules/Gambling/Raffle/CurrencyRaffleService.cs +++ /dev/null @@ -1,81 +0,0 @@ -ο»Ώ#nullable disable -using EllieBot.Modules.Gambling.Common; - -namespace EllieBot.Modules.Gambling.Services; - -public class CurrencyRaffleService : IEService -{ - public enum JoinErrorType - { - NotEnoughCurrency, - AlreadyJoinedOrInvalidAmount - } - - public Dictionary Games { get; } = new(); - private readonly SemaphoreSlim _locker = new(1, 1); - private readonly ICurrencyService _cs; - - public CurrencyRaffleService(ICurrencyService cs) - => _cs = cs; - - public async Task<(CurrencyRaffleGame, JoinErrorType?)> JoinOrCreateGame( - ulong channelId, - IUser user, - long amount, - bool mixed, - Func onEnded) - { - await _locker.WaitAsync(); - try - { - var newGame = false; - if (!Games.TryGetValue(channelId, out var crg)) - { - newGame = true; - crg = new(mixed ? CurrencyRaffleGame.Type.Mixed : CurrencyRaffleGame.Type.Normal); - Games.Add(channelId, crg); - } - - //remove money, and stop the game if this - // user created it and doesn't have the money - if (!await _cs.RemoveAsync(user.Id, amount, new("raffle", "join"))) - { - if (newGame) - Games.Remove(channelId); - return (null, JoinErrorType.NotEnoughCurrency); - } - - if (!crg.AddUser(user, amount)) - { - await _cs.AddAsync(user.Id, amount, new("raffle", "refund")); - return (null, JoinErrorType.AlreadyJoinedOrInvalidAmount); - } - - if (newGame) - { - _ = Task.Run(async () => - { - await Task.Delay(60000); - await _locker.WaitAsync(); - try - { - var winner = crg.GetWinner(); - var won = crg.Users.Sum(x => x.Amount); - - await _cs.AddAsync(winner.DiscordUser.Id, won, new("raffle", "win")); - Games.Remove(channelId, out _); - _ = onEnded(winner.DiscordUser, won); - } - catch { } - finally { _locker.Release(); } - }); - } - - return (crg, null); - } - finally - { - _locker.Release(); - } - } -} \ No newline at end of file diff --git a/src/EllieBot/Modules/Gambling/Slot/SlotCommands.cs b/src/EllieBot/Modules/Gambling/Slot/SlotCommands.cs index b925cd8..a5c7465 100644 --- a/src/EllieBot/Modules/Gambling/Slot/SlotCommands.cs +++ b/src/EllieBot/Modules/Gambling/Slot/SlotCommands.cs @@ -23,9 +23,6 @@ public partial class Gambling [Group] public partial class SlotCommands : GamblingSubmodule { - private static decimal totalBet; - private static decimal totalPaidOut; - private readonly IImageCache _images; private readonly FontProvider _fonts; private readonly DbService _db; @@ -69,17 +66,19 @@ public partial class Gambling var eb = _sender.CreateEmbed() - .WithAuthor(ctx.User) - .WithDescription(Format.Bold(text)) - .WithImageUrl($"attachment://result.png") - .WithOkColor(); + .WithAuthor(ctx.User) + .WithDescription(Format.Bold(text)) + .WithImageUrl($"attachment://result.png") + .WithOkColor(); var bb = new ButtonBuilder(emote: Emoji.Parse("πŸ”"), customId: "slot:again", label: "Pull Again"); - var inter = _inter.Create(ctx.User.Id, bb, smc => - { - smc.DeferAsync(); - return Slot(amount); - }); + var inter = _inter.Create(ctx.User.Id, + bb, + smc => + { + smc.DeferAsync(); + return Slot(amount); + }); var msg = await ctx.Channel.SendFileAsync(imgStream, "result.png", @@ -159,12 +158,6 @@ public partial class Gambling { return null; } - - lock (_slotStatsLock) - { - totalBet += amount; - totalPaidOut += result.Won; - } return result; } @@ -211,7 +204,7 @@ public partial class Gambling { HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, - Origin = new(393, 480) + Origin = new(393, 480) }, ownedAmount.ToString(), fontColor)); diff --git a/src/EllieBot/Modules/Searches/Anime/AnimeSearchCommands.cs b/src/EllieBot/Modules/Searches/Anime/AnimeSearchCommands.cs index 8acae72..d86cd14 100644 --- a/src/EllieBot/Modules/Searches/Anime/AnimeSearchCommands.cs +++ b/src/EllieBot/Modules/Searches/Anime/AnimeSearchCommands.cs @@ -10,133 +10,6 @@ public partial class Searches [Group] public partial class AnimeSearchCommands : EllieModule { - // [EllieCommand, Aliases] - // public async Task Novel([Leftover] string query) - // { - // if (string.IsNullOrWhiteSpace(query)) - // return; - // - // var novelData = await _service.GetNovelData(query); - // - // if (novelData is null) - // { - // await Response().Error(strs.failed_finding_novel).SendAsync(); - // return; - // } - // - // var embed = _sender.CreateEmbed() - // .WithOkColor() - // .WithDescription(novelData.Description.Replace("
", Environment.NewLine, StringComparison.InvariantCulture)) - // .WithTitle(novelData.Title) - // .WithUrl(novelData.Link) - // .WithImageUrl(novelData.ImageUrl) - // .AddField(GetText(strs.authors), string.Join("\n", novelData.Authors), true) - // .AddField(GetText(strs.status), novelData.Status, true) - // .AddField(GetText(strs.genres), string.Join(" ", novelData.Genres.Any() ? novelData.Genres : new[] { "none" }), true) - // .WithFooter($"{GetText(strs.score)} {novelData.Score}"); - // - // await Response().Embed(embed).SendAsync(); - // } - - [Cmd] - [Priority(0)] - public async Task Mal([Leftover] string name) - { - if (string.IsNullOrWhiteSpace(name)) - return; - - var fullQueryLink = "https://myanimelist.net/profile/" + name; - - var config = Configuration.Default.WithDefaultLoader(); - using var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink); - var imageElem = - document.QuerySelector( - "body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-left > div.user-profile > div.user-image > img"); - var imageUrl = ((IHtmlImageElement)imageElem)?.Source - ?? "http://icecream.me/uploads/870b03f36b59cc16ebfe314ef2dde781.png"; - - var stats = document - .QuerySelectorAll( - "body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-right > div#statistics > div.user-statistics-stats > div.stats > div.clearfix > ul.stats-status > li > span") - .Select(x => x.InnerHtml) - .ToList(); - - var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc"); - - 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") - .Shuffle() - .Take(3) - .Select(x => - { - 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)) - .ToList(); - - var daysAndMean = document.QuerySelectorAll("div.anime:nth-child(1) > div:nth-child(2) > div") - .Select(x => x.TextContent.Split(':').Select(y => y.Trim()).ToArray()) - .ToArray(); - - var embed = _sender.CreateEmbed() - .WithOkColor() - .WithTitle(GetText(strs.mal_profile(name))) - .AddField("πŸ’š " + GetText(strs.watching), stats[0], true) - .AddField("πŸ’™ " + GetText(strs.completed), stats[1], true); - if (info.Count < 3) - embed.AddField("πŸ’› " + GetText(strs.on_hold), stats[2], true); - embed.AddField("πŸ’” " + GetText(strs.dropped), stats[3], true) - .AddField("βšͺ " + GetText(strs.plan_to_watch), stats[4], true) - .AddField("πŸ• " + daysAndMean[0][0], daysAndMean[0][1], true) - .AddField("πŸ“Š " + daysAndMean[1][0], daysAndMean[1][1], true) - .AddField(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1, info[0].Item2.TrimTo(20), true) - .AddField(MalInfoToEmoji(info[1].Item1) + " " + info[1].Item1, info[1].Item2.TrimTo(20), true); - if (info.Count > 2) - embed.AddField(MalInfoToEmoji(info[2].Item1) + " " + info[2].Item1, info[2].Item2.TrimTo(20), true); - - embed.WithDescription($@" -** https://myanimelist.net/animelist/{name} ** - -**{GetText(strs.top_3_fav_anime)}** -{favAnime}") - .WithUrl(fullQueryLink) - .WithImageUrl(imageUrl); - - await Response().Embed(embed).SendAsync(); - } - - private static string MalInfoToEmoji(string info) - { - info = info.Trim().ToLowerInvariant(); - switch (info) - { - case "gender": - return "🚁"; - case "location": - return "πŸ—Ί"; - case "last online": - return "πŸ‘₯"; - case "birthday": - return "πŸ“†"; - default: - return "❔"; - } - } - - [Cmd] - [RequireContext(ContextType.Guild)] - [Priority(1)] - public Task Mal(IGuildUser usr) - => Mal(usr.Username); - [Cmd] public async Task Anime([Leftover] string query) { @@ -152,19 +25,19 @@ public partial class Searches } var embed = _sender.CreateEmbed() - .WithOkColor() - .WithDescription(animeData.Synopsis.Replace("
", - Environment.NewLine, - StringComparison.InvariantCulture)) - .WithTitle(animeData.TitleEnglish) - .WithUrl(animeData.Link) - .WithImageUrl(animeData.ImageUrlLarge) - .AddField(GetText(strs.episodes), animeData.TotalEpisodes.ToString(), true) - .AddField(GetText(strs.status), animeData.AiringStatus, true) - .AddField(GetText(strs.genres), - string.Join(",\n", animeData.Genres.Any() ? animeData.Genres : ["none"]), - true) - .WithFooter($"{GetText(strs.score)} {animeData.AverageScore} / 100"); + .WithOkColor() + .WithDescription(animeData.Synopsis.Replace("
", + Environment.NewLine, + StringComparison.InvariantCulture)) + .WithTitle(animeData.TitleEnglish) + .WithUrl(animeData.Link) + .WithImageUrl(animeData.ImageUrlLarge) + .AddField(GetText(strs.episodes), animeData.TotalEpisodes.ToString(), true) + .AddField(GetText(strs.status), animeData.AiringStatus, true) + .AddField(GetText(strs.genres), + string.Join(",\n", animeData.Genres.Any() ? animeData.Genres : ["none"]), + true) + .WithFooter($"{GetText(strs.score)} {animeData.AverageScore} / 100"); await Response().Embed(embed).SendAsync(); } @@ -184,19 +57,19 @@ public partial class Searches } var embed = _sender.CreateEmbed() - .WithOkColor() - .WithDescription(mangaData.Synopsis.Replace("
", - Environment.NewLine, - StringComparison.InvariantCulture)) - .WithTitle(mangaData.TitleEnglish) - .WithUrl(mangaData.Link) - .WithImageUrl(mangaData.ImageUrlLge) - .AddField(GetText(strs.chapters), mangaData.TotalChapters.ToString(), true) - .AddField(GetText(strs.status), mangaData.PublishingStatus, true) - .AddField(GetText(strs.genres), - string.Join(",\n", mangaData.Genres.Any() ? mangaData.Genres : ["none"]), - true) - .WithFooter($"{GetText(strs.score)} {mangaData.AverageScore} / 100"); + .WithOkColor() + .WithDescription(mangaData.Synopsis.Replace("
", + Environment.NewLine, + StringComparison.InvariantCulture)) + .WithTitle(mangaData.TitleEnglish) + .WithUrl(mangaData.Link) + .WithImageUrl(mangaData.ImageUrlLge) + .AddField(GetText(strs.chapters), mangaData.TotalChapters.ToString(), true) + .AddField(GetText(strs.status), mangaData.PublishingStatus, true) + .AddField(GetText(strs.genres), + string.Join(",\n", mangaData.Genres.Any() ? mangaData.Genres : ["none"]), + true) + .WithFooter($"{GetText(strs.score)} {mangaData.AverageScore} / 100"); await Response().Embed(embed).SendAsync(); } diff --git a/src/EllieBot/Modules/Searches/MemegenCommands.cs b/src/EllieBot/Modules/Searches/MemegenCommands.cs deleted file mode 100644 index dbe2679..0000000 --- a/src/EllieBot/Modules/Searches/MemegenCommands.cs +++ /dev/null @@ -1,99 +0,0 @@ -#nullable disable -using Newtonsoft.Json; -using System.Collections.Immutable; -using System.Text; - -namespace EllieBot.Modules.Searches; - -public partial class Searches -{ - [Group] - public partial class MemegenCommands : EllieModule - { - private static readonly ImmutableDictionary _map = new Dictionary - { - { '?', "~q" }, - { '%', "~p" }, - { '#', "~h" }, - { '/', "~s" }, - { ' ', "-" }, - { '-', "--" }, - { '_', "__" }, - { '"', "''" } - }.ToImmutableDictionary(); - - private readonly IHttpClientFactory _httpFactory; - - public MemegenCommands(IHttpClientFactory factory) - => _httpFactory = factory; - - [Cmd] - public async Task Memelist(int page = 1) - { - if (--page < 0) - return; - - using var http = _httpFactory.CreateClient("memelist"); - using var res = await http.GetAsync("https://api.memegen.link/templates/"); - - var rawJson = await res.Content.ReadAsStringAsync(); - - var data = JsonConvert.DeserializeObject>(rawJson)!; - - await Response() - .Paginated() - .Items(data) - .PageSize(15) - .CurrentPage(page) - .Page((items, curPage) => - { - var templates = string.Empty; - foreach (var template in items) - templates += $"**{template.Name}:**\n key: `{template.Id}`\n"; - var embed = _sender.CreateEmbed().WithOkColor().WithDescription(templates); - - return embed; - }) - .SendAsync(); - } - - [Cmd] - public async Task Memegen(string meme, [Leftover] string memeText = null) - { - var memeUrl = $"http://api.memegen.link/{meme}"; - if (!string.IsNullOrWhiteSpace(memeText)) - { - var memeTextArray = memeText.Split(';'); - foreach (var text in memeTextArray) - { - var newText = Replace(text); - memeUrl += $"/{newText}"; - } - } - - memeUrl += ".png"; - await Response().Text(memeUrl).SendAsync(); - } - - private static string Replace(string input) - { - var sb = new StringBuilder(); - - foreach (var c in input) - { - if (_map.TryGetValue(c, out var tmp)) - sb.Append(tmp); - else - sb.Append(c); - } - - return sb.ToString(); - } - - private class MemegenTemplate - { - public string Name { get; set; } - public string Id { get; set; } - } - } -} \ No newline at end of file diff --git a/src/EllieBot/Modules/Utility/Info/InfoCommands.cs b/src/EllieBot/Modules/Utility/Info/InfoCommands.cs index e2533e0..c9ae861 100644 --- a/src/EllieBot/Modules/Utility/Info/InfoCommands.cs +++ b/src/EllieBot/Modules/Utility/Info/InfoCommands.cs @@ -176,39 +176,5 @@ public partial class Utility return joinedAt; } - - [Cmd] - [RequireContext(ContextType.Guild)] - [OwnerOnly] - public async Task Activity(int page = 1) - { - const int activityPerPage = 10; - page -= 1; - - if (page < 0) - return; - - var startCount = page * activityPerPage; - - var str = new StringBuilder(); - foreach (var kvp in _cmdHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value) - .Skip(page * activityPerPage) - .Take(activityPerPage)) - { - str.AppendLine(GetText(strs.activity_line(++startCount, - Format.Bold(kvp.Key.ToString()), - kvp.Value / _stats.GetUptime().TotalSeconds, - kvp.Value))); - } - - await Response() - .Embed(_sender.CreateEmbed() - .WithTitle(GetText(strs.activity_page(page + 1))) - .WithOkColor() - .WithFooter(GetText( - strs.activity_users_total(_cmdHandler.UserMessagesSent.Count))) - .WithDescription(str.ToString())) - .SendAsync(); - } } } \ No newline at end of file diff --git a/src/EllieBot/Modules/Utility/Utility.cs b/src/EllieBot/Modules/Utility/Utility.cs index d3e9c37..ac76f93 100644 --- a/src/EllieBot/Modules/Utility/Utility.cs +++ b/src/EllieBot/Modules/Utility/Utility.cs @@ -557,29 +557,38 @@ public partial class Utility : EllieModule [Cmd] [OwnerOnly] - public async Task ListServers(int page = 1) + public async Task ServerList(int page = 1) { page -= 1; if (page < 0) return; - var guilds = _client.Guilds.OrderBy(g => g.Name) - .Skip(page * 15) - .Take(15) + var allGuilds = _client.Guilds + .OrderBy(g => g.Name) .ToList(); - if (!guilds.Any()) - { - await Response().Error(strs.listservers_none).SendAsync(); - return; - } + await Response() + .Paginated() + .Items(allGuilds) + .PageSize(9) + .Page((guilds, _) => + { + if (!guilds.Any()) + { + return _sender.CreateEmbed() + .WithDescription(GetText(strs.listservers_none)) + .WithErrorColor(); + } - var embed = _sender.CreateEmbed().WithOkColor(); - foreach (var guild in guilds) - embed.AddField(guild.Name, GetText(strs.listservers(guild.Id, guild.MemberCount, guild.OwnerId))); + var embed = _sender.CreateEmbed() + .WithOkColor(); + foreach (var guild in guilds) + embed.AddField(guild.Name, GetText(strs.listservers(guild.Id, guild.MemberCount, guild.OwnerId))); - await Response().Embed(embed).SendAsync(); + return embed; + }) + .SendAsync(); } [Cmd] diff --git a/src/EllieBot/data/aliases.yml b/src/EllieBot/data/aliases.yml index d8c13df..ff53850 100644 --- a/src/EllieBot/data/aliases.yml +++ b/src/EllieBot/data/aliases.yml @@ -587,10 +587,6 @@ pokemon: pokemonability: - pokemonability - pokeab -memelist: - - memelist -memegen: - - memegen weather: - weather - we @@ -771,7 +767,7 @@ typedel: - typedel typelist: - typelist -listservers: +serverlist: - listservers cleverbot: - cleverbot @@ -804,8 +800,6 @@ autodisconnect: define: - define - def -activity: - - activity setstatus: - setstatus invitecreate: @@ -855,8 +849,6 @@ divorce: waifuinfo: - waifuinfo - waifustats -mal: - - mal setmusicchannel: - setmusicchannel - smch @@ -1148,8 +1140,6 @@ discordpermoverridelist: - dpoli discordpermoverridereset: - dpor -rafflecur: - - rafflecur timelyset: - timelyset timely: @@ -1166,8 +1156,6 @@ massban: - massban masskill: - masskill -rollduel: - - rollduel reroadd: - reroadd - reroa @@ -1209,8 +1197,6 @@ roleid: agerestricttoggle: - nsfwtoggle - artoggle -economy: - - economy purgeuser: - purgeuser imageonlychannel: diff --git a/src/EllieBot/data/strings/commands/commands.en-US.yml b/src/EllieBot/data/strings/commands/commands.en-US.yml index 8e5ff9d..8d378c7 100644 --- a/src/EllieBot/data/strings/commands/commands.en-US.yml +++ b/src/EllieBot/data/strings/commands/commands.en-US.yml @@ -1987,23 +1987,6 @@ pokemonability: params: - ability: desc: "The type of the PokΓ©mon's special power or trait that can be used in battle." -memelist: - desc: Shows a list of template keys (and their respective names) used for `{0}memegen`. - ex: - - '' - params: - - page: - desc: "The number of pages in the list to be displayed." -memegen: - desc: Generates a meme from memelist with specified text. Separate multiple text values with semicolons. Provide no meme text to see an example meme with that template. - ex: - - biw gets iced coffee;in the winter - - ntot - params: - - meme: - desc: "The caption or punchline of the meme, which can be a single sentence or multiple sentences separated by semicolons." - memeText: - desc: "The user-provided text to be displayed on the generated meme." weather: desc: Shows current weather data for the specified city. ex: @@ -2550,7 +2533,7 @@ typelist: params: - page: desc: "The current page number for the list of typing articles." -listservers: +serverlist: desc: Lists servers the bot is on with some basic info. 15 per page. ex: - 3 @@ -2645,13 +2628,6 @@ define: params: - word: desc: "The word being searched for." -activity: - desc: Checks for spammers. - ex: - - '' - params: - - page: - desc: "The number of pages to scan for spam." setstatus: desc: Sets the bot's status. (Online/Idle/Dnd/Invisible) ex: @@ -2808,15 +2784,6 @@ waifuinfo: desc: "The user being targeted, whose waifu information will be displayed." - targetId: desc: "The ID of the person whose waifu stats are being displayed." -mal: - desc: Shows basic info from a MyAnimeList profile. - ex: - - straysocks - params: - - name: - desc: "The username or identifier for the MyAnimeList account being queried." - - usr: - desc: "The user's guild membership information is used to fetch their anime list and other relevant data." setmusicchannel: desc: Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. Persistent server setting. ex: @@ -3871,20 +3838,6 @@ discordpermoverridereset: - '' params: - {} -rafflecur: - desc: Starts or joins a currency raffle with a specified amount. Users who join the raffle will lose the amount of currency specified and add it to the pot. After 30 seconds, random winner will be selected who will receive the whole pot. There is also a `mixed` mode in which the users will be able to join the game with any amount of currency, and have their chances be proportional to the amount they've bet. - ex: - - 20 - - mixed 15 - params: - - _: - desc: "The type of game mode to use, either \"fixed\" or \"mixed\"." - amount: - desc: "The minimum or maximum amount of currency that can be used for betting." - - amount: - desc: "The minimum or maximum amount of currency that can be used for betting." - mixed: - desc: "The parameter determines whether the raffle operates in \"fixed\" or \"proportional\" mode." autodisconnect: desc: Toggles whether the bot should disconnect from the voice channel once it's done playing all of the songs and queue repeat option is set to `none`. ex: @@ -3956,18 +3909,6 @@ masskill: params: - people: desc: "The list of user IDs or usernames to ban from the server and blacklist from the bot." -rollduel: - desc: Challenge someone to a roll duel by specifying the amount and the user you wish to challenge as the parameters. To accept the challenge, just specify the name of the user who challenged you, without the amount. - ex: - - 50 @Someone - - '@Challenger' - params: - - u: - desc: "The user being challenged or accepting the challenge." - - amount: - desc: "The stakes for the roll duel." - u: - desc: "The user being challenged or accepting the challenge." reroadd: desc: |- Specify a message id, emote and a role name to have the bot assign the specified role to the user who reacts to the specified message (in this channel) with the specified emoji. @@ -4158,12 +4099,6 @@ agerestricttoggle: - '' params: - {} -economy: - desc: Breakdown of the current state of the bot's economy. Updates every 3 minutes. - ex: - - '' - params: - - {} purgeuser: desc: Purge user from the database completely. This includes currency, xp, clubs that user owns, waifu info ex: diff --git a/src/EllieBot/data/strings/responses/responses.en-US.json b/src/EllieBot/data/strings/responses/responses.en-US.json index b55d8ad..fe495bb 100644 --- a/src/EllieBot/data/strings/responses/responses.en-US.json +++ b/src/EllieBot/data/strings/responses/responses.en-US.json @@ -977,14 +977,7 @@ "reset_user_confirm": "Are you sure that you want to reset specified user's XP on this server?", "reset_user": "User with id {0} has had their XP reset on this server.", "reset_server": "XP of all users on the server has been reset.", - "economy_state": "State of the economy", - "currency_owned": "Total currency owned by users", - "currency_one_percent": "% of currency owned by top 1%", - "currency_planted": "Currency currently planted", - "owned_waifus_total": "Total value of owned waifus", - "bot_currency": "Currency owned by the bot", "total": "Total", - "bank_accounts": "Bank Accounts", "no_invites": "No invites on this page.", "invite_deleted": "Invite {0} has been deleted.", "group_name_added": "Group #{0} now has a name: {1}",