diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9386bd391..dd9babade 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,6 +7,7 @@ stages:
- release
- publish-windows
- upload-windows-updater-release
+ - publish-medusa-package
variables:
project: "NadekoBot"
@@ -97,6 +98,16 @@ upload-windows-updater-release:
- aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/$INSTALLER_FILE_NAME" --acl public-read --body "$INSTALLER_OUTPUT_DIR/$INSTALLER_FILE_NAME"
- aws --endpoint-url $AWS_SERVICE_URL s3api put-object --bucket "$AWS_BUCKET_NAME" --key "dl/bot/releases-v3.json" --acl public-read --body "releases-v3.json"
+publish-medusa-package:
+ stage: publish-medusa-package
+ rules:
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ script:
+ - LAST_TAG=$(git describe --tags --abbrev=0)
+ - if [ $CI_COMMIT_TAG ]; then MEDUSA_VERSION=$CI_COMMIT_TAG; else MEDUSA_VERSION="$LAST_TAG-$CI_COMMIT_REF_SLUG"
+ - dotnet pack -c Release /p:Version=$MEDUSA_VERSION -o bin/Release/packed
+ - dotnet nuget push bin/Release/packed/ --api-key $MYGET_API_KEY --source https://www.myget.org/F/nadeko/api/v2/package
+
docker-build:
# Use the official docker image.
image: docker:latest
@@ -120,6 +131,6 @@ docker-build:
- docker push "$CI_REGISTRY_IMAGE${tag}"
# Run this job in a branch where a Dockerfile exists
rules:
- - if: $CI_COMMIT_BRANCH
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
exists:
- Dockerfile
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2700b3db..048df1e9f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,134 @@
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
-## Unreleased
+## [4.2.0] - 14.06.2022
+
+### Added
+
+- Added `data/searches.yml` file which configures some of the new search functionality
+ The file comments explaining what each property does.
+ Explained briefly here:
+ ```yml
+ # what will be used for .google command. Either google (official api) or searx
+ webSearchEngine: Google
+ # what will be used for .img command. Either google (official api) or searx
+ imgSearchEngine: Google
+ # how will yt results be retrieved: ytdataapi or ytdl or ytdlp
+ ytProvider: YtDataApiv3
+ # in case web or img search is set to searx, the following instances will be used:
+ searxInstances: []
+ # in case ytProvider is set to invidious, the following instances will be used
+ invidiousInstances: []
+ ```
+- Added new properties to `creds.yml`. google -> searchId and google -> searchImageId.
+- These properties are used as `cx` (google api query parameter) in case you've setup your `data/searches.yml` to use the official google api.
+ `searchId` is used for web search
+ `searchimageId` is used for image search
+ ```yml
+ google:
+ searchId: ""
+ searchImageId: ""
+ ```
+- Check `creds_example.yml` for comments explaining how to obtain them.
+
+#### Patronage system added
+- Added `data/patron.yml` for configuration
+- Implemented only for patreon so far
+- Patreon subscription code completely rewritten
+- Users who pledge on patreon get benefits based on the amount they pledged
+- Public nadeko only. But selfhosters can adapt it to their own patreon pages by configuring their patreon credentials in `creds.yml` and enabling the system in `data/patron.yml` file.
+ - Most of the patronage system strings are hardcoded atm, so if you wish to use this system on selfhosts, you will have to modify the source
+- Pledge amounts are split into tiers. This is not configurable atm.
+ - Tier I - 1$ - 4.99$ a month
+ - Tier V - 5$ - 9.99$ a month
+ - Tier X - 10$ - 19.99$ a month
+ - Tier XX - 20$ - 49.99$ a month
+ - Tier L - 50$ - 99.99$ a month
+ - Tier C - 100$+ a month
+- Rewards and command quotas for each of the tiers are configurable
+- Limitations to certain features are also configurable. ex:
+```yml
+quotas:
+ features:
+ "rero:max_count":
+ x: 50
+```
+- ^ this setting would set the maximum number of reaction roles to be 50 for a user who is in Patron Tier X
+- Read the comments in the .yml file for (much) more info
+- Quota system allows the owner to set up hourly, daily and monthly quota usage for each tier
+- Quota system applies to entire server owner by a patron
+ - Patron spends own quota by using the commands on any server
+ - Any user on *any* server owned by a patron spends that patron's quota
+- When users subscribe to patreon they will receive a welcome message
+ - If you're enabling patron system for a selfhost, you will want to edit it
+
+Added `.patron` and `.patronmessage` commands
+- `.patron` checks your patronage status, and quotas. Requires patron system to be enabled.
+- `.patronmessage` (owner only) sends message to all patrons with the specified tier or higher. Supports embeds
+
+- Added a fake `.cmdcd` command `cleverbot:response` which can be used to limit how often users can talk to the cleverbot.
+
+### Changed
+
+- CurrencyReward now support adding additional flowers to patrons.
+- `.donate` command completely reworked.
+ - Works only on public bot (OnlyPublicBotAttribute)
+ - Guides user on how to donate to support the project
+ - Added interaction explaining selfhosting
+
+- `.google` reimplemented. It now has 2 modes configurable in `data/searches.yml` under the `webSearchengine` property
+ - If set to `google`, official custom search api will be used. You will need to set googleapikey and google.searchId in `creds.yml`
+ - if set to `searx` one of the instances specified in the `searxInstances:` property will be randomly chosen for each request
+ - instances must have `format=json` allowed (public ones usually don't allow it)
+ - instances are specified as a fully qualified url, example: `https://my.cool.searx.instance.io`
+- `.image` reimplemented. Same as `.google` - it uses either `google` official api (in which case it uses `google.searchImageId` from `creds.yml`) or `searx`
+
+- `.youtube` reimplemented. It will use a `ytProvider:` property from `data/searches.yml` to determine how to retrieve results
+ - `ytdataapi` will use the official google api (requires `GoogleApiKey` specified in `creds.yml`) and YoutubeDataApi enabled in the dev console
+ - `ytdl` will use `youtube-dl` program from the host machine. It must be downloaded and it's location must be added to path env variable.
+ - `ytdlp` will use `yt-dlp` program from the host machine. Same as `youtube-dl` - must be in path env variable.
+ - `invidious` will use one of invidious instances specified in the `invidiousInstances` property. Very good.
+
+- `.google`, `.youtube` and `.image` moved to the new Search group
+
+Note: Results of each `.youtube` query will be cached for 1 hour to improve perfomance
+- Removed 30 second `.ping` ratelimit on public nadeko
+
+- xp image generation changes
+ - In case you have default settings, your xp image will look slightly different
+ - If you've modified xp_template.json, your xp image might look broken. Your old template will be saved in xp_template.json.old
+ - Xp number outline is now slightly thicker
+ - Xp number will now have Center vertical and horizontal alignment
+ - LastLevelUp no longer supported
+
+- Some commands will now use timestamp tags for better user experience
+- `.prune` was slightly slowed down to avoid ratelimits
+- `.wof` moved from it's own group to the default Gambling group
+- `.feed` urls which error for more than 100 times will be automatically removed.
+- `.ve` is now enabled by default
+
+- [dev] nadeko interaction slightly improved to make it less nonsense (they still don't make sense)
+- [dev] RewardedUsers table slightly changed to make it more general
+- [dev] renamed `// todo`s which aren't planned soon to `// FUTURE`
+- [dev] currency rewards have been reimplemented and moved to a separate service
+
+### Fixed
+
+- `.rh` no longer needs quotes for multi word roles
+- `.deletexp` will now properly delete server xp too
+- [dev] added support for configs to properly parse enums without case sensitivity (ConfigParsers.InsensitiveEnum)
+- [dev] Fixed a bug in .gencmdlist
+- [dev] small fixes to creds provider
+
+### Removed
+
+- `.ddg` removed.
+- [dev] removed some dead code and comments
+
+### Obsolete
+
+
+
### Fixed
diff --git a/src/Nadeko.Medusa/IEmbedBuilder.cs b/src/Nadeko.Medusa/IEmbedBuilder.cs
index 81c4ce55b..001848967 100644
--- a/src/Nadeko.Medusa/IEmbedBuilder.cs
+++ b/src/Nadeko.Medusa/IEmbedBuilder.cs
@@ -5,7 +5,7 @@ namespace NadekoBot;
public interface IEmbedBuilder
{
IEmbedBuilder WithDescription(string? desc);
- IEmbedBuilder WithTitle(string title);
+ IEmbedBuilder WithTitle(string? title);
IEmbedBuilder AddField(string title, object value, bool isInline = false);
IEmbedBuilder WithFooter(string text, string? iconUrl = null);
IEmbedBuilder WithAuthor(string name, string? iconUrl = null, string? url = null);
diff --git a/src/Nadeko.Medusa/Nadeko.Medusa.csproj b/src/Nadeko.Medusa/Nadeko.Medusa.csproj
index ea50a75c6..ea296c692 100644
--- a/src/Nadeko.Medusa/Nadeko.Medusa.csproj
+++ b/src/Nadeko.Medusa/Nadeko.Medusa.csproj
@@ -9,7 +9,6 @@
Nadeko.Snake
The NadekoBot Team
- 1.0.3
diff --git a/src/NadekoBot/Bot.cs b/src/NadekoBot/Bot.cs
index a6c87f713..fe65dc16d 100644
--- a/src/NadekoBot/Bot.cs
+++ b/src/NadekoBot/Bot.cs
@@ -4,9 +4,11 @@ using NadekoBot.Common.Configs;
using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db;
using NadekoBot.Modules.Administration;
+using NadekoBot.Modules.Utility;
using NadekoBot.Services.Database.Models;
using System.Collections.Immutable;
using System.Diagnostics;
+using System.Net;
using System.Reflection;
using RunMode = Discord.Commands.RunMode;
@@ -125,6 +127,12 @@ public sealed class Bot
{
AllowAutoRedirect = false
});
+
+ svcs.AddHttpClient("google:search")
+ .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler()
+ {
+ AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
+ });
if (Environment.GetEnvironmentVariable("NADEKOBOT_IS_COORDINATED") != "1")
svcs.AddSingleton();
@@ -164,6 +172,7 @@ public sealed class Bot
//initialize Services
Services = svcs.BuildServiceProvider();
Services.GetRequiredService().Initialize();
+ Services.GetRequiredService();
if (Client.ShardId == 0)
ApplyConfigMigrations();
diff --git a/src/NadekoBot/Common/Attributes/DontAddToIocContainerAttribute.cs b/src/NadekoBot/Common/Attributes/DontAddToIocContainerAttribute.cs
new file mode 100644
index 000000000..308681372
--- /dev/null
+++ b/src/NadekoBot/Common/Attributes/DontAddToIocContainerAttribute.cs
@@ -0,0 +1,11 @@
+#nullable disable
+namespace NadekoBot.Common;
+
+///
+/// Classed marked with this attribute will not be added to the service provider
+///
+[AttributeUsage(AttributeTargets.Class)]
+public class DontAddToIocContainerAttribute : Attribute
+{
+
+}
\ No newline at end of file
diff --git a/src/NadekoBot/Common/NoPublicBotPrecondition.cs b/src/NadekoBot/Common/Attributes/NoPublicBotPrecondition.cs
similarity index 56%
rename from src/NadekoBot/Common/NoPublicBotPrecondition.cs
rename to src/NadekoBot/Common/Attributes/NoPublicBotPrecondition.cs
index 432c1ef6c..7ed29dcb9 100644
--- a/src/NadekoBot/Common/NoPublicBotPrecondition.cs
+++ b/src/NadekoBot/Common/Attributes/NoPublicBotPrecondition.cs
@@ -20,11 +20,19 @@ public sealed class NoPublicBotAttribute : PreconditionAttribute
}
}
-///
-/// Classed marked with this attribute will not be added to the service provider
-///
-[AttributeUsage(AttributeTargets.Class)]
-public class DontAddToIocContainerAttribute : Attribute
+[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
+[SuppressMessage("Style", "IDE0022:Use expression body for methods")]
+public sealed class OnlyPublicBotAttribute : PreconditionAttribute
{
-
+ public override Task CheckPermissionsAsync(
+ ICommandContext context,
+ CommandInfo command,
+ IServiceProvider services)
+ {
+#if GLOBAL_NADEKO || DEBUG
+ return Task.FromResult(PreconditionResult.FromSuccess());
+#else
+ return Task.FromResult(PreconditionResult.FromError("Only available on the public bot."));
+#endif
+ }
}
\ No newline at end of file
diff --git a/src/NadekoBot/Common/Creds.cs b/src/NadekoBot/Common/Creds.cs
index d19b7ab9a..1bd11fa9e 100644
--- a/src/NadekoBot/Common/Creds.cs
+++ b/src/NadekoBot/Common/Creds.cs
@@ -18,7 +18,7 @@ public sealed class Creds : IBotCredentials
[Comment("Keep this on 'true' unless you're sure your bot shouldn't use privileged intents or you're waiting to be accepted")]
public bool UsePrivilegedIntents { get; set; }
- [Comment(@"The number of shards that the bot will running on.
+ [Comment(@"The number of shards that the bot will be running on.
Leave at 1 if you don't know what you're doing.")]
public int TotalShards { get; set; }
@@ -27,6 +27,16 @@ Leave at 1 if you don't know what you're doing.")]
Then, go to APIs and Services -> Credentials and click Create credentials -> API key.
Used only for Youtube Data Api (at the moment).")]
public string GoogleApiKey { get; set; }
+
+ [Comment(
+ @"Create a new custom search here https://programmablesearchengine.google.com/cse/create/new
+Enable SafeSearch
+Remove all Sites to Search
+Enable Search the entire web
+Copy the 'Search Engine ID' to the SearchId field
+
+Do all steps again but enable image search for the ImageSearchId")]
+ public GoogleApiConfig Google { get; set; }
[Comment(@"Settings for voting system for discordbots. Meant for use on global Nadeko.")]
public VotesSettings Votes { get; set; }
@@ -119,6 +129,7 @@ Windows default
CoordinatorUrl = "http://localhost:3442";
RestartCommand = new();
+ Google = new();
}
@@ -200,4 +211,10 @@ This should be equivalent to the DiscordsKey in your NadekoBot.Votes api appsett
DiscordsKey = discordsKey;
}
}
+}
+
+public class GoogleApiConfig
+{
+ public string SearchId { get; init; }
+ public string ImageSearchId { get; init; }
}
\ No newline at end of file
diff --git a/src/NadekoBot/Common/IBotCredentials.cs b/src/NadekoBot/Common/IBotCredentials.cs
index 859226e6d..8681c76a7 100644
--- a/src/NadekoBot/Common/IBotCredentials.cs
+++ b/src/NadekoBot/Common/IBotCredentials.cs
@@ -25,6 +25,7 @@ public interface IBotCredentials
string CoordinatorUrl { get; set; }
string TwitchClientId { get; set; }
string TwitchClientSecret { get; set; }
+ GoogleApiConfig Google { get; set; }
}
public class RestartConfig
diff --git a/src/NadekoBot/Common/Interaction/NadekoActionInteraction.cs b/src/NadekoBot/Common/Interaction/NadekoActionInteraction.cs
index 72673b265..62183a520 100644
--- a/src/NadekoBot/Common/Interaction/NadekoActionInteraction.cs
+++ b/src/NadekoBot/Common/Interaction/NadekoActionInteraction.cs
@@ -1,11 +1,11 @@
namespace NadekoBot;
-public sealed class NadekoActionInteraction : NadekoOwnInteraction
+public sealed class NadekoButtonActionInteraction : NadekoButtonOwnInteraction
{
private readonly NadekoInteractionData _data;
private readonly Func _action;
- public NadekoActionInteraction(
+ public NadekoButtonActionInteraction(
DiscordSocketClient client,
ulong authorId,
NadekoInteractionData data,
@@ -17,10 +17,12 @@ public sealed class NadekoActionInteraction : NadekoOwnInteraction
_action = action;
}
- public override string Name
+ protected override string Name
=> _data.CustomId;
- public override IEmote Emote
+ protected override IEmote Emote
=> _data.Emote;
+ protected override string? Text
+ => _data.Text;
public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
=> _action(smc);
diff --git a/src/NadekoBot/Common/Interaction/NadekoInteraction.cs b/src/NadekoBot/Common/Interaction/NadekoInteraction.cs
index cb7d0bfb6..02771c9f7 100644
--- a/src/NadekoBot/Common/Interaction/NadekoInteraction.cs
+++ b/src/NadekoBot/Common/Interaction/NadekoInteraction.cs
@@ -1,23 +1,24 @@
namespace NadekoBot;
-public abstract class NadekoInteraction
+public abstract class NadekoButtonInteraction
{
// improvements:
// - state in OnAction
// - configurable delay
// -
- public abstract string Name { get; }
- public abstract IEmote Emote { get; }
+ protected abstract string Name { get; }
+ protected abstract IEmote Emote { get; }
+ protected virtual string? Text { get; } = null;
- protected readonly DiscordSocketClient _client;
+ public DiscordSocketClient Client { get; }
protected readonly TaskCompletionSource _interactionCompletedSource;
protected IUserMessage message = null!;
- protected NadekoInteraction(DiscordSocketClient client)
+ protected NadekoButtonInteraction(DiscordSocketClient client)
{
- _client = client;
+ Client = client;
_interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
}
@@ -25,9 +26,9 @@ public abstract class NadekoInteraction
{
message = msg;
- _client.InteractionCreated += OnInteraction;
+ Client.InteractionCreated += OnInteraction;
await Task.WhenAny(Task.Delay(10_000), _interactionCompletedSource.Task);
- _client.InteractionCreated -= OnInteraction;
+ Client.InteractionCreated -= OnInteraction;
await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
}
@@ -65,13 +66,18 @@ public abstract class NadekoInteraction
}
- public MessageComponent CreateComponent()
+ public virtual MessageComponent CreateComponent()
{
var comp = new ComponentBuilder()
- .WithButton(new ButtonBuilder(style: ButtonStyle.Secondary, emote: Emote, customId: Name));
+ .WithButton(GetButtonBuilder());
return comp.Build();
}
+ public ButtonBuilder GetButtonBuilder()
+ => new ButtonBuilder(style: ButtonStyle.Secondary, emote: Emote, customId: Name, label: Text);
+
public abstract Task ExecuteOnActionAsync(SocketMessageComponent smc);
-}
\ No newline at end of file
+}
+
+// this is all so wrong ...
\ No newline at end of file
diff --git a/src/NadekoBot/Common/Interaction/NadekoInteractionArray.cs b/src/NadekoBot/Common/Interaction/NadekoInteractionArray.cs
new file mode 100644
index 000000000..b2471c1c9
--- /dev/null
+++ b/src/NadekoBot/Common/Interaction/NadekoInteractionArray.cs
@@ -0,0 +1,43 @@
+// namespace NadekoBot;
+//
+// public class NadekoButtonInteractionArray : NadekoButtonInteraction
+// {
+// private readonly ButtonBuilder[] _bbs;
+// private readonly NadekoButtonInteraction[] _inters;
+//
+// public NadekoButtonInteractionArray(params NadekoButtonInteraction[] inters)
+// : base(inters[0].Client)
+// {
+// _inters = inters;
+// _bbs = inters.Map(x => x.GetButtonBuilder());
+// }
+//
+// protected override string Name
+// => throw new NotSupportedException();
+// protected override IEmote Emote
+// => throw new NotSupportedException();
+//
+// protected override ValueTask Validate(SocketMessageComponent smc)
+// => new(true);
+//
+// public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
+// {
+// for (var i = 0; i < _bbs.Length; i++)
+// {
+// if (_bbs[i].CustomId == smc.Data.CustomId)
+// return _inters[i].ExecuteOnActionAsync(smc);
+// }
+//
+// return Task.CompletedTask;
+// }
+//
+// public override MessageComponent CreateComponent()
+// {
+// var comp = new ComponentBuilder();
+//
+// foreach (var bb in _bbs)
+// comp.WithButton(bb);
+//
+// return comp.Build();
+// }
+// }
\ No newline at end of file
diff --git a/src/NadekoBot/Common/Interaction/NadekoInteractionBuilder.cs b/src/NadekoBot/Common/Interaction/NadekoInteractionBuilder.cs
index 81d96dda2..585e98481 100644
--- a/src/NadekoBot/Common/Interaction/NadekoInteractionBuilder.cs
+++ b/src/NadekoBot/Common/Interaction/NadekoInteractionBuilder.cs
@@ -20,6 +20,7 @@ public class NadekoInteractionBuilder
// {
// this.isOwn = isOwn;
// return this;
+
// }
public NadekoInteractionBuilder WithAction(in Func fn)
@@ -28,7 +29,7 @@ public class NadekoInteractionBuilder
return this;
}
- public NadekoActionInteraction Build(DiscordSocketClient client, ulong userId)
+ public NadekoButtonActionInteraction Build(DiscordSocketClient client, ulong userId)
{
if (iData is null)
throw new InvalidOperationException("You have to specify the data before building the interaction");
diff --git a/src/NadekoBot/Common/Interaction/NadekoInteractionData.cs b/src/NadekoBot/Common/Interaction/NadekoInteractionData.cs
index 56e871c46..1ad8c99e4 100644
--- a/src/NadekoBot/Common/Interaction/NadekoInteractionData.cs
+++ b/src/NadekoBot/Common/Interaction/NadekoInteractionData.cs
@@ -5,4 +5,4 @@
///
/// Emote which will show on a button
/// Custom interaction id
-public record NadekoInteractionData(IEmote Emote, string CustomId);
\ No newline at end of file
+public record NadekoInteractionData(IEmote Emote, string CustomId, string? Text = null);
\ No newline at end of file
diff --git a/src/NadekoBot/Common/Interaction/NadekoOwnInteraction.cs b/src/NadekoBot/Common/Interaction/NadekoOwnInteraction.cs
index f6111b607..1d59f2835 100644
--- a/src/NadekoBot/Common/Interaction/NadekoOwnInteraction.cs
+++ b/src/NadekoBot/Common/Interaction/NadekoOwnInteraction.cs
@@ -3,11 +3,11 @@
///
/// Interaction which only the author can use
///
-public abstract class NadekoOwnInteraction : NadekoInteraction
+public abstract class NadekoButtonOwnInteraction : NadekoButtonInteraction
{
protected readonly ulong _authorId;
- protected NadekoOwnInteraction(DiscordSocketClient client, ulong authorId) : base(client)
+ protected NadekoButtonOwnInteraction(DiscordSocketClient client, ulong authorId) : base(client)
=> _authorId = authorId;
protected override ValueTask Validate(SocketMessageComponent smc)
diff --git a/src/NadekoBot/Common/NInteraction.cs b/src/NadekoBot/Common/NInteraction.cs
new file mode 100644
index 000000000..723fd0b72
--- /dev/null
+++ b/src/NadekoBot/Common/NInteraction.cs
@@ -0,0 +1,26 @@
+namespace NadekoBot.Common;
+
+public abstract class NInteraction
+{
+ private readonly DiscordSocketClient _client;
+ private readonly ulong _userId;
+ private readonly Func _action;
+
+ protected abstract NadekoInteractionData Data { get; }
+
+ public NInteraction(
+ DiscordSocketClient client,
+ ulong userId,
+ Func action)
+ {
+ _client = client;
+ _userId = userId;
+ _action = action;
+ }
+
+ public NadekoButtonInteraction GetInteraction()
+ => new NadekoInteractionBuilder()
+ .WithData(Data)
+ .WithAction(_action)
+ .Build(_client, _userId);
+}
\ No newline at end of file
diff --git a/src/NadekoBot/Common/NadekoModule.cs b/src/NadekoBot/Common/NadekoModule.cs
index add1769aa..d261fb4cf 100644
--- a/src/NadekoBot/Common/NadekoModule.cs
+++ b/src/NadekoBot/Common/NadekoModule.cs
@@ -36,7 +36,7 @@ public abstract class NadekoModule : ModuleBase
string error,
string url = null,
string footer = null,
- NadekoInteraction inter = null)
+ NadekoButtonInteraction inter = null)
=> ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
public Task SendConfirmAsync(
@@ -47,32 +47,32 @@ public abstract class NadekoModule : ModuleBase
=> ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
//
- public Task SendErrorAsync(string text, NadekoInteraction inter = null)
+ public Task SendErrorAsync(string text, NadekoButtonInteraction inter = null)
=> ctx.Channel.SendAsync(_eb, text, MessageType.Error, inter);
- public Task SendConfirmAsync(string text, NadekoInteraction inter = null)
+ public Task SendConfirmAsync(string text, NadekoButtonInteraction inter = null)
=> ctx.Channel.SendAsync(_eb, text, MessageType.Ok, inter);
- public Task SendPendingAsync(string text, NadekoInteraction inter = null)
+ public Task SendPendingAsync(string text, NadekoButtonInteraction inter = null)
=> ctx.Channel.SendAsync(_eb, text, MessageType.Pending, inter);
// localized normal
- public Task ErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
+ public Task ErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
=> SendErrorAsync(GetText(str), inter);
- public Task PendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
+ public Task PendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
=> SendPendingAsync(GetText(str), inter);
- public Task ConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
+ public Task ConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
=> SendConfirmAsync(GetText(str), inter);
// localized replies
- public Task ReplyErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
+ public Task ReplyErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
- public Task ReplyPendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
+ public Task ReplyPendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
- public Task ReplyConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
+ public Task ReplyConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
=> SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
public async Task PromptUserConfirmAsync(IEmbedBuilder embed)
diff --git a/src/NadekoBot/Common/SmartText/SmartText.cs b/src/NadekoBot/Common/SmartText/SmartText.cs
index e9e68b78c..0c4c9d6ed 100644
--- a/src/NadekoBot/Common/SmartText/SmartText.cs
+++ b/src/NadekoBot/Common/SmartText/SmartText.cs
@@ -1,5 +1,4 @@
#nullable disable
-using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace NadekoBot;
@@ -45,7 +44,6 @@ public abstract record SmartText
_ => throw new ArgumentOutOfRangeException(nameof(text))
};
- [CanBeNull]
public static SmartText CreateFrom(string input)
{
if (string.IsNullOrWhiteSpace(input))
diff --git a/src/NadekoBot/Db/Models/DiscordUser.cs b/src/NadekoBot/Db/Models/DiscordUser.cs
index 5b95f95e6..2e14ffcdf 100644
--- a/src/NadekoBot/Db/Models/DiscordUser.cs
+++ b/src/NadekoBot/Db/Models/DiscordUser.cs
@@ -3,6 +3,8 @@ using NadekoBot.Services.Database.Models;
namespace NadekoBot.Db.Models;
+
+// FUTURE remove LastLevelUp from here and UserXpStats
public class DiscordUser : DbEntity
{
public ulong UserId { get; set; }
diff --git a/src/NadekoBot/Db/Models/GuildConfig.cs b/src/NadekoBot/Db/Models/GuildConfig.cs
index 7c39fa270..38211e565 100644
--- a/src/NadekoBot/Db/Models/GuildConfig.cs
+++ b/src/NadekoBot/Db/Models/GuildConfig.cs
@@ -84,7 +84,7 @@ public class GuildConfig : DbEntity
public List ShopEntries { get; set; }
public ulong? GameVoiceChannel { get; set; }
- public bool VerboseErrors { get; set; }
+ public bool VerboseErrors { get; set; } = true;
public StreamRoleSettings StreamRole { get; set; }
diff --git a/src/NadekoBot/Db/Models/PatronQuota.cs b/src/NadekoBot/Db/Models/PatronQuota.cs
new file mode 100644
index 000000000..8b909afed
--- /dev/null
+++ b/src/NadekoBot/Db/Models/PatronQuota.cs
@@ -0,0 +1,38 @@
+#nullable disable
+namespace NadekoBot.Db.Models;
+
+///
+/// Contains data about usage of Patron-Only commands per user
+/// in order to provide support for quota limitations
+/// (allow user x who is pledging amount y to use the specified command only
+/// x amount of times in the specified time period)
+///
+public class PatronQuota
+{
+ public ulong UserId { get; set; }
+ public FeatureType FeatureType { get; set; }
+ public string Feature { get; set; }
+ public uint HourlyCount { get; set; }
+ public uint DailyCount { get; set; }
+ public uint MonthlyCount { get; set; }
+}
+
+public enum FeatureType
+{
+ Command,
+ Group,
+ Module,
+ Limit
+}
+
+public class PatronUser
+{
+ public string UniquePlatformUserId { get; set; }
+ public ulong UserId { get; set; }
+ public int AmountCents { get; set; }
+
+ public DateTime LastCharge { get; set; }
+
+ // Date Only component
+ public DateTime ValidThru { get; set; }
+}
\ No newline at end of file
diff --git a/src/NadekoBot/Db/Models/RewardedUser.cs b/src/NadekoBot/Db/Models/RewardedUser.cs
index f0a8f926d..13f5b9997 100644
--- a/src/NadekoBot/Db/Models/RewardedUser.cs
+++ b/src/NadekoBot/Db/Models/RewardedUser.cs
@@ -4,7 +4,7 @@ namespace NadekoBot.Services.Database.Models;
public class RewardedUser : DbEntity
{
public ulong UserId { get; set; }
- public string PatreonUserId { get; set; }
- public int AmountRewardedThisMonth { get; set; }
+ public string PlatformUserId { get; set; }
+ public long AmountRewardedThisMonth { get; set; }
public DateTime LastReward { get; set; }
}
\ No newline at end of file
diff --git a/src/NadekoBot/Db/NadekoContext.cs b/src/NadekoBot/Db/NadekoContext.cs
index 1c5887e54..5f1f85e63 100644
--- a/src/NadekoBot/Db/NadekoContext.cs
+++ b/src/NadekoBot/Db/NadekoContext.cs
@@ -1,6 +1,5 @@
#nullable disable
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Logging;
using NadekoBot.Db.Models;
using NadekoBot.Services.Database.Models;
@@ -26,7 +25,7 @@ public abstract class NadekoContext : DbContext
public DbSet Clubs { get; set; }
public DbSet ClubBans { get; set; }
public DbSet ClubApplicants { get; set; }
-
+
//logging
public DbSet LogSettings { get; set; }
@@ -51,19 +50,23 @@ public abstract class NadekoContext : DbContext
public DbSet AutoTranslateUsers { get; set; }
public DbSet Permissions { get; set; }
-
+
public DbSet BankUsers { get; set; }
-
+
public DbSet ReactionRoles { get; set; }
+ public DbSet Patrons { get; set; }
+
+ public DbSet PatronQuotas { get; set; }
+
#region Mandatory Provider-Specific Values
protected abstract string CurrencyTransactionOtherIdDefaultValue { get; }
protected abstract string DiscordUserLastXpGainDefaultValue { get; }
protected abstract string LastLevelUpDefaultValue { get; }
-
+
#endregion
-
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
#region QUOTES
@@ -77,7 +80,11 @@ public abstract class NadekoContext : DbContext
#region GuildConfig
var configEntity = modelBuilder.Entity();
- configEntity.HasIndex(c => c.GuildId).IsUnique();
+ configEntity.HasIndex(c => c.GuildId)
+ .IsUnique();
+
+ configEntity.Property(x => x.VerboseErrors)
+ .HasDefaultValue(true);
modelBuilder.Entity().HasOne(x => x.GuildConfig).WithOne(x => x.AntiSpamSetting);
@@ -193,13 +200,6 @@ public abstract class NadekoContext : DbContext
#endregion
- #region PatreonRewards
-
- var pr = modelBuilder.Entity();
- pr.HasIndex(x => x.PatreonUserId).IsUnique();
-
- #endregion
-
#region XpStats
var xps = modelBuilder.Entity();
@@ -369,12 +369,13 @@ public abstract class NadekoContext : DbContext
.IsUnique(false);
rr2.HasIndex(x => new
- {
- x.MessageId,
- x.Emote
- }).IsUnique();
+ {
+ x.MessageId,
+ x.Emote
+ })
+ .IsUnique();
});
-
+
#endregion
#region LogSettings
@@ -419,7 +420,37 @@ public abstract class NadekoContext : DbContext
modelBuilder.Entity(bu => bu.HasIndex(x => x.UserId).IsUnique());
#endregion
-
+
+
+ #region Patron
+
+ // currency rewards
+ var pr = modelBuilder.Entity();
+ pr.HasIndex(x => x.PlatformUserId).IsUnique();
+
+ // patrons
+ // patrons are not identified by their user id, but by their platform user id
+ // as multiple accounts (even maybe on different platforms) could have
+ // the same account connected to them
+ modelBuilder.Entity(pu =>
+ {
+ pu.HasIndex(x => x.UniquePlatformUserId).IsUnique();
+ pu.HasKey(x => x.UserId);
+ });
+
+ // quotes are per user id
+ modelBuilder.Entity(pq =>
+ {
+ pq.HasIndex(x => x.UserId).IsUnique(false);
+ pq.HasKey(x => new
+ {
+ x.UserId,
+ x.FeatureType,
+ x.Feature
+ });
+ });
+
+ #endregion
}
#if DEBUG
diff --git a/src/NadekoBot/Migrations/MySql/20220614071410_patronage-system.Designer.cs b/src/NadekoBot/Migrations/MySql/20220614071410_patronage-system.Designer.cs
new file mode 100644
index 000000000..70961f3cf
--- /dev/null
+++ b/src/NadekoBot/Migrations/MySql/20220614071410_patronage-system.Designer.cs
@@ -0,0 +1,3481 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NadekoBot.Services.Database;
+
+#nullable disable
+
+namespace NadekoBot.Migrations.Mysql
+{
+ [DbContext(typeof(MysqlContext))]
+ [Migration("20220614071410_patronage-system")]
+ partial class patronagesystem
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "6.0.5")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ modelBuilder.Entity("NadekoBot.Db.Models.BankUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("Balance")
+ .HasColumnType("bigint")
+ .HasColumnName("balance");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("UserId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("userid");
+
+ b.HasKey("Id")
+ .HasName("pk_bankusers");
+
+ b.HasIndex("UserId")
+ .IsUnique()
+ .HasDatabaseName("ix_bankusers_userid");
+
+ b.ToTable("bankusers", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
+ {
+ b.Property("ClubId")
+ .HasColumnType("int")
+ .HasColumnName("clubid");
+
+ b.Property("UserId")
+ .HasColumnType("int")
+ .HasColumnName("userid");
+
+ b.HasKey("ClubId", "UserId")
+ .HasName("pk_clubapplicants");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_clubapplicants_userid");
+
+ b.ToTable("clubapplicants", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Db.Models.ClubBans", b =>
+ {
+ b.Property("ClubId")
+ .HasColumnType("int")
+ .HasColumnName("clubid");
+
+ b.Property("UserId")
+ .HasColumnType("int")
+ .HasColumnName("userid");
+
+ b.HasKey("ClubId", "UserId")
+ .HasName("pk_clubbans");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_clubbans_userid");
+
+ b.ToTable("clubbans", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Db.Models.ClubInfo", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("Description")
+ .HasColumnType("longtext")
+ .HasColumnName("description");
+
+ b.Property("ImageUrl")
+ .HasColumnType("longtext")
+ .HasColumnName("imageurl");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("varchar(20)")
+ .HasColumnName("name")
+ .UseCollation("utf8mb4_bin");
+
+ b.Property("OwnerId")
+ .HasColumnType("int")
+ .HasColumnName("ownerid");
+
+ b.Property("Xp")
+ .HasColumnType("int")
+ .HasColumnName("xp");
+
+ b.HasKey("Id")
+ .HasName("pk_clubs");
+
+ b.HasAlternateKey("Name")
+ .HasName("ak_clubs_name");
+
+ b.HasIndex("OwnerId")
+ .IsUnique()
+ .HasDatabaseName("ix_clubs_ownerid");
+
+ b.ToTable("clubs", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Db.Models.DiscordUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("AvatarId")
+ .HasColumnType("longtext")
+ .HasColumnName("avatarid");
+
+ b.Property("ClubId")
+ .HasColumnType("int")
+ .HasColumnName("clubid");
+
+ b.Property("CurrencyAmount")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasDefaultValue(0L)
+ .HasColumnName("currencyamount");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("Discriminator")
+ .HasColumnType("longtext")
+ .HasColumnName("discriminator");
+
+ b.Property("IsClubAdmin")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(false)
+ .HasColumnName("isclubadmin");
+
+ b.Property("LastLevelUp")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime(6)")
+ .HasColumnName("lastlevelup")
+ .HasDefaultValueSql("(UTC_TIMESTAMP)");
+
+ b.Property("LastXpGain")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("datetime(6)")
+ .HasColumnName("lastxpgain")
+ .HasDefaultValueSql("(UTC_TIMESTAMP - INTERVAL 1 year)");
+
+ b.Property("NotifyOnLevelUp")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasDefaultValue(0)
+ .HasColumnName("notifyonlevelup");
+
+ b.Property("TotalXp")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint")
+ .HasDefaultValue(0L)
+ .HasColumnName("totalxp");
+
+ b.Property("UserId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("userid");
+
+ b.Property("Username")
+ .HasColumnType("longtext")
+ .HasColumnName("username");
+
+ b.HasKey("Id")
+ .HasName("pk_discorduser");
+
+ b.HasAlternateKey("UserId")
+ .HasName("ak_discorduser_userid");
+
+ b.HasIndex("ClubId")
+ .HasDatabaseName("ix_discorduser_clubid");
+
+ b.HasIndex("CurrencyAmount")
+ .HasDatabaseName("ix_discorduser_currencyamount");
+
+ b.HasIndex("TotalXp")
+ .HasDatabaseName("ix_discorduser_totalxp");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_discorduser_userid");
+
+ b.ToTable("discorduser", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("GuildId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("guildid");
+
+ b.Property("Message")
+ .HasColumnType("longtext")
+ .HasColumnName("message");
+
+ b.Property("Type")
+ .HasColumnType("int")
+ .HasColumnName("type");
+
+ b.Property("Username")
+ .HasColumnType("longtext")
+ .HasColumnName("username");
+
+ b.HasKey("Id")
+ .HasName("pk_followedstream");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_followedstream_guildconfigid");
+
+ b.ToTable("followedstream", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Db.Models.PatronQuota", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("userid");
+
+ b.Property("FeatureType")
+ .HasColumnType("int")
+ .HasColumnName("featuretype");
+
+ b.Property("Feature")
+ .HasColumnType("varchar(255)")
+ .HasColumnName("feature");
+
+ b.Property("DailyCount")
+ .HasColumnType("int unsigned")
+ .HasColumnName("dailycount");
+
+ b.Property("HourlyCount")
+ .HasColumnType("int unsigned")
+ .HasColumnName("hourlycount");
+
+ b.Property("MonthlyCount")
+ .HasColumnType("int unsigned")
+ .HasColumnName("monthlycount");
+
+ b.HasKey("UserId", "FeatureType", "Feature")
+ .HasName("pk_patronquotas");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_patronquotas_userid");
+
+ b.ToTable("patronquotas", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Db.Models.PatronUser", b =>
+ {
+ b.Property("UserId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("userid");
+
+ b.Property("AmountCents")
+ .HasColumnType("int")
+ .HasColumnName("amountcents");
+
+ b.Property("LastCharge")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("lastcharge");
+
+ b.Property("UniquePlatformUserId")
+ .HasColumnType("varchar(255)")
+ .HasColumnName("uniqueplatformuserid");
+
+ b.Property("ValidThru")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("validthru");
+
+ b.HasKey("UserId")
+ .HasName("pk_patrons");
+
+ b.HasIndex("UniquePlatformUserId")
+ .IsUnique()
+ .HasDatabaseName("ix_patrons_uniqueplatformuserid");
+
+ b.ToTable("patrons", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiAltSetting", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("Action")
+ .HasColumnType("int")
+ .HasColumnName("action");
+
+ b.Property("ActionDurationMinutes")
+ .HasColumnType("int")
+ .HasColumnName("actiondurationminutes");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("MinAge")
+ .HasColumnType("time(6)")
+ .HasColumnName("minage");
+
+ b.Property("RoleId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("roleid");
+
+ b.HasKey("Id")
+ .HasName("pk_antialtsetting");
+
+ b.HasIndex("GuildConfigId")
+ .IsUnique()
+ .HasDatabaseName("ix_antialtsetting_guildconfigid");
+
+ b.ToTable("antialtsetting", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("Action")
+ .HasColumnType("int")
+ .HasColumnName("action");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("PunishDuration")
+ .HasColumnType("int")
+ .HasColumnName("punishduration");
+
+ b.Property("Seconds")
+ .HasColumnType("int")
+ .HasColumnName("seconds");
+
+ b.Property("UserThreshold")
+ .HasColumnType("int")
+ .HasColumnName("userthreshold");
+
+ b.HasKey("Id")
+ .HasName("pk_antiraidsetting");
+
+ b.HasIndex("GuildConfigId")
+ .IsUnique()
+ .HasDatabaseName("ix_antiraidsetting_guildconfigid");
+
+ b.ToTable("antiraidsetting", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("AntiSpamSettingId")
+ .HasColumnType("int")
+ .HasColumnName("antispamsettingid");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.HasKey("Id")
+ .HasName("pk_antispamignore");
+
+ b.HasIndex("AntiSpamSettingId")
+ .HasDatabaseName("ix_antispamignore_antispamsettingid");
+
+ b.ToTable("antispamignore", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("Action")
+ .HasColumnType("int")
+ .HasColumnName("action");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("MessageThreshold")
+ .HasColumnType("int")
+ .HasColumnName("messagethreshold");
+
+ b.Property("MuteTime")
+ .HasColumnType("int")
+ .HasColumnName("mutetime");
+
+ b.Property("RoleId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("roleid");
+
+ b.HasKey("Id")
+ .HasName("pk_antispamsetting");
+
+ b.HasIndex("GuildConfigId")
+ .IsUnique()
+ .HasDatabaseName("ix_antispamsetting_guildconfigid");
+
+ b.ToTable("antispamsetting", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoCommand", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("ChannelName")
+ .HasColumnType("longtext")
+ .HasColumnName("channelname");
+
+ b.Property("CommandText")
+ .HasColumnType("longtext")
+ .HasColumnName("commandtext");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("guildid");
+
+ b.Property("GuildName")
+ .HasColumnType("longtext")
+ .HasColumnName("guildname");
+
+ b.Property("Interval")
+ .HasColumnType("int")
+ .HasColumnName("interval");
+
+ b.Property("VoiceChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("voicechannelid");
+
+ b.Property("VoiceChannelName")
+ .HasColumnType("longtext")
+ .HasColumnName("voicechannelname");
+
+ b.HasKey("Id")
+ .HasName("pk_autocommands");
+
+ b.ToTable("autocommands", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateChannel", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("AutoDelete")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("autodelete");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("guildid");
+
+ b.HasKey("Id")
+ .HasName("pk_autotranslatechannels");
+
+ b.HasIndex("ChannelId")
+ .IsUnique()
+ .HasDatabaseName("ix_autotranslatechannels_channelid");
+
+ b.HasIndex("GuildId")
+ .HasDatabaseName("ix_autotranslatechannels_guildid");
+
+ b.ToTable("autotranslatechannels", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.AutoTranslateUser", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("int")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("Source")
+ .HasColumnType("longtext")
+ .HasColumnName("source");
+
+ b.Property("Target")
+ .HasColumnType("longtext")
+ .HasColumnName("target");
+
+ b.Property("UserId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("userid");
+
+ b.HasKey("Id")
+ .HasName("pk_autotranslateusers");
+
+ b.HasAlternateKey("ChannelId", "UserId")
+ .HasName("ak_autotranslateusers_channelid_userid");
+
+ b.ToTable("autotranslateusers", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.BanTemplate", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("guildid");
+
+ b.Property("Text")
+ .HasColumnType("longtext")
+ .HasColumnName("text");
+
+ b.HasKey("Id")
+ .HasName("pk_bantemplates");
+
+ b.HasIndex("GuildId")
+ .IsUnique()
+ .HasDatabaseName("ix_bantemplates_guildid");
+
+ b.ToTable("bantemplates", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistEntry", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("ItemId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("itemid");
+
+ b.Property("Type")
+ .HasColumnType("int")
+ .HasColumnName("type");
+
+ b.HasKey("Id")
+ .HasName("pk_blacklist");
+
+ b.ToTable("blacklist", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandAlias", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("Mapping")
+ .HasColumnType("longtext")
+ .HasColumnName("mapping");
+
+ b.Property("Trigger")
+ .HasColumnType("longtext")
+ .HasColumnName("trigger");
+
+ b.HasKey("Id")
+ .HasName("pk_commandalias");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_commandalias_guildconfigid");
+
+ b.ToTable("commandalias", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("CommandName")
+ .HasColumnType("longtext")
+ .HasColumnName("commandname");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("Seconds")
+ .HasColumnType("int")
+ .HasColumnName("seconds");
+
+ b.HasKey("Id")
+ .HasName("pk_commandcooldown");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_commandcooldown_guildconfigid");
+
+ b.ToTable("commandcooldown", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("Amount")
+ .HasColumnType("bigint")
+ .HasColumnName("amount");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("Extra")
+ .IsRequired()
+ .HasColumnType("longtext")
+ .HasColumnName("extra");
+
+ b.Property("Note")
+ .HasColumnType("longtext")
+ .HasColumnName("note");
+
+ b.Property("OtherId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("otherid")
+ .HasDefaultValueSql("NULL");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("longtext")
+ .HasColumnName("type");
+
+ b.Property("UserId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("userid");
+
+ b.HasKey("Id")
+ .HasName("pk_currencytransactions");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_currencytransactions_userid");
+
+ b.ToTable("currencytransactions", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.DelMsgOnCmdChannel", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("State")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("state");
+
+ b.HasKey("Id")
+ .HasName("pk_delmsgoncmdchannel");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_delmsgoncmdchannel_guildconfigid");
+
+ b.ToTable("delmsgoncmdchannel", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordPermOverride", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("Command")
+ .HasColumnType("varchar(255)")
+ .HasColumnName("command");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("guildid");
+
+ b.Property("Perm")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("perm");
+
+ b.HasKey("Id")
+ .HasName("pk_discordpermoverrides");
+
+ b.HasIndex("GuildId", "Command")
+ .IsUnique()
+ .HasDatabaseName("ix_discordpermoverrides_guildid_command");
+
+ b.ToTable("discordpermoverrides", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.ExcludedItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("ItemId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("itemid");
+
+ b.Property("ItemType")
+ .HasColumnType("int")
+ .HasColumnName("itemtype");
+
+ b.Property("XpSettingsId")
+ .HasColumnType("int")
+ .HasColumnName("xpsettingsid");
+
+ b.HasKey("Id")
+ .HasName("pk_excludeditem");
+
+ b.HasIndex("XpSettingsId")
+ .HasDatabaseName("ix_excludeditem_xpsettingsid");
+
+ b.ToTable("excludeditem", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.FeedSub", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("varchar(255)")
+ .HasColumnName("url");
+
+ b.HasKey("Id")
+ .HasName("pk_feedsub");
+
+ b.HasAlternateKey("GuildConfigId", "Url")
+ .HasName("ak_feedsub_guildconfigid_url");
+
+ b.ToTable("feedsub", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.HasKey("Id")
+ .HasName("pk_filterchannelid");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_filterchannelid_guildconfigid");
+
+ b.ToTable("filterchannelid", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("Word")
+ .HasColumnType("longtext")
+ .HasColumnName("word");
+
+ b.HasKey("Id")
+ .HasName("pk_filteredword");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_filteredword_guildconfigid");
+
+ b.ToTable("filteredword", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterLinksChannelId", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.HasKey("Id")
+ .HasName("pk_filterlinkschannelid");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_filterlinkschannelid_guildconfigid");
+
+ b.ToTable("filterlinkschannelid", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterWordsChannelId", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.HasKey("Id")
+ .HasName("pk_filterwordschannelid");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_filterwordschannelid_guildconfigid");
+
+ b.ToTable("filterwordschannelid", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("ChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("channelid");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.HasKey("Id")
+ .HasName("pk_gcchannelid");
+
+ b.HasIndex("GuildConfigId")
+ .HasDatabaseName("ix_gcchannelid_guildconfigid");
+
+ b.ToTable("gcchannelid", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.GroupName", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("GuildConfigId")
+ .HasColumnType("int")
+ .HasColumnName("guildconfigid");
+
+ b.Property("Name")
+ .HasColumnType("longtext")
+ .HasColumnName("name");
+
+ b.Property("Number")
+ .HasColumnType("int")
+ .HasColumnName("number");
+
+ b.HasKey("Id")
+ .HasName("pk_groupname");
+
+ b.HasIndex("GuildConfigId", "Number")
+ .IsUnique()
+ .HasDatabaseName("ix_groupname_guildconfigid_number");
+
+ b.ToTable("groupname", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("AutoAssignRoleIds")
+ .HasColumnType("longtext")
+ .HasColumnName("autoassignroleids");
+
+ b.Property("AutoDeleteByeMessagesTimer")
+ .HasColumnType("int")
+ .HasColumnName("autodeletebyemessagestimer");
+
+ b.Property("AutoDeleteGreetMessagesTimer")
+ .HasColumnType("int")
+ .HasColumnName("autodeletegreetmessagestimer");
+
+ b.Property("AutoDeleteSelfAssignedRoleMessages")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("autodeleteselfassignedrolemessages");
+
+ b.Property("BoostMessage")
+ .HasColumnType("longtext")
+ .HasColumnName("boostmessage");
+
+ b.Property("BoostMessageChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("boostmessagechannelid");
+
+ b.Property("BoostMessageDeleteAfter")
+ .HasColumnType("int")
+ .HasColumnName("boostmessagedeleteafter");
+
+ b.Property("ByeMessageChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("byemessagechannelid");
+
+ b.Property("ChannelByeMessageText")
+ .HasColumnType("longtext")
+ .HasColumnName("channelbyemessagetext");
+
+ b.Property("ChannelGreetMessageText")
+ .HasColumnType("longtext")
+ .HasColumnName("channelgreetmessagetext");
+
+ b.Property("CleverbotEnabled")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("cleverbotenabled");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property("DeleteMessageOnCommand")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("deletemessageoncommand");
+
+ b.Property("DeleteStreamOnlineMessage")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("deletestreamonlinemessage");
+
+ b.Property("DmGreetMessageText")
+ .HasColumnType("longtext")
+ .HasColumnName("dmgreetmessagetext");
+
+ b.Property("ExclusiveSelfAssignedRoles")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("exclusiveselfassignedroles");
+
+ b.Property("FilterInvites")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("filterinvites");
+
+ b.Property("FilterLinks")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("filterlinks");
+
+ b.Property("FilterWords")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("filterwords");
+
+ b.Property("GameVoiceChannel")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("gamevoicechannel");
+
+ b.Property("GreetMessageChannelId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("greetmessagechannelid");
+
+ b.Property("GuildId")
+ .HasColumnType("bigint unsigned")
+ .HasColumnName("guildid");
+
+ b.Property("Locale")
+ .HasColumnType("longtext")
+ .HasColumnName("locale");
+
+ b.Property("MuteRoleName")
+ .HasColumnType("longtext")
+ .HasColumnName("muterolename");
+
+ b.Property("NotifyStreamOffline")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("notifystreamoffline");
+
+ b.Property("PermissionRole")
+ .HasColumnType("longtext")
+ .HasColumnName("permissionrole");
+
+ b.Property("Prefix")
+ .HasColumnType("longtext")
+ .HasColumnName("prefix");
+
+ b.Property("SendBoostMessage")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("sendboostmessage");
+
+ b.Property("SendChannelByeMessage")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("sendchannelbyemessage");
+
+ b.Property("SendChannelGreetMessage")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("sendchannelgreetmessage");
+
+ b.Property("SendDmGreetMessage")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("senddmgreetmessage");
+
+ b.Property("TimeZoneId")
+ .HasColumnType("longtext")
+ .HasColumnName("timezoneid");
+
+ b.Property("VerboseErrors")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(true)
+ .HasColumnName("verboseerrors");
+
+ b.Property("VerbosePermissions")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("verbosepermissions");
+
+ b.Property("WarnExpireAction")
+ .HasColumnType("int")
+ .HasColumnName("warnexpireaction");
+
+ b.Property("WarnExpireHours")
+ .HasColumnType("int")
+ .HasColumnName("warnexpirehours");
+
+ b.Property("WarningsInitialized")
+ .HasColumnType("tinyint(1)")
+ .HasColumnName("warningsinitialized");
+
+ b.HasKey("Id")
+ .HasName("pk_guildconfigs");
+
+ b.HasIndex("GuildId")
+ .IsUnique()
+ .HasDatabaseName("ix_guildconfigs_guildid");
+
+ b.HasIndex("WarnExpireHours")
+ .HasDatabaseName("ix_guildconfigs_warnexpirehours");
+
+ b.ToTable("guildconfigs", (string)null);
+ });
+
+ modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int")
+ .HasColumnName("id");
+
+ b.Property("DateAdded")
+ .HasColumnType("datetime(6)")
+ .HasColumnName("dateadded");
+
+ b.Property