Compare commits

..

19 commits

Author SHA1 Message Date
3244bad7da
5.1.6 2024-08-08 14:32:31 +12:00
09a4a69521
Updated changelog, upped version to 5.1.6 2024-08-08 13:55:05 +12:00
e26e2a7404
fixed one of the migrations which was preventing some bots from starting 2024-08-08 13:48:43 +12:00
c61f3f1433
fixed user id in afk message dm 2024-08-08 13:46:32 +12:00
6e0a129bc0
Afk messages should now be sent in DMs to prevent abuse
Also updated EllieBot.sln
2024-08-08 13:37:39 +12:00
7a82f262f8
Updated Dockerfile
Why did I not update this when 5.1 rleased?
2024-08-07 12:09:11 +12:00
a8aa1d909f
doing 'gencmdlist will now generate a data/commandlist.json which is the same as the file you receive in discord 2024-08-07 11:49:24 +12:00
027690b3d7
More cleanup 2024-08-07 11:45:10 +12:00
9892de7c2e
Removed a lot of old useless/broken commands. Improved 'listserver.
Full changes in changelog
2024-08-07 11:32:51 +12:00
811c126174
Fixed a build error introduced in 7399e8bac1
How did this pass the test?
2024-08-07 11:30:26 +12:00
a6c2ec0511
small cleanup 2024-08-06 22:52:02 +12:00
7399e8bac1
small cleanup in clubs 2024-08-06 22:38:09 +12:00
fe7445eefb
removed unused usings, no functional change 2024-08-06 22:33:22 +12:00
d428fc5532
Removed some unused code, no functional change 2024-08-06 21:53:28 +12:00
66560bb769
lmgtfy should now be properly shortened, small refactor of 'shorten command 2024-08-06 00:29:40 +12:00
7d0ef44e8e
Removed .gitlab-ci.yml 2024-08-05 22:52:58 +12:00
20a5d25eca
Fixed .gitlab-ci.yml 2024-08-05 21:45:08 +12:00
82f82d0fb6
Added a space to CHANGELOG.md and added .gitlab-ci.yml 2024-08-05 21:34:29 +12:00
3f7e5d5054
Possible fix for a broken timestamp tag in remind list 2024-08-05 14:04:18 +12:00
149 changed files with 398 additions and 1351 deletions

View file

@ -2,6 +2,33 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
## [5.1.6] - 08.08.2024
### Added
- `'serverlist` is now paginated
### Changed
- `'listservers` renamed to `'serverlist`
### Fixed
- `'afk` messages can no longer ping, and the response is moved to DMs to avoid abuse
- Possible fix for `'remind` timestamp
### 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
@ -25,6 +52,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
- You can once again disable cleverbot responses using fake 'cleverbot:response' module name in permission commands
### Removed
- Removed 'rip command
## [5.1.4] - 15.07.2024

View file

@ -1,16 +1,24 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
# Use the .NET 8.0 SDK as the base image for the build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /source
# Copy the .csproj files for each project
COPY src/Ellie.Marmalade/*.csproj src/Ellie.Marmalade/
COPY src/EllieBot/*.csproj src/EllieBot/
COPY src/EllieBot.Coordinator/*.csproj src/EllieBot.Coordinator/
COPY src/EllieBot.Generators/*.csproj src/EllieBot.Generators/
COPY src/EllieBot.Voice/*.csproj src/EllieBot.Voice/
COPY NuGet.Config ./
# Restore the dependencies for the EllieBot project
RUN dotnet restore src/EllieBot/
# Copy the rest of the source code
COPY . .
# Set the working directory to the EllieBot project
WORKDIR /source/src/EllieBot
# Build and publish the EllieBot project, then clean up unnecessary files
RUN set -xe; \
dotnet --version; \
dotnet publish -c Release -o /app --no-restore; \
@ -19,28 +27,33 @@ RUN set -xe; \
find /app -type f -exec chmod -x {} \; ;\
chmod +x /app/EllieBot
# final stage/image
FROM mcr.microsoft.com/dotnet/runtime:6.0
# Use the .NET 8.0 runtime as the base image for the final stage
FROM mcr.microsoft.com/dotnet/runtime:8.0
WORKDIR /app
# Create a new user, install dependencies, and set up sudoers file
RUN set -xe; \
useradd -m ellie; \
apt-get update; \
apt-get install -y --no-install-recommends libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 sudo; \
update-alternatives --install /usr/bin/python python /usr/bin/python3.9 1; \
echo 'Defaults>ellie env_keep+="ASPNETCORE_* DOTNET_* EllieBot_* shard_id total_shards TZ"' > /etc/sudoers.d/ellie; \
curl -Lo /usr/local/bin/yt-dlp https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp; \
chmod a+rx /usr/local/bin/yt-dlp; \
apt-get autoremove -y; \
apt-get autoclean -y
# Copy the built application and the entrypoint script from the build stage
COPY --from=build /app ./
COPY docker-entrypoint.sh /usr/local/sbin
# Set environment variables
ENV shard_id=0
ENV total_shards=1
ENV EllieBot__creds=/app/data/creds.yml
# Define the data directory as a volume
VOLUME [" /app/data "]
# Set the entrypoint and default command
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
CMD dotnet EllieBot.dll "$shard_id" "$total_shards"

View file

@ -13,7 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
migrate.ps1 = migrate.ps1
README.md = README.md
remove-migrations.ps1 = remove-migrations.ps1
TODO.md = TODO.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EllieBot", "src\EllieBot\EllieBot.csproj", "{4D9001F7-B3E8-48FE-97AA-CFD36DA65A64}"

View file

@ -1,5 +1,4 @@
using Discord;
using EllieBot;
namespace EllieBot.Marmalade;

View file

@ -88,7 +88,7 @@ public sealed class Bot : IBot
public IReadOnlyList<ulong> GetCurrentGuildIds()
=> Client.Guilds.Select(x => x.Id).ToList();
=> Client.Guilds.Select(x => x.Id).ToList().ToList();
private void AddServices()
{
@ -114,7 +114,7 @@ public sealed class Bot : IBot
// svcs.Components.Remove<IPlanner, Planner>();
// svcs.Components.Add<IPlanner, RemovablePlanner>();
svcs.AddSingleton<IBotCredentials, IBotCredentials>(_ => _credsProvider.GetCreds());
svcs.AddSingleton<IBotCredentials>(_ => _credsProvider.GetCreds());
svcs.AddSingleton<DbService, DbService>(_db);
svcs.AddSingleton<IBotCredsProvider>(_credsProvider);
svcs.AddSingleton<DiscordSocketClient>(Client);

View file

@ -1,6 +1,4 @@
#nullable disable
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;

View file

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

View file

@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
using EllieBot.Db.Models;
namespace EllieBot.Migrations;
@ -50,5 +49,9 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
builder.Sql($"""
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
""");
builder.Sql("""
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
""");
}
}

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EllieBot.Migrations
{

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EllieBot.Migrations
{

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EllieBot.Migrations
{

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EllieBot.Migrations
{

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EllieBot.Migrations
{

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,5 +1,4 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Db.Models;
using EllieBot.Modules.Administration._common.results;

View file

@ -4,7 +4,6 @@ using System.Net;
using System.Threading.Channels;
using LinqToDB;
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
namespace EllieBot.Modules.Administration.Services;

View file

@ -1,6 +1,4 @@
#nullable disable
using EllieBot.Db;
namespace EllieBot.Modules.Administration.Services;
public class GameVoiceChannelService : IEService

View file

@ -1,5 +1,4 @@
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Db.Models;
using System.Threading.Channels;

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration.Services;

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Db.Models;
using System.Threading.Channels;

View file

@ -1,5 +1,4 @@
#nullable disable
using EllieBot.Modules.Patronage;
using EllieBot.Db.Models;
using OneOf;
using OneOf.Types;

View file

@ -2,7 +2,6 @@
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Modules.Patronage;
using EllieBot.Db.Models;
using OneOf.Types;

View file

@ -3,7 +3,6 @@ using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Db.Models;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
namespace EllieBot.Modules.Administration;

View file

@ -546,7 +546,7 @@ public partial class Administration
text = await repSvc.ReplaceAsync(text, repCtx);
await Response().Channel(ch).Text(text).SendAsync();
await ctx.OkAsync();;
await ctx.OkAsync();
}
[Cmd]

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration.Services;

View file

@ -1,7 +1,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Modules.Administration.Services;
using EllieBot.Db.Models;

View file

@ -1,5 +1,4 @@
#nullable disable
using EllieBot.Db;
using EllieBot.Db.Models;
using EllieBot.Common.ModuleBehaviors;

View file

@ -4,7 +4,6 @@ using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Common.TypeReaders.Models;
using EllieBot.Db;
using EllieBot.Modules.Permissions.Services;
using EllieBot.Db.Models;
using Newtonsoft.Json;

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Administration.Services;

View file

@ -2,7 +2,6 @@
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Common.Yml;
using EllieBot.Db;
using EllieBot.Db.Models;
using System.Runtime.CompilerServices;
using LinqToDB.EntityFrameworkCore;

View file

@ -3,7 +3,6 @@ using EllieBot.Common.TypeReaders;
using EllieBot.Modules.Gambling.Common;
using EllieBot.Modules.Gambling.Common.Blackjack;
using EllieBot.Modules.Gambling.Services;
using EllieBot.Modules.Utility;
namespace EllieBot.Modules.Gambling;

View file

@ -38,11 +38,11 @@ public sealed class Connect4Game : IDisposable
public Phase CurrentPhase { get; private set; } = Phase.Joining;
public ImmutableArray<Field> GameState
=> _gameState.ToImmutableArray();
public IReadOnlyList<Field> 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<bool> Join(ulong userId, string userName, int bet)
public async Task<bool> 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;
}
}
}

View file

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

View file

@ -13,7 +13,6 @@ using System.Text;
using EllieBot.Modules.Gambling.Rps;
using EllieBot.Common.TypeReaders;
using EllieBot.Modules.Patronage;
using EllieBot.Modules.Utility;
namespace EllieBot.Modules.Gambling;
@ -31,8 +30,6 @@ public partial class Gambling : GamblingModule<GamblingService>
private readonly GamblingTxTracker _gamblingTxTracker;
private readonly IPatronageService _ps;
private IUserMessage rdMsg;
public Gambling(
IGamblingService gs,
DbService db,
@ -105,34 +102,6 @@ public partial class Gambling : GamblingModule<GamblingService>
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);
@ -602,117 +571,6 @@ public partial class Gambling : GamblingModule<GamblingService>
}
}
[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)
{

View file

@ -2,7 +2,6 @@
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Db.Models;
using EllieBot.Modules.Gambling.Common;
using EllieBot.Modules.Gambling.Common.Connect4;
@ -129,38 +128,6 @@ public class GamblingService : IEService, IReadyExecutor
private static readonly TypedKey<EconomyResult> _ecoKey = new("ellie:economy");
public async Task<EconomyResult> GetEconomyAsync()
{
var data = await _cache.GetOrAddAsync(_ecoKey,
async () =>
{
await using var uow = _db.GetDbContext();
var cash = uow.Set<DiscordUser>().GetTotalCurrency();
var onePercent = uow.Set<DiscordUser>().GetTopOnePercentCurrency(_client.CurrentUser.Id);
decimal planted = uow.Set<PlantedCurrency>().AsQueryable().Sum(x => x.Amount);
var waifus = uow.Set<WaifuInfo>().GetTotalValue();
var bot = await uow.Set<DiscordUser>().GetUserCurrencyAsync(_client.CurrentUser.Id);
decimal bank = await uow.GetTable<BankUser>()
.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<Dictionary<ulong, long>> _timelyKey

View file

@ -1,7 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Db.Models;
using SixLabors.Fonts;
using SixLabors.ImageSharp;
@ -32,7 +31,6 @@ public class PlantPickService : IEService, IExecNoCommand
public PlantPickService(
DbService db,
CommandHandler cmd,
IBotStrings strings,
IImageCache images,
FontProvider fonts,
@ -108,7 +106,6 @@ public class PlantPickService : IEService, IExecNoCommand
/// Get a random currency image stream, with an optional password sticked onto it.
/// </summary>
/// <param name="pass">Optional password to add to top left corner.</param>
/// <param name="extension">Extension of the file, defaults to png</param>
/// <returns>Stream of the currency image</returns>
public async Task<(Stream, string)> GetRandomCurrencyImageAsync(string pass)
{

View file

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

View file

@ -1,69 +0,0 @@
#nullable disable
namespace EllieBot.Modules.Gambling.Common;
public class CurrencyRaffleGame
{
public enum Type
{
Mixed,
Normal
}
public IEnumerable<User> Users
=> _users;
public Type GameType { get; }
private readonly HashSet<User> _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;
}
}

View file

@ -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<ulong, CurrencyRaffleGame> 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<IUser, long, Task> 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();
}
}
}

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Modules.Gambling.Common;
using EllieBot.Modules.Gambling.Services;
using EllieBot.Db.Models;

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Gambling.Services;

View file

@ -7,9 +7,7 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using EllieBot.Modules.Gambling;
using EllieBot.Common.TypeReaders;
using EllieBot.Modules.Utility;
using Color = SixLabors.ImageSharp.Color;
using Image = SixLabors.ImageSharp.Image;
@ -25,9 +23,6 @@ public partial class Gambling
[Group]
public partial class SlotCommands : GamblingSubmodule<IGamblingService>
{
private static decimal totalBet;
private static decimal totalPaidOut;
private readonly IImageCache _images;
private readonly FontProvider _fonts;
private readonly DbService _db;
@ -71,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",
@ -161,12 +158,6 @@ public partial class Gambling
{
return null;
}
lock (_slotStatsLock)
{
totalBet += amount;
totalPaidOut += result.Won;
}
return result;
}
@ -213,7 +204,7 @@ public partial class Gambling
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Origin = new(393, 480)
Origin = new(393, 480)
},
ownedAmount.ToString(),
fontColor));

View file

@ -3,7 +3,7 @@ using EllieBot.Modules.Gambling.Common;
using EllieBot.Modules.Gambling.Common.Waifu;
using EllieBot.Modules.Gambling.Services;
using EllieBot.Db.Models;
using TwitchLib.Api.Helix.Models.Teams;
using System.Globalization;
namespace EllieBot.Modules.Gambling;
@ -153,12 +153,12 @@ public partial class Gambling
await Response().Confirm(strs.waifu_divorced_notlike(N(amount))).SendAsync();
else if (result == DivorceResult.NotYourWife)
await Response().Error(strs.waifu_not_yours).SendAsync();
else
else if (remaining is { } rem)
{
await Response()
.Error(strs.waifu_recent_divorce(
Format.Bold(((int)remaining?.TotalHours).ToString()),
Format.Bold(remaining?.Minutes.ToString())))
Format.Bold(((int)rem.TotalHours).ToString()),
Format.Bold(rem.Minutes.ToString())))
.SendAsync();
}
}
@ -238,7 +238,7 @@ public partial class Gambling
private string GetLbString(WaifuLbResult w)
{
var claimer = "no one";
var status = string.Empty;
string status;
var waifuUsername = w.Username.TrimTo(20);
var claimerUsername = w.Claimer?.TrimTo(20);
@ -377,7 +377,8 @@ public partial class Gambling
if (sucess)
{
await Response()
.Confirm(strs.waifu_gift(Format.Bold($"{GetCountString(items)}{items.Item} {items.Item.ItemEmoji}"),
.Confirm(strs.waifu_gift(
Format.Bold($"{GetCountString(items)}{items.Item} {items.Item.ItemEmoji}"),
Format.Bold(waifu.ToString())))
.SendAsync();
}

View file

@ -3,11 +3,9 @@ using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Db.Models;
using EllieBot.Modules.Gambling.Common;
using EllieBot.Modules.Gambling.Common.Waifu;
using SixLabors.ImageSharp;
namespace EllieBot.Modules.Gambling.Services;

View file

@ -1,5 +1,4 @@
#nullable disable
using EllieBot.Modules.Gambling;
using EllieBot.Modules.Gambling.Betdraw;
using EllieBot.Modules.Gambling.Rps;
using OneOf;

View file

@ -1,5 +1,4 @@
#nullable disable
using EllieBot.Modules.Gambling;
using EllieBot.Modules.Gambling.Betdraw;
using EllieBot.Modules.Gambling.Rps;
using EllieBot.Modules.Gambling.Services;

View file

@ -1,5 +1,4 @@
using System.Text.RegularExpressions;
using EllieBot.Db;
using EllieBot.Db.Models;
using EllieBot.Modules.Gambling.Services;
using NCalc;

View file

@ -1,7 +1,5 @@
#nullable disable
using EllieBot.Db;
using EllieBot.Modules.Games.Services;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Games;

View file

@ -1,6 +1,4 @@
#nullable disable
using System.CodeDom;
namespace EllieBot.Modules.Games.Common.ChatterBot;
public sealed class ThinkResult

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.Extensions.Caching.Memory;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Modules.Games.Common;
using EllieBot.Modules.Games.Common.Acrophobia;
using EllieBot.Modules.Games.Common.Nunchi;
@ -8,11 +7,10 @@ using Newtonsoft.Json;
namespace EllieBot.Modules.Games.Services;
public class GamesService : IEService, IReadyExecutor
public class GamesService : IEService
{
private const string TYPING_ARTICLES_PATH = "data/typing_articles3.json";
public ConcurrentDictionary<ulong, GirlRating> GirlRatings { get; } = new();
public IReadOnlyList<string> EightBallResponses
=> _gamesConfig.Data.EightBallResponses;
@ -25,7 +23,6 @@ public class GamesService : IEService, IReadyExecutor
public ConcurrentDictionary<ulong, TypingGame> RunningContests { get; } = new();
public ConcurrentDictionary<ulong, NunchiGame> NunchiGames { get; } = new();
public AsyncLazy<RatingTexts> Ratings { get; }
private readonly GamesConfigService _gamesConfig;
private readonly IHttpClientFactory _httpFactory;
@ -41,7 +38,6 @@ public class GamesService : IEService, IReadyExecutor
SizeLimit = 500_000
});
Ratings = new(GetRatingTexts);
_rng = new EllieRandom();
try
@ -55,22 +51,6 @@ public class GamesService : IEService, IReadyExecutor
}
}
public async Task OnReadyAsync()
{
// reset rating once a day
using var timer = new PeriodicTimer(TimeSpan.FromDays(1));
while (await timer.WaitForNextTickAsync())
GirlRatings.Clear();
}
private async Task<RatingTexts> GetRatingTexts()
{
using var http = _httpFactory.CreateClient();
var text = await http.GetStringAsync(
"https://nadeko-pictures.nyc3.digitaloceanspaces.com/other/rategirl/rates.json");
return JsonConvert.DeserializeObject<RatingTexts>(text);
}
public void AddTypingArticle(IUser user, string text)
{
TypingArticles.Add(new()
@ -104,15 +84,4 @@ public class GamesService : IEService, IReadyExecutor
File.WriteAllText(TYPING_ARTICLES_PATH, JsonConvert.SerializeObject(articles));
return removed;
}
public class RatingTexts
{
public string Nog { get; set; }
public string Tra { get; set; }
public string Fun { get; set; }
public string Uni { get; set; }
public string Wif { get; set; }
public string Dat { get; set; }
public string Dan { get; set; }
}
}

View file

@ -1,61 +0,0 @@
#nullable disable
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using Image = SixLabors.ImageSharp.Image;
namespace EllieBot.Modules.Games.Common;
public class GirlRating
{
public double Crazy { get; }
public double Hot { get; }
public int Roll { get; }
public string Advice { get; }
public AsyncLazy<Stream> Stream { get; }
private readonly IImageCache _images;
public GirlRating(
IImageCache images,
double crazy,
double hot,
int roll,
string advice)
{
_images = images;
Crazy = crazy;
Hot = hot;
Roll = roll;
Advice = advice; // convenient to have it here, even though atm there are only few different ones.
Stream = new(async () =>
{
try
{
var bgBytes = await _images.GetRategirlBgAsync();
using var img = Image.Load(bgBytes);
const int minx = 35;
const int miny = 385;
const int length = 345;
var pointx = (int)(minx + (length * (Hot / 10)));
var pointy = (int)(miny - (length * ((Crazy - 4) / 6)));
var dotBytes = await _images.GetRategirlDotAsync();
using (var pointImg = Image.Load(dotBytes))
{
img.Mutate(x => x.DrawImage(pointImg, new(pointx - 10, pointy - 10), new GraphicsOptions()));
}
var imgStream = new MemoryStream();
img.SaveAsPng(imgStream);
return imgStream;
}
catch (Exception ex)
{
Log.Warning(ex, "Error getting RateGirl image");
return null;
}
});
}
}

View file

@ -107,9 +107,9 @@ public partial class Games
return sb.ToString();
}
private EmbedBuilder? questionEmbed = null;
private IUserMessage? questionMessage = null;
private bool showHowToQuit = false;
private EmbedBuilder? questionEmbed;
private IUserMessage? questionMessage;
private bool showHowToQuit;
private void RegisterEvents(TriviaGame trivia)
{

View file

@ -11,12 +11,19 @@ public sealed class TriviaGame
private readonly IQuestionPool _questionPool;
#region Events
public event Func<TriviaGame, TriviaQuestion, Task> OnQuestion = static delegate { return Task.CompletedTask; };
public event Func<TriviaGame, TriviaQuestion, Task> OnHint = static delegate { return Task.CompletedTask; };
public event Func<TriviaGame, Task> OnStats = static delegate { return Task.CompletedTask; };
public event Func<TriviaGame, TriviaUser, TriviaQuestion, bool, Task> OnGuess = static delegate { return Task.CompletedTask; };
public event Func<TriviaGame, TriviaUser, TriviaQuestion, bool, Task> OnGuess = static delegate
{
return Task.CompletedTask;
};
public event Func<TriviaGame, TriviaQuestion, Task> OnTimeout = static delegate { return Task.CompletedTask; };
public event Func<TriviaGame, Task> OnEnded = static delegate { return Task.CompletedTask; };
#endregion
private bool _isStopped;
@ -24,7 +31,7 @@ public sealed class TriviaGame
public TriviaQuestion? CurrentQuestion { get; set; }
private readonly ConcurrentDictionary<ulong, int> _users = new ();
private readonly ConcurrentDictionary<ulong, int> _users = new();
private readonly Channel<(TriviaUser User, string Input)> _inputs
= Channel.CreateUnbounded<(TriviaUser, string)>(new UnboundedChannelOptions
@ -41,8 +48,8 @@ public sealed class TriviaGame
_questionPool = _opts.IsPokemon
? new PokemonQuestionPool(cache)
: new DefaultQuestionPool(cache);
}
public async Task RunAsync()
{
await GameLoop();
@ -50,7 +57,8 @@ public sealed class TriviaGame
private async Task GameLoop()
{
Task TimeOutFactory() => Task.Delay(_opts.QuestionTimer * 1000 / 2);
Task TimeOutFactory()
=> Task.Delay(_opts.QuestionTimer * 1000 / 2);
var errorCount = 0;
var inactivity = 0;
@ -91,7 +99,8 @@ public sealed class TriviaGame
{
// clear out all of the past guesses
while (_inputs.Reader.TryRead(out _))
;
{
}
await OnQuestion(this, question);
}
@ -121,7 +130,7 @@ public sealed class TriviaGame
if (task == halfGuessTimerTask)
{
readCancel.Cancel();
// if hint is already sent, means time expired
// break (end the round)
if (hintSent)
@ -213,7 +222,7 @@ public sealed class TriviaGame
public async Task TriggerQuestionAsync()
{
if(CurrentQuestion is TriviaQuestion q)
if (CurrentQuestion is TriviaQuestion q)
await OnQuestion(this, q);
}
}

View file

@ -509,6 +509,7 @@ public sealed partial class Help : EllieModule<HelpService>
// send the indented file to chat
await using var rDataStream = new MemoryStream(Encoding.ASCII.GetBytes(readableData));
await File.WriteAllTextAsync("data/commandlist.json", readableData);
await ctx.Channel.SendFileAsync(rDataStream, "cmds.json", GetText(strs.commandlist_regen));
}

View file

@ -1,7 +1,6 @@
#nullable disable
using EllieBot.Modules.Music.Services;
using EllieBot.Db.Models;
using EllieBot.Modules.Utility;
namespace EllieBot.Modules.Music;

View file

@ -1,6 +1,5 @@
#nullable disable
using LinqToDB;
using EllieBot.Db;
using EllieBot.Modules.Music.Services;
using EllieBot.Db.Models;

View file

@ -1,5 +1,4 @@
using EllieBot.Db;
using EllieBot.Db.Models;
using EllieBot.Db.Models;
using System.Diagnostics.CodeAnalysis;
namespace EllieBot.Modules.Music.Services;

View file

@ -61,8 +61,8 @@ public class RadioResolver : IRadioResolver
try
{
var m = _m3URegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
var res = m.Groups["url"].ToString();
return res.Trim();
}
catch
{
@ -76,8 +76,8 @@ public class RadioResolver : IRadioResolver
try
{
var m = _asxRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
var res = m.Groups["url"].ToString();
return res.Trim();
}
catch
{
@ -91,8 +91,8 @@ public class RadioResolver : IRadioResolver
try
{
var m = _xspfRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
var res = m.Groups["url"].ToString();
return res.Trim();
}
catch
{

View file

@ -2,8 +2,6 @@
using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using StackExchange.Redis;
using System.Diagnostics;
namespace EllieBot.Modules.Patronage;
@ -21,16 +19,11 @@ public sealed class PatronageService
public int Priority
=> int.MinValue;
private static readonly PatronTier[] _tiers = Enum.GetValues<PatronTier>();
private readonly PatronageConfig _pConf;
private readonly DbService _db;
private readonly DiscordSocketClient _client;
private readonly ISubscriptionHandler _subsHandler;
private static readonly TypedKey<long> _quotaKey
= new($"quota:last_hourly_reset");
private readonly IBotCache _cache;
private readonly IBotCredsProvider _creds;
private readonly IMessageSenderService _sender;
@ -135,19 +128,19 @@ public sealed class PatronageService
// user is charged again for this month
// if his sub would end in teh future, extend it by one month.
// if it's not, just add 1 month to the last charge date
var count = await ctx.GetTable<PatronUser>()
.Where(x => x.UniquePlatformUserId
== subscriber.UniquePlatformUserId)
.UpdateAsync(old => new()
{
UserId = subscriber.UserId,
AmountCents = subscriber.Cents,
LastCharge = lastChargeUtc,
ValidThru = old.ValidThru >= todayDate
// ? Sql.DateAdd(Sql.DateParts.Month, 1, old.ValidThru).Value
? old.ValidThru.AddMonths(1)
: dateInOneMonth,
});
await ctx.GetTable<PatronUser>()
.Where(x => x.UniquePlatformUserId
== subscriber.UniquePlatformUserId)
.UpdateAsync(old => new()
{
UserId = subscriber.UserId,
AmountCents = subscriber.Cents,
LastCharge = lastChargeUtc,
ValidThru = old.ValidThru >= todayDate
// ? Sql.DateAdd(Sql.DateParts.Month, 1, old.ValidThru).Value
? old.ValidThru.AddMonths(1)
: dateInOneMonth,
});
dbPatron.UserId = subscriber.UserId;
@ -332,7 +325,7 @@ public sealed class PatronageService
{
if (!_pConf.Data.IsEnabled)
return _infiniteQuota;
var maybePatron = await GetPatronAsync(userId);
if (maybePatron is not { } patron)

View file

@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
namespace EllieBot.Modules.Permissions.Services;

View file

@ -1,7 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.TypeReaders;
using EllieBot.Db;
using EllieBot.Modules.Permissions.Services;
using EllieBot.Db.Models;

View file

@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Db;
using EllieBot.Modules.Permissions.Services;
using EllieBot.Db.Models;

View file

@ -1,7 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Db.Models;
namespace EllieBot.Modules.Permissions.Services;

View file

@ -1,7 +1,6 @@
#nullable disable
using EllieBot.Common.TypeReaders;
using EllieBot.Common.TypeReaders.Models;
using EllieBot.Db;
using EllieBot.Modules.Permissions.Common;
using EllieBot.Modules.Permissions.Services;
using EllieBot.Db.Models;

View file

@ -1,7 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Modules.Permissions.Common;
using EllieBot.Db.Models;

View file

@ -10,133 +10,6 @@ public partial class Searches
[Group]
public partial class AnimeSearchCommands : EllieModule<AnimeSearchService>
{
// [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("<br>", 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("<br>",
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("<br>",
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("<br>",
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("<br>",
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();
}

View file

@ -65,18 +65,6 @@ public partial class Searches
var change = (stock.Price - stock.Close).ToString("N2", Culture);
var changePercent = (1 - (stock.Close / stock.Price)).ToString("P1", Culture);
var sign50 = stock.Change50d >= 0
? "\\🔼"
: "\\🔻";
var change50 = (stock.Change50d).ToString("P1", Culture);
var sign200 = stock.Change200d >= 0
? "\\🔼"
: "\\🔻";
var change200 = (stock.Change200d).ToString("P1", Culture);
var price = stock.Price.ToString("C2", localCulture);
var eb = _sender.CreateEmbed()

Some files were not shown because too many files have changed in this diff Show more