Initial v6 version #32

Manually merged
toastie_t0ast merged 54 commits from v6-dev into v6 2025-02-28 23:18:39 +00:00
338 changed files with 11514 additions and 263676 deletions
.dockerignore.gitignoreDockerfiledocker-entrypoint.shexe_builder.iss
src
Ellie.Marmalade
EllieBot.Coordinator
EllieBot.Generators
EllieBot.GrpcApiBase
EllieBot.Tests
EllieBot.Voice
EllieBot.VotesApi
EllieBot
.editorconfigBot.cs
Db
EllieBot.csprojEllieBot.csproj.DotSettings
Migrations

View file

@ -10,4 +10,5 @@
# ignore bin and obj folders in projects
src/**/bin/*
src/**/obj/*
src/**/obj/*
src/EllieBot/data/creds.yml

14
.gitignore vendored
View file

@ -7,19 +7,12 @@ src/EllieBot/data/marmalades/
# other
command_errors*.txt
output/
src/EllieBot/output
src/EllieBot/creds.yml
src/EllieBot/data/creds.yml
src/EllieBot/Command Errors*.txt
src/EllieBot/creds.yml
# credentials file before and after v3
src/EllieBot/credentials.json
src/EllieBot/old_credentials.json
src/EllieBot/credentials.json.bak
src/EllieBot/data/EllieBot.db
src/EllieBot/data/EllieBot.db.*
# scripts
ellie-menu.ps1
package.sh
@ -377,4 +370,5 @@ site/
## AI
.aider.*
PROMPT.md
PROMPT.md
.aider*

View file

@ -1,4 +1,4 @@
# Use the .NET 8.0 SDK as the base image for the build stage
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /source
@ -8,53 +8,42 @@ 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 src/EllieBot.GrpcApiBase/*.csproj src/EllieBot.GrpcApiBase/
# Restore the dependencies for the EllieBot project
RUN dotnet restore src/EllieBot/
RUN dotnet restore src/EllieBot/ -r linux-musl-x64
# 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 restore; \
dotnet publish -c Release -o /app --no-restore; \
mv /app/data /app/data_init; \
rm -Rf libopus* libsodium* opus.* runtimes/win* runtimes/osx* runtimes/linux-arm* runtimes/linux-mips*; \
find /app -type f -exec chmod -x {} \; ;\
chmod +x /app/EllieBot
# Build for linux-musl-x64 runtime as the image is based on alpine
RUN dotnet publish -c Release -o /app --self-contained -r linux-musl-x64 --no-restore \
&& mv /app/data /app/data_init \
&& chmod +x /app/EllieBot
# Use the .NET 8.0 runtime as the base image for the final stage
FROM mcr.microsoft.com/dotnet/runtime:8.0
# Final stage
FROM alpine:3.20
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; \
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
# Music dependencies
ADD --chmod=755 https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux /usr/local/bin/yt-dlp
RUN apk add --no-cache ffmpeg libsodium
# Required dependencies
# icu-libs is required for globalization
RUN apk update; \
apk add --no-cache libstdc++ libgcc icu-libs libc6-compat \
&& rm -rf /var/cache/apk/*;
# Copy the built application and the entrypoint script from the build stage
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 total_shards=1
ENV EllieBot__creds=/app/data/creds.yml
RUN rm /app/data_init/lib/libsodium.so \
&& ln -s /usr/lib/libsodium.so.26 /app/data_init/lib/libsodium.so
# 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" ]
CMD dotnet EllieBot.dll "$shard_id" "$total_shards"
CMD [ "./EllieBot" ]

View file

@ -1,28 +1,20 @@
#!/bin/sh
set -e;
data_init=/app/data_init
data=/app/data
set -e
# populate /app/data if empty
for i in $(ls $data_init)
do
if [ ! -e "$data/$i" ]; then
[ -f "$data_init/$i" ] && cp "$data_init/$i" "$data/$i"
[ -d "$data_init/$i" ] && cp -r "$data_init/$i" "$data/$i"
fi
done
data_init="/app/data_init"
data="/app/data"
# creds.yml migration
if [ -f /app/creds.yml ]; then
echo "Default location for creds.yml is now /app/data/creds.yml."
echo "Please move your creds.yml and update your docker-compose.yml accordingly."
ls $data
# Merge data_init into data without overwrites.
cp -R -n $data_init/* $data
cp -n "$data_init/creds_example.yml" "$data/creds.yml"
export Ellie_creds=/app/creds.yml
fi
ls $data
# ensure ellie can write on /app/data
chown -R ellie:ellie "$data"
echo "Yt-dlp update"
# TODO: Update yt-dlp. It should not crash the entrypoint if ca-certificates is not installed
# yt-dlp -U
# drop to regular user and launch command
exec sudo -u ellie "$@"
echo "Running EllieBot"
exec "$@"

View file

@ -6,7 +6,7 @@
[Setup]
AppName = {param:botname|EllieBot}
AppVersion={#version}
AppPublisher=Toastie
AppPublisher=Toastie_t0ast
DefaultDirName={param:installpath|{commonpf}\EllieBot}
DefaultGroupName=EllieBot
UninstallDisplayIcon={app}\{#sysfolder}\ellie_icon.ico
@ -33,28 +33,25 @@ Uninstallable=no
;install
Source: "src\EllieBot\bin\Release\{#platform}\{#target}\publish\*"; DestDir: "{app}\{#sysfolder}"; Permissions: users-full; Flags: recursesubdirs onlyifdoesntexist ignoreversion createallsubdirs; Excludes: "*.pdb, *.db"
;reinstall - i want to copy all files, but i don't want to overwrite any data files because users will lose their customization if they don't have a backup,
; and i don't want them to have to backup and then copy-merge into data folder themselves, or lose their currency images due to overwrite.
Source: "src\EllieBot\bin\Release\{#platform}\{#target}\publish\*"; DestDir: "{app}\{#sysfolder}"; Permissions: users-full; Flags: recursesubdirs ignoreversion onlyifdestfileexists createallsubdirs; Excludes: "*.pdb, *.db, data\*, credentials.json, creds.yml";
;reinstall - Copy all files, but don't overwrite any data files because users will lose their customization if they don't have a backup
Source: "src\EllieBot\bin\Release\{#platform}\{#target}\publish\*"; DestDir: "{app}\{#sysfolder}"; Permissions: users-full; Flags: recursesubdirs ignoreversion onlyifdestfileexists createallsubdirs; Excludes: "*.pdb, *.db, data\*, creds.yml";
Source: "src\EllieBot\bin\Release\{#platform}\{#target}\publish\data\*"; DestDir: "{app}\{#sysfolder}\data"; Permissions: users-full; Flags: recursesubdirs onlyifdoesntexist createallsubdirs;
; overwrite strings and aliases
Source: "src\EllieBot\bin\Release\{#platform}\{#target}\publish\data\aliases.yml"; DestDir: "{app}\{#sysfolder}\data\"; Permissions: users-full; Flags: recursesubdirs ignoreversion onlyifdestfileexists createallsubdirs;
Source: "src\EllieBot\bin\Release\{#platform}\{#target}\publish\data\strings\*"; DestDir: "{app}\{#sysfolder}\data\strings"; Permissions: users-full; Flags: recursesubdirs ignoreversion onlyifdestfileexists createallsubdirs;
; overwrite strings
Source: "src\EllieBot\bin\Release\{#platform}\{#target}\publish\strings\*"; DestDir: "{app}\{#sysfolder}\strings"; Permissions: users-full; Flags: recursesubdirs ignoreversion onlyifdestfileexists createallsubdirs;
[Dirs]
Name:"{app}\{#sysfolder}\data"; Permissions: everyone-modify
Name:"{app}\{#sysfolder}\config"; Permissions: everyone-modify
Name:"{app}\{#sysfolder}"; Permissions: everyone-modify
; [Run]
; Filename: "https://docs.elliebot.net/ellie/"; Flags: postinstall shellexec runasoriginaluser; Description: "Open setup guide"
; Filename: "https://docs.elliebot.net/ellie/features/yml-explained/"; Flags: postinstall shellexec runasoriginaluser; Description: "Open setup guide"
; Filename: "{app}\{#sysfolder}\creds.yml"; Flags: postinstall shellexec runasoriginaluser; Description: "Open creds file"
[Icons]
; for pretty install directory
Name: "{app}\EllieBot"; Filename: "{app}\{#sysfolder}\EllieBot.exe"; IconFilename: "{app}\{#sysfolder}\ellie_icon.ico"
Name: "{app}\creds"; Filename: "{app}\{#sysfolder}\creds.yml"
Name: "{app}\EllieBot"; Filename: "{app}\{#sysfolder}\EllieBot.exe"; IconFilename: "{app}\{#sysfolder}\ellie_icon.ico"
Name: "{app}\data"; Filename: "{app}\{#sysfolder}\data"
Name: "{app}\strings"; Filename: "{app}\{#sysfolder}\strings"
; desktop shortcut
Name: "{commondesktop}\{#SetupSetting("AppName")}"; Filename: "{app}\EllieBot";
@ -62,7 +59,7 @@ Name: "{commondesktop}\{#SetupSetting("AppName")}"; Filename: "{app}\EllieBot";
[Code]
function GetFileName(const AFileName: string): string;
begin
Result := ExpandConstant('{app}\{#sysfolder}\' + AFileName);
Result := ExpandConstant('{app}\{#sysfolder}\data\' + AFileName);
end;
procedure CurStepChanged(CurStep: TSetupStep);

View file

@ -9,9 +9,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net.Core" Version="3.16.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="YamlDotNet" Version="15.1.4" />
<PackageReference Include="Discord.Net.Core" Version="3.17.1" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="YamlDotNet" Version="15.1.6" />
</ItemGroup>
<PropertyGroup Condition=" '$(Version)' == '' ">

View file

@ -9,11 +9,11 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.62.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="YamlDotNet" Version="15.1.4" />
<PackageReference Include="Grpc.AspNetCore" Version="2.67.0" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageReference Include="YamlDotNet" Version="15.1.6" />
</ItemGroup>
</Project>

View file

@ -11,7 +11,7 @@ Project which contains source generators required for EllieBot project
-- How it works --
Creates a file "strs.cs" containing a class called "strs" in "EllieBot" namespace.
Loads "data/strings/responses.en-US.json" and creates a property or a function for each key in the responses json file based on whether the value has string format placeholders or not.
Loads "strings/responses.en-US.json" and creates a property or a function for each key in the responses json file based on whether the value has string format placeholders or not.
- If a value has no placeholders, it creates a property in the strs class which returns an instance of a LocStr struct containing only the key and no replacement parameters

View file

@ -7,9 +7,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.28.2" />
<PackageReference Include="Google.Protobuf" Version="3.29.3" />
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Tools" Version="2.66.0" PrivateAssets="All" />
<PackageReference Include="Grpc.Tools" Version="2.69.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>

View file

@ -11,9 +11,9 @@ namespace EllieBot.Tests
{
public class CommandStringsTests
{
private const string responsesPath = "../../../../EllieBot/data/strings/responses";
private const string commandsPath = "../../../../EllieBot/data/strings/commands";
private const string aliasesPath = "../../../../EllieBot/data/aliases.yml";
private const string responsesPath = "../../../../EllieBot/strings/responses";
private const string commandsPath = "../../../../EllieBot/strings/commands";
private const string aliasesPath = "../../../../EllieBot/strings/aliases.yml";
[Test]
public void AllCommandNamesHaveStrings()
@ -36,7 +36,7 @@ namespace EllieBot.Tests
}
}
Assert.IsTrue(isSuccess);
Assert.That(isSuccess, Is.True);
}
private static string[] GetCommandMethodNames()
@ -46,8 +46,8 @@ namespace EllieBot.Tests
.Where(type => typeof(EllieModule).IsAssignableFrom(type) // if its a top level module
|| !(type.GetCustomAttribute<GroupAttribute>(true) is null)) // or a submodule
.SelectMany(x => x.GetMethods()
.Where(mi => mi.CustomAttributes
.Any(ca => ca.AttributeType == typeof(CmdAttribute))))
.Where(mi => mi.CustomAttributes
.Any(ca => ca.AttributeType == typeof(CmdAttribute))))
.Select(x => x.Name.ToLowerInvariant())
.ToArray();
@ -69,7 +69,7 @@ namespace EllieBot.Tests
}
}
Assert.IsTrue(isSuccess);
Assert.That(isSuccess, Is.True);
}
[Test]
@ -96,7 +96,7 @@ namespace EllieBot.Tests
if (isSuccess)
Assert.Pass();
else
Assert.Warn("There are some unused entries in data/aliases.yml");
Assert.Warn("There are some unused entries in aliases.yml");
}
[Test]
@ -107,7 +107,7 @@ namespace EllieBot.Tests
var culture = new CultureInfo("en-US");
var methodNames = GetCommandMethodNames()
.ToHashSet();
.ToHashSet();
var isSuccess = true;
// var allCommandNames = CommandNameLoadHelper.LoadCommandStrings(commandsPath));
@ -117,15 +117,13 @@ namespace EllieBot.Tests
if (!methodNames.Contains(cmdName))
{
TestContext.Out.WriteLine($"'{cmdName}' from commands.en-US.yml doesn't have a matching command method.");
TestContext.Out.WriteLine(
$"'{cmdName}' from commands.en-US.yml doesn't have a matching command method.");
isSuccess = false;
}
}
if (isSuccess)
Assert.IsTrue(isSuccess);
else
Assert.Warn("There are some unused command strings in data/strings/commands.en-US.yml");
Assert.That(isSuccess, Is.True, "There are some unused command strings in strings/commands.en-US.yml");
}
}
}

View file

@ -18,11 +18,11 @@ public class ConcurrentHashSetTests
{
var result = _set.Add((1, 2));
Assert.AreEqual(true, result);
Assert.That(result, Is.EqualTo(true));
result = _set.Add((1, 2));
Assert.AreEqual(false, result);
Assert.That(result, Is.EqualTo(false));
}
[Test]
@ -31,10 +31,10 @@ public class ConcurrentHashSetTests
_set.Add((1, 2));
var result = _set.TryRemove((1, 2));
Assert.AreEqual(true, result);
Assert.That(result, Is.EqualTo(true));
result = _set.TryRemove((1, 2));
Assert.AreEqual(false, result);
Assert.That(result, Is.EqualTo(false));
}
[Test]
@ -48,7 +48,7 @@ public class ConcurrentHashSetTests
_set.Add((3, 2)); // 3
_set.Add((3, 2)); // 3
Assert.AreEqual(3, _set.Count);
Assert.That(_set.Count, Is.EqualTo(3));
}
[Test]
@ -60,7 +60,7 @@ public class ConcurrentHashSetTests
_set.Clear();
Assert.AreEqual(0, _set.Count);
Assert.That(_set.Count, Is.EqualTo(0));
}
[Test]
@ -69,10 +69,10 @@ public class ConcurrentHashSetTests
_set.Add((1, 2));
_set.Add((3, 2));
Assert.AreEqual(true, _set.Contains((1, 2)));
Assert.AreEqual(true, _set.Contains((3, 2)));
Assert.AreEqual(false, _set.Contains((2, 1)));
Assert.AreEqual(false, _set.Contains((2, 3)));
Assert.That(_set.Contains((1, 2)), Is.EqualTo(true));
Assert.That(_set.Contains((3, 2)), Is.EqualTo(true));
Assert.That(_set.Contains((2, 1)), Is.EqualTo(false));
Assert.That(_set.Contains((2, 3)), Is.EqualTo(false));
}
[Test]
@ -86,8 +86,8 @@ public class ConcurrentHashSetTests
// remove tuples which have even second item
_set.RemoveWhere(static x => x.Item2 % 2 == 0);
Assert.AreEqual(2, _set.Count);
Assert.AreEqual(true, _set.Contains((1, 3)));
Assert.AreEqual(true, _set.Contains((2, 5)));
Assert.That(_set.Count, Is.EqualTo(2));
Assert.That(_set.Contains((1, 3)), Is.EqualTo(true));
Assert.That(_set.Contains((2, 5)), Is.EqualTo(true));
}
}

View file

@ -6,9 +6,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
</ItemGroup>
<ItemGroup>

View file

@ -1,88 +0,0 @@
// using System;
// using System.Collections.Generic;
// using System.Diagnostics;
// using System.IO;
// using System.Linq;
// using Ellie.Common;
// using EllieBot.Modules.Games;
// using NUnit.Framework;
//
// namespace EllieBot.Tests;
//
// public class FishTests
// {
// [Test]
// public void TestWeather()
// {
// var fs = new FishService(null, null);
//
// var rng = new Random();
//
// // output = @"ro+dD:bN0uVqV3ZOAv6r""EFeA'A]u]uSyz2Qd'r#0Vf:5zOX\VgSsF8LgRCL/uOW";
// while (true)
// {
// var output = "";
// for (var i = 0; i < 64; i++)
// {
// var c = (char)rng.Next(33, 123);
// output += c;
// }
//
// output = "";
// var weathers = new List<FishingWeather>();
// for (var i = 0; i < 1_000_000; i++)
// {
// var w = fs.GetWeather(DateTime.UtcNow.AddHours(6 * i), output);
// weathers.Add(w);
// }
//
// var vals = weathers.GroupBy(x => x)
// .ToDictionary(x => x.Key, x => x.Count());
//
// var str = weathers.Select(x => (int)x).Join("");
// var maxLength = MaxLength(str);
//
// if (maxLength < 12)
// {
// foreach (var v in vals)
// {
// Console.WriteLine($"{v.Key}: {v.Value}");
// }
//
// Console.WriteLine(output);
// Console.WriteLine(maxLength);
//
// File.WriteAllText("data.txt", weathers.Select(x => (int)x).Join(""));
//
// break;
// }
// }
// }
//
// // string with same characters
// static int MaxLength(String s)
// {
// int ans = 1, temp = 1;
//
// // Traverse the string
// for (int i = 1; i < s.Length; i++)
// {
// // If character is same as
// // previous increment temp value
// if (s[i] == s[i - 1])
// {
// ++temp;
// }
// else
// {
// ans = Math.Max(ans, temp);
// temp = 1;
// }
// }
//
// ans = Math.Max(ans, temp);
//
// // Return the required answer
// return ans;
// }
// }

View file

@ -1,4 +1,4 @@
using Ellie.Common;
using Ellie.Common;
using EllieBot.Db.Models;
using NUnit.Framework;
using System;
@ -32,7 +32,8 @@ namespace EllieBot.Tests
// Evaluate the indices are ordered
CheckIndices(collection);
Assert.AreEqual(8, collection.Count);
Assert.That(collection.Count, Is.EqualTo(8));
}
[Test]
@ -45,8 +46,9 @@ namespace EllieBot.Tests
collection.RemoveAt(6);
// Evaluate if the items got removed
foreach (var item in collection)
Assert.IsFalse(item.Id == 5 || item.Id == 7, $"Item at index {item.Index} was not removed");
foreach (var item in collection){
Assert.That(item.Id, Is.Not.EqualTo(5).Or.EqualTo(7), $"Item at index {item.Index} was not removed");
}
CheckIndices(collection);
@ -61,7 +63,7 @@ namespace EllieBot.Tests
var collection = GetCollectionSample<ShopEntry>();
collection.Clear();
Assert.IsTrue(collection.Count == 0, "Collection has not been cleared.");
Assert.That(collection.Count, Is.EqualTo(0), "Collection has not been cleared.");
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
_ = collection[0];
@ -78,7 +80,7 @@ namespace EllieBot.Tests
// Evaluate copy
for (var index = 0; index < fullCopy.Length; index++)
Assert.AreEqual(index, fullCopy[index].Index);
Assert.That(index, Is.EqualTo(fullCopy[index].Index));
Assert.Throws<ArgumentException>(() => collection.CopyTo(new ShopEntry[10], 4));
Assert.Throws<ArgumentException>(() => collection.CopyTo(new ShopEntry[6], 0));
@ -89,10 +91,10 @@ namespace EllieBot.Tests
{
var collection = GetCollectionSample<ShopEntry>();
Assert.AreEqual(4, collection.IndexOf(collection[4]));
Assert.AreEqual(0, collection.IndexOf(collection[0]));
Assert.AreEqual(7, collection.IndexOf(collection[7]));
Assert.AreEqual(9, collection.IndexOf(collection[9]));
Assert.That(collection.IndexOf(collection[4]), Is.EqualTo(4));
Assert.That(collection.IndexOf(collection[0]), Is.EqualTo(0));
Assert.That(collection.IndexOf(collection[7]), Is.EqualTo(7));
Assert.That(collection.IndexOf(collection[9]), Is.EqualTo(9));
}
[Test]
@ -104,9 +106,9 @@ namespace EllieBot.Tests
collection.Insert(5, new ShopEntry() { Id = 555 });
collection.Insert(7, new ShopEntry() { Id = 777 });
Assert.AreEqual(12, collection.Count);
Assert.AreEqual(555, collection[5].Id);
Assert.AreEqual(777, collection[7].Id);
Assert.That(collection.Count, Is.EqualTo(12));
Assert.That(collection[5].Id, Is.EqualTo(555));
Assert.That(collection[7].Id, Is.EqualTo(777));
CheckIndices(collection);
@ -134,9 +136,9 @@ namespace EllieBot.Tests
collection.Remove(subCol[1]);
CheckIndices(collection);
Assert.IsTrue(collection.Contains(subCol[0]));
Assert.IsFalse(collection.Contains(subCol[1]));
Assert.IsTrue(collection.Contains(subCol[2]));
Assert.That(collection.Contains(subCol[0]), Is.True);
Assert.That(collection.Contains(subCol[1]), Is.False);
Assert.That(collection.Contains(subCol[2]), Is.True);
}
[Test]
@ -148,7 +150,7 @@ namespace EllieBot.Tests
foreach (var item in collection)
{
enumerator.MoveNext();
Assert.AreEqual(item, enumerator.Current);
Assert.That(enumerator.Current, Is.EqualTo(item));
}
}
@ -161,8 +163,8 @@ namespace EllieBot.Tests
collection[7] = new ShopEntry() { Id = 777 };
CheckIndices(collection);
Assert.AreEqual(444, collection[4].Id);
Assert.AreEqual(777, collection[7].Id);
Assert.That(collection[4].Id, Is.EqualTo(444));
Assert.That(collection[7].Id, Is.EqualTo(777));
}
/// <summary>
@ -173,7 +175,7 @@ namespace EllieBot.Tests
private void CheckIndices<T>(IndexedCollection<T> collection) where T : class, IIndexed
{
for (var index = 0; index < collection.Count; index++)
Assert.AreEqual(index, collection[index].Index);
Assert.That(collection[index].Index, Is.EqualTo(index));
}
/// <summary>
@ -185,4 +187,4 @@ namespace EllieBot.Tests
private IndexedCollection<T> GetCollectionSample<T>(IEnumerable<T> sample = default) where T : DbEntity, IIndexed, new()
=> new IndexedCollection<T>(sample ?? Enumerable.Range(0, 10).Select(x => new T() { Id = x }));
}
}
}

View file

@ -11,7 +11,7 @@ namespace EllieBot.Tests
{
var num = default(kwum);
Assert.AreEqual(0, num.GetHashCode());
Assert.That(0, Is.EqualTo(num.GetHashCode()));
}
[Test]
@ -20,7 +20,7 @@ namespace EllieBot.Tests
var num1 = new kwum("234");
var num2 = new kwum("234");
Assert.AreEqual(num1.GetHashCode(), num2.GetHashCode());
Assert.That(num1.GetHashCode(), Is.EqualTo(num2.GetHashCode()));
}
[Test]
@ -29,7 +29,7 @@ namespace EllieBot.Tests
var num1 = new kwum("234");
var num2 = new kwum("235");
Assert.AreNotEqual(num1.GetHashCode(), num2.GetHashCode());
Assert.That(num1.GetHashCode(), Is.Not.EqualTo(num2.GetHashCode()));
}
[Test]
@ -38,7 +38,7 @@ namespace EllieBot.Tests
var num1 = new kwum("hgbkhdbk");
var num2 = new kwum("hgbkhdbk");
Assert.AreEqual(num1.GetHashCode(), num2.GetHashCode());
Assert.That(num1.GetHashCode(), Is.EqualTo(num2.GetHashCode()));
}
[Test]
@ -47,7 +47,7 @@ namespace EllieBot.Tests
var num1 = new kwum("hgbkhd");
var num2 = new kwum("hgbkhd");
Assert.AreEqual(num1, num2);
Assert.That(num1, Is.EqualTo(num2));
}
[Test]
@ -56,21 +56,22 @@ namespace EllieBot.Tests
var num1 = new kwum("hgbk5d");
var num2 = new kwum("hgbk4d");
Assert.AreNotEqual(num1, num2);
Assert.That(num1, Is.Not.EqualTo(num2));
}
[Test]
public void TestParseValidValue()
{
var validValue = "234e";
Assert.True(kwum.TryParse(validValue, out _));
Assert.That(kwum.TryParse(validValue, out _), Is.True);
}
[Test]
public void TestParseInvalidValue()
{
var invalidValue = "1234";
Assert.False(kwum.TryParse(invalidValue, out _));
Assert.That(kwum.TryParse(invalidValue, out _), Is.False);
}
[Test]
@ -79,7 +80,7 @@ namespace EllieBot.Tests
var validValue = "qwerf4bm";
kwum.TryParse(validValue, out var parsedValue);
Assert.AreEqual(parsedValue, new kwum(validValue));
Assert.That(parsedValue, Is.EqualTo(new kwum(validValue)));
}
[Test]
@ -88,7 +89,7 @@ namespace EllieBot.Tests
var validValue = "46g5yh";
kwum.TryParse(validValue, out var parsedValue);
Assert.AreEqual(validValue, parsedValue.ToString());
Assert.That(validValue, Is.EqualTo(parsedValue.ToString()));
}
[Test]
@ -96,37 +97,37 @@ namespace EllieBot.Tests
{
var num = new kwum(10);
Assert.AreEqual(10, (int)num);
Assert.AreEqual(num, (kwum)10);
Assert.That(10, Is.EqualTo((int)num));
Assert.That(num, Is.EqualTo((kwum)10));
}
[Test]
public void TestConverstionsToString()
{
var num = new kwum(10);
Assert.AreEqual("c", num.ToString());
Assert.That("c", Is.EqualTo(num.ToString()));
num = new kwum(123);
Assert.AreEqual("5v", num.ToString());
Assert.That("5v", Is.EqualTo(num.ToString()));
// leading zeros have no meaning
Assert.AreEqual(new kwum("22225v"), num);
Assert.That(new kwum("22225v"), Is.EqualTo(num));
}
[Test]
public void TestMaxValue()
{
var num = new kwum(int.MaxValue - 1);
Assert.AreEqual("3zzzzzy", num.ToString());
Assert.That("3zzzzzy", Is.EqualTo(num.ToString()));
num = new kwum(int.MaxValue);
Assert.AreEqual("3zzzzzz", num.ToString());
Assert.That("3zzzzzz", Is.EqualTo(num.ToString()));
}
[Test]
public void TestPower()
{
var num = new kwum((int)Math.Pow(32, 2));
Assert.AreEqual("322", num.ToString());
Assert.That("322", Is.EqualTo(num.ToString()));
}
}
}

View file

@ -3,7 +3,6 @@ using NUnit.Framework;
namespace EllieBot.Tests;
public class NewDeckTests
{
private RegularDeck _deck;
@ -17,8 +16,8 @@ public class NewDeckTests
[Test]
public void TestCount()
{
Assert.AreEqual(52, _deck.TotalCount);
Assert.AreEqual(52, _deck.CurrentCount);
Assert.That(52, Is.EqualTo(_deck.TotalCount));
Assert.That(52, Is.EqualTo(_deck.CurrentCount));
}
[Test]
@ -26,10 +25,10 @@ public class NewDeckTests
{
var card = _deck.Draw();
Assert.IsNotNull(card);
Assert.AreEqual(card.Suit, RegularSuit.Hearts);
Assert.AreEqual(card.Value, RegularValue.Ace);
Assert.AreEqual(_deck.CurrentCount, _deck.TotalCount - 1);
Assert.That(card, Is.Not.Null);
Assert.That(card.Suit, Is.EqualTo(RegularSuit.Hearts));
Assert.That(card.Value, Is.EqualTo(RegularValue.Ace));
Assert.That(_deck.CurrentCount, Is.EqualTo(_deck.TotalCount - 1));
}
[Test]
@ -42,12 +41,12 @@ public class NewDeckTests
var lastCard = _deck.Draw();
Assert.IsNotNull(lastCard);
Assert.AreEqual(new RegularCard(RegularSuit.Spades, RegularValue.King), lastCard);
Assert.That(lastCard, Is.Not.Null);
Assert.That(lastCard, Is.EqualTo(new RegularCard(RegularSuit.Spades, RegularValue.King)));
var noCard = _deck.Draw();
Assert.IsNull(noCard);
Assert.That(noCard, Is.Null);
}
[Test]
@ -56,8 +55,8 @@ public class NewDeckTests
var ace = _deck.Draw()!;
var two = _deck.Draw()!;
Assert.AreEqual("Ace of Hearts", ace.GetName());
Assert.AreEqual("Two of Hearts", two.GetName());
Assert.That("Ace of Hearts", Is.EqualTo(ace.GetName()));
Assert.That("Two of Hearts", Is.EqualTo(two.GetName()));
}
[Test]
@ -66,8 +65,8 @@ public class NewDeckTests
var ace = _deck.Peek()!;
var tenOfSpades = _deck.Peek(48);
Assert.AreEqual(new RegularCard(RegularSuit.Hearts, RegularValue.Ace), ace);
Assert.AreEqual(new RegularCard(RegularSuit.Spades, RegularValue.Ten), tenOfSpades);
Assert.That(new RegularCard(RegularSuit.Hearts, RegularValue.Ace), Is.EqualTo(ace));
Assert.That(new RegularCard(RegularSuit.Spades, RegularValue.Ten), Is.EqualTo(tenOfSpades));
}
[Test]
@ -76,9 +75,9 @@ public class NewDeckTests
var quadDeck = new MultipleRegularDeck(4);
var count = quadDeck.TotalCount;
Assert.AreEqual(52 * 4, count);
Assert.That(52 * 4, Is.EqualTo(count));
var card = quadDeck.Peek(54);
Assert.AreEqual(new RegularCard(RegularSuit.Hearts, RegularValue.Three), card);
Assert.That(new RegularCard(RegularSuit.Hearts, RegularValue.Three), Is.EqualTo(card));
}
}

View file

@ -15,7 +15,7 @@ namespace EllieBot.Tests
var pubsub = new EventPubSub();
await pubsub.Sub(key, data =>
{
Assert.AreEqual(expected, data);
Assert.That(expected, Is.EqualTo(data));
Assert.Pass();
return default;
});
@ -31,7 +31,7 @@ namespace EllieBot.Tests
var pubsub = new EventPubSub();
await pubsub.Sub(key, data =>
{
Assert.AreEqual(expected, data);
Assert.That(expected, Is.EqualTo(data));
Assert.Pass();
return default;
});
@ -48,13 +48,13 @@ namespace EllieBot.Tests
var pubsub = new EventPubSub();
await pubsub.Sub(key, data =>
{
Assert.AreEqual(expected, data);
Assert.That(expected, Is.EqualTo(data));
Assert.Pass();
return default;
});
await pubsub.Unsub(key, data =>
{
Assert.AreEqual(expected, data);
Assert.That(expected, Is.EqualTo(data));
Assert.Pass();
return default;
});
@ -90,7 +90,7 @@ namespace EllieBot.Tests
ValueTask Action(byte[] data)
{
Assert.AreEqual(localData, data);
Assert.That(localData, Is.EqualTo(data));
Assert.Pass();
return default;
}
@ -112,14 +112,14 @@ namespace EllieBot.Tests
ValueTask Action1(object data)
{
Assert.AreEqual(localData, data);
Assert.That(localData, Is.EqualTo(data));
successCounter += 10;
return default;
}
ValueTask Action2(object data)
{
Assert.AreEqual(localData, data);
Assert.That(localData, Is.EqualTo(data));
successCounter++;
return default;
}
@ -130,7 +130,7 @@ namespace EllieBot.Tests
await pubsub.Unsub(key, Action2); // - 1/
await pubsub.Pub(key, localData);
Assert.AreEqual(successCounter, 11, "Not all events are raised.");
Assert.That(successCounter, Is.EqualTo(11), "Not all events are raised.");
}
}
}

View file

@ -17,7 +17,7 @@ namespace EllieBot.Tests
var point = @"0001F338";
var hopefullyEmoji = YamlHelper.UnescapeUnicodeCodePoint(point);
Assert.AreEqual("🌸", hopefullyEmoji, hopefullyEmoji);
Assert.That("🌸", Is.EqualTo(hopefullyEmoji), "Yaml unescape doesn't work properly.");
}
}
}

View file

@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
<PackageReference Include="Serilog" Version="4.2.0" />
<PackageReference Include="System.Threading.Channels" Version="9.0.1" />
</ItemGroup>
</Project>

View file

@ -5,6 +5,7 @@ namespace EllieBot.Voice
{
internal static unsafe class Sodium
{
private const string SODIUM = "data/lib/libsodium";
[DllImport(SODIUM, EntryPoint = "crypto_secretbox_easy", CallingConvention = CallingConvention.Cdecl)]

View file

@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="MorseCode.ITask" Version="2.0.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
</ItemGroup>
</Project>

View file

@ -1,360 +0,0 @@
root = true
# Remove the line below if you want to inherit .editorconfig settings from higher directories
[obj/**]
generated_code = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
# this. and Me. preferences
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
# Modifier preferences
dotnet_style_require_accessibility_modifiers = always:error
# Expression-level preferences
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion
dotnet_style_prefer_conditional_expression_over_return = false:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = true
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true
# Field preferences
dotnet_style_readonly_field = true:suggestion
# Parameter preferences
dotnet_code_quality_unused_parameters = all:warning
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = true
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_lambdas = true:suggestion
csharp_style_expression_bodied_local_functions = true:suggestion
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
csharp_style_expression_bodied_properties = true:suggestion
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:error
csharp_style_pattern_matching_over_is_with_cast_check = true:error
csharp_style_prefer_not_pattern = true:error
csharp_style_prefer_pattern_matching = true:suggestion
csharp_style_prefer_switch_expression = true
# Null-checking preferences
csharp_style_conditional_delegate_call = true:error
# Modifier preferences
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async
# Code-block preferences
csharp_prefer_braces = when_multiline:warning
csharp_prefer_simple_using_statement = true
# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_deconstructed_variable_declaration = true
csharp_style_implicit_object_creation_when_type_is_apparent = true:error
csharp_style_inlined_variable_declaration = true:warning
csharp_style_pattern_local_over_anonymous_function = true
csharp_style_prefer_index_operator = true
csharp_style_prefer_range_operator = true
csharp_style_throw_expression = true:error
csharp_style_unused_value_assignment_preference = discard_variable:warning
csharp_style_unused_value_expression_statement_preference = discard_variable
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:error
# Enforce file-scoped namespaces
csharp_style_namespace_declarations = file_scoped:error
# New line preferences
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
csharp_style_allow_embedded_statements_on_same_line_experimental = false
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = false
#### Naming styles ####
# Naming rules
dotnet_naming_rule.private_readonly_field.symbols = private_readonly_field
dotnet_naming_rule.private_readonly_field.style = begins_with_underscore
dotnet_naming_rule.private_readonly_field.severity = warning
# dotnet_naming_rule.private_field.symbols = private_field
# dotnet_naming_rule.private_field.style = camel_case
# dotnet_naming_rule.private_field.severity = warning
dotnet_naming_rule.const_fields.symbols = const_fields
dotnet_naming_rule.const_fields.style = all_upper
dotnet_naming_rule.const_fields.severity = warning
# dotnet_naming_rule.class_should_be_pascal_case.severity = error
# dotnet_naming_rule.class_should_be_pascal_case.symbols = class
# dotnet_naming_rule.class_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.struct_should_be_pascal_case.severity = error
dotnet_naming_rule.struct_should_be_pascal_case.symbols = struct
dotnet_naming_rule.struct_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
# dotnet_naming_rule.types_should_be_pascal_case.severity = error
# dotnet_naming_rule.types_should_be_pascal_case.symbols = types
# dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
# dotnet_naming_rule.enum_should_be_pascal_case.severity = error
# dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum
# dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case
# dotnet_naming_rule.property_should_be_pascal_case.severity = error
# dotnet_naming_rule.property_should_be_pascal_case.symbols = property
# dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.method_should_be_pascal_case.severity = error
dotnet_naming_rule.method_should_be_pascal_case.symbols = method
dotnet_naming_rule.method_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.async_method_should_be_ends_with_async.severity = error
dotnet_naming_rule.async_method_should_be_ends_with_async.symbols = async_method
dotnet_naming_rule.async_method_should_be_ends_with_async.style = ends_with_async
# dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
# dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
# dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.local_variable_should_be_camel_case.severity = error
dotnet_naming_rule.local_variable_should_be_camel_case.symbols = local_variable
dotnet_naming_rule.local_variable_should_be_camel_case.style = camel_case
# Symbol specifications
dotnet_naming_symbols.const_fields.required_modifiers = const
dotnet_naming_symbols.const_fields.applicable_kinds = field
dotnet_naming_symbols.class.applicable_kinds = class
dotnet_naming_symbols.class.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.class.required_modifiers =
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.struct.applicable_kinds = struct
dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.struct.required_modifiers =
dotnet_naming_symbols.enum.applicable_kinds = enum
dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.enum.required_modifiers =
dotnet_naming_symbols.method.applicable_kinds = method
dotnet_naming_symbols.method.applicable_accessibilities = public
dotnet_naming_symbols.method.required_modifiers =
dotnet_naming_symbols.property.applicable_kinds = property
dotnet_naming_symbols.property.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.property.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.private_readonly_field.applicable_kinds = field
dotnet_naming_symbols.private_readonly_field.applicable_accessibilities = private, protected
dotnet_naming_symbols.private_readonly_field.required_modifiers = readonly
dotnet_naming_symbols.private_field.applicable_kinds = field
dotnet_naming_symbols.private_field.applicable_accessibilities = private, protected
dotnet_naming_symbols.private_field.required_modifiers =
dotnet_naming_symbols.async_method.applicable_kinds = method, local_function
dotnet_naming_symbols.async_method.applicable_accessibilities = *
dotnet_naming_symbols.async_method.required_modifiers = async
dotnet_naming_symbols.local_variable.applicable_kinds = parameter, local
dotnet_naming_symbols.local_variable.applicable_accessibilities = local
dotnet_naming_symbols.local_variable.required_modifiers =
# Naming styles
dotnet_naming_style.all_upper.capitalization = all_upper
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.begins_with_underscore.required_prefix = _
dotnet_naming_style.begins_with_underscore.required_suffix =
dotnet_naming_style.begins_with_underscore.word_separator =
dotnet_naming_style.begins_with_underscore.capitalization = camel_case
dotnet_naming_style.ends_with_async.required_prefix =
# dotnet_naming_style.ends_with_async.required_suffix = Async
dotnet_naming_style.ends_with_async.word_separator =
dotnet_naming_style.ends_with_async.capitalization = pascal_case
dotnet_naming_style.camel_case.required_prefix =
dotnet_naming_style.camel_case.required_suffix =
dotnet_naming_style.camel_case.word_separator =
dotnet_naming_style.camel_case.capitalization = camel_case
# CA1822: Mark members as static
dotnet_diagnostic.ca1822.severity = suggestion
# IDE0004: Cast is redundant
dotnet_diagnostic.ide0004.severity = warning
# IDE0058: Expression value is never used
dotnet_diagnostic.ide0058.severity = none
# # IDE0011: Add braces to 'if'/'else' statement
# dotnet_diagnostic.ide0011.severity = none
resharper_wrap_after_invocation_lpar = false
resharper_wrap_before_invocation_rpar = false
# ReSharper properties
resharper_align_multiline_calls_chain = true
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_after_invocation_lpar = false
resharper_csharp_wrap_before_binary_opsign = true
resharper_csharp_wrap_before_invocation_rpar = false
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_force_chop_compound_if_expression = false
resharper_keep_existing_linebreaks = true
resharper_keep_user_linebreaks = true
resharper_max_formal_parameters_on_line = 3
resharper_place_simple_embedded_statement_on_same_line = false
resharper_wrap_chained_binary_expressions = chop_if_long
resharper_wrap_chained_binary_patterns = chop_if_long
resharper_wrap_chained_method_calls = chop_if_long
resharper_wrap_object_and_collection_initializer_style = chop_always
resharper_csharp_wrap_before_first_type_parameter_constraint = true
resharper_csharp_place_type_constraints_on_same_line = false
resharper_csharp_wrap_before_extends_colon = true
resharper_csharp_place_constructor_initializer_on_same_line = false
resharper_force_attribute_style = separate
resharper_csharp_braces_for_ifelse = required_for_multiline_statement
resharper_csharp_braces_for_foreach = required_for_multiline
resharper_csharp_braces_for_while = required_for_multiline
resharper_csharp_braces_for_for = required_for_multiline
resharper_arrange_redundant_parentheses_highlighting = hint
# IDE0011: Add braces
dotnet_diagnostic.IDE0011.severity = warning
resharper_arrange_type_member_modifiers_highlighting = hint

View file

@ -1,12 +1,9 @@
#nullable disable
using DryIoc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using EllieBot.Common.Configs;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection;
using RunMode = Discord.Commands.RunMode;
@ -15,15 +12,12 @@ namespace EllieBot;
public sealed class Bot : IBot
{
public event Func<GuildConfig, Task> JoinedGuild = delegate { return Task.CompletedTask; };
public DiscordSocketClient Client { get; }
public IReadOnlyCollection<GuildConfig> AllGuildConfigs { get; private set; }
private IContainer Services { get; set; }
public bool IsReady { get; private set; }
public int ShardId { get; set; }
public int ShardId { get; }
private readonly IBotCreds _creds;
private readonly CommandService _commandService;
@ -34,12 +28,13 @@ public sealed class Bot : IBot
private readonly Assembly[] _loadedAssemblies;
// private readonly InteractionService _interactionService;
public Bot(int shardId, int? totalShards, string credPath = null)
public Bot(int shardId, int? totalShards)
{
ArgumentOutOfRangeException.ThrowIfLessThan(shardId, 0);
LogSetup.SetupLogger(shardId, null);
ShardId = shardId;
_credsProvider = new BotCredsProvider(totalShards, credPath);
_credsProvider = new BotCredsProvider(totalShards);
_creds = _credsProvider.GetCreds();
LogSetup.SetupLogger(shardId, _creds);
@ -102,7 +97,6 @@ public sealed class Bot : IBot
await using (var uow = _db.GetDbContext())
{
AllGuildConfigs = await uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList);
uow.EnsureUserCreated(bot.Id, bot.Username, bot.Discriminator, bot.AvatarId);
}
@ -114,10 +108,6 @@ public sealed class Bot : IBot
var svcs = new Container();
// this is required in order for marmalade unloading to work
// svcs.Components.Remove<IPlanner, Planner>();
// svcs.Components.Add<IPlanner, RemovablePlanner>();
svcs.AddSingleton<IBotCreds>(_ => _credsProvider.GetCreds());
svcs.AddSingleton<DbService, DbService>(_db);
svcs.AddSingleton<IBotCredsProvider>(_credsProvider);
@ -130,7 +120,6 @@ public sealed class Bot : IBot
svcs.AddSingleton<IConfigSeria, YamlSeria>();
svcs.AddSingleton<IMemoryCache, MemoryCache>(new MemoryCache(new MemoryCacheOptions()));
svcs.AddSingleton<IBehaviorHandler, BehaviorHandler>();
svcs.AddSingleton<ILocalization, Localization>();
foreach (var a in _loadedAssemblies)
@ -245,16 +234,6 @@ public sealed class Bot : IBot
private Task Client_JoinedGuild(SocketGuild arg)
{
Log.Information("Joined server: {GuildName} [{GuildId}]", arg.Name, arg.Id);
_ = Task.Run(async () =>
{
GuildConfig gc;
await using (var uow = _db.GetDbContext())
{
gc = uow.GuildConfigsForId(arg.Id, null);
}
await JoinedGuild.Invoke(gc);
});
return Task.CompletedTask;
}
@ -308,7 +287,7 @@ public sealed class Bot : IBot
Log.Information("Initializing Owner Id...");
var info = await Client.GetApplicationInfoAsync();
_credsProvider.ModifyCredsFile(x => x.OwnerIds = new[] { info.Owner.Id });
_credsProvider.ModifyCredsFile(x => x.OwnerIds = [info.Owner.Id]);
}
catch (Exception ex)
{
@ -331,6 +310,8 @@ public sealed class Bot : IBot
"Failed running OnReadyAsync method on {Type} type: {Message}",
toExec.GetType().Name,
ex.Message);
Environment.Exit(9);
}
});
@ -368,12 +349,10 @@ public sealed class Bot : IBot
return Task.CompletedTask;
}
#if GLOBAL_ELLIE || DEBUG
if (arg.Exception is not null)
Log.Warning(arg.Exception, "{ErrorSource} | {ErrorMessage}", arg.Source, arg.Message);
else
Log.Warning("{ErrorSource} | {ErrorMessage}", arg.Source, arg.Message);
#endif
return Task.CompletedTask;
}

View file

@ -1,8 +1,8 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using EllieBot.Db.Models;
using EllieBot.Modules.Administration.Services;
// ReSharper disable UnusedAutoPropertyAccessor.Global
@ -11,6 +11,11 @@ namespace EllieBot.Db;
public abstract class EllieContext : DbContext
{
public DbSet<GuildConfig> GuildConfigs { get; set; }
public DbSet<Permissionv2> Permissions { get; set; }
//new
public DbSet<XpSettings> XpSettings { get; set; }
public DbSet<GreetSettings> GreetSettings { get; set; }
public DbSet<Quote> Quotes { get; set; }
@ -46,7 +51,6 @@ public abstract class EllieContext : DbContext
public DbSet<AutoTranslateChannel> AutoTranslateChannels { get; set; }
public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; }
public DbSet<Permissionv2> Permissions { get; set; }
public DbSet<BankUser> BankUsers { get; set; }
@ -76,7 +80,7 @@ public abstract class EllieContext : DbContext
{
// load all entities from current assembly
modelBuilder.ApplyConfigurationsFromAssembly(typeof(EllieContext).Assembly);
#region Notify
modelBuilder.Entity<Notify>(e =>
@ -170,10 +174,10 @@ public abstract class EllieContext : DbContext
modelBuilder.Entity<UserBetStats>(ubs =>
{
ubs.HasIndex(x => new
{
x.UserId,
x.Game
})
{
x.UserId,
x.Game
})
.IsUnique();
ubs.HasIndex(x => x.MaxWin)
@ -221,174 +225,8 @@ public abstract class EllieContext : DbContext
configEntity.Property(x => x.VerboseErrors)
.HasDefaultValue(true);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.DelMsgOnCmdChannels)
.WithOne()
.HasForeignKey(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FollowedStreams)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.GenerateCurrencyChannelIds)
.WithOne(x => x.GuildConfig)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.Permissions)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.CommandCooldowns)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterInvitesChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterLinksChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilteredWords)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FilterWordsChannelIds)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.MutedUsers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiRaidSetting)
.WithOne()
.HasForeignKey<AntiRaidSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
// start antispam
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiSpamSetting)
.WithOne()
.HasForeignKey<AntiSpamSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<AntiSpamSetting>()
.HasMany(x => x.IgnoredChannels)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// end antispam
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.AntiAltSetting)
.WithOne()
.HasForeignKey<AntiAltSetting>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnmuteTimers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnbanTimer)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.UnroleTimer)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.VcRoleInfos)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.CommandAliases)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.SlowmodeIgnoredRoles)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.SlowmodeIgnoredUsers)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// start shop
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.ShopEntries)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<ShopEntry>()
.HasMany(x => x.Items)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
// end shop
// start streamrole
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.StreamRole)
.WithOne(x => x.GuildConfig)
.HasForeignKey<StreamRoleSettings>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<StreamRoleSettings>()
.HasMany(x => x.Whitelist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<StreamRoleSettings>()
.HasMany(x => x.Blacklist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
// end streamrole
modelBuilder.Entity<GuildConfig>()
.HasOne(x => x.XpSettings)
.WithOne(x => x.GuildConfig)
.HasForeignKey<XpSettings>(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<GuildConfig>()
.HasMany(x => x.FeedSubs)
.WithOne(x => x.GuildConfig)
.HasForeignKey(x => x.GuildConfigId)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<FeedSub>()
.HasAlternateKey(x => new
{
x.GuildConfigId,
x.Url
});
modelBuilder.Entity<PlantedCurrency>().HasIndex(x => x.MessageId).IsUnique();
modelBuilder.Entity<PlantedCurrency>().HasIndex(x => x.ChannelId);
@ -397,9 +235,7 @@ public abstract class EllieContext : DbContext
#endregion
#region WarningPunishments
var warnpunishmentEntity = modelBuilder.Entity<WarningPunishment>(b =>
modelBuilder.Entity<WarningPunishment>(b =>
{
b.HasAlternateKey(x => new
{
@ -408,8 +244,6 @@ public abstract class EllieContext : DbContext
});
});
#endregion
#region MusicPlaylists
var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>();
@ -489,33 +323,6 @@ public abstract class EllieContext : DbContext
#endregion
#region XpRoleReward
modelBuilder.Entity<XpRoleReward>()
.HasIndex(x => new
{
x.XpSettingsId,
x.Level
})
.IsUnique();
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.RoleRewards)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.CurrencyRewards)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<XpSettings>()
.HasMany(x => x.ExclusionList)
.WithOne(x => x.XpSettings)
.OnDelete(DeleteBehavior.Cascade);
#endregion
#region Club
var ci = modelBuilder.Entity<ClubInfo>();
@ -806,8 +613,15 @@ public abstract class EllieContext : DbContext
#if DEBUG
private static readonly ILoggerFactory _debugLoggerFactory = LoggerFactory.Create(x => x.AddConsole());
#endif
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseLoggerFactory(_debugLoggerFactory);
{
#if DEBUG
optionsBuilder.UseLoggerFactory(_debugLoggerFactory);
#endif
optionsBuilder.ConfigureWarnings(x => x.Log(RelationalEventId.PendingModelChangesWarning)
.Ignore());
}
}

View file

@ -1,17 +1,21 @@
using LinqToDB.Common;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace EllieBot.Db;
public sealed class EllieDbService : DbService
public sealed class EllieDbService : DbService
{
private readonly IBotCredsProvider _creds;
// these are props because creds can change at runtime
private string DbType => _creds.GetCreds().Db.Type.ToLowerInvariant().Trim();
private string ConnString => _creds.GetCreds().Db.ConnectionString;
private string DbType
=> _creds.GetCreds().Db.Type.ToLowerInvariant().Trim();
private string ConnString
=> _creds.GetCreds().Db.ConnectionString;
public EllieDbService(IBotCredsProvider creds)
{
LinqToDBForEFTools.Initialize();
@ -22,18 +26,15 @@ public sealed class EllieDbService : DbService
public override async Task SetupAsync()
{
var dbType = DbType;
var connString = ConnString;
await using var context = CreateRawDbContext(DbType, ConnString);
await RunMigration(context);
await using var context = CreateRawDbContext(dbType, connString);
// make sure sqlite db is in wal journal mode
if (context is SqliteContext)
{
await context.Database.ExecuteSqlRawAsync("PRAGMA journal_mode=WAL");
}
await context.Database.MigrateAsync();
}
public override EllieContext CreateRawDbContext(string dbType, string connString)
@ -50,7 +51,7 @@ public sealed class EllieDbService : DbService
throw new NotSupportedException($"The database provide type of '{dbType}' is not supported.");
}
}
private EllieContext GetDbContextInternal()
{
var dbType = DbType;
@ -71,4 +72,58 @@ public sealed class EllieDbService : DbService
public override EllieContext GetDbContext()
=> GetDbContextInternal();
private static async Task RunMigration(DbContext ctx)
{
// if database doesn't exist, run the baseline migration
if (!await ctx.Database.CanConnectAsync())
{
Log.Information("Database does not exist. Creating a new database...");
await ctx.Database.MigrateAsync();
return;
}
// get the latest applied migration
var applied = await ctx.Database.GetAppliedMigrationsAsync();
// get all .sql file names from the migrations folder
var available = Directory.GetFiles("Migrations/" + GetMigrationDirectory(ctx.Database), "*_*.sql")
.Select(x => Path.GetFileNameWithoutExtension(x))
.OrderBy(x => x);
var lastApplied = applied.Last();
Log.Information("Last applied migration: {LastApplied}", lastApplied);
// apply all migrations with names greater than the last applied
foreach (var runnable in available)
{
if (string.Compare(lastApplied, runnable, StringComparison.Ordinal) < 0)
{
Log.Warning("Migration {MigrationName} has not been applied yet", runnable);
var query = await File.ReadAllTextAsync(GetMigrationPath(ctx.Database, runnable));
await ctx.Database.ExecuteSqlRawAsync(query);
}
}
// run all migrations that have not been applied yet
}
private static string GetMigrationPath(DatabaseFacade ctxDatabase, string runnable)
{
return $"Migrations/{GetMigrationDirectory(ctxDatabase)}/{runnable}.sql";
}
private static string GetMigrationDirectory(DatabaseFacade ctxDatabase)
{
if (ctxDatabase.IsSqlite())
return "Sqlite";
if (ctxDatabase.IsNpgsql())
return "PostgreSql";
throw new NotSupportedException("This database type is not supported.");
}
}

View file

@ -9,4 +9,48 @@ public static class DbExtensions
public static T GetById<T>(this DbSet<T> set, int id)
where T : DbEntity
=> set.FirstOrDefault(x => x.Id == id);
public static GuildFilterConfig FilterConfigForId(
this DbContext ctx,
ulong guildId,
Func<IQueryable<GuildFilterConfig>, IQueryable<GuildFilterConfig>> includes = default)
{
includes ??= static set => set;
var gfc = includes(ctx.Set<GuildFilterConfig>()
.Where(gc => gc.GuildId == guildId))
.FirstOrDefault();
if (gfc is null)
{
ctx.Add(gfc = new()
{
GuildId = guildId,
});
}
return gfc;
}
public static GuildConfig GuildConfigsForId(
this DbContext ctx,
ulong guildId,
Func<IQueryable<GuildConfig>, IQueryable<GuildConfig>> includes = default)
{
includes ??= static set => set;
var gc = includes(ctx.Set<GuildConfig>()
.Where(gc => gc.GuildId == guildId))
.FirstOrDefault();
if (gc is null)
{
ctx.Add(gc = new()
{
GuildId = guildId,
});
}
return gc;
}
}

View file

@ -84,7 +84,7 @@ public static class DiscordUserExtensions
> users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault())
.Count()
+ 1;
public static Task<List<DiscordUser>> GetTopRichest(
this DbSet<DiscordUser> users,
ulong botId,

View file

@ -1,4 +1,5 @@
#nullable disable
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models;
@ -29,125 +30,47 @@ public static class GuildConfigExtensions
/// <param name="ctx">Db Context</param>
/// <param name="guildId">Id of the guild to get stream role settings for.</param>
/// <returns>Guild'p stream role settings</returns>
public static StreamRoleSettings GetStreamRoleSettings(this DbContext ctx, ulong guildId)
public static async Task<StreamRoleSettings> GetOrCreateStreamRoleSettings(this DbContext ctx, ulong guildId)
{
var conf = ctx.GuildConfigsForId(guildId,
set => set.Include(y => y.StreamRole)
.Include(y => y.StreamRole.Whitelist)
.Include(y => y.StreamRole.Blacklist));
var srs = await ctx.GetTable<StreamRoleSettings>()
.Where(x => x.GuildId == guildId)
.FirstOrDefaultAsyncEF();
if (conf.StreamRole is null)
conf.StreamRole = new();
if (srs is not null)
return srs;
return conf.StreamRole;
}
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
=> configs
.AsSplitQuery()
.Include(gc => gc.CommandCooldowns)
.Include(gc => gc.FollowedStreams)
.Include(gc => gc.StreamRole)
.Include(gc => gc.DelMsgOnCmdChannels)
.Include(gc => gc.XpSettings)
.ThenInclude(x => x.ExclusionList);
public static async Task<GuildConfig[]> GetAllGuildConfigs(
this DbSet<GuildConfig> configs,
List<ulong> availableGuilds)
{
var result = await configs
.IncludeEverything()
.Where(x => availableGuilds.Contains(x.GuildId))
.AsNoTracking()
.ToArrayAsync();
return result;
}
/// <summary>
/// Gets and creates if it doesn't exist a config for a guild.
/// </summary>
/// <param name="ctx">Context</param>
/// <param name="guildId">Id of the guide</param>
/// <param name="includes">Use to manipulate the set however you want. Pass null to include everything</param>
/// <returns>Config for the guild</returns>
public static GuildConfig GuildConfigsForId(
this DbContext ctx,
ulong guildId,
Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes)
{
GuildConfig config;
if (includes is null)
config = ctx.Set<GuildConfig>().IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
else
srs = new()
{
var set = includes(ctx.Set<GuildConfig>());
config = set.FirstOrDefault(c => c.GuildId == guildId);
}
GuildId = guildId,
};
if (config is null)
{
ctx.Set<GuildConfig>()
.Add(config = new()
{
GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist,
WarningsInitialized = true,
});
ctx.SaveChanges();
}
ctx.Set<StreamRoleSettings>().Add(srs);
if (!config.WarningsInitialized)
{
config.WarningsInitialized = true;
}
return config;
// ctx.GuildConfigs
// .ToLinqToDBTable()
// .InsertOrUpdate(() => new()
// {
// GuildId = guildId,
// Permissions = Permissionv2.GetDefaultPermlist,
// WarningsInitialized = true,
// WarnPunishments = DefaultWarnPunishments
// },
// _ => new(),
// () => new()
// {
// GuildId = guildId
// });
//
// if(includes is null)
// return ctx.GuildConfigs
// .ToLinqToDBTable()
// .First(x => x.GuildId == guildId);
return srs;
}
public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId)
{
var logSetting = ctx.Set<LogSetting>()
.AsQueryable()
.Include(x => x.LogIgnores)
.Where(x => x.GuildId == guildId)
.FirstOrDefault();
.AsQueryable()
.Include(x => x.LogIgnores)
.Where(x => x.GuildId == guildId)
.FirstOrDefault();
if (logSetting is null)
{
ctx.Set<LogSetting>()
.Add(logSetting = new()
{
GuildId = guildId
});
.Add(logSetting = new()
{
GuildId = guildId
});
ctx.SaveChanges();
}
return logSetting;
}
public static IEnumerable<GuildConfig> PermissionsForAll(this DbSet<GuildConfig> configs, List<ulong> include)
{
var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions);
@ -158,19 +81,19 @@ public static class GuildConfigExtensions
public static GuildConfig GcWithPermissionsFor(this DbContext ctx, ulong guildId)
{
var config = ctx.Set<GuildConfig>()
.AsQueryable()
.Where(gc => gc.GuildId == guildId)
.Include(gc => gc.Permissions)
.FirstOrDefault();
.AsQueryable()
.Where(gc => gc.GuildId == guildId)
.Include(gc => gc.Permissions)
.FirstOrDefault();
if (config is null) // if there is no guildconfig, create new one
{
ctx.Set<GuildConfig>()
.Add(config = new()
{
GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist
});
.Add(config = new()
{
GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist
});
ctx.SaveChanges();
}
else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones
@ -182,45 +105,28 @@ public static class GuildConfigExtensions
return config;
}
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs)
=> configs.AsQueryable().Include(x => x.FollowedStreams).SelectMany(gc => gc.FollowedStreams).ToArray();
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs, List<ulong> included)
=> configs.AsQueryable()
.Where(gc => included.Contains(gc.GuildId))
.Include(gc => gc.FollowedStreams)
.SelectMany(gc => gc.FollowedStreams)
.ToList();
public static XpSettings XpSettingsFor(this DbContext ctx, ulong guildId)
public static async Task<XpSettings> XpSettingsFor(this DbContext ctx, ulong guildId,
Func<IQueryable<XpSettings>, IQueryable<XpSettings>> includes = default)
{
var gc = ctx.GuildConfigsForId(guildId,
set => set.Include(x => x.XpSettings)
.ThenInclude(x => x.RoleRewards)
.Include(x => x.XpSettings)
.ThenInclude(x => x.CurrencyRewards)
.Include(x => x.XpSettings)
.ThenInclude(x => x.ExclusionList));
includes ??= static set => set;
if (gc.XpSettings is null)
gc.XpSettings = new();
var srs = await includes(ctx.GetTable<XpSettings>()
.Where(x => x.GuildId == guildId))
.FirstOrDefaultAsyncLinqToDB();
return gc.XpSettings;
if (srs is not null)
return srs;
srs = await ctx.GetTable<XpSettings>()
.InsertWithOutputAsync(() => new()
{
GuildId = guildId,
ServerExcluded = false,
});
return srs;
}
public static IEnumerable<GeneratingChannel> GetGeneratingChannels(this DbSet<GuildConfig> configs)
=> configs.AsQueryable()
.Include(x => x.GenerateCurrencyChannelIds)
.Where(x => x.GenerateCurrencyChannelIds.Any())
.SelectMany(x => x.GenerateCurrencyChannelIds)
.Select(x => new GeneratingChannel
{
ChannelId = x.ChannelId,
GuildId = x.GuildConfig.GuildId
})
.ToArray();
public class GeneratingChannel
{
public ulong GuildId { get; set; }

View file

@ -9,7 +9,7 @@ public static class UserXpExtensions
{
public static async Task<UserXpStats?> GetGuildUserXp(this ITable<UserXpStats> table, ulong guildId, ulong userId)
=> await table.FirstOrDefaultAsyncLinqToDB(x => x.GuildId == guildId && x.UserId == userId);
public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
{
var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);

View file

@ -1,8 +1,21 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class CommandAlias : DbEntity
{
public ulong GuildId { get; set; }
public string Trigger { get; set; }
public string Mapping { get; set; }
}
public class CommandAliasEntityConfiguration : IEntityTypeConfiguration<CommandAlias>
{
public void Configure(EntityTypeBuilder<CommandAlias> builder)
{
builder.HasIndex(x => x.GuildId);
}
}

View file

@ -1,8 +1,25 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class CommandCooldown : DbEntity
{
{
public ulong GuildId { get; set; }
public int Seconds { get; set; }
public string CommandName { get; set; }
}
public class CommandCooldownEntityConfiguration : IEntityTypeConfiguration<CommandCooldown>
{
public void Configure(EntityTypeBuilder<CommandCooldown> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.CommandName
})
.IsUnique();
}
}

View file

@ -1,16 +1,25 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class DelMsgOnCmdChannel : DbEntity
{
public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
public bool State { get; set; }
}
public override int GetHashCode()
=> ChannelId.GetHashCode();
public override bool Equals(object obj)
=> obj is DelMsgOnCmdChannel x && x.ChannelId == ChannelId;
}
public class DelMsgOnCmdChannelEntityConfiguration : IEntityTypeConfiguration<DelMsgOnCmdChannel>
{
public void Configure(EntityTypeBuilder<DelMsgOnCmdChannel> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.ChannelId
}).IsUnique();
}
}

View file

@ -1,16 +1,17 @@
#nullable disable
namespace EllieBot.Db.Models;
// FUTURE remove LastLevelUp from here and UserXpStats
public class DiscordUser : DbEntity
{
public const string DEFAULT_USERNAME = "??Unknown";
public ulong UserId { get; set; }
public string Username { get; set; }
// public string Discriminator { get; set; }
public string AvatarId { get; set; }
public string? Username { get; set; }
public string? AvatarId { get; set; }
public int? ClubId { get; set; }
public ClubInfo Club { get; set; }
public ClubInfo? Club { get; set; }
public bool IsClubAdmin { get; set; }
public long TotalXp { get; set; }
@ -18,14 +19,12 @@ public class DiscordUser : DbEntity
public long CurrencyAmount { get; set; }
public override bool Equals(object obj)
public override bool Equals(object? obj)
=> obj is DiscordUser du ? du.UserId == UserId : false;
public override int GetHashCode()
=> UserId.GetHashCode();
public override string ToString()
{
return Username;
}
=> Username ?? DEFAULT_USERNAME;
}

View file

@ -1,19 +1,29 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class FeedSub : DbEntity
{
public int GuildConfigId { get; set; }
public GuildConfig GuildConfig { get; set; }
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
public string Url { get; set; }
public string Message { get; set; }
}
public override int GetHashCode()
=> Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode();
public override bool Equals(object obj)
=> obj is FeedSub s && s.Url.ToLower() == Url.ToLower() && s.GuildConfigId == GuildConfigId;
public sealed class FeedSubEntityConfiguration : IEntityTypeConfiguration<FeedSub>
{
public void Configure(EntityTypeBuilder<FeedSub> builder)
{
builder
.HasIndex(x => new
{
x.GuildId,
x.Url
})
.IsUnique();
}
}

View file

@ -1,4 +1,4 @@
#nullable disable
#nullable disable
namespace EllieBot.Db.Models;
public class FlagTranslateChannel : DbEntity

View file

@ -1,7 +1,10 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class FollowedStream : DbEntity
public class FollowedStream
{
public enum FType
{
@ -12,7 +15,7 @@ public class FollowedStream : DbEntity
Trovo = 6,
Kick = 7,
}
public int Id { get; set; }
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
public string Username { get; set; }
@ -29,6 +32,17 @@ public class FollowedStream : DbEntity
public override bool Equals(object obj)
=> obj is FollowedStream fs && Equals(fs);
}
public sealed class FollowedStreamEntityConfig : IEntityTypeConfiguration<FollowedStream>
{
public void Configure(EntityTypeBuilder<FollowedStream> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.Username,
x.Type,
});
}
}

View file

@ -1,14 +1,24 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class GCChannelId : DbEntity
{
public GuildConfig GuildConfig { get; set; }
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
}
public override bool Equals(object obj)
=> obj is GCChannelId gc && gc.ChannelId == ChannelId;
public override int GetHashCode()
=> ChannelId.GetHashCode();
public class GCChannelIdEntityConfiguration : IEntityTypeConfiguration<GCChannelId>
{
public void Configure(EntityTypeBuilder<GCChannelId> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.ChannelId
})
.IsUnique();
}
}

View file

@ -1,85 +1,58 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class GuildFilterConfig
{
[Key]
public int Id { get; set; }
public ulong GuildId { get; set; }
public bool FilterInvites { get; set; }
public bool FilterLinks { get; set; }
public bool FilterWords { get; set; }
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new();
public HashSet<FilterLinksChannelId> FilterLinksChannelIds { get; set; } = new();
public HashSet<FilteredWord> FilteredWords { get; set; } = new();
public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
}
public sealed class GuildFilterConfigEntityConfiguration : IEntityTypeConfiguration<GuildFilterConfig>
{
public void Configure(EntityTypeBuilder<GuildFilterConfig> builder)
{
builder.HasIndex(x => x.GuildId);
}
}
public class GuildConfig : DbEntity
{
// public bool Keep { get; set; }
public ulong GuildId { get; set; }
public string Prefix { get; set; }
public bool DeleteMessageOnCommand { get; set; }
public HashSet<DelMsgOnCmdChannel> DelMsgOnCmdChannels { get; set; } = new();
public string AutoAssignRoleIds { get; set; }
//todo FUTURE: DELETE, UNUSED
public bool ExclusiveSelfAssignedRoles { get; set; }
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
//stream notifications
public HashSet<FollowedStream> FollowedStreams { get; set; } = new();
//currencyGeneration
public HashSet<GCChannelId> GenerateCurrencyChannelIds { get; set; } = new();
public List<Permissionv2> Permissions { get; set; }
public bool VerbosePermissions { get; set; } = true;
public string PermissionRole { get; set; }
public HashSet<CommandCooldown> CommandCooldowns { get; set; } = new();
//filtering
public bool FilterInvites { get; set; }
public bool FilterLinks { get; set; }
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new();
public HashSet<FilterLinksChannelId> FilterLinksChannelIds { get; set; } = new();
public bool FilterWords { get; set; }
public HashSet<FilteredWord> FilteredWords { get; set; } = new();
public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
// mute
public HashSet<MutedUserId> MutedUsers { get; set; } = new();
public string MuteRoleName { get; set; }
// chatterbot
public bool CleverbotEnabled { get; set; }
// protection
public AntiRaidSetting AntiRaidSetting { get; set; }
public AntiSpamSetting AntiSpamSetting { get; set; }
public AntiAltSetting AntiAltSetting { get; set; }
// time
public string Locale { get; set; }
public string TimeZoneId { get; set; }
// timers
public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new();
public HashSet<UnbanTimer> UnbanTimer { get; set; } = new();
public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
// vcrole
public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
// aliases
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
public bool WarningsInitialized { get; set; }
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
public HashSet<SlowmodeIgnoredRole> SlowmodeIgnoredRoles { get; set; }
public List<ShopEntry> ShopEntries { get; set; }
public ulong? GameVoiceChannel { get; set; }
public bool VerboseErrors { get; set; } = true;
public StreamRoleSettings StreamRole { get; set; }
public XpSettings XpSettings { get; set; }
public List<FeedSub> FeedSubs { get; set; } = new();
public bool NotifyStreamOffline { get; set; }
public bool DeleteStreamOnlineMessage { get; set; }
public int WarnExpireHours { get; set; }
@ -88,4 +61,9 @@ public class GuildConfig : DbEntity
public bool DisableGlobalExpressions { get; set; } = false;
public bool StickyRoles { get; set; }
public string TimeZoneId { get; set; }
public string Locale { get; set; }
public List<Permissionv2> Permissions { get; set; } = [];
}

View file

@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
@ -6,14 +6,14 @@ public class NCPixel
{
[Key]
public int Id { get; set; }
public required int Position { get; init; }
public required long Price { get; init; }
public required ulong OwnerId { get; init; }
public required uint Color { get; init; }
[MaxLength(256)]
public required string Text { get; init; }
}

View file

@ -1,4 +1,6 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
@ -7,7 +9,8 @@ namespace EllieBot.Db.Models;
[DebuggerDisplay("{PrimaryTarget}{SecondaryTarget} {SecondaryTargetName} {State} {PrimaryTargetId}")]
public class Permissionv2 : DbEntity, IIndexed
{
public int? GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public int Index { get; set; }
public PrimaryPermissionType PrimaryTarget { get; set; }
@ -49,4 +52,12 @@ public enum SecondaryPermissionType
Module,
Command,
AllModules
}
}
public sealed class Permissionv2EntityConfiguration : IEntityTypeConfiguration<Permissionv2>
{
public void Configure(EntityTypeBuilder<Permissionv2> builder)
{
builder.HasIndex(x => x.GuildId);
}
}

View file

@ -12,7 +12,7 @@ public sealed class SarGroup
public ulong? RoleReq { get; set; }
public ICollection<Sar> Roles { get; set; } = [];
public bool IsExclusive { get; set; }
[MaxLength(100)]
public string? Name { get; set; }
}

View file

@ -1,16 +1,21 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public enum ShopEntryType
{
Role,
List,
Command
}
public class ShopEntry : DbEntity, IIndexed
{
public ulong GuildId { get; set; }
public int Index { get; set; }
public int Price { get; set; }
public string Name { get; set; }
@ -25,7 +30,7 @@ public class ShopEntry : DbEntity, IIndexed
//list
public HashSet<ShopEntryItem> Items { get; set; } = new();
public ulong? RoleRequirement { get; set; }
// command
public string Command { get; set; }
}
@ -43,4 +48,22 @@ public class ShopEntryItem : DbEntity
public override int GetHashCode()
=> Text.GetHashCode(StringComparison.InvariantCulture);
}
public class ShopEntryEntityConfiguration : IEntityTypeConfiguration<ShopEntry>
{
public void Configure(EntityTypeBuilder<ShopEntry> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.Index
})
.IsUnique();
builder
.HasMany(x => x.Items)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
}
}

View file

@ -1,10 +1,16 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class StreamRoleSettings : DbEntity
{
public int GuildConfigId { get; set; }
public GuildConfig GuildConfig { get; set; }
public ulong GuildId { get; set; }
/// <summary>
/// Whether the feature is enabled in the guild.
@ -42,7 +48,7 @@ public class StreamRoleBlacklistedUser : DbEntity
{
public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; }
public string Username { get; set; }
@ -62,7 +68,7 @@ public class StreamRoleWhitelistedUser : DbEntity
{
public int StreamRoleSettingsId { get; set; }
public StreamRoleSettings StreamRoleSettings { get; set; }
public ulong UserId { get; set; }
public string Username { get; set; }
@ -71,4 +77,25 @@ public class StreamRoleWhitelistedUser : DbEntity
public override int GetHashCode()
=> UserId.GetHashCode();
}
public class StreamRoleSettingsEntityConfiguration : IEntityTypeConfiguration<StreamRoleSettings>
{
public void Configure(EntityTypeBuilder<StreamRoleSettings> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
builder
.HasMany(x => x.Whitelist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
builder
.HasMany(x => x.Blacklist)
.WithOne(x => x.StreamRoleSettings)
.HasForeignKey(x => x.StreamRoleSettingsId)
.OnDelete(DeleteBehavior.Cascade);
}
}

View file

@ -1,8 +1,26 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class VcRoleInfo : DbEntity
{
public ulong GuildId { get; set; }
public ulong VoiceChannelId { get; set; }
public ulong RoleId { get; set; }
}
public class VcRoleInfoEntityConfiguration : IEntityTypeConfiguration<VcRoleInfo>
{
public void Configure(EntityTypeBuilder<VcRoleInfo> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.VoiceChannelId
})
.IsUnique();
}
}

View file

@ -1,12 +1,30 @@
namespace EllieBot.Db.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class AntiAltSetting
{
public int GuildConfigId { get; set; }
[Key]
public int Id { get; set; }
public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public TimeSpan MinAge { get; set; }
public PunishmentAction Action { get; set; }
public int ActionDurationMinutes { get; set; }
public ulong? RoleId { get; set; }
}
public class AntiAltSettingEntityConfiguration : IEntityTypeConfiguration<AntiAltSetting>
{
public void Configure(EntityTypeBuilder<AntiAltSetting> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
}
}

View file

@ -1,11 +1,13 @@
#nullable disable
namespace EllieBot.Db.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class AntiRaidSetting : DbEntity
{
public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public int UserThreshold { get; set; }
public int Seconds { get; set; }
public PunishmentAction Action { get; set; }
@ -15,4 +17,13 @@ public class AntiRaidSetting : DbEntity
/// Mute, Chatmute, Voicemute, etc...
/// </summary>
public int PunishDuration { get; set; }
}
public class AntiRaidSettingEntityConfiguration : IEntityTypeConfiguration<AntiRaidSetting>
{
public void Configure(EntityTypeBuilder<AntiRaidSetting> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
}
}

View file

@ -3,10 +3,4 @@
public class AntiSpamIgnore : DbEntity
{
public ulong ChannelId { get; set; }
public override int GetHashCode()
=> ChannelId.GetHashCode();
public override bool Equals(object? obj)
=> obj is AntiSpamIgnore inst && inst.ChannelId == ChannelId;
}

View file

@ -1,13 +1,34 @@
namespace EllieBot.Db.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
#nullable disable
public class AntiSpamSetting : DbEntity
{
public int GuildConfigId { get; set; }
public ulong GuildId { get; set; }
public PunishmentAction Action { get; set; }
public int MessageThreshold { get; set; } = 3;
public int MuteTime { get; set; }
public ulong? RoleId { get; set; }
public HashSet<AntiSpamIgnore> IgnoredChannels { get; set; } = new();
public List<AntiSpamIgnore> IgnoredChannels { get; set; } = new();
}
// setup model
public class AntiSpamEntityConfiguration : IEntityTypeConfiguration<AntiSpamSetting>
{
public void Configure(EntityTypeBuilder<AntiSpamSetting> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
builder.HasMany(x => x.IgnoredChannels)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
}
}

View file

@ -1,4 +1,4 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
@ -24,4 +24,4 @@ public sealed class ButtonRole
public string Label { get; set; } = string.Empty;
public bool Exclusive { get; set; }
}
}

View file

@ -1,16 +1,23 @@
#nullable disable
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class FilterChannelId : DbEntity
public class FilterChannelId
{
[Key]
public int Id { get; set; }
public int? GuildFilterConfigId { get; set; }
public ulong ChannelId { get; set; }
public bool Equals(FilterChannelId other)
protected bool Equals(FilterChannelId other)
=> ChannelId == other.ChannelId;
public override bool Equals(object obj)
=> obj is FilterChannelId fci && Equals(fci);
public override bool Equals(object? obj)
=> obj is FilterChannelId fci && fci.Equals(this);
public override int GetHashCode()
=> ChannelId.GetHashCode();
}
}

View file

@ -1,12 +1,15 @@
#nullable disable
namespace EllieBot.Db.Models;
public class FilterLinksChannelId : DbEntity
{
public ulong ChannelId { get; set; }
public int? GuildFilterConfigId { get; set; }
public override bool Equals(object obj)
=> obj is FilterLinksChannelId f && f.ChannelId == ChannelId;
protected bool Equals(FilterLinksChannelId other)
=> ChannelId == other.ChannelId;
public override bool Equals(object? obj)
=> obj is FilterLinksChannelId other && Equals(other);
public override int GetHashCode()
=> ChannelId.GetHashCode();

View file

@ -3,14 +3,14 @@ namespace EllieBot.Db.Models;
public class FilterWordsChannelId : DbEntity
{
public int? GuildConfigId { get; set; }
public int? GuildFilterConfigId { get; set; }
public ulong ChannelId { get; set; }
public bool Equals(FilterWordsChannelId other)
protected bool Equals(FilterWordsChannelId other)
=> ChannelId == other.ChannelId;
public override bool Equals(object obj)
=> obj is FilterWordsChannelId fci && Equals(fci);
=> obj is FilterWordsChannelId other && Equals(other);
public override int GetHashCode()
=> ChannelId.GetHashCode();

View file

@ -1,7 +1,11 @@
#nullable disable
namespace EllieBot.Db.Models;
public class FilteredWord : DbEntity
{
public string Word { get; set; }
public int? GuildFilterConfigId { get; set; }
public string? Word { get; set; }
public override bool Equals(object? obj) => obj is FilteredWord fw && fw.Word == Word;
public override int GetHashCode() => Word?.GetHashCode() ?? 0;
}

View file

@ -1,13 +1,24 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class MutedUserId : DbEntity
{
public ulong GuildId { get; set; }
public ulong UserId { get; set; }
}
public override int GetHashCode()
=> UserId.GetHashCode();
public override bool Equals(object obj)
=> obj is MutedUserId mui ? mui.UserId == UserId : false;
public class MutedUserIdEntityConfiguration : IEntityTypeConfiguration<MutedUserId>
{
public void Configure(EntityTypeBuilder<MutedUserId> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
}

View file

@ -7,6 +7,6 @@ public class TempRole
public bool Remove { get; set; }
public ulong RoleId { get; set; }
public ulong UserId { get; set; }
public DateTime ExpiresAt { get; set; }
}

View file

@ -1,20 +1,24 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class SlowmodeIgnoredRole : DbEntity
{
public ulong GuildId { get; set; }
public ulong RoleId { get; set; }
}
// override object.Equals
public override bool Equals(object obj)
public class SlowmodeIgnoredRoleEntityConfiguration : IEntityTypeConfiguration<SlowmodeIgnoredRole>
{
public void Configure(EntityTypeBuilder<SlowmodeIgnoredRole> builder)
{
if (obj is null || GetType() != obj.GetType())
return false;
return ((SlowmodeIgnoredRole)obj).RoleId == RoleId;
builder.HasIndex(x => new
{
x.GuildId,
x.RoleId
})
.IsUnique();
}
// override object.GetHashCode
public override int GetHashCode()
=> RoleId.GetHashCode();
}

View file

@ -1,20 +1,24 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class SlowmodeIgnoredUser : DbEntity
{
public ulong GuildId { get; set; }
public ulong UserId { get; set; }
}
// override object.Equals
public override bool Equals(object obj)
public class SlowmodeIgnoredUserEntityConfiguration : IEntityTypeConfiguration<SlowmodeIgnoredUser>
{
public void Configure(EntityTypeBuilder<SlowmodeIgnoredUser> builder)
{
if (obj is null || GetType() != obj.GetType())
return false;
return ((SlowmodeIgnoredUser)obj).UserId == UserId;
builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
// override object.GetHashCode
public override int GetHashCode()
=> UserId.GetHashCode();
}

View file

@ -1,14 +1,26 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class UnbanTimer : DbEntity
{
public ulong GuildId { get; set; }
public ulong UserId { get; set; }
public DateTime UnbanAt { get; set; }
}
public override int GetHashCode()
=> UserId.GetHashCode();
public override bool Equals(object obj)
=> obj is UnbanTimer ut ? ut.UserId == UserId : false;
public class UnbanTimerEntityConfiguration : IEntityTypeConfiguration<UnbanTimer>
{
public void Configure(EntityTypeBuilder<UnbanTimer> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
}

View file

@ -1,14 +1,25 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public class UnmuteTimer : DbEntity
{
public ulong GuildId { get; set; }
public ulong UserId { get; set; }
public DateTime UnmuteAt { get; set; }
}
public override int GetHashCode()
=> UserId.GetHashCode();
public override bool Equals(object obj)
=> obj is UnmuteTimer ut ? ut.UserId == UserId : false;
public class UnmuteTimerEntityConfiguration : IEntityTypeConfiguration<UnmuteTimer>
{
public void Configure(EntityTypeBuilder<UnmuteTimer> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.UserId
}).IsUnique();
}
}

View file

@ -1,15 +1,27 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
// todo UN* remove unroletimer in favor of temprole
public class UnroleTimer : DbEntity
{
public ulong GuildId { get; set; }
public ulong UserId { get; set; }
public ulong RoleId { get; set; }
public DateTime UnbanAt { get; set; }
}
public override int GetHashCode()
=> UserId.GetHashCode() ^ RoleId.GetHashCode();
public override bool Equals(object obj)
=> obj is UnroleTimer ut ? ut.UserId == UserId && ut.RoleId == RoleId : false;
public class UnroleTimerEntityConfiguration : IEntityTypeConfiguration<UnroleTimer>
{
public void Configure(EntityTypeBuilder<UnroleTimer> builder)
{
builder.HasIndex(x => new
{
x.GuildId,
x.UserId
})
.IsUnique();
}
}

View file

@ -0,0 +1,14 @@
namespace EllieBot.Db.Models;
public class ExcludedItem : DbEntity
{
public int? XpSettingsId { get; set; }
public ulong ItemId { get; set; }
public ExcludedItemType ItemType { get; set; }
public override int GetHashCode()
=> ItemId.GetHashCode() ^ ItemType.GetHashCode();
public override bool Equals(object? obj)
=> obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType;
}

View file

@ -0,0 +1,3 @@
namespace EllieBot.Db.Models;
public enum ExcludedItemType { Channel, Role }

View file

@ -0,0 +1,8 @@
namespace EllieBot.Db.Models;
public class XpCurrencyReward : DbEntity
{
public int XpSettingsId { get; set; }
public int Level { get; set; }
public int Amount { get; set; }
}

View file

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class XpCurrencyRewardEntityConfiguration : IEntityTypeConfiguration<XpCurrencyReward>
{
public void Configure(EntityTypeBuilder<XpCurrencyReward> builder)
{
builder.HasIndex(x => new
{
x.Level,
x.XpSettingsId
})
.IsUnique();
}
}

View file

@ -1,4 +1,4 @@
namespace EllieBot.Db.Models;
namespace EllieBot.Db.Models;
public enum XpNotificationLocation
{

View file

@ -0,0 +1,13 @@
namespace EllieBot.Db.Models;
public class XpRoleReward : DbEntity
{
public int XpSettingsId { get; set; }
public int Level { get; set; }
public ulong RoleId { get; set; }
/// <summary>
/// Whether the role should be removed (true) or added (false)
/// </summary>
public bool Remove { get; set; }
}

View file

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class XpRoleRewardEntityConfiguration : IEntityTypeConfiguration<XpRoleReward>
{
public void Configure(EntityTypeBuilder<XpRoleReward> builder)
{
builder.HasIndex(x => new
{
x.XpSettingsId,
x.Level
})
.IsUnique();
}
}

View file

@ -1,64 +1,15 @@
#nullable disable
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EllieBot.Db.Models;
public class XpSettings : DbEntity
{
public int GuildConfigId { get; set; }
public GuildConfig GuildConfig { get; set; }
public ulong GuildId { get; set; }
public bool ServerExcluded { get; set; }
public HashSet<ExcludedItem> ExclusionList { get; set; } = new();
public HashSet<XpRoleReward> RoleRewards { get; set; } = new();
public HashSet<XpCurrencyReward> CurrencyRewards { get; set; } = new();
public HashSet<ExcludedItem> ExclusionList { get; set; } = new();
public bool ServerExcluded { get; set; }
}
public enum ExcludedItemType { Channel, Role }
public class XpRoleReward : DbEntity
{
public int XpSettingsId { get; set; }
public XpSettings XpSettings { get; set; }
public int Level { get; set; }
public ulong RoleId { get; set; }
/// <summary>
/// Whether the role should be removed (true) or added (false)
/// </summary>
public bool Remove { get; set; }
public override int GetHashCode()
=> Level.GetHashCode() ^ XpSettingsId.GetHashCode();
public override bool Equals(object obj)
=> obj is XpRoleReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId;
}
public class XpCurrencyReward : DbEntity
{
public int XpSettingsId { get; set; }
public XpSettings XpSettings { get; set; }
public int Level { get; set; }
public int Amount { get; set; }
public override int GetHashCode()
=> Level.GetHashCode() ^ XpSettingsId.GetHashCode();
public override bool Equals(object obj)
=> obj is XpCurrencyReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId;
}
public class ExcludedItem : DbEntity
{
public XpSettings XpSettings { get; set; }
public ulong ItemId { get; set; }
public ExcludedItemType ItemType { get; set; }
public override int GetHashCode()
=> ItemId.GetHashCode() ^ ItemType.GetHashCode();
public override bool Equals(object obj)
=> obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType;
}

View file

@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace EllieBot.Db.Models;
public class XpSettingsEntityConfiguration : IEntityTypeConfiguration<XpSettings>
{
public void Configure(EntityTypeBuilder<XpSettings> builder)
{
builder.HasIndex(x => x.GuildId)
.IsUnique();
builder.HasMany(x => x.CurrencyRewards)
.WithOne();
builder.HasMany(x => x.RoleRewards)
.WithOne();
builder.HasMany(x => x.ExclusionList)
.WithOne();
}
}

View file

@ -1,146 +1,138 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Version>5.3.9</Version>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Version>6.0.0-alpha1</Version>
<!-- Output/build -->
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
<OutputType>exe</OutputType>
<ApplicationIcon>ellie_icon.ico</ApplicationIcon>
<!-- Output/build -->
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
<OutputType>exe</OutputType>
<ApplicationIcon>ellie_icon.ico</ApplicationIcon>
<NoWarn>CS1066;CS8981</NoWarn>
<!-- Analysis/Warnings -->
<!-- <AnalysisMode>Recommended</AnalysisMode>-->
<!-- <AnalysisModeGlobalization>None</AnalysisModeGlobalization>-->
<NoWarn>CS1066;CS8981</NoWarn>
<!-- Profile-guided optimization -->
<TieredPGO>true</TieredPGO>
<DebugType>embedded</DebugType>
<!-- Profile-guided optimization -->
<TieredPGO>true</TieredPGO>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.1.2">
<PrivateAssets>all</PrivateAssets>
<Publish>True</Publish>
</PackageReference>
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Discord.Net" Version="3.16.0" />
<PackageReference Include="CoreCLR-NCalc" Version="3.1.246" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
<PackageReference Include="Google.Protobuf" Version="3.28.2" />
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Net.Client" Version="2.62.0" />
<PackageReference Include="Grpc.Tools" Version="2.66.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.5.0" />
<ItemGroup>
<PackageReference Include="AngleSharp" Version="1.2.0">
<PrivateAssets>all</PrivateAssets>
<Publish>True</Publish>
</PackageReference>
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Discord.Net" Version="3.17.1" />
<PackageReference Include="CoreCLR-NCalc" Version="3.1.253" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3653" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
<PackageReference Include="Google.Protobuf" Version="3.29.3" />
<PackageReference Include="Grpc" Version="2.46.6" />
<PackageReference Include="Grpc.Net.Client" Version="2.67.0" />
<PackageReference Include="Grpc.Tools" Version="2.69.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.8.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.1" />
<PackageReference Include="MorseCode.ITask" Version="2.0.3" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
<PackageReference Include="MorseCode.ITask" Version="2.0.3" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.1.0" />
<!-- DI -->
<!-- <PackageReference Include="Ninject" Version="3.3.6"/>-->
<!-- <PackageReference Include="Ninject.Extensions.Conventions" Version="3.3.0"/>-->
<!-- <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />-->
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
<PackageReference Include="DryIoc.dll" Version="5.4.3" />
<!-- <PackageReference Include="Scrutor" Version="4.2.0" />-->
<!-- DI -->
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
<PackageReference Include="DryIoc.dll" Version="5.4.3" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NonBlocking" Version="2.1.2" />
<PackageReference Include="OneOf" Version="3.0.263" />
<PackageReference Include="OneOf.SourceGenerator" Version="3.0.263" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.Seq" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.1" />
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NonBlocking" Version="2.1.2" />
<PackageReference Include="OneOf" Version="3.0.271" />
<PackageReference Include="OneOf.SourceGenerator" Version="3.0.271" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
<PackageReference Include="SixLabors.Fonts" Version="2.0.4" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.4" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009" />
<PackageReference Include="StackExchange.Redis" Version="2.8.0" />
<PackageReference Include="YamlDotNet" Version="15.1.4" />
<PackageReference Include="SharpToken" Version="2.0.3" />
<PackageReference Include="SixLabors.Fonts" Version="2.1.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.5" />
<PackageReference Include="SixLabors.Shapes" Version="1.0.0-beta0009" />
<PackageReference Include="StackExchange.Redis" Version="2.8.24" />
<PackageReference Include="YamlDotNet" Version="15.1.6" />
<PackageReference Include="SharpToken" Version="2.0.3" />
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<!-- Db-related packages -->
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<!-- Db-related packages -->
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="linq2db.EntityFrameworkCore" Version="8.1.0" />
<PackageReference Include="linq2db.EntityFrameworkCore" Version="9.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<!-- Used by stream notifications -->
<PackageReference Include="TwitchLib.Api" Version="3.4.1" />
<!-- Used by stream notifications -->
<PackageReference Include="TwitchLib.Api" Version="3.4.1" />
<!-- sqlselectcsv and stock -->
<PackageReference Include="CsvHelper" Version="32.0.3" />
<!-- sqlselectcsv and stock -->
<PackageReference Include="CsvHelper" Version="33.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EllieBot.GrpcApiBase\EllieBot.GrpcApiBase.csproj" />
<ProjectReference Include="..\Ellie.Marmalade\Ellie.Marmalade.csproj" />
<ProjectReference Include="..\EllieBot.Voice\EllieBot.Voice.csproj" />
<ProjectReference Include="..\EllieBot.Generators\EllieBot.Generators.csproj" OutputItemType="Analyzer" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="data\strings\responses\responses.en-US.json" />
<ProjectReference Include="..\EllieBot.GrpcApiBase\EllieBot.GrpcApiBase.csproj" />
<ProjectReference Include="..\Ellie.Marmalade\Ellie.Marmalade.csproj" />
<ProjectReference Include="..\EllieBot.Voice\EllieBot.Voice.csproj" />
<ProjectReference Include="..\EllieBot.Generators\EllieBot.Generators.csproj" OutputItemType="Analyzer" />
</ItemGroup>
<ItemGroup>
<None Update="data\**\*">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="creds.yml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="creds_example.yml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<AdditionalFiles Include="strings\responses\responses.en-US.json" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="..\EllieBot.Coordinator\Protos\coordinator.proto">
<Link>_common\CoordinatorProtos\coordinator.proto</Link>
<!-- <GrpcServices>Client</GrpcServices>-->
</Protobuf>
<None Update="data/**/*">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="strings/**">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Migrations/**/*.sql">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Protobuf Include="..\EllieBot.Coordinator\Protos\coordinator.proto">
<Link>_common\CoordinatorProtos\coordinator.proto</Link>
<GrpcServices>Client</GrpcServices>
</Protobuf>
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'GlobalEllie' ">
<!-- Define trace doesn't seem to affect the build at all so I had to remove $(DefineConstants)-->
<DefineTrace>false</DefineTrace>
<DefineConstants>GLOBAL_ELLIE</DefineConstants>
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
<Optimize>true</Optimize>
<DebugType>portable</DebugType>
<DebugSymbols>false</DebugSymbols>
<!-- Define trace doesn't seem to affect the build at all so I had to remove $(DefineConstants)-->
<DefineTrace>false</DefineTrace>
<DefineConstants>GLOBAL_ELLIE</DefineConstants>
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
<Optimize>true</Optimize>
<DebugType>portable</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,17 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Canti/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cbtnrole/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cclub/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Ccurrency/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cexpr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cfilter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cgiveaway/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cncanvas/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cpunish/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Croles/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cslowmode/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Csupport/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Ctodo/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cuntimer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=db_005Cmodels_005Cxp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cadministration_005Cnotify_005Cmodels/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View file

@ -1,133 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
namespace EllieBot.Migrations;
public static class MigrationQueries
{
public static void MergeAwardedXp(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("""
UPDATE UserXpStats
SET Xp = AwardedXp + Xp,
AwardedXp = 0
WHERE AwardedXp > 0;
""");
}
public static void MigrateSar(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsNpgsql())
return;
migrationBuilder.Sql("""
INSERT INTO GroupName (Number, GuildConfigId)
SELECT DISTINCT "Group", GC.Id
FROM SelfAssignableRoles as SAR
INNER JOIN GuildConfigs as GC
ON SAR.GuildId = GC.GuildId
WHERE SAR.GuildId not in (SELECT GuildConfigs.GuildId from GroupName LEFT JOIN GuildConfigs ON GroupName.GuildConfigId = GuildConfigs.Id);
INSERT INTO SarGroup (Id, GroupNumber, Name, IsExclusive, GuildId)
SELECT GN.Id, GN.Number, GN.Name, GC.ExclusiveSelfAssignedRoles, GC.GuildId
FROM GroupName as GN
INNER JOIN GuildConfigs as GC ON GN.GuildConfigId = GC.Id;
INSERT INTO Sar (GuildId, RoleId, SarGroupId, LevelReq)
SELECT SAR.GuildId, SAR.RoleId, (SELECT Id FROM SarGroup WHERE SG.Number = SarGroup.GroupNumber AND SG.GuildId = SarGroup.GuildId), MIN(SAR.LevelRequirement)
FROM SelfAssignableRoles as SAR
INNER JOIN (SELECT GuildId, gn.Number FROM GroupName as gn
INNER JOIN GuildConfigs as gc ON gn.GuildConfigId =gc.Id
) as SG
ON SG.GuildId = SAR.GuildId
WHERE SG.Number IN (SELECT GroupNumber FROM SarGroup WHERE Sar.GuildId = SarGroup.GuildId)
GROUP BY SAR.GuildId, SAR.RoleId;
INSERT INTO SarAutoDelete (GuildId, IsEnabled)
SELECT GuildId, AutoDeleteSelfAssignedRoleMessages FROM GuildConfigs WHERE AutoDeleteSelfAssignedRoleMessages = TRUE;
""");
}
public static void UpdateUsernames(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("UPDATE DiscordUser SET Username = '??' || Username WHERE Discriminator = '????';");
}
public static void MigrateRero(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.IsSqlite())
{
migrationBuilder.Sql(
@"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded)
select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded
from reactionrole
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;");
}
else if (migrationBuilder.IsNpgsql())
{
migrationBuilder.Sql(
@"insert into reactionroles(guildid, channelid, messageid, emote, roleid, ""group"", levelreq, dateadded)
select guildid, channelid, messageid, emotename, roleid, exclusive::int, 0, reactionrolemessage.dateadded
from reactionrole
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id
ON CONFLICT DO NOTHING;");
}
else
{
throw new NotSupportedException("This database provider doesn't have an implementation for MigrateRero");
}
}
public static void GuildConfigCleanup(MigrationBuilder builder)
{
builder.Sql($"""
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
DELETE FROM "StreamRoleBlacklistedUser" WHERE "StreamRoleSettingsId" is NULL;
DELETE FROM "Permissions" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
""");
}
public static void GreetSettingsCopy(MigrationBuilder builder)
{
builder.Sql("""
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 0, ChannelGreetMessageText, SendChannelGreetMessage, GreetMessageChannelId, AutoDeleteGreetMessagesTimer
FROM GuildConfigs
WHERE SendChannelGreetMessage = TRUE;
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 1, DmGreetMessageText, SendDmGreetMessage, GreetMessageChannelId, 0
FROM GuildConfigs
WHERE SendDmGreetMessage = TRUE;
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 2, ChannelByeMessageText, SendChannelByeMessage, ByeMessageChannelId, AutoDeleteByeMessagesTimer
FROM GuildConfigs
WHERE SendChannelByeMessage = TRUE;
INSERT INTO GreetSettings (GuildId, GreetType, MessageText, IsEnabled, ChannelId, AutoDeleteTimer)
SELECT GuildId, 3, BoostMessage, SendBoostMessage, BoostMessageChannelId, BoostMessageDeleteAfter
FROM GuildConfigs
WHERE SendBoostMessage = TRUE;
""");
}
public static void AddGuildIdsToWarningPunishment(MigrationBuilder builder)
{
builder.Sql("""
DELETE FROM WarningPunishment WHERE GuildConfigId IS NULL OR GuildConfigId NOT IN (SELECT Id FROM GuildConfigs);
UPDATE WarningPunishment
SET GuildId = (SELECT GuildId FROM GuildConfigs WHERE Id = GuildConfigId);
DELETE FROM WarningPunishment as wp
WHERE (wp.Count, wp.GuildConfigId) in (
SELECT wp2.Count, wp2.GuildConfigId FROM WarningPunishment as wp2
GROUP BY wp2.Count, wp2.GuildConfigId
HAVING COUNT(id) > 1
);
""");
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,26 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class stondel : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "deletestreamonlinemessage",
table: "guildconfigs",
type: "boolean",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "deletestreamonlinemessage",
table: "guildconfigs");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,40 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class bank : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "bankusers",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
balance = table.Column<long>(type: "bigint", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_bankusers", x => x.id);
});
migrationBuilder.CreateIndex(
name: "ix_bankusers_userid",
table: "bankusers",
column: "userid",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "bankusers");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,114 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class newrero : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "reactionroles",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
messageid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
emote = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true),
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
group = table.Column<int>(type: "integer", nullable: false),
levelreq = table.Column<int>(type: "integer", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_reactionroles", x => x.id);
});
migrationBuilder.CreateIndex(
name: "ix_reactionroles_guildid",
table: "reactionroles",
column: "guildid");
migrationBuilder.CreateIndex(
name: "ix_reactionroles_messageid_emote",
table: "reactionroles",
columns: new[] { "messageid", "emote" },
unique: true);
MigrationQueries.MigrateRero(migrationBuilder);
migrationBuilder.DropTable(
name: "reactionrole");
migrationBuilder.DropTable(
name: "reactionrolemessage");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "reactionroles");
migrationBuilder.CreateTable(
name: "reactionrolemessage",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
guildconfigid = table.Column<int>(type: "integer", nullable: false),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
exclusive = table.Column<bool>(type: "boolean", nullable: false),
index = table.Column<int>(type: "integer", nullable: false),
messageid = table.Column<decimal>(type: "numeric(20,0)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_reactionrolemessage", x => x.id);
table.ForeignKey(
name: "fk_reactionrolemessage_guildconfigs_guildconfigid",
column: x => x.guildconfigid,
principalTable: "guildconfigs",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "reactionrole",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
dateadded = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
emotename = table.Column<string>(type: "text", nullable: true),
reactionrolemessageid = table.Column<int>(type: "integer", nullable: true),
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_reactionrole", x => x.id);
table.ForeignKey(
name: "fk_reactionrole_reactionrolemessage_reactionrolemessageid",
column: x => x.reactionrolemessageid,
principalTable: "reactionrolemessage",
principalColumn: "id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "ix_reactionrole_reactionrolemessageid",
table: "reactionrole",
column: "reactionrolemessageid");
migrationBuilder.CreateIndex(
name: "ix_reactionrolemessage_guildconfigid",
table: "reactionrolemessage",
column: "guildconfigid");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,169 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class patronagesystem : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "patreonuserid",
table: "rewardedusers",
newName: "platformuserid");
migrationBuilder.RenameIndex(
name: "ix_rewardedusers_patreonuserid",
table: "rewardedusers",
newName: "ix_rewardedusers_platformuserid");
migrationBuilder.AlterColumn<long>(
name: "xp",
table: "userxpstats",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AlterColumn<long>(
name: "awardedxp",
table: "userxpstats",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AlterColumn<long>(
name: "amountrewardedthismonth",
table: "rewardedusers",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AlterColumn<bool>(
name: "verboseerrors",
table: "guildconfigs",
type: "boolean",
nullable: false,
defaultValue: true,
oldClrType: typeof(bool),
oldType: "boolean");
migrationBuilder.AlterColumn<long>(
name: "totalxp",
table: "discorduser",
type: "bigint",
nullable: false,
defaultValue: 0L,
oldClrType: typeof(int),
oldType: "integer",
oldDefaultValue: 0);
migrationBuilder.CreateTable(
name: "patronquotas",
columns: table => new
{
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
featuretype = table.Column<int>(type: "integer", nullable: false),
feature = table.Column<string>(type: "text", nullable: false),
hourlycount = table.Column<long>(type: "bigint", nullable: false),
dailycount = table.Column<long>(type: "bigint", nullable: false),
monthlycount = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_patronquotas", x => new { x.userid, x.featuretype, x.feature });
});
migrationBuilder.CreateTable(
name: "patrons",
columns: table => new
{
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
uniqueplatformuserid = table.Column<string>(type: "text", nullable: true),
amountcents = table.Column<int>(type: "integer", nullable: false),
lastcharge = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
validthru = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_patrons", x => x.userid);
});
migrationBuilder.CreateIndex(
name: "ix_patronquotas_userid",
table: "patronquotas",
column: "userid");
migrationBuilder.CreateIndex(
name: "ix_patrons_uniqueplatformuserid",
table: "patrons",
column: "uniqueplatformuserid",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "patronquotas");
migrationBuilder.DropTable(
name: "patrons");
migrationBuilder.RenameColumn(
name: "platformuserid",
table: "rewardedusers",
newName: "patreonuserid");
migrationBuilder.RenameIndex(
name: "ix_rewardedusers_platformuserid",
table: "rewardedusers",
newName: "ix_rewardedusers_patreonuserid");
migrationBuilder.AlterColumn<int>(
name: "xp",
table: "userxpstats",
type: "integer",
nullable: false,
oldClrType: typeof(long),
oldType: "bigint");
migrationBuilder.AlterColumn<int>(
name: "awardedxp",
table: "userxpstats",
type: "integer",
nullable: false,
oldClrType: typeof(long),
oldType: "bigint");
migrationBuilder.AlterColumn<int>(
name: "amountrewardedthismonth",
table: "rewardedusers",
type: "integer",
nullable: false,
oldClrType: typeof(long),
oldType: "bigint");
migrationBuilder.AlterColumn<bool>(
name: "verboseerrors",
table: "guildconfigs",
type: "boolean",
nullable: false,
oldClrType: typeof(bool),
oldType: "boolean",
oldDefaultValue: true);
migrationBuilder.AlterColumn<int>(
name: "totalxp",
table: "discorduser",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(long),
oldType: "bigint",
oldDefaultValue: 0L);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,36 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class stondeldbcache : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "streamonlinemessages",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
messageid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
type = table.Column<int>(type: "integer", nullable: false),
name = table.Column<string>(type: "text", nullable: true),
dateadded = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_streamonlinemessages", x => x.id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "streamonlinemessages");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,25 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class logwarns : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<decimal>(
name: "logwarnsid",
table: "logsettings",
type: "numeric(20,0)",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "logwarnsid",
table: "logsettings");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,42 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class xpitemshop : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "xpshopowneditem",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
itemtype = table.Column<int>(type: "integer", nullable: false),
isusing = table.Column<bool>(type: "boolean", nullable: false),
itemkey = table.Column<string>(type: "text", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_xpshopowneditem", x => x.id);
});
migrationBuilder.CreateIndex(
name: "ix_xpshopowneditem_userid_itemtype_itemkey",
table: "xpshopowneditem",
columns: new[] { "userid", "itemtype", "itemkey" },
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "xpshopowneditem");
}
}
}

View file

@ -1,26 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class linkonlychannels : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "type",
table: "imageonlychannels",
type: "integer",
nullable: false,
defaultValue: 0);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "type",
table: "imageonlychannels");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,25 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class banprune : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "prunedays",
table: "bantemplates",
type: "integer",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "prunedays",
table: "bantemplates");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,25 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class shoprolereq : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<decimal>(
name: "rolerequirement",
table: "shopentry",
type: "numeric(20,0)",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "rolerequirement",
table: "shopentry");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,40 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class autopub : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "autopublishchannel",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_autopublishchannel", x => x.id);
});
migrationBuilder.CreateIndex(
name: "ix_autopublishchannel_guildid",
table: "autopublishchannel",
column: "guildid",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "autopublishchannel");
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,41 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
public partial class gamblingstats : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "gamblingstats",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
feature = table.Column<string>(type: "text", nullable: true),
bet = table.Column<decimal>(type: "numeric", nullable: false),
paidout = table.Column<decimal>(type: "numeric", nullable: false),
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_gamblingstats", x => x.id);
});
migrationBuilder.CreateIndex(
name: "ix_gamblingstats_feature",
table: "gamblingstats",
column: "feature",
unique: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "gamblingstats");
}
}
}

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