This commit is contained in:
Toastie 2024-08-08 14:32:31 +12:00
commit 8560eb2ef5
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
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 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 ## [5.1.5] - 01.08.2024
### Added ### 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 - You can once again disable cleverbot responses using fake 'cleverbot:response' module name in permission commands
### Removed ### Removed
- Removed 'rip command - Removed 'rip command
## [5.1.4] - 15.07.2024 ## [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 WORKDIR /source
# Copy the .csproj files for each project
COPY src/Ellie.Marmalade/*.csproj src/Ellie.Marmalade/ COPY src/Ellie.Marmalade/*.csproj src/Ellie.Marmalade/
COPY src/EllieBot/*.csproj src/EllieBot/ COPY src/EllieBot/*.csproj src/EllieBot/
COPY src/EllieBot.Coordinator/*.csproj src/EllieBot.Coordinator/ COPY src/EllieBot.Coordinator/*.csproj src/EllieBot.Coordinator/
COPY src/EllieBot.Generators/*.csproj src/EllieBot.Generators/ COPY src/EllieBot.Generators/*.csproj src/EllieBot.Generators/
COPY src/EllieBot.Voice/*.csproj src/EllieBot.Voice/ COPY src/EllieBot.Voice/*.csproj src/EllieBot.Voice/
COPY NuGet.Config ./
# Restore the dependencies for the EllieBot project
RUN dotnet restore src/EllieBot/ RUN dotnet restore src/EllieBot/
# Copy the rest of the source code
COPY . . COPY . .
# Set the working directory to the EllieBot project
WORKDIR /source/src/EllieBot WORKDIR /source/src/EllieBot
# Build and publish the EllieBot project, then clean up unnecessary files
RUN set -xe; \ RUN set -xe; \
dotnet --version; \ dotnet --version; \
dotnet publish -c Release -o /app --no-restore; \ dotnet publish -c Release -o /app --no-restore; \
@ -19,28 +27,33 @@ RUN set -xe; \
find /app -type f -exec chmod -x {} \; ;\ find /app -type f -exec chmod -x {} \; ;\
chmod +x /app/EllieBot chmod +x /app/EllieBot
# final stage/image # Use the .NET 8.0 runtime as the base image for the final stage
FROM mcr.microsoft.com/dotnet/runtime:6.0 FROM mcr.microsoft.com/dotnet/runtime:8.0
WORKDIR /app WORKDIR /app
# Create a new user, install dependencies, and set up sudoers file
RUN set -xe; \ RUN set -xe; \
useradd -m ellie; \ useradd -m ellie; \
apt-get update; \ apt-get update; \
apt-get install -y --no-install-recommends libopus0 libsodium23 libsqlite3-0 curl ffmpeg python3 sudo; \ 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; \ 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; \ 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; \ chmod a+rx /usr/local/bin/yt-dlp; \
apt-get autoremove -y; \ apt-get autoremove -y; \
apt-get autoclean -y apt-get autoclean -y
# Copy the built application and the entrypoint script from the build stage
COPY --from=build /app ./ COPY --from=build /app ./
COPY docker-entrypoint.sh /usr/local/sbin COPY docker-entrypoint.sh /usr/local/sbin
# Set environment variables
ENV shard_id=0 ENV shard_id=0
ENV total_shards=1 ENV total_shards=1
ENV EllieBot__creds=/app/data/creds.yml ENV EllieBot__creds=/app/data/creds.yml
# Define the data directory as a volume
VOLUME [" /app/data "] VOLUME [" /app/data "]
# Set the entrypoint and default command
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ] ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
CMD dotnet EllieBot.dll "$shard_id" "$total_shards" 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 migrate.ps1 = migrate.ps1
README.md = README.md README.md = README.md
remove-migrations.ps1 = remove-migrations.ps1 remove-migrations.ps1 = remove-migrations.ps1
TODO.md = TODO.md
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EllieBot", "src\EllieBot\EllieBot.csproj", "{4D9001F7-B3E8-48FE-97AA-CFD36DA65A64}" 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 Discord;
using EllieBot;
namespace EllieBot.Marmalade; namespace EllieBot.Marmalade;

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations;
using EllieBot.Db.Models;
namespace EllieBot.Migrations; namespace EllieBot.Migrations;
@ -50,5 +49,9 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
builder.Sql($""" builder.Sql($"""
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL; 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; using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable #nullable disable

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -38,11 +38,11 @@ public sealed class Connect4Game : IDisposable
public Phase CurrentPhase { get; private set; } = Phase.Joining; public Phase CurrentPhase { get; private set; } = Phase.Joining;
public ImmutableArray<Field> GameState public IReadOnlyList<Field> GameState
=> _gameState.ToImmutableArray(); => _gameState.AsReadOnly();
public ImmutableArray<(ulong UserId, string Username)?> Players public IReadOnlyCollection<(ulong UserId, string Username)?> Players
=> _players.ToImmutableArray(); => _players.AsReadOnly();
public (ulong UserId, string Username) CurrentPlayer public (ulong UserId, string Username) CurrentPlayer
=> CurrentPhase == Phase.P1Move ? _players[0].Value : _players[1].Value; => 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 SemaphoreSlim _locker = new(1, 1);
private readonly Options _options; private readonly Options _options;
private readonly ICurrencyService _cs;
private readonly EllieRandom _rng; private readonly EllieRandom _rng;
private Timer playerTimeoutTimer; private Timer playerTimeoutTimer;
@ -73,12 +72,11 @@ public sealed class Connect4Game : IDisposable
public Connect4Game( public Connect4Game(
ulong userId, ulong userId,
string userName, string userName,
Options options, Options options
ICurrencyService cs) )
{ {
_players[0] = (userId, userName); _players[0] = (userId, userName);
_options = options; _options = options;
_cs = cs;
_rng = new(); _rng = new();
for (var i = 0; i < NUMBER_OF_COLUMNS * NUMBER_OF_ROWS; i++) for (var i = 0; i < NUMBER_OF_COLUMNS * NUMBER_OF_ROWS; i++)
@ -99,14 +97,13 @@ public sealed class Connect4Game : IDisposable
{ {
_ = OnGameFailedToStart?.Invoke(this); _ = OnGameFailedToStart?.Invoke(this);
CurrentPhase = Phase.Ended; CurrentPhase = Phase.Ended;
await _cs.AddAsync(_players[0].Value.UserId, _options.Bet, new("connect4", "refund"));
} }
} }
finally { _locker.Release(); } 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(); await _locker.WaitAsync();
try try
@ -117,11 +114,6 @@ public sealed class Connect4Game : IDisposable
if (_players[0].Value.UserId == userId) // same user can't join own game if (_players[0].Value.UserId == userId) // same user can't join own game
return false; 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 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 CurrentPhase = Phase.P1Move; //start the game
playerTimeoutTimer = new(async _ => playerTimeoutTimer = new(async _ =>
{
await _locker.WaitAsync();
try
{ {
await _locker.WaitAsync(); EndGame(Result.OtherPlayerWon, OtherPlayer.UserId);
try }
{ finally { _locker.Release(); }
EndGame(Result.OtherPlayerWon, OtherPlayer.UserId); },
}
finally { _locker.Release(); }
},
null, null,
TimeSpan.FromSeconds(_options.TurnTimer), TimeSpan.FromSeconds(_options.TurnTimer),
TimeSpan.FromSeconds(_options.TurnTimer)); TimeSpan.FromSeconds(_options.TurnTimer));
@ -351,13 +343,8 @@ public sealed class Connect4Game : IDisposable
if (result == Result.Draw) if (result == Result.Draw)
{ {
_cs.AddAsync(CurrentPlayer.UserId, _options.Bet, new("connect4", "draw"));
_cs.AddAsync(OtherPlayer.UserId, _options.Bet, new("connect4", "draw"));
return; return;
} }
if (winId is not null)
_cs.AddAsync(winId.Value, (long)(_options.Bet * 1.98), new("connect4", "win"));
} }
private Field GetPlayerPiece(ulong userId) 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.")] HelpText = "Turn time in seconds. It has to be between 5 and 60. Default 15.")]
public int TurnTimer { get; set; } = 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() public void NormalizeOptions()
{ {
if (TurnTimer is < 5 or > 60) if (TurnTimer is < 5 or > 60)
TurnTimer = 15; TurnTimer = 15;
if (Bet < 0)
Bet = 0;
} }
} }
} }

View file

@ -29,17 +29,15 @@ public partial class Gambling
} }
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly ICurrencyService _cs;
private IUserMessage msg; private IUserMessage msg;
private int repostCounter; private int repostCounter;
public Connect4Commands(DiscordSocketClient client, ICurrencyService cs, GamblingConfigService gamb) public Connect4Commands(DiscordSocketClient client, GamblingConfigService gamb)
: base(gamb) : base(gamb)
{ {
_client = client; _client = client;
_cs = cs;
} }
[Cmd] [Cmd]
@ -48,10 +46,8 @@ public partial class Gambling
public async Task Connect4(params string[] args) public async Task Connect4(params string[] args)
{ {
var (options, _) = OptionsParser.ParseFrom(new Connect4Game.Options(), 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; Connect4Game game;
if ((game = _service.Connect4Games.GetOrAdd(ctx.Channel.Id, newGame)) != newGame) if ((game = _service.Connect4Games.GetOrAdd(ctx.Channel.Id, newGame)) != newGame)
{ {
@ -60,31 +56,17 @@ public partial class Gambling
newGame.Dispose(); newGame.Dispose();
//means game already exists, try to join //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; 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.OnGameStateUpdated += Game_OnGameStateUpdated;
game.OnGameFailedToStart += GameOnGameFailedToStart; game.OnGameFailedToStart += GameOnGameFailedToStart;
game.OnGameEnded += GameOnGameEnded; game.OnGameEnded += GameOnGameEnded;
_client.MessageReceived += ClientMessageReceived; _client.MessageReceived += ClientMessageReceived;
game.Initialize(); game.Initialize();
if (options.Bet == 0) await Response().Confirm(strs.connect4_created).SendAsync();
await Response().Confirm(strs.connect4_created).SendAsync();
else
await Response().Error(strs.connect4_created_bet(N(options.Bet))).SendAsync();
Task ClientMessageReceived(SocketMessage arg) Task ClientMessageReceived(SocketMessage arg)
{ {
@ -99,7 +81,8 @@ public partial class Gambling
if (success) if (success)
{ {
try { await arg.DeleteAsync(); } try
{ await arg.DeleteAsync(); }
catch { } catch { }
} }
else else
@ -109,7 +92,8 @@ public partial class Gambling
RepostCounter++; RepostCounter++;
if (RepostCounter == 0) if (RepostCounter == 0)
{ {
try { msg = await Response().Embed(msg.Embeds.First().ToEmbedBuilder()).SendAsync(); } try
{ msg = await Response().Embed(msg.Embeds.First().ToEmbedBuilder()).SendAsync(); }
catch { } catch { }
} }
} }
@ -151,19 +135,19 @@ public partial class Gambling
title = GetText(strs.connect4_draw); title = GetText(strs.connect4_draw);
return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed() return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
.WithTitle(title) .WithTitle(title)
.WithDescription(GetGameStateText(game)) .WithDescription(GetGameStateText(game))
.WithOkColor() .WithOkColor()
.Build()); .Build());
} }
} }
private async Task Game_OnGameStateUpdated(Connect4Game game) private async Task Game_OnGameStateUpdated(Connect4Game game)
{ {
var embed = _sender.CreateEmbed() var embed = _sender.CreateEmbed()
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}") .WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
.WithDescription(GetGameStateText(game)) .WithDescription(GetGameStateText(game))
.WithOkColor(); .WithOkColor();
if (msg is null) if (msg is null)
@ -198,7 +182,7 @@ public partial class Gambling
for (var i = 0; i < Connect4Game.NUMBER_OF_COLUMNS; i++) for (var i = 0; i < Connect4Game.NUMBER_OF_COLUMNS; i++)
sb.Append(_numbers[i]); sb.Append(_numbers[i]);
return sb.ToString(); return sb.ToString();
} }
} }

View file

@ -13,7 +13,6 @@ using System.Text;
using EllieBot.Modules.Gambling.Rps; using EllieBot.Modules.Gambling.Rps;
using EllieBot.Common.TypeReaders; using EllieBot.Common.TypeReaders;
using EllieBot.Modules.Patronage; using EllieBot.Modules.Patronage;
using EllieBot.Modules.Utility;
namespace EllieBot.Modules.Gambling; namespace EllieBot.Modules.Gambling;
@ -31,8 +30,6 @@ public partial class Gambling : GamblingModule<GamblingService>
private readonly GamblingTxTracker _gamblingTxTracker; private readonly GamblingTxTracker _gamblingTxTracker;
private readonly IPatronageService _ps; private readonly IPatronageService _ps;
private IUserMessage rdMsg;
public Gambling( public Gambling(
IGamblingService gs, IGamblingService gs,
DbService db, DbService db,
@ -105,34 +102,6 @@ public partial class Gambling : GamblingModule<GamblingService>
await Response().Embed(eb).SendAsync(); 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) private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when)
{ {
var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative); 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] [Cmd]
public async Task BetRoll([OverrideTypeReader(typeof(BalanceTypeReader))] long amount) public async Task BetRoll([OverrideTypeReader(typeof(BalanceTypeReader))] long amount)
{ {

View file

@ -2,7 +2,6 @@
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.Gambling.Common; using EllieBot.Modules.Gambling.Common;
using EllieBot.Modules.Gambling.Common.Connect4; using EllieBot.Modules.Gambling.Common.Connect4;
@ -129,38 +128,6 @@ public class GamblingService : IEService, IReadyExecutor
private static readonly TypedKey<EconomyResult> _ecoKey = new("ellie:economy"); 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 readonly SemaphoreSlim _timelyLock = new(1, 1);
private static TypedKey<Dictionary<ulong, long>> _timelyKey private static TypedKey<Dictionary<ulong, long>> _timelyKey

View file

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

View file

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

View file

@ -7,9 +7,7 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using EllieBot.Modules.Gambling;
using EllieBot.Common.TypeReaders; using EllieBot.Common.TypeReaders;
using EllieBot.Modules.Utility;
using Color = SixLabors.ImageSharp.Color; using Color = SixLabors.ImageSharp.Color;
using Image = SixLabors.ImageSharp.Image; using Image = SixLabors.ImageSharp.Image;
@ -25,9 +23,6 @@ public partial class Gambling
[Group] [Group]
public partial class SlotCommands : GamblingSubmodule<IGamblingService> public partial class SlotCommands : GamblingSubmodule<IGamblingService>
{ {
private static decimal totalBet;
private static decimal totalPaidOut;
private readonly IImageCache _images; private readonly IImageCache _images;
private readonly FontProvider _fonts; private readonly FontProvider _fonts;
private readonly DbService _db; private readonly DbService _db;
@ -71,17 +66,19 @@ public partial class Gambling
var eb = _sender.CreateEmbed() var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(Format.Bold(text)) .WithDescription(Format.Bold(text))
.WithImageUrl($"attachment://result.png") .WithImageUrl($"attachment://result.png")
.WithOkColor(); .WithOkColor();
var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again"); var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
var inter = _inter.Create(ctx.User.Id, bb, smc => var inter = _inter.Create(ctx.User.Id,
{ bb,
smc.DeferAsync(); smc =>
return Slot(amount); {
}); smc.DeferAsync();
return Slot(amount);
});
var msg = await ctx.Channel.SendFileAsync(imgStream, var msg = await ctx.Channel.SendFileAsync(imgStream,
"result.png", "result.png",
@ -161,12 +158,6 @@ public partial class Gambling
{ {
return null; return null;
} }
lock (_slotStatsLock)
{
totalBet += amount;
totalPaidOut += result.Won;
}
return result; return result;
} }
@ -213,7 +204,7 @@ public partial class Gambling
{ {
HorizontalAlignment = HorizontalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center, VerticalAlignment = VerticalAlignment.Center,
Origin = new(393, 480) Origin = new(393, 480)
}, },
ownedAmount.ToString(), ownedAmount.ToString(),
fontColor)); fontColor));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
#nullable disable #nullable disable
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Modules.Games.Common; using EllieBot.Modules.Games.Common;
using EllieBot.Modules.Games.Common.Acrophobia; using EllieBot.Modules.Games.Common.Acrophobia;
using EllieBot.Modules.Games.Common.Nunchi; using EllieBot.Modules.Games.Common.Nunchi;
@ -8,11 +7,10 @@ using Newtonsoft.Json;
namespace EllieBot.Modules.Games.Services; namespace EllieBot.Modules.Games.Services;
public class GamesService : IEService, IReadyExecutor public class GamesService : IEService
{ {
private const string TYPING_ARTICLES_PATH = "data/typing_articles3.json"; private const string TYPING_ARTICLES_PATH = "data/typing_articles3.json";
public ConcurrentDictionary<ulong, GirlRating> GirlRatings { get; } = new();
public IReadOnlyList<string> EightBallResponses public IReadOnlyList<string> EightBallResponses
=> _gamesConfig.Data.EightBallResponses; => _gamesConfig.Data.EightBallResponses;
@ -25,7 +23,6 @@ public class GamesService : IEService, IReadyExecutor
public ConcurrentDictionary<ulong, TypingGame> RunningContests { get; } = new(); public ConcurrentDictionary<ulong, TypingGame> RunningContests { get; } = new();
public ConcurrentDictionary<ulong, NunchiGame> NunchiGames { get; } = new(); public ConcurrentDictionary<ulong, NunchiGame> NunchiGames { get; } = new();
public AsyncLazy<RatingTexts> Ratings { get; }
private readonly GamesConfigService _gamesConfig; private readonly GamesConfigService _gamesConfig;
private readonly IHttpClientFactory _httpFactory; private readonly IHttpClientFactory _httpFactory;
@ -41,7 +38,6 @@ public class GamesService : IEService, IReadyExecutor
SizeLimit = 500_000 SizeLimit = 500_000
}); });
Ratings = new(GetRatingTexts);
_rng = new EllieRandom(); _rng = new EllieRandom();
try 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) public void AddTypingArticle(IUser user, string text)
{ {
TypingArticles.Add(new() TypingArticles.Add(new()
@ -104,15 +84,4 @@ public class GamesService : IEService, IReadyExecutor
File.WriteAllText(TYPING_ARTICLES_PATH, JsonConvert.SerializeObject(articles)); File.WriteAllText(TYPING_ARTICLES_PATH, JsonConvert.SerializeObject(articles));
return removed; 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(); return sb.ToString();
} }
private EmbedBuilder? questionEmbed = null; private EmbedBuilder? questionEmbed;
private IUserMessage? questionMessage = null; private IUserMessage? questionMessage;
private bool showHowToQuit = false; private bool showHowToQuit;
private void RegisterEvents(TriviaGame trivia) private void RegisterEvents(TriviaGame trivia)
{ {

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
#nullable disable #nullable disable
using LinqToDB; using LinqToDB;
using EllieBot.Db;
using EllieBot.Modules.Music.Services; using EllieBot.Modules.Music.Services;
using EllieBot.Db.Models; 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; using System.Diagnostics.CodeAnalysis;
namespace EllieBot.Modules.Music.Services; namespace EllieBot.Modules.Music.Services;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,133 +10,6 @@ public partial class Searches
[Group] [Group]
public partial class AnimeSearchCommands : EllieModule<AnimeSearchService> 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] [Cmd]
public async Task Anime([Leftover] string query) public async Task Anime([Leftover] string query)
{ {
@ -152,19 +25,19 @@ public partial class Searches
} }
var embed = _sender.CreateEmbed() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(animeData.Synopsis.Replace("<br>", .WithDescription(animeData.Synopsis.Replace("<br>",
Environment.NewLine, Environment.NewLine,
StringComparison.InvariantCulture)) StringComparison.InvariantCulture))
.WithTitle(animeData.TitleEnglish) .WithTitle(animeData.TitleEnglish)
.WithUrl(animeData.Link) .WithUrl(animeData.Link)
.WithImageUrl(animeData.ImageUrlLarge) .WithImageUrl(animeData.ImageUrlLarge)
.AddField(GetText(strs.episodes), animeData.TotalEpisodes.ToString(), true) .AddField(GetText(strs.episodes), animeData.TotalEpisodes.ToString(), true)
.AddField(GetText(strs.status), animeData.AiringStatus, true) .AddField(GetText(strs.status), animeData.AiringStatus, true)
.AddField(GetText(strs.genres), .AddField(GetText(strs.genres),
string.Join(",\n", animeData.Genres.Any() ? animeData.Genres : ["none"]), string.Join(",\n", animeData.Genres.Any() ? animeData.Genres : ["none"]),
true) true)
.WithFooter($"{GetText(strs.score)} {animeData.AverageScore} / 100"); .WithFooter($"{GetText(strs.score)} {animeData.AverageScore} / 100");
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }
@ -184,19 +57,19 @@ public partial class Searches
} }
var embed = _sender.CreateEmbed() var embed = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(mangaData.Synopsis.Replace("<br>", .WithDescription(mangaData.Synopsis.Replace("<br>",
Environment.NewLine, Environment.NewLine,
StringComparison.InvariantCulture)) StringComparison.InvariantCulture))
.WithTitle(mangaData.TitleEnglish) .WithTitle(mangaData.TitleEnglish)
.WithUrl(mangaData.Link) .WithUrl(mangaData.Link)
.WithImageUrl(mangaData.ImageUrlLge) .WithImageUrl(mangaData.ImageUrlLge)
.AddField(GetText(strs.chapters), mangaData.TotalChapters.ToString(), true) .AddField(GetText(strs.chapters), mangaData.TotalChapters.ToString(), true)
.AddField(GetText(strs.status), mangaData.PublishingStatus, true) .AddField(GetText(strs.status), mangaData.PublishingStatus, true)
.AddField(GetText(strs.genres), .AddField(GetText(strs.genres),
string.Join(",\n", mangaData.Genres.Any() ? mangaData.Genres : ["none"]), string.Join(",\n", mangaData.Genres.Any() ? mangaData.Genres : ["none"]),
true) true)
.WithFooter($"{GetText(strs.score)} {mangaData.AverageScore} / 100"); .WithFooter($"{GetText(strs.score)} {mangaData.AverageScore} / 100");
await Response().Embed(embed).SendAsync(); 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 change = (stock.Price - stock.Close).ToString("N2", Culture);
var changePercent = (1 - (stock.Close / stock.Price)).ToString("P1", 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 price = stock.Price.ToString("C2", localCulture);
var eb = _sender.CreateEmbed() var eb = _sender.CreateEmbed()

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