mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c473669cbc | ||
|
b97c486b80 | ||
|
716e092fd0 | ||
|
a362ee90fc | ||
|
1de6cdb8dc | ||
|
f473014fe9 | ||
|
2c3e5fe507 | ||
|
ecc192c6a9 | ||
|
f7bd181034 | ||
|
664a4b3604 | ||
|
0326e88910 | ||
|
e4202b33f5 | ||
|
021e7978da | ||
|
28ad6db2de | ||
|
fb62df7aa2 | ||
|
33663d7efc | ||
|
6d1edc07cb | ||
|
c36ab34c4f | ||
|
e85e7c49cb | ||
|
e56190e9da | ||
|
2d16ecf6de | ||
|
2b12269917 | ||
|
79c2dfec2d | ||
|
73356b6beb | ||
|
bc22987330 | ||
|
c033c0e3c8 | ||
|
c9ed2cf4b5 | ||
|
52b87c7776 | ||
|
8b2ed0dbdc | ||
|
9424d4d5f9 | ||
|
67b186a1a5 | ||
|
436f9ed074 | ||
|
c1e51329be | ||
|
ae1193c1c5 | ||
|
9601a4d1a9 | ||
|
bdfde1205a | ||
|
5992628f80 | ||
|
d24e6fd8e7 | ||
|
c31c2e8d8e | ||
|
9aaf062d78 | ||
|
0b9e812d59 | ||
|
dc63e46852 | ||
|
e314686a03 | ||
|
f764a650da | ||
|
67616deb79 | ||
|
d0aa80a004 | ||
|
f66c105cc0 | ||
|
2a528cb3d6 | ||
|
8b40f97a3d | ||
|
fa9263ed32 | ||
|
88c42b74c7 | ||
|
f7406ec90b | ||
|
e446c8ee8b | ||
|
5839e944e1 | ||
|
15e41c10db | ||
|
99a8ea18bb | ||
|
5453f8acfa | ||
|
c95d1421c6 | ||
|
f631f16690 | ||
|
d397c2dce8 |
66
CHANGELOG.md
66
CHANGELOG.md
@@ -2,6 +2,70 @@
|
||||
|
||||
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||
|
||||
## [5.1.10] - 24.09.2024
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed claimed waifu decay in `games.yml`
|
||||
|
||||
### Changed
|
||||
|
||||
- Added some logs for greet service in case there are unforeseen issues, for easier debugging
|
||||
|
||||
## [5.1.9] - 21.09.2024
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `.greettest`, and other `.*test` commands if you didn't have them enabled.
|
||||
- Fixed `.greetdmtest` sending messages twice.
|
||||
- Fixed a serious bug which caused greet messages to be jumbled up, and wrong ones to be sent for the wrong events.
|
||||
- There is no database issue, all greet messages are safe, the cache was caching any setting every 3 seconds with no regard for the type of the event
|
||||
- This also caused `.greetdm` messages to not be sent if `.greet` is enabled
|
||||
- This bug was introduced in 5.1.8. PLEASE UPDATE if you are on 5.1.8
|
||||
- Selfhosters only: Fixed medusa dependency loading
|
||||
- Note: Make sure to not publish any other DLLs besides the ones you are sure you will need, as there can be version conflicts which didn't happen before.
|
||||
|
||||
## [5.1.8] - 19.09.2024
|
||||
|
||||
### Added
|
||||
|
||||
- Added `.leaveunkeptservers` which will make the bot leave all servers on all shards whose owners didn't run `.keep` command.
|
||||
- This is a dangerous and irreversible command, don't use it. Meant for use on the public bot.
|
||||
- `.adpl` now supports custom statuses (you no longer need to specify Playing, Watching, etc...)
|
||||
|
||||
### Changed
|
||||
|
||||
- `.quote` commands cleaned up and improved
|
||||
- All quote commands now start with `.q<whatever>` and follow the same naming pattern as Expression commands
|
||||
- `.liqu` renamed to `.qli`
|
||||
- `.quotesearch` / `.qse` is now paginated for easier searching
|
||||
- `.whosplaying` is now paginated
|
||||
- `.img` is now paginated
|
||||
- `.setgame` renamed to`.setactivity` and now supports custom text activity. You don't have to specify playing, listening etc before the activity
|
||||
- Clarified and added some embed / placeholder links to command help where needed
|
||||
- dev: A lot of code cleanup and internal improvements
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `.xpcurrew` breaking xp gain if user gains 0 xp from being in a voice channel
|
||||
- Fixed a bug in `.gatari` command
|
||||
- Fixed some waifu related strings
|
||||
- Fixed `.quoteshow` and `.quoteid` commands
|
||||
- Fixed some placeholders not working in `.greetdm`
|
||||
- Fixed postgres support
|
||||
- Fixed and clarified some command strings/parameter descriptions
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed mysql support as it didn't work for a while, and requires some special handling/maintenance
|
||||
- Sqlite and Postgres support stays
|
||||
|
||||
## [5.1.7] - 08.08.2024
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed some command groups incorrectly showing up as modules
|
||||
|
||||
## [5.1.6] - 07.08.2024
|
||||
|
||||
### Added
|
||||
@@ -14,7 +78,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except da
|
||||
|
||||
### Fixed
|
||||
|
||||
- `.afk` messages can no longer ping, and the response is moved to DMs to avoid
|
||||
- `.afk` messages can no longer ping, and the response is moved to DMs to avoid abuse
|
||||
- Possible fix for `.remind` timestamp
|
||||
|
||||
### Removed
|
||||
|
@@ -5,6 +5,5 @@ else {
|
||||
$migrationName = $args[0]
|
||||
dotnet ef migrations add $migrationName -c SqliteContext -p src/NadekoBot/NadekoBot.csproj
|
||||
dotnet ef migrations add $migrationName -c PostgreSqlContext -p src/NadekoBot/NadekoBot.csproj
|
||||
dotnet ef migrations add $migrationName -c MysqlContext -p src/NadekoBot/NadekoBot.csproj
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
dotnet ef migrations remove -c SqliteContext -f -p src/NadekoBot/NadekoBot.csproj
|
||||
dotnet ef migrations remove -c PostgreSqlContext -f -p src/NadekoBot/NadekoBot.csproj
|
||||
dotnet ef migrations remove -c MysqlContext -f -p src/NadekoBot/NadekoBot.csproj
|
||||
|
||||
|
@@ -1,76 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Nadeko.Common;
|
||||
using NadekoBot.Services;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace NadekoBot.Tests
|
||||
{
|
||||
public class GroupGreetTests
|
||||
{
|
||||
private GreetGrouper<int> _grouper;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
=> _grouper = new GreetGrouper<int>();
|
||||
|
||||
[Test]
|
||||
public void CreateTest()
|
||||
{
|
||||
var created = _grouper.CreateOrAdd(0, 5);
|
||||
|
||||
Assert.True(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateClearTest()
|
||||
{
|
||||
_grouper.CreateOrAdd(0, 5);
|
||||
_grouper.ClearGroup(0, 5, out var items);
|
||||
|
||||
Assert.AreEqual(0, items.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NotCreatedTest()
|
||||
{
|
||||
_grouper.CreateOrAdd(0, 5);
|
||||
var created = _grouper.CreateOrAdd(0, 4);
|
||||
|
||||
Assert.False(created);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearAddedTest()
|
||||
{
|
||||
_grouper.CreateOrAdd(0, 5);
|
||||
_grouper.CreateOrAdd(0, 4);
|
||||
_grouper.ClearGroup(0, 5, out var items);
|
||||
|
||||
var list = items.ToList();
|
||||
|
||||
Assert.AreEqual(1, list.Count, $"Count was {list.Count}");
|
||||
Assert.AreEqual(4, list[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task ClearManyTest()
|
||||
{
|
||||
_grouper.CreateOrAdd(0, 5);
|
||||
|
||||
// add 15 items
|
||||
await Enumerable.Range(10, 15)
|
||||
.Select(x => Task.Run(() => _grouper.CreateOrAdd(0, x))).WhenAll();
|
||||
|
||||
// get 5 at most
|
||||
_grouper.ClearGroup(0, 5, out var items);
|
||||
var list = items.ToList();
|
||||
Assert.AreEqual(5, list.Count, $"Count was {list.Count}");
|
||||
|
||||
// try to get 15, but there should be 10 left
|
||||
_grouper.ClearGroup(0, 15, out items);
|
||||
list = items.ToList();
|
||||
Assert.AreEqual(10, list.Count, $"Count was {list.Count}");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
#nullable disable
|
||||
using DryIoc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NadekoBot.Common.Configs;
|
||||
@@ -88,18 +89,18 @@ public sealed class Bot : IBot
|
||||
|
||||
|
||||
public IReadOnlyList<ulong> GetCurrentGuildIds()
|
||||
=> Client.Guilds.Select(x => x.Id).ToList().ToList();
|
||||
=> Client.Guilds.Select(x => x.Id).ToList().AsReadOnly();
|
||||
|
||||
private void AddServices()
|
||||
private async Task AddServices()
|
||||
{
|
||||
var startingGuildIdList = GetCurrentGuildIds();
|
||||
var startingGuildIdList = GetCurrentGuildIds().ToList();
|
||||
var startTime = Stopwatch.GetTimestamp();
|
||||
var bot = Client.CurrentUser;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
AllGuildConfigs = await uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList);
|
||||
uow.EnsureUserCreated(bot.Id, bot.Username, bot.Discriminator, bot.AvatarId);
|
||||
AllGuildConfigs = uow.Set<GuildConfig>().GetAllGuildConfigs(startingGuildIdList).ToImmutableArray();
|
||||
}
|
||||
|
||||
// var svcs = new StandardKernel(new NinjectSettings()
|
||||
@@ -109,7 +110,7 @@ public sealed class Bot : IBot
|
||||
// });
|
||||
|
||||
var svcs = new Container();
|
||||
|
||||
|
||||
// this is required in order for medusa unloading to work
|
||||
// svcs.Components.Remove<IPlanner, Planner>();
|
||||
// svcs.Components.Add<IPlanner, RemovablePlanner>();
|
||||
@@ -160,8 +161,9 @@ public sealed class Bot : IBot
|
||||
{
|
||||
LoadTypeReaders(a);
|
||||
}
|
||||
|
||||
Log.Information("All services loaded in {ServiceLoadTime:F2}s", Stopwatch.GetElapsedTime(startTime) .TotalSeconds);
|
||||
|
||||
Log.Information("All services loaded in {ServiceLoadTime:F2}s",
|
||||
Stopwatch.GetElapsedTime(startTime).TotalSeconds);
|
||||
}
|
||||
|
||||
private void LoadTypeReaders(Assembly assembly)
|
||||
@@ -261,11 +263,11 @@ public sealed class Bot : IBot
|
||||
var startTime = Stopwatch.GetTimestamp();
|
||||
|
||||
await LoginAsync(_creds.Token);
|
||||
|
||||
|
||||
Log.Information("Shard {ShardId} loading services...", Client.ShardId);
|
||||
try
|
||||
{
|
||||
AddServices();
|
||||
await AddServices();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -273,7 +275,9 @@ public sealed class Bot : IBot
|
||||
Helpers.ReadErrorAndExit(9);
|
||||
}
|
||||
|
||||
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s", Client.ShardId, Stopwatch.GetElapsedTime(startTime).TotalSeconds);
|
||||
Log.Information("Shard {ShardId} connected in {Elapsed:F2}s",
|
||||
Client.ShardId,
|
||||
Stopwatch.GetElapsedTime(startTime).TotalSeconds);
|
||||
var commandHandler = Services.GetRequiredService<CommandHandler>();
|
||||
|
||||
// start handling messages received in commandhandler
|
||||
@@ -338,26 +342,26 @@ public sealed class Bot : IBot
|
||||
if (arg.Exception is { InnerException: WebSocketClosedException { CloseCode: 4014 } })
|
||||
{
|
||||
Log.Error("""
|
||||
Login failed.
|
||||
|
||||
*** Please enable privileged intents ***
|
||||
|
||||
Certain Nadeko features require Discord's privileged gateway intents.
|
||||
These include greeting and goodbye messages, as well as creating the Owner message channels for DM forwarding.
|
||||
|
||||
How to enable privileged intents:
|
||||
1. Head over to the Discord Developer Portal https://discord.com/developers/applications/
|
||||
2. Select your Application.
|
||||
3. Click on `Bot` in the left side navigation panel, and scroll down to the intents section.
|
||||
4. Enable all intents.
|
||||
5. Restart your bot.
|
||||
|
||||
Read this only if your bot is in 100 or more servers:
|
||||
|
||||
You'll need to apply to use the intents with Discord, but for small selfhosts, all that is required is enabling the intents in the developer portal.
|
||||
Yes, this is a new thing from Discord, as of October 2020. No, there's nothing we can do about it. Yes, we're aware it worked before.
|
||||
While waiting for your bot to be accepted, you can change the 'usePrivilegedIntents' inside your creds.yml to 'false', although this will break many of the nadeko's features
|
||||
""");
|
||||
Login failed.
|
||||
|
||||
*** Please enable privileged intents ***
|
||||
|
||||
Certain Nadeko features require Discord's privileged gateway intents.
|
||||
These include greeting and goodbye messages, as well as creating the Owner message channels for DM forwarding.
|
||||
|
||||
How to enable privileged intents:
|
||||
1. Head over to the Discord Developer Portal https://discord.com/developers/applications/
|
||||
2. Select your Application.
|
||||
3. Click on `Bot` in the left side navigation panel, and scroll down to the intents section.
|
||||
4. Enable all intents.
|
||||
5. Restart your bot.
|
||||
|
||||
Read this only if your bot is in 100 or more servers:
|
||||
|
||||
You'll need to apply to use the intents with Discord, but for small selfhosts, all that is required is enabling the intents in the developer portal.
|
||||
Yes, this is a new thing from Discord, as of October 2020. No, there's nothing we can do about it. Yes, we're aware it worked before.
|
||||
While waiting for your bot to be accepted, you can change the 'usePrivilegedIntents' inside your creds.yml to 'false', although this will break many of the nadeko's features
|
||||
""");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
#nullable disable
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
@@ -32,8 +33,8 @@ public static class GuildConfigExtensions
|
||||
{
|
||||
var conf = ctx.GuildConfigsForId(guildId,
|
||||
set => set.Include(y => y.StreamRole)
|
||||
.Include(y => y.StreamRole.Whitelist)
|
||||
.Include(y => y.StreamRole.Blacklist));
|
||||
.Include(y => y.StreamRole.Whitelist)
|
||||
.Include(y => y.StreamRole.Blacklist));
|
||||
|
||||
if (conf.StreamRole is null)
|
||||
conf.StreamRole = new();
|
||||
@@ -42,19 +43,28 @@ public static class GuildConfigExtensions
|
||||
}
|
||||
|
||||
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
|
||||
=> configs.AsQueryable()
|
||||
.AsSplitQuery()
|
||||
.Include(gc => gc.CommandCooldowns)
|
||||
.Include(gc => gc.FollowedStreams)
|
||||
.Include(gc => gc.StreamRole)
|
||||
.Include(gc => gc.XpSettings)
|
||||
.ThenInclude(x => x.ExclusionList)
|
||||
.Include(gc => gc.DelMsgOnCmdChannels);
|
||||
=> 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 IEnumerable<GuildConfig> GetAllGuildConfigs(
|
||||
public static async Task<GuildConfig[]> GetAllGuildConfigs(
|
||||
this DbSet<GuildConfig> configs,
|
||||
IReadOnlyList<ulong> availableGuilds)
|
||||
=> configs.IncludeEverything().AsNoTracking().Where(x => availableGuilds.Contains(x.GuildId)).ToList();
|
||||
List<ulong> availableGuilds)
|
||||
{
|
||||
var result = await configs
|
||||
.AsQueryable()
|
||||
.Include(x => x.CommandCooldowns)
|
||||
.Where(x => availableGuilds.Contains(x.GuildId))
|
||||
.AsNoTracking()
|
||||
.ToArrayAsync();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets and creates if it doesn't exist a config for a guild.
|
||||
@@ -80,13 +90,14 @@ public static class GuildConfigExtensions
|
||||
|
||||
if (config is null)
|
||||
{
|
||||
ctx.Set<GuildConfig>().Add(config = new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Permissions = Permissionv2.GetDefaultPermlist,
|
||||
WarningsInitialized = true,
|
||||
WarnPunishments = DefaultWarnPunishments
|
||||
});
|
||||
ctx.Set<GuildConfig>()
|
||||
.Add(config = new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Permissions = Permissionv2.GetDefaultPermlist,
|
||||
WarningsInitialized = true,
|
||||
WarnPunishments = DefaultWarnPunishments
|
||||
});
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
@@ -122,18 +133,18 @@ public static class GuildConfigExtensions
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -149,18 +160,20 @@ 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();
|
||||
var config = ctx.Set<GuildConfig>()
|
||||
.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
|
||||
});
|
||||
ctx.Set<GuildConfig>()
|
||||
.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
|
||||
@@ -177,21 +190,21 @@ public static class GuildConfigExtensions
|
||||
|
||||
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();
|
||||
.Where(gc => included.Contains(gc.GuildId))
|
||||
.Include(gc => gc.FollowedStreams)
|
||||
.SelectMany(gc => gc.FollowedStreams)
|
||||
.ToList();
|
||||
|
||||
|
||||
public static XpSettings XpSettingsFor(this DbContext ctx, ulong guildId)
|
||||
{
|
||||
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));
|
||||
.ThenInclude(x => x.RoleRewards)
|
||||
.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.CurrencyRewards)
|
||||
.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.ExclusionList));
|
||||
|
||||
if (gc.XpSettings is null)
|
||||
gc.XpSettings = new();
|
||||
@@ -201,15 +214,15 @@ public static class GuildConfigExtensions
|
||||
|
||||
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();
|
||||
.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
|
||||
{
|
||||
|
@@ -1,53 +0,0 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class QuoteExtensions
|
||||
{
|
||||
public static IEnumerable<Quote> GetForGuild(this DbSet<Quote> quotes, ulong guildId)
|
||||
=> quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||
|
||||
public static IReadOnlyCollection<Quote> GetGroup(
|
||||
this DbSet<Quote> quotes,
|
||||
ulong guildId,
|
||||
int page,
|
||||
OrderType order)
|
||||
{
|
||||
var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||
if (order == OrderType.Keyword)
|
||||
q = q.OrderBy(x => x.Keyword);
|
||||
else
|
||||
q = q.OrderBy(x => x.Id);
|
||||
|
||||
return q.Skip(15 * page).Take(15).ToArray();
|
||||
}
|
||||
|
||||
public static async Task<Quote> GetRandomQuoteByKeywordAsync(
|
||||
this DbSet<Quote> quotes,
|
||||
ulong guildId,
|
||||
string keyword)
|
||||
{
|
||||
return (await quotes.AsQueryable().Where(q => q.GuildId == guildId && q.Keyword == keyword).ToArrayAsync())
|
||||
.RandomOrDefault();
|
||||
}
|
||||
|
||||
public static async Task<Quote> SearchQuoteKeywordTextAsync(
|
||||
this DbSet<Quote> quotes,
|
||||
ulong guildId,
|
||||
string keyword,
|
||||
string text)
|
||||
{
|
||||
return (await quotes.AsQueryable()
|
||||
.Where(q => q.GuildId == guildId
|
||||
&& (keyword == null || q.Keyword == keyword)
|
||||
&& (EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
||||
|| EF.Functions.Like(q.AuthorName, text)))
|
||||
.ToArrayAsync())
|
||||
.RandomOrDefault();
|
||||
}
|
||||
|
||||
public static void RemoveAllByKeyword(this DbSet<Quote> quotes, ulong guildId, string keyword)
|
||||
=> quotes.RemoveRange(quotes.AsQueryable().Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword));
|
||||
}
|
@@ -13,21 +13,23 @@ public class GuildConfig : DbEntity
|
||||
|
||||
public string AutoAssignRoleIds { get; set; }
|
||||
|
||||
//greet stuff
|
||||
public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
|
||||
public int AutoDeleteByeMessagesTimer { get; set; } = 30;
|
||||
|
||||
public ulong GreetMessageChannelId { get; set; }
|
||||
public ulong ByeMessageChannelId { get; set; }
|
||||
|
||||
public bool SendDmGreetMessage { get; set; }
|
||||
public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
||||
|
||||
public bool SendChannelGreetMessage { get; set; }
|
||||
public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
||||
|
||||
public bool SendChannelByeMessage { get; set; }
|
||||
public string ChannelByeMessageText { get; set; } = "%user% has left!";
|
||||
// //greet stuff
|
||||
// public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
|
||||
// public int AutoDeleteByeMessagesTimer { get; set; } = 30;
|
||||
//
|
||||
// public ulong GreetMessageChannelId { get; set; }
|
||||
// public ulong ByeMessageChannelId { get; set; }
|
||||
//
|
||||
// public bool SendDmGreetMessage { get; set; }
|
||||
// public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
||||
//
|
||||
// public bool SendChannelGreetMessage { get; set; }
|
||||
// public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
||||
//
|
||||
// public bool SendChannelByeMessage { get; set; }
|
||||
// public string ChannelByeMessageText { get; set; } = "%user% has left!";
|
||||
// public bool SendBoostMessage { get; set; }
|
||||
// pulic int BoostMessageDeleteAfter { get; set; }
|
||||
|
||||
//self assignable roles
|
||||
public bool ExclusiveSelfAssignedRoles { get; set; }
|
||||
@@ -98,10 +100,6 @@ public class GuildConfig : DbEntity
|
||||
|
||||
#region Boost Message
|
||||
|
||||
public bool SendBoostMessage { get; set; }
|
||||
public string BoostMessage { get; set; } = "%user% just boosted this server!";
|
||||
public ulong BoostMessageChannelId { get; set; }
|
||||
public int BoostMessageDeleteAfter { get; set; }
|
||||
public bool StickyRoles { get; set; }
|
||||
|
||||
#endregion
|
||||
|
@@ -8,6 +8,4 @@ public class UserXpStats : DbEntity
|
||||
public long Xp { get; set; }
|
||||
public long AwardedXp { get; set; }
|
||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||
}
|
||||
|
||||
public enum XpNotificationLocation { None, Dm, Channel }
|
||||
}
|
8
src/NadekoBot/Db/Models/xp/XpNotificationLocation.cs
Normal file
8
src/NadekoBot/Db/Models/xp/XpNotificationLocation.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
public enum XpNotificationLocation
|
||||
{
|
||||
None,
|
||||
Dm,
|
||||
Channel
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public sealed class MysqlContext : NadekoContext
|
||||
{
|
||||
private readonly string _connStr;
|
||||
private readonly string _version;
|
||||
|
||||
protected override string CurrencyTransactionOtherIdDefaultValue
|
||||
=> "NULL";
|
||||
|
||||
public MysqlContext(string connStr = "Server=localhost", string version = "8.0")
|
||||
{
|
||||
_connStr = connStr;
|
||||
_version = version;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
optionsBuilder
|
||||
.UseLowerCaseNamingConvention()
|
||||
.UseMySql(_connStr, ServerVersion.Parse(_version));
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
// mysql is case insensitive by default
|
||||
// we can set binary collation to change that
|
||||
modelBuilder.Entity<ClubInfo>()
|
||||
.Property(x => x.Name)
|
||||
.UseCollation("utf8mb4_bin");
|
||||
}
|
||||
}
|
@@ -10,6 +10,7 @@ namespace NadekoBot.Db;
|
||||
public abstract class NadekoContext : DbContext
|
||||
{
|
||||
public DbSet<GuildConfig> GuildConfigs { get; set; }
|
||||
public DbSet<GreetSettings> GreetSettings { get; set; }
|
||||
|
||||
public DbSet<Quote> Quotes { get; set; }
|
||||
public DbSet<Reminder> Reminders { get; set; }
|
||||
@@ -149,7 +150,7 @@ public abstract class NadekoContext : DbContext
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// start antispam
|
||||
|
||||
|
||||
modelBuilder.Entity<GuildConfig>()
|
||||
.HasOne(x => x.AntiSpamSetting)
|
||||
.WithOne()
|
||||
@@ -678,6 +679,29 @@ public abstract class NadekoContext : DbContext
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
#endregion
|
||||
|
||||
#region GreetSettings
|
||||
|
||||
modelBuilder
|
||||
.Entity<GreetSettings>(gs => gs.HasIndex(x => new
|
||||
{
|
||||
x.GuildId,
|
||||
x.GreetType
|
||||
})
|
||||
.IsUnique());
|
||||
|
||||
modelBuilder.Entity<GreetSettings>(gs =>
|
||||
{
|
||||
gs
|
||||
.Property(x => x.IsEnabled)
|
||||
.HasDefaultValue(false);
|
||||
|
||||
gs
|
||||
.Property(x => x.AutoDeleteTimer)
|
||||
.HasDefaultValue(0);
|
||||
});
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
@@ -44,8 +44,6 @@ public sealed class NadekoDbService : DbService
|
||||
case "postgres":
|
||||
case "pgsql":
|
||||
return new PostgreSqlContext(connString);
|
||||
case "mysql":
|
||||
return new MysqlContext(connString);
|
||||
case "sqlite":
|
||||
return new SqliteContext(connString);
|
||||
default:
|
||||
|
@@ -7,16 +7,7 @@ public static class MigrationQueries
|
||||
{
|
||||
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsMySql())
|
||||
{
|
||||
migrationBuilder.Sql(
|
||||
@"INSERT 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.IsSqlite())
|
||||
if (migrationBuilder.IsSqlite())
|
||||
{
|
||||
migrationBuilder.Sql(
|
||||
@"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded)
|
||||
@@ -27,7 +18,8 @@ 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)
|
||||
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
|
||||
@@ -43,11 +35,34 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
|
||||
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;
|
||||
""");
|
||||
}
|
||||
|
||||
builder.Sql($"""
|
||||
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
|
||||
""");
|
||||
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;
|
||||
""");
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class stondel : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "deletestreamonlinemessage",
|
||||
table: "guildconfigs",
|
||||
type: "tinyint(1)",
|
||||
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
@@ -1,41 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class bank : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "bankusers",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
userid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
balance = table.Column<long>(type: "bigint", nullable: false),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_bankusers", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
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
@@ -1,120 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class newrero : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "reactionroles",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
guildid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
messageid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
emote = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
roleid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
group = table.Column<int>(type: "int", nullable: false),
|
||||
levelreq = table.Column<int>(type: "int", nullable: false),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_reactionroles", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
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: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
guildconfigid = table.Column<int>(type: "int", nullable: false),
|
||||
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
exclusive = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
index = table.Column<int>(type: "int", nullable: false),
|
||||
messageid = table.Column<ulong>(type: "bigint unsigned", 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);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "reactionrole",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true),
|
||||
emotename = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
reactionrolemessageid = table.Column<int>(type: "int", nullable: true),
|
||||
roleid = table.Column<ulong>(type: "bigint unsigned", 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);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
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
@@ -1,175 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
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: "int");
|
||||
|
||||
migrationBuilder.AlterColumn<long>(
|
||||
name: "awardedxp",
|
||||
table: "userxpstats",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.AlterColumn<long>(
|
||||
name: "amountrewardedthismonth",
|
||||
table: "rewardedusers",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.AlterColumn<bool>(
|
||||
name: "verboseerrors",
|
||||
table: "guildconfigs",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: true,
|
||||
oldClrType: typeof(bool),
|
||||
oldType: "tinyint(1)");
|
||||
|
||||
migrationBuilder.AlterColumn<long>(
|
||||
name: "totalxp",
|
||||
table: "discorduser",
|
||||
type: "bigint",
|
||||
nullable: false,
|
||||
defaultValue: 0L,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldDefaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "patronquotas",
|
||||
columns: table => new
|
||||
{
|
||||
userid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
featuretype = table.Column<int>(type: "int", nullable: false),
|
||||
feature = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
hourlycount = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
dailycount = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
monthlycount = table.Column<uint>(type: "int unsigned", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_patronquotas", x => new { x.userid, x.featuretype, x.feature });
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "patrons",
|
||||
columns: table => new
|
||||
{
|
||||
userid = table.Column<ulong>(type: "bigint unsigned", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
uniqueplatformuserid = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
amountcents = table.Column<int>(type: "int", nullable: false),
|
||||
lastcharge = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
validthru = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_patrons", x => x.userid);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
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: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(long),
|
||||
oldType: "bigint");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "awardedxp",
|
||||
table: "userxpstats",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(long),
|
||||
oldType: "bigint");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "amountrewardedthismonth",
|
||||
table: "rewardedusers",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(long),
|
||||
oldType: "bigint");
|
||||
|
||||
migrationBuilder.AlterColumn<bool>(
|
||||
name: "verboseerrors",
|
||||
table: "guildconfigs",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(bool),
|
||||
oldType: "tinyint(1)",
|
||||
oldDefaultValue: true);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "totalxp",
|
||||
table: "discorduser",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(long),
|
||||
oldType: "bigint",
|
||||
oldDefaultValue: 0L);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,38 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class stondeldbcache : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "streamonlinemessages",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
messageid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
type = table.Column<int>(type: "int", nullable: false),
|
||||
name = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_streamonlinemessages", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "streamonlinemessages");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class logwarns : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<ulong>(
|
||||
name: "logwarnsid",
|
||||
table: "logsettings",
|
||||
type: "bigint unsigned",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "logwarnsid",
|
||||
table: "logsettings");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class xpitemshop : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "xpshopowneditem",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
userid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
itemtype = table.Column<int>(type: "int", nullable: false),
|
||||
isusing = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
itemkey = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_xpshopowneditem", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class linkonlychannels : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "type",
|
||||
table: "imageonlychannels",
|
||||
type: "int",
|
||||
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
@@ -1,48 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class removeobsoletexpcolumns : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "lastlevelup",
|
||||
table: "userxpstats");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "lastlevelup",
|
||||
table: "discorduser");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "lastxpgain",
|
||||
table: "discorduser");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "lastlevelup",
|
||||
table: "userxpstats",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValueSql: "(UTC_TIMESTAMP)");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "lastlevelup",
|
||||
table: "discorduser",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValueSql: "(UTC_TIMESTAMP)");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "lastxpgain",
|
||||
table: "discorduser",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValueSql: "(UTC_TIMESTAMP - INTERVAL 1 year)");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class banprune : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "prunedays",
|
||||
table: "bantemplates",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "prunedays",
|
||||
table: "bantemplates");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class shoprolereq : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<ulong>(
|
||||
name: "rolerequirement",
|
||||
table: "shopentry",
|
||||
type: "bigint unsigned",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "rolerequirement",
|
||||
table: "shopentry");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,41 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class autopub : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "autopublishchannel",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
guildid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_autopublishchannel", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
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
@@ -1,43 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class gamblingstats : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "gamblingstats",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
feature = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
bet = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||
paidout = table.Column<decimal>(type: "decimal(65,30)", nullable: false),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_gamblingstats", x => x.id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_gamblingstats_feature",
|
||||
table: "gamblingstats",
|
||||
column: "feature",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "gamblingstats");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class toggleglobalexpressions : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "disableglobalexpressions",
|
||||
table: "guildconfigs",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "disableglobalexpressions",
|
||||
table: "guildconfigs");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,35 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class logthread : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<ulong>(
|
||||
name: "threadcreatedid",
|
||||
table: "logsettings",
|
||||
type: "bigint unsigned",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<ulong>(
|
||||
name: "threaddeletedid",
|
||||
table: "logsettings",
|
||||
type: "bigint unsigned",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "threadcreatedid",
|
||||
table: "logsettings");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "threaddeletedid",
|
||||
table: "logsettings");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class feedtext : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "message",
|
||||
table: "feedsub",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "message",
|
||||
table: "feedsub");
|
||||
}
|
||||
}
|
||||
}
|
3830
src/NadekoBot/Migrations/Mysql/20240502233216_v5.Designer.cs
generated
3830
src/NadekoBot/Migrations/Mysql/20240502233216_v5.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,702 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class guidlconfigcleanup : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
|
||||
table: "antiraidsetting");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_antispamignore_antispamsetting_antispamsettingid",
|
||||
table: "antispamignore");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_antispamsetting_guildconfigs_guildconfigid",
|
||||
table: "antispamsetting");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_commandalias_guildconfigs_guildconfigid",
|
||||
table: "commandalias");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_commandcooldown_guildconfigs_guildconfigid",
|
||||
table: "commandcooldown");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
|
||||
table: "delmsgoncmdchannel");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_excludeditem_xpsettings_xpsettingsid",
|
||||
table: "excludeditem");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filterchannelid_guildconfigs_guildconfigid",
|
||||
table: "filterchannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filteredword_guildconfigs_guildconfigid",
|
||||
table: "filteredword");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterlinkschannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterwordschannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_followedstream_guildconfigs_guildconfigid",
|
||||
table: "followedstream");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_gcchannelid_guildconfigs_guildconfigid",
|
||||
table: "gcchannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_muteduserid_guildconfigs_guildconfigid",
|
||||
table: "muteduserid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_permissions_guildconfigs_guildconfigid",
|
||||
table: "permissions");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_shopentry_guildconfigs_guildconfigid",
|
||||
table: "shopentry");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_shopentryitem_shopentry_shopentryid",
|
||||
table: "shopentryitem");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoredrole");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoreduser");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamroleblacklisteduser");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamrolewhitelisteduser");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_unbantimer_guildconfigs_guildconfigid",
|
||||
table: "unbantimer");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_unmutetimer_guildconfigs_guildconfigid",
|
||||
table: "unmutetimer");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_unroletimer_guildconfigs_guildconfigid",
|
||||
table: "unroletimer");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
|
||||
table: "vcroleinfo");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_warningpunishment_guildconfigs_guildconfigid",
|
||||
table: "warningpunishment");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ignoredvoicepresencechannels");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "streamrolesettingsid",
|
||||
table: "streamrolewhitelisteduser",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "streamrolesettingsid",
|
||||
table: "streamroleblacklisteduser",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "guildconfigid",
|
||||
table: "delmsgoncmdchannel",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
|
||||
table: "antiraidsetting",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_antispamignore_antispamsetting_antispamsettingid",
|
||||
table: "antispamignore",
|
||||
column: "antispamsettingid",
|
||||
principalTable: "antispamsetting",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_antispamsetting_guildconfigs_guildconfigid",
|
||||
table: "antispamsetting",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_commandalias_guildconfigs_guildconfigid",
|
||||
table: "commandalias",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_commandcooldown_guildconfigs_guildconfigid",
|
||||
table: "commandcooldown",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
|
||||
table: "delmsgoncmdchannel",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_excludeditem_xpsettings_xpsettingsid",
|
||||
table: "excludeditem",
|
||||
column: "xpsettingsid",
|
||||
principalTable: "xpsettings",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filterchannelid_guildconfigs_guildconfigid",
|
||||
table: "filterchannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filteredword_guildconfigs_guildconfigid",
|
||||
table: "filteredword",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterlinkschannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterwordschannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_followedstream_guildconfigs_guildconfigid",
|
||||
table: "followedstream",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_gcchannelid_guildconfigs_guildconfigid",
|
||||
table: "gcchannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_muteduserid_guildconfigs_guildconfigid",
|
||||
table: "muteduserid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_permissions_guildconfigs_guildconfigid",
|
||||
table: "permissions",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_shopentry_guildconfigs_guildconfigid",
|
||||
table: "shopentry",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_shopentryitem_shopentry_shopentryid",
|
||||
table: "shopentryitem",
|
||||
column: "shopentryid",
|
||||
principalTable: "shopentry",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoredrole",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoreduser",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamroleblacklisteduser",
|
||||
column: "streamrolesettingsid",
|
||||
principalTable: "streamrolesettings",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamrolewhitelisteduser",
|
||||
column: "streamrolesettingsid",
|
||||
principalTable: "streamrolesettings",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_unbantimer_guildconfigs_guildconfigid",
|
||||
table: "unbantimer",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_unmutetimer_guildconfigs_guildconfigid",
|
||||
table: "unmutetimer",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_unroletimer_guildconfigs_guildconfigid",
|
||||
table: "unroletimer",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
|
||||
table: "vcroleinfo",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_warningpunishment_guildconfigs_guildconfigid",
|
||||
table: "warningpunishment",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
|
||||
table: "antiraidsetting");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_antispamignore_antispamsetting_antispamsettingid",
|
||||
table: "antispamignore");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_antispamsetting_guildconfigs_guildconfigid",
|
||||
table: "antispamsetting");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_commandalias_guildconfigs_guildconfigid",
|
||||
table: "commandalias");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_commandcooldown_guildconfigs_guildconfigid",
|
||||
table: "commandcooldown");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
|
||||
table: "delmsgoncmdchannel");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_excludeditem_xpsettings_xpsettingsid",
|
||||
table: "excludeditem");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filterchannelid_guildconfigs_guildconfigid",
|
||||
table: "filterchannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filteredword_guildconfigs_guildconfigid",
|
||||
table: "filteredword");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterlinkschannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterwordschannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_followedstream_guildconfigs_guildconfigid",
|
||||
table: "followedstream");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_gcchannelid_guildconfigs_guildconfigid",
|
||||
table: "gcchannelid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_muteduserid_guildconfigs_guildconfigid",
|
||||
table: "muteduserid");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_permissions_guildconfigs_guildconfigid",
|
||||
table: "permissions");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_shopentry_guildconfigs_guildconfigid",
|
||||
table: "shopentry");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_shopentryitem_shopentry_shopentryid",
|
||||
table: "shopentryitem");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoredrole");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoreduser");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamroleblacklisteduser");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamrolewhitelisteduser");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_unbantimer_guildconfigs_guildconfigid",
|
||||
table: "unbantimer");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_unmutetimer_guildconfigs_guildconfigid",
|
||||
table: "unmutetimer");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_unroletimer_guildconfigs_guildconfigid",
|
||||
table: "unroletimer");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
|
||||
table: "vcroleinfo");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "fk_warningpunishment_guildconfigs_guildconfigid",
|
||||
table: "warningpunishment");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "streamrolesettingsid",
|
||||
table: "streamrolewhitelisteduser",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "streamrolesettingsid",
|
||||
table: "streamroleblacklisteduser",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "guildconfigid",
|
||||
table: "delmsgoncmdchannel",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ignoredvoicepresencechannels",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
logsettingid = table.Column<int>(type: "int", nullable: true),
|
||||
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_ignoredvoicepresencechannels", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "fk_ignoredvoicepresencechannels_logsettings_logsettingid",
|
||||
column: x => x.logsettingid,
|
||||
principalTable: "logsettings",
|
||||
principalColumn: "id");
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_ignoredvoicepresencechannels_logsettingid",
|
||||
table: "ignoredvoicepresencechannels",
|
||||
column: "logsettingid");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_antiraidsetting_guildconfigs_guildconfigid",
|
||||
table: "antiraidsetting",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_antispamignore_antispamsetting_antispamsettingid",
|
||||
table: "antispamignore",
|
||||
column: "antispamsettingid",
|
||||
principalTable: "antispamsetting",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_antispamsetting_guildconfigs_guildconfigid",
|
||||
table: "antispamsetting",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_commandalias_guildconfigs_guildconfigid",
|
||||
table: "commandalias",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_commandcooldown_guildconfigs_guildconfigid",
|
||||
table: "commandcooldown",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_delmsgoncmdchannel_guildconfigs_guildconfigid",
|
||||
table: "delmsgoncmdchannel",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_excludeditem_xpsettings_xpsettingsid",
|
||||
table: "excludeditem",
|
||||
column: "xpsettingsid",
|
||||
principalTable: "xpsettings",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filterchannelid_guildconfigs_guildconfigid",
|
||||
table: "filterchannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filteredword_guildconfigs_guildconfigid",
|
||||
table: "filteredword",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filterlinkschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterlinkschannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_filterwordschannelid_guildconfigs_guildconfigid",
|
||||
table: "filterwordschannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_followedstream_guildconfigs_guildconfigid",
|
||||
table: "followedstream",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_gcchannelid_guildconfigs_guildconfigid",
|
||||
table: "gcchannelid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_muteduserid_guildconfigs_guildconfigid",
|
||||
table: "muteduserid",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_permissions_guildconfigs_guildconfigid",
|
||||
table: "permissions",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_shopentry_guildconfigs_guildconfigid",
|
||||
table: "shopentry",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_shopentryitem_shopentry_shopentryid",
|
||||
table: "shopentryitem",
|
||||
column: "shopentryid",
|
||||
principalTable: "shopentry",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_slowmodeignoredrole_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoredrole",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_slowmodeignoreduser_guildconfigs_guildconfigid",
|
||||
table: "slowmodeignoreduser",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_streamroleblacklisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamroleblacklisteduser",
|
||||
column: "streamrolesettingsid",
|
||||
principalTable: "streamrolesettings",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_streamrolewhitelisteduser_streamrolesettings_streamrolesetti~",
|
||||
table: "streamrolewhitelisteduser",
|
||||
column: "streamrolesettingsid",
|
||||
principalTable: "streamrolesettings",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_unbantimer_guildconfigs_guildconfigid",
|
||||
table: "unbantimer",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_unmutetimer_guildconfigs_guildconfigid",
|
||||
table: "unmutetimer",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_unroletimer_guildconfigs_guildconfigid",
|
||||
table: "unroletimer",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_vcroleinfo_guildconfigs_guildconfigid",
|
||||
table: "vcroleinfo",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "fk_warningpunishment_guildconfigs_guildconfigid",
|
||||
table: "warningpunishment",
|
||||
column: "guildconfigid",
|
||||
principalTable: "guildconfigs",
|
||||
principalColumn: "id");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class removepatronlimits : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "patronquotas");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "patronquotas",
|
||||
columns: table => new
|
||||
{
|
||||
userid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
|
||||
featuretype = table.Column<int>(type: "int", nullable: false),
|
||||
feature = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
dailycount = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
hourlycount = table.Column<uint>(type: "int unsigned", nullable: false),
|
||||
monthlycount = table.Column<uint>(type: "int unsigned", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_patronquotas", x => new { x.userid, x.featuretype, x.feature });
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_patronquotas_userid",
|
||||
table: "patronquotas",
|
||||
column: "userid");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class honeypot : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "honeypotchannels",
|
||||
columns: table => new
|
||||
{
|
||||
guildid = table.Column<ulong>(type: "bigint unsigned", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
channelid = table.Column<ulong>(type: "bigint unsigned", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_honeypotchannels", x => x.guildid);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "honeypotchannels");
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -9,12 +9,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "rolerequirement",
|
||||
table: "shopentry",
|
||||
type: "numeric(20,0)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "autopublishchannel",
|
||||
columns: table => new
|
||||
@@ -41,10 +35,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "autopublishchannel");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "rolerequirement",
|
||||
table: "shopentry");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,199 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.PostgreSql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class greetsettings : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "greetsettings",
|
||||
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),
|
||||
greettype = table.Column<int>(type: "integer", nullable: false),
|
||||
messagetext = table.Column<string>(type: "text", nullable: true),
|
||||
isenabled = table.Column<bool>(type: "boolean", nullable: false, defaultValue: false),
|
||||
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
|
||||
autodeletetimer = table.Column<int>(type: "integer", nullable: false, defaultValue: 0)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_greetsettings", x => x.id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_greetsettings_guildid_greettype",
|
||||
table: "greetsettings",
|
||||
columns: new[] { "guildid", "greettype" },
|
||||
unique: true);
|
||||
|
||||
MigrationQueries.GreetSettingsCopy(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "autodeletebyemessagestimer",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "autodeletegreetmessagestimer",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "boostmessage",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "boostmessagechannelid",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "boostmessagedeleteafter",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "byemessagechannelid",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "channelbyemessagetext",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "channelgreetmessagetext",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "dmgreetmessagetext",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "greetmessagechannelid",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "sendboostmessage",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "sendchannelbyemessage",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "sendchannelgreetmessage",
|
||||
table: "guildconfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "senddmgreetmessage",
|
||||
table: "guildconfigs");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "greetsettings");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "autodeletebyemessagestimer",
|
||||
table: "guildconfigs",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "autodeletegreetmessagestimer",
|
||||
table: "guildconfigs",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "boostmessage",
|
||||
table: "guildconfigs",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "boostmessagechannelid",
|
||||
table: "guildconfigs",
|
||||
type: "numeric(20,0)",
|
||||
nullable: false,
|
||||
defaultValue: 0m);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "boostmessagedeleteafter",
|
||||
table: "guildconfigs",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "byemessagechannelid",
|
||||
table: "guildconfigs",
|
||||
type: "numeric(20,0)",
|
||||
nullable: false,
|
||||
defaultValue: 0m);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "channelbyemessagetext",
|
||||
table: "guildconfigs",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "channelgreetmessagetext",
|
||||
table: "guildconfigs",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "dmgreetmessagetext",
|
||||
table: "guildconfigs",
|
||||
type: "text",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "greetmessagechannelid",
|
||||
table: "guildconfigs",
|
||||
type: "numeric(20,0)",
|
||||
nullable: false,
|
||||
defaultValue: 0m);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "sendboostmessage",
|
||||
table: "guildconfigs",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "sendchannelbyemessage",
|
||||
table: "guildconfigs",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "sendchannelgreetmessage",
|
||||
table: "guildconfigs",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "senddmgreetmessage",
|
||||
table: "guildconfigs",
|
||||
type: "boolean",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -17,7 +17,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.4")
|
||||
.HasAnnotation("ProductVersion", "8.0.8")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@@ -1220,42 +1220,10 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("autoassignroleids");
|
||||
|
||||
b.Property<int>("AutoDeleteByeMessagesTimer")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("autodeletebyemessagestimer");
|
||||
|
||||
b.Property<int>("AutoDeleteGreetMessagesTimer")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("autodeletegreetmessagestimer");
|
||||
|
||||
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("autodeleteselfassignedrolemessages");
|
||||
|
||||
b.Property<string>("BoostMessage")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("boostmessage");
|
||||
|
||||
b.Property<decimal>("BoostMessageChannelId")
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("boostmessagechannelid");
|
||||
|
||||
b.Property<int>("BoostMessageDeleteAfter")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("boostmessagedeleteafter");
|
||||
|
||||
b.Property<decimal>("ByeMessageChannelId")
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("byemessagechannelid");
|
||||
|
||||
b.Property<string>("ChannelByeMessageText")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("channelbyemessagetext");
|
||||
|
||||
b.Property<string>("ChannelGreetMessageText")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("channelgreetmessagetext");
|
||||
|
||||
b.Property<bool>("CleverbotEnabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("cleverbotenabled");
|
||||
@@ -1276,10 +1244,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("disableglobalexpressions");
|
||||
|
||||
b.Property<string>("DmGreetMessageText")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("dmgreetmessagetext");
|
||||
|
||||
b.Property<bool>("ExclusiveSelfAssignedRoles")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("exclusiveselfassignedroles");
|
||||
@@ -1300,10 +1264,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("gamevoicechannel");
|
||||
|
||||
b.Property<decimal>("GreetMessageChannelId")
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("greetmessagechannelid");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("guildid");
|
||||
@@ -1328,22 +1288,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("prefix");
|
||||
|
||||
b.Property<bool>("SendBoostMessage")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("sendboostmessage");
|
||||
|
||||
b.Property<bool>("SendChannelByeMessage")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("sendchannelbyemessage");
|
||||
|
||||
b.Property<bool>("SendChannelGreetMessage")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("sendchannelgreetmessage");
|
||||
|
||||
b.Property<bool>("SendDmGreetMessage")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("senddmgreetmessage");
|
||||
|
||||
b.Property<bool>("StickyRoles")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("stickyroles");
|
||||
@@ -3163,6 +3107,53 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
b.ToTable("xpshopowneditem", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.GreetSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("AutoDeleteTimer")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasDefaultValue(0)
|
||||
.HasColumnName("autodeletetimer");
|
||||
|
||||
b.Property<decimal?>("ChannelId")
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<int>("GreetType")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("greettype");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("guildid");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("boolean")
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("isenabled");
|
||||
|
||||
b.Property<string>("MessageText")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("messagetext");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_greetsettings");
|
||||
|
||||
b.HasIndex("GuildId", "GreetType")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("ix_greetsettings_guildid_greettype");
|
||||
|
||||
b.ToTable("greetsettings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||
|
2929
src/NadekoBot/Migrations/Sqlite/20240912124710_greet-settings.Designer.cs
generated
Normal file
2929
src/NadekoBot/Migrations/Sqlite/20240912124710_greet-settings.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
197
src/NadekoBot/Migrations/Sqlite/20240912124710_greet-settings.cs
Normal file
197
src/NadekoBot/Migrations/Sqlite/20240912124710_greet-settings.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class greetsettings : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "GreetSettings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||
GreetType = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
MessageText = table.Column<string>(type: "TEXT", nullable: true),
|
||||
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false, defaultValue: false),
|
||||
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: true),
|
||||
AutoDeleteTimer = table.Column<int>(type: "INTEGER", nullable: false, defaultValue: 0)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_GreetSettings", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_GreetSettings_GuildId_GreetType",
|
||||
table: "GreetSettings",
|
||||
columns: new[] { "GuildId", "GreetType" },
|
||||
unique: true);
|
||||
|
||||
MigrationQueries.GreetSettingsCopy(migrationBuilder);
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AutoDeleteByeMessagesTimer",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AutoDeleteGreetMessagesTimer",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "BoostMessage",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "BoostMessageChannelId",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "BoostMessageDeleteAfter",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ByeMessageChannelId",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ChannelByeMessageText",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ChannelGreetMessageText",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "DmGreetMessageText",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "GreetMessageChannelId",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SendBoostMessage",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SendChannelByeMessage",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SendChannelGreetMessage",
|
||||
table: "GuildConfigs");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SendDmGreetMessage",
|
||||
table: "GuildConfigs");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "GreetSettings");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AutoDeleteByeMessagesTimer",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AutoDeleteGreetMessagesTimer",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "BoostMessage",
|
||||
table: "GuildConfigs",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<ulong>(
|
||||
name: "BoostMessageChannelId",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0ul);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "BoostMessageDeleteAfter",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<ulong>(
|
||||
name: "ByeMessageChannelId",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0ul);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ChannelByeMessageText",
|
||||
table: "GuildConfigs",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ChannelGreetMessageText",
|
||||
table: "GuildConfigs",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "DmGreetMessageText",
|
||||
table: "GuildConfigs",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<ulong>(
|
||||
name: "GreetMessageChannelId",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0ul);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "SendBoostMessage",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "SendChannelByeMessage",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "SendChannelGreetMessage",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "SendDmGreetMessage",
|
||||
table: "GuildConfigs",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@ namespace NadekoBot.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.4");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
|
||||
{
|
||||
@@ -907,33 +907,9 @@ namespace NadekoBot.Migrations
|
||||
b.Property<string>("AutoAssignRoleIds")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AutoDeleteByeMessagesTimer")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AutoDeleteGreetMessagesTimer")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("BoostMessage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<ulong>("BoostMessageChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("BoostMessageDeleteAfter")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("ByeMessageChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ChannelByeMessageText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ChannelGreetMessageText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("CleverbotEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -949,9 +925,6 @@ namespace NadekoBot.Migrations
|
||||
b.Property<bool>("DisableGlobalExpressions")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DmGreetMessageText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("ExclusiveSelfAssignedRoles")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -967,9 +940,6 @@ namespace NadekoBot.Migrations
|
||||
b.Property<ulong?>("GameVoiceChannel")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("GreetMessageChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("GuildId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -988,18 +958,6 @@ namespace NadekoBot.Migrations
|
||||
b.Property<string>("Prefix")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("SendBoostMessage")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("SendChannelByeMessage")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("SendChannelGreetMessage")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("SendDmGreetMessage")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("StickyRoles")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -2351,6 +2309,42 @@ namespace NadekoBot.Migrations
|
||||
b.ToTable("XpShopOwnedItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Services.GreetSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AutoDeleteTimer")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<ulong?>("ChannelId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("GreetType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ulong>("GuildId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<string>("MessageText")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("GuildId", "GreetType")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("GreetSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
|
||||
{
|
||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||
|
@@ -36,6 +36,8 @@ public class AutoPublishService : IExecNoCommand, IReadyExecutor, INService
|
||||
RetryMode = RetryMode.AlwaysFail
|
||||
});
|
||||
}
|
||||
|
||||
// todo GUILDS
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
|
@@ -1,14 +1,20 @@
|
||||
namespace NadekoBot.Modules.Administration.DangerousCommands;
|
||||
using NadekoBot.Modules.Administration.DangerousCommands;
|
||||
|
||||
public partial class Administration
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public partial class Administration
|
||||
{
|
||||
[Group]
|
||||
public class CleanupCommands : CleanupModuleBase
|
||||
public partial class CleanupCommands : CleanupModuleBase
|
||||
{
|
||||
private readonly ICleanupService _svc;
|
||||
private readonly IBotCredsProvider _creds;
|
||||
|
||||
public CleanupCommands(ICleanupService svc)
|
||||
=> _svc = svc;
|
||||
public CleanupCommands(ICleanupService svc, IBotCredsProvider creds)
|
||||
{
|
||||
_svc = svc;
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
@@ -37,5 +43,32 @@ public partial class Administration
|
||||
|
||||
await Response().Text("This guild's bot data will be saved.").SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task LeaveUnkeptServers(int startShardId, int shardMultiplier = 3000)
|
||||
{
|
||||
var keptGuildCount = await _svc.GetKeptGuildCount();
|
||||
|
||||
var response = await PromptUserConfirmAsync(new EmbedBuilder()
|
||||
.WithDescription($"""
|
||||
Do you want the bot to leave all unkept servers?
|
||||
|
||||
There are currently {keptGuildCount} kept servers.
|
||||
|
||||
**This is a highly destructive and irreversible action.**
|
||||
"""));
|
||||
|
||||
if (!response)
|
||||
return;
|
||||
|
||||
for (var shardId = startShardId; shardId < _creds.GetCreds().TotalShards; shardId++)
|
||||
{
|
||||
await _svc.StartLeavingUnkeptServers(shardId);
|
||||
await Task.Delay(shardMultiplier * 1000);
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
}
|
||||
}
|
@@ -9,9 +9,12 @@ namespace NadekoBot.Modules.Administration.DangerousCommands;
|
||||
|
||||
public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
||||
{
|
||||
private TypedKey<KeepReport> _cleanupReportKey = new("cleanup:report");
|
||||
private TypedKey<bool> _cleanupTriggerKey = new("cleanup:trigger");
|
||||
|
||||
private TypedKey<int> _keepTriggerKey = new("keep:trigger");
|
||||
|
||||
private readonly IPubSub _pubSub;
|
||||
private TypedKey<KeepReport> _keepReportKey = new("cleanup:report");
|
||||
private TypedKey<bool> _keepTriggerKey = new("cleanup:trigger");
|
||||
private readonly DiscordSocketClient _client;
|
||||
private ConcurrentDictionary<int, ulong[]> guildIds = new();
|
||||
private readonly IBotCredsProvider _creds;
|
||||
@@ -29,11 +32,90 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
await _pubSub.Sub(_cleanupTriggerKey, OnCleanupTrigger);
|
||||
await _pubSub.Sub(_keepTriggerKey, InternalTriggerKeep);
|
||||
|
||||
_client.JoinedGuild += ClientOnJoinedGuild;
|
||||
|
||||
if (_client.ShardId == 0)
|
||||
await _pubSub.Sub(_cleanupReportKey, OnKeepReport);
|
||||
}
|
||||
|
||||
private bool keepTriggered = false;
|
||||
|
||||
private async ValueTask InternalTriggerKeep(int shardId)
|
||||
{
|
||||
if (_client.ShardId != shardId)
|
||||
return;
|
||||
|
||||
if (keepTriggered)
|
||||
return;
|
||||
|
||||
keepTriggered = true;
|
||||
try
|
||||
{
|
||||
var allGuildIds = _client.Guilds.Select(x => x.Id).ToArray();
|
||||
|
||||
HashSet<ulong> dontDelete;
|
||||
await using (var db = _db.GetDbContext())
|
||||
{
|
||||
await using var ctx = db.CreateLinqToDBContext();
|
||||
var table = ctx.CreateTable<KeptGuilds>(tableOptions: TableOptions.CheckExistence);
|
||||
|
||||
var dontDeleteList = await table
|
||||
.Where(x => allGuildIds.Contains(x.GuildId))
|
||||
.Select(x => x.GuildId)
|
||||
.ToListAsyncLinqToDB();
|
||||
|
||||
dontDelete = dontDeleteList.ToHashSet();
|
||||
}
|
||||
|
||||
Log.Information("Leaving {RemainingCount} guilds, 1 every second. {DontDeleteCount} will remain",
|
||||
allGuildIds.Length - dontDelete.Count,
|
||||
dontDelete.Count);
|
||||
|
||||
foreach (var guildId in allGuildIds)
|
||||
{
|
||||
if (dontDelete.Contains(guildId))
|
||||
continue;
|
||||
|
||||
await Task.Delay(1016);
|
||||
|
||||
SocketGuild? guild = null;
|
||||
try
|
||||
{
|
||||
guild = _client.GetGuild(guildId);
|
||||
|
||||
if (guild is null)
|
||||
{
|
||||
Log.Warning("Unable to find guild {GuildId}", guildId);
|
||||
continue;
|
||||
}
|
||||
|
||||
await guild.LeaveAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning("Unable to leave guild {GuildName} [{GuildId}]: {ErrorMessage}",
|
||||
guild?.Name,
|
||||
guildId,
|
||||
ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
keepTriggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<KeepResult?> DeleteMissingGuildDataAsync()
|
||||
{
|
||||
guildIds = new();
|
||||
var totalShards = _creds.GetCreds().TotalShards;
|
||||
await _pubSub.Pub(_keepTriggerKey, true);
|
||||
await _pubSub.Pub(_cleanupTriggerKey, true);
|
||||
var counter = 0;
|
||||
while (guildIds.Keys.Count < totalShards)
|
||||
{
|
||||
@@ -135,9 +217,7 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
||||
{
|
||||
await using var db = _db.GetDbContext();
|
||||
await using var ctx = db.CreateLinqToDBContext();
|
||||
|
||||
var table = ctx.CreateTable<KeptGuilds>(tableOptions: TableOptions.CheckExistence);
|
||||
|
||||
if (await table.AnyAsyncLinqToDB(x => x.GuildId == guildId))
|
||||
return false;
|
||||
|
||||
@@ -149,30 +229,31 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<int> GetKeptGuildCount()
|
||||
{
|
||||
await using var db = _db.GetDbContext();
|
||||
await using var ctx = db.CreateLinqToDBContext();
|
||||
var table = ctx.CreateTable<KeptGuilds>(tableOptions: TableOptions.CheckExistence);
|
||||
return await table.CountAsync();
|
||||
}
|
||||
|
||||
public async Task StartLeavingUnkeptServers(int shardId)
|
||||
=> await _pubSub.Pub(_keepTriggerKey, shardId);
|
||||
|
||||
private ValueTask OnKeepReport(KeepReport report)
|
||||
{
|
||||
guildIds[report.ShardId] = report.GuildIds;
|
||||
return default;
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
await _pubSub.Sub(_keepTriggerKey, OnKeepTrigger);
|
||||
|
||||
_client.JoinedGuild += ClientOnJoinedGuild;
|
||||
|
||||
if (_client.ShardId == 0)
|
||||
await _pubSub.Sub(_keepReportKey, OnKeepReport);
|
||||
}
|
||||
|
||||
private async Task ClientOnJoinedGuild(SocketGuild arg)
|
||||
{
|
||||
await KeepGuild(arg.Id);
|
||||
}
|
||||
|
||||
private ValueTask OnKeepTrigger(bool arg)
|
||||
private ValueTask OnCleanupTrigger(bool arg)
|
||||
{
|
||||
_pubSub.Pub(_keepReportKey,
|
||||
_pubSub.Pub(_cleanupReportKey,
|
||||
new KeepReport()
|
||||
{
|
||||
ShardId = _client.ShardId,
|
||||
|
@@ -4,4 +4,6 @@ public interface ICleanupService
|
||||
{
|
||||
Task<KeepResult?> DeleteMissingGuildDataAsync();
|
||||
Task<bool> KeepGuild(ulong guildId);
|
||||
Task<int> GetKeptGuildCount();
|
||||
Task StartLeavingUnkeptServers(int shardId);
|
||||
}
|
@@ -8,236 +8,237 @@ public partial class Administration
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task Boost()
|
||||
{
|
||||
var enabled = await _service.ToggleBoost(ctx.Guild.Id, ctx.Channel.Id);
|
||||
|
||||
if (enabled)
|
||||
await Response().Confirm(strs.boost_on).SendAsync();
|
||||
else
|
||||
await Response().Pending(strs.boost_off).SendAsync();
|
||||
}
|
||||
public Task Boost()
|
||||
=> Toggle(GreetType.Boost);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task BoostDel(int timer = 30)
|
||||
public Task BoostDel(int timer = 30)
|
||||
=> SetDel(GreetType.Boost, timer);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task BoostMsg([Leftover] string? text = null)
|
||||
=> SetMsg(GreetType.Boost, text);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task Greet()
|
||||
=> Toggle(GreetType.Greet);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetDel(int timer = 30)
|
||||
=> SetDel(GreetType.Greet, timer);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetMsg([Leftover] string? text = null)
|
||||
=> SetMsg(GreetType.Greet, text);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetDm()
|
||||
=> Toggle(GreetType.GreetDm);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetDmMsg([Leftover] string? text = null)
|
||||
=> SetMsg(GreetType.GreetDm, text);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task Bye()
|
||||
=> Toggle(GreetType.Bye);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task ByeDel(int timer = 30)
|
||||
=> SetDel(GreetType.Bye, timer);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task ByeMsg([Leftover] string? text = null)
|
||||
=> SetMsg(GreetType.Bye, text);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetTest([Leftover] IGuildUser? user = null)
|
||||
=> Test(GreetType.Greet, user);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public Task GreetDmTest([Leftover] IGuildUser? user = null)
|
||||
=> Test(GreetType.GreetDm, user);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public Task ByeTest([Leftover] IGuildUser? user = null)
|
||||
=> Test(GreetType.Bye, user);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public Task BoostTest([Leftover] IGuildUser? user = null)
|
||||
=> Test(GreetType.Boost, user);
|
||||
|
||||
|
||||
public async Task Toggle(GreetType type)
|
||||
{
|
||||
var enabled = await _service.SetGreet(ctx.Guild.Id, ctx.Channel.Id, type);
|
||||
|
||||
if (enabled)
|
||||
await Response()
|
||||
.Confirm(
|
||||
type switch
|
||||
{
|
||||
GreetType.Boost => strs.boost_on,
|
||||
GreetType.Greet => strs.greet_on,
|
||||
GreetType.Bye => strs.bye_on,
|
||||
GreetType.GreetDm => strs.greetdm_on,
|
||||
_ => strs.error
|
||||
}
|
||||
)
|
||||
.SendAsync();
|
||||
else
|
||||
await Response()
|
||||
.Pending(
|
||||
type switch
|
||||
{
|
||||
GreetType.Boost => strs.boost_off,
|
||||
GreetType.Greet => strs.greet_off,
|
||||
GreetType.Bye => strs.bye_off,
|
||||
GreetType.GreetDm => strs.greetdm_off,
|
||||
_ => strs.error
|
||||
}
|
||||
)
|
||||
.SendAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task SetDel(GreetType type, int timer)
|
||||
{
|
||||
if (timer is < 0 or > 600)
|
||||
return;
|
||||
|
||||
await _service.SetBoostDel(ctx.Guild.Id, timer);
|
||||
await _service.SetDeleteTimer(ctx.Guild.Id, type, timer);
|
||||
|
||||
if (timer > 0)
|
||||
await Response().Confirm(strs.boostdel_on(timer)).SendAsync();
|
||||
await Response()
|
||||
.Confirm(
|
||||
type switch
|
||||
{
|
||||
GreetType.Boost => strs.boostdel_on(timer),
|
||||
GreetType.Greet => strs.greetdel_on(timer),
|
||||
GreetType.Bye => strs.byedel_on(timer),
|
||||
_ => strs.error
|
||||
}
|
||||
)
|
||||
.SendAsync();
|
||||
else
|
||||
await Response().Pending(strs.boostdel_off).SendAsync();
|
||||
await Response()
|
||||
.Pending(
|
||||
type switch
|
||||
{
|
||||
GreetType.Boost => strs.boostdel_off,
|
||||
GreetType.Greet => strs.greetdel_off,
|
||||
GreetType.Bye => strs.byedel_off,
|
||||
_ => strs.error
|
||||
})
|
||||
.SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task BoostMsg([Leftover] string? text = null)
|
||||
|
||||
public async Task SetMsg(GreetType type, string? text = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
var boostMessage = _service.GetBoostMessage(ctx.Guild.Id);
|
||||
await Response().Confirm(strs.boostmsg_cur(boostMessage?.SanitizeMentions())).SendAsync();
|
||||
var conf = await _service.GetGreetSettingsAsync(ctx.Guild.Id, type);
|
||||
var msg = conf?.MessageText ?? GreetService.GetDefaultGreet(type);
|
||||
await Response()
|
||||
.Confirm(
|
||||
type switch
|
||||
{
|
||||
GreetType.Boost => strs.boostmsg_cur(msg),
|
||||
GreetType.Greet => strs.greetmsg_cur(msg),
|
||||
GreetType.Bye => strs.byemsg_cur(msg),
|
||||
GreetType.GreetDm => strs.greetdmmsg_cur(msg),
|
||||
_ => strs.error
|
||||
})
|
||||
.SendAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var sendBoostEnabled = _service.SetBoostMessage(ctx.Guild.Id, ref text);
|
||||
var isEnabled = await _service.SetMessage(ctx.Guild.Id, type, text);
|
||||
|
||||
await Response().Confirm(strs.boostmsg_new).SendAsync();
|
||||
if (!sendBoostEnabled)
|
||||
await Response().Pending(strs.boostmsg_enable($"`{prefix}boost`")).SendAsync();
|
||||
}
|
||||
await Response()
|
||||
.Confirm(type switch
|
||||
{
|
||||
GreetType.Boost => strs.boostmsg_new,
|
||||
GreetType.Greet => strs.greetmsg_new,
|
||||
GreetType.Bye => strs.byemsg_new,
|
||||
GreetType.GreetDm => strs.greetdmmsg_new,
|
||||
_ => strs.error
|
||||
})
|
||||
.SendAsync();
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetDel(int timer = 30)
|
||||
{
|
||||
if (timer is < 0 or > 600)
|
||||
return;
|
||||
|
||||
await _service.SetGreetDel(ctx.Guild.Id, timer);
|
||||
|
||||
if (timer > 0)
|
||||
await Response().Confirm(strs.greetdel_on(timer)).SendAsync();
|
||||
else
|
||||
await Response().Pending(strs.greetdel_off).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task Greet()
|
||||
{
|
||||
var enabled = await _service.SetGreet(ctx.Guild.Id, ctx.Channel.Id);
|
||||
|
||||
if (enabled)
|
||||
await Response().Confirm(strs.greet_on).SendAsync();
|
||||
else
|
||||
await Response().Pending(strs.greet_off).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetMsg([Leftover] string? text = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
if (!isEnabled)
|
||||
{
|
||||
var greetMsg = _service.GetGreetMsg(ctx.Guild.Id);
|
||||
await Response().Confirm(strs.greetmsg_cur(greetMsg?.SanitizeMentions())).SendAsync();
|
||||
return;
|
||||
var cmdName = GetCmdName(type);
|
||||
|
||||
await Response().Pending(strs.boostmsg_enable($"`{prefix}{cmdName}`")).SendAsync();
|
||||
}
|
||||
|
||||
var sendGreetEnabled = _service.SetGreetMessage(ctx.Guild.Id, ref text);
|
||||
|
||||
await Response().Confirm(strs.greetmsg_new).SendAsync();
|
||||
|
||||
if (!sendGreetEnabled)
|
||||
await Response().Pending(strs.greetmsg_enable($"`{prefix}greet`")).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetDm()
|
||||
private static string GetCmdName(GreetType type)
|
||||
{
|
||||
var enabled = await _service.SetGreetDm(ctx.Guild.Id);
|
||||
|
||||
if (enabled)
|
||||
await Response().Confirm(strs.greetdm_on).SendAsync();
|
||||
else
|
||||
await Response().Confirm(strs.greetdm_off).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task GreetDmMsg([Leftover] string? text = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
var cmdName = type switch
|
||||
{
|
||||
var dmGreetMsg = _service.GetDmGreetMsg(ctx.Guild.Id);
|
||||
await Response().Confirm(strs.greetdmmsg_cur(dmGreetMsg?.SanitizeMentions())).SendAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
var sendGreetEnabled = _service.SetGreetDmMessage(ctx.Guild.Id, ref text);
|
||||
|
||||
await Response().Confirm(strs.greetdmmsg_new).SendAsync();
|
||||
if (!sendGreetEnabled)
|
||||
await Response().Pending(strs.greetdmmsg_enable($"`{prefix}greetdm`")).SendAsync();
|
||||
GreetType.Greet => "greet",
|
||||
GreetType.Bye => "bye",
|
||||
GreetType.Boost => "boost",
|
||||
GreetType.GreetDm => "greetdm",
|
||||
_ => "unknown_command"
|
||||
};
|
||||
return cmdName;
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task Bye()
|
||||
public async Task Test(GreetType type, IGuildUser? user = null)
|
||||
{
|
||||
var enabled = await _service.SetBye(ctx.Guild.Id, ctx.Channel.Id);
|
||||
user ??= (IGuildUser)ctx.User;
|
||||
|
||||
if (enabled)
|
||||
await Response().Confirm(strs.bye_on).SendAsync();
|
||||
else
|
||||
await Response().Confirm(strs.bye_off).SendAsync();
|
||||
}
|
||||
await _service.Test(ctx.Guild.Id, type, (ITextChannel)ctx.Channel, user);
|
||||
var conf = await _service.GetGreetSettingsAsync(ctx.Guild.Id, type);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task ByeMsg([Leftover] string? text = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
var cmd = $"`{prefix}{GetCmdName(type)}`";
|
||||
|
||||
var str = type switch
|
||||
{
|
||||
var byeMsg = _service.GetByeMessage(ctx.Guild.Id);
|
||||
await Response().Confirm(strs.byemsg_cur(byeMsg?.SanitizeMentions())).SendAsync();
|
||||
return;
|
||||
}
|
||||
GreetType.Greet => strs.boostmsg_enable(cmd),
|
||||
GreetType.Bye => strs.greetmsg_enable(cmd),
|
||||
GreetType.Boost => strs.byemsg_enable(cmd),
|
||||
GreetType.GreetDm => strs.greetdmmsg_enable(cmd),
|
||||
_ => strs.error
|
||||
};
|
||||
|
||||
var sendByeEnabled = _service.SetByeMessage(ctx.Guild.Id, ref text);
|
||||
|
||||
await Response().Confirm(strs.byemsg_new).SendAsync();
|
||||
if (!sendByeEnabled)
|
||||
await Response().Pending(strs.byemsg_enable($"`{prefix}bye`")).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
public async Task ByeDel(int timer = 30)
|
||||
{
|
||||
await _service.SetByeDel(ctx.Guild.Id, timer);
|
||||
|
||||
if (timer > 0)
|
||||
await Response().Confirm(strs.byedel_on(timer)).SendAsync();
|
||||
else
|
||||
await Response().Pending(strs.byedel_off).SendAsync();
|
||||
}
|
||||
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public async Task ByeTest([Leftover] IGuildUser? user = null)
|
||||
{
|
||||
user ??= (IGuildUser)ctx.User;
|
||||
|
||||
await _service.ByeTest((ITextChannel)ctx.Channel, user);
|
||||
var enabled = _service.GetByeEnabled(ctx.Guild.Id);
|
||||
if (!enabled)
|
||||
await Response().Pending(strs.byemsg_enable($"`{prefix}bye`")).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public async Task GreetTest([Leftover] IGuildUser? user = null)
|
||||
{
|
||||
user ??= (IGuildUser)ctx.User;
|
||||
|
||||
await _service.GreetTest((ITextChannel)ctx.Channel, user);
|
||||
var enabled = _service.GetGreetEnabled(ctx.Guild.Id);
|
||||
if (!enabled)
|
||||
await Response().Pending(strs.greetmsg_enable($"`{prefix}greet`")).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public async Task GreetDmTest([Leftover] IGuildUser? user = null)
|
||||
{
|
||||
user ??= (IGuildUser)ctx.User;
|
||||
|
||||
var success = await _service.GreetDmTest(user);
|
||||
if (success)
|
||||
await ctx.OkAsync();
|
||||
else
|
||||
await ctx.WarningAsync();
|
||||
var enabled = _service.GetGreetDmEnabled(ctx.Guild.Id);
|
||||
if (!enabled)
|
||||
await Response().Pending(strs.greetdmmsg_enable($"`{prefix}greetdm`")).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageGuild)]
|
||||
[Ratelimit(5)]
|
||||
public async Task BoostTest([Leftover] IGuildUser? user = null)
|
||||
{
|
||||
user ??= (IGuildUser)ctx.User;
|
||||
|
||||
await _service.BoostTest((ITextChannel)ctx.Channel, user);
|
||||
var enabled = _service.GetBoostEnabled(ctx.Guild.Id);
|
||||
if (!enabled)
|
||||
await Response().Pending(strs.boostmsg_enable($"`{prefix}boost`")).SendAsync();
|
||||
if (conf?.IsEnabled is not true)
|
||||
await Response().Pending(str).SendAsync();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,71 +0,0 @@
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public class GreetGrouper<T>
|
||||
{
|
||||
private readonly Dictionary<ulong, HashSet<T>> _group;
|
||||
private readonly object _locker = new();
|
||||
|
||||
public GreetGrouper()
|
||||
=> _group = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a group, if group already exists, adds the specified user
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the server for which to create group for</param>
|
||||
/// <param name="toAddIfExists">User to add if group already exists</param>
|
||||
/// <returns></returns>
|
||||
public bool CreateOrAdd(ulong guildId, T toAddIfExists)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_group.TryGetValue(guildId, out var list))
|
||||
{
|
||||
list.Add(toAddIfExists);
|
||||
return false;
|
||||
}
|
||||
|
||||
_group[guildId] = new();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the specified amount of items from the group. If all items are removed, group will be removed.
|
||||
/// </summary>
|
||||
/// <param name="guildId">Id of the group</param>
|
||||
/// <param name="count">Maximum number of items to retrieve</param>
|
||||
/// <param name="items">Items retrieved</param>
|
||||
/// <returns>Whether the group has no more items left and is deleted</returns>
|
||||
public bool ClearGroup(ulong guildId, int count, out IReadOnlyCollection<T> items)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_group.TryGetValue(guildId, out var set))
|
||||
{
|
||||
// if we want more than there are, return everything
|
||||
if (count >= set.Count)
|
||||
{
|
||||
items = set;
|
||||
_group.Remove(guildId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// if there are more in the group than what's needed
|
||||
// take the requested number, remove them from the set
|
||||
// and return them
|
||||
var toReturn = set.TakeWhile(_ => count-- != 0).ToList();
|
||||
foreach (var item in toReturn)
|
||||
set.Remove(item);
|
||||
|
||||
items = toReturn;
|
||||
// returning falsemeans group is not yet deleted
|
||||
// because there are items left
|
||||
return false;
|
||||
}
|
||||
|
||||
items = Array.Empty<T>();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,58 +1,93 @@
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using LinqToDB.Tools;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db.Models;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public class GreetService : INService, IReadyExecutor
|
||||
{
|
||||
public bool GroupGreets
|
||||
=> _bss.Data.GroupGreets;
|
||||
|
||||
private readonly DbService _db;
|
||||
|
||||
private readonly ConcurrentDictionary<ulong, GreetSettings> _guildConfigsCache;
|
||||
private ConcurrentDictionary<GreetType, ConcurrentHashSet<ulong>> _enabled = new();
|
||||
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
private readonly GreetGrouper<IGuildUser> _greets = new();
|
||||
private readonly GreetGrouper<IUser> _byes = new();
|
||||
private readonly BotConfigService _bss;
|
||||
private readonly IReplacementService _repSvc;
|
||||
private readonly IBotCache _cache;
|
||||
private readonly IMessageSenderService _sender;
|
||||
|
||||
private readonly Channel<(GreetSettings, IUser, ITextChannel?)> _greetQueue =
|
||||
Channel.CreateBounded<(GreetSettings, IUser, ITextChannel?)>(
|
||||
new BoundedChannelOptions(60)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.DropOldest
|
||||
});
|
||||
|
||||
public GreetService(
|
||||
DiscordSocketClient client,
|
||||
IBot bot,
|
||||
DbService db,
|
||||
BotConfigService bss,
|
||||
IMessageSenderService sender,
|
||||
IReplacementService repSvc)
|
||||
IReplacementService repSvc,
|
||||
IBotCache cache
|
||||
)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
_bss = bss;
|
||||
_repSvc = repSvc;
|
||||
_cache = cache;
|
||||
_sender = sender;
|
||||
|
||||
_guildConfigsCache = new(bot.AllGuildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
|
||||
|
||||
_client.UserJoined += OnUserJoined;
|
||||
_client.UserLeft += OnUserLeft;
|
||||
|
||||
bot.JoinedGuild += OnBotJoinedGuild;
|
||||
_client.LeftGuild += OnClientLeftGuild;
|
||||
|
||||
_client.GuildMemberUpdated += ClientOnGuildMemberUpdated;
|
||||
foreach (var type in Enum.GetValues<GreetType>())
|
||||
{
|
||||
_enabled[type] = new();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
// cache all enabled guilds
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var guilds = _client.Guilds.Select(x => x.Id).ToList();
|
||||
var enabled = await uow.GetTable<GreetSettings>()
|
||||
.Where(x => x.GuildId.In(guilds))
|
||||
.Where(x => x.IsEnabled)
|
||||
.Select(x => new
|
||||
{
|
||||
x.GuildId,
|
||||
x.GreetType
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var e in enabled)
|
||||
{
|
||||
_enabled[e.GreetType].Add(e.GuildId);
|
||||
}
|
||||
}
|
||||
|
||||
_client.UserJoined += OnUserJoined;
|
||||
_client.UserLeft += OnUserLeft;
|
||||
|
||||
_client.LeftGuild += OnClientLeftGuild;
|
||||
|
||||
_client.GuildMemberUpdated += ClientOnGuildMemberUpdated;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var (conf, user, compl) = await _greetDmQueue.Reader.ReadAsync();
|
||||
var res = await GreetDmUserInternal(conf, user);
|
||||
compl.TrySetResult(res);
|
||||
await Task.Delay(2000);
|
||||
try
|
||||
{
|
||||
var (conf, user, ch) = await _greetQueue.Reader.ReadAsync();
|
||||
await GreetUsers(conf, ch, user);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Greet Loop almost crashed. Please report this!");
|
||||
}
|
||||
|
||||
await Task.Delay(2016);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,61 +100,38 @@ public class GreetService : INService, IReadyExecutor
|
||||
&& newUser.PremiumSince is { } newDate
|
||||
&& newDate > oldDate))
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(newUser.Guild.Id);
|
||||
if (!conf.SendBoostMessage)
|
||||
return Task.CompletedTask;
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var conf = await GetGreetSettingsAsync(newUser.Guild.Id, GreetType.Boost);
|
||||
|
||||
_ = Task.Run(TriggerBoostMessage(conf, newUser));
|
||||
if (conf is null || !conf.IsEnabled)
|
||||
return;
|
||||
|
||||
ITextChannel? channel = null;
|
||||
if (conf.ChannelId is { } cid)
|
||||
channel = newUser.Guild.GetTextChannel(cid);
|
||||
|
||||
if (channel is null)
|
||||
return;
|
||||
|
||||
await GreetUsers(conf, channel, newUser);
|
||||
});
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Func<Task> TriggerBoostMessage(GreetSettings conf, SocketGuildUser user)
|
||||
=> async () =>
|
||||
{
|
||||
var channel = user.Guild.GetTextChannel(conf.BoostMessageChannelId);
|
||||
if (channel is null)
|
||||
return;
|
||||
|
||||
await SendBoostMessage(conf, user, channel);
|
||||
};
|
||||
|
||||
private async Task<bool> SendBoostMessage(GreetSettings conf, IGuildUser user, ITextChannel channel)
|
||||
private async Task OnClientLeftGuild(SocketGuild guild)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(conf.BoostMessage))
|
||||
return false;
|
||||
|
||||
var toSend = SmartText.CreateFrom(conf.BoostMessage);
|
||||
|
||||
try
|
||||
foreach (var gt in Enum.GetValues<GreetType>())
|
||||
{
|
||||
var newContent = await _repSvc.ReplaceAsync(toSend,
|
||||
new(client: _client, guild: user.Guild, channel: channel, users: user));
|
||||
var toDelete = await _sender.Response(channel).Text(newContent).Sanitize(false).SendAsync();
|
||||
if (conf.BoostMessageDeleteAfter > 0)
|
||||
toDelete.DeleteAfter(conf.BoostMessageDeleteAfter);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error sending boost message");
|
||||
_enabled[gt].TryRemove(guild.Id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Task OnClientLeftGuild(SocketGuild arg)
|
||||
{
|
||||
_guildConfigsCache.TryRemove(arg.Id, out _);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task OnBotJoinedGuild(GuildConfig gc)
|
||||
{
|
||||
_guildConfigsCache[gc.GuildId] = GreetSettings.Create(gc);
|
||||
return Task.CompletedTask;
|
||||
await using var uow = _db.GetDbContext();
|
||||
await uow.GetTable<GreetSettings>()
|
||||
.Where(x => x.GuildId == guild.Id)
|
||||
.DeleteAsync();
|
||||
}
|
||||
|
||||
private Task OnUserLeft(SocketGuild guild, SocketUser user)
|
||||
@@ -128,35 +140,20 @@ public class GreetService : INService, IReadyExecutor
|
||||
{
|
||||
try
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(guild.Id);
|
||||
var conf = await GetGreetSettingsAsync(guild.Id, GreetType.Bye);
|
||||
|
||||
if (!conf.SendChannelByeMessage)
|
||||
if (conf is null)
|
||||
return;
|
||||
var channel = guild.TextChannels.FirstOrDefault(c => c.Id == conf.ByeMessageChannelId);
|
||||
|
||||
var channel = guild.TextChannels.FirstOrDefault(c => c.Id == conf.ChannelId);
|
||||
|
||||
if (channel is null) //maybe warn the server owner that the channel is missing
|
||||
return;
|
||||
|
||||
if (GroupGreets)
|
||||
{
|
||||
// if group is newly created, greet that user right away,
|
||||
// but any user which joins in the next 5 seconds will
|
||||
// be greeted in a group greet
|
||||
if (_byes.CreateOrAdd(guild.Id, user))
|
||||
{
|
||||
// greet single user
|
||||
await ByeUsers(conf, channel, new[] { user });
|
||||
var groupClear = false;
|
||||
while (!groupClear)
|
||||
{
|
||||
await Task.Delay(5000);
|
||||
groupClear = _byes.ClearGroup(guild.Id, 5, out var toBye);
|
||||
await ByeUsers(conf, channel, toBye);
|
||||
}
|
||||
}
|
||||
await SetGreet(guild.Id, null, GreetType.Bye, false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
await ByeUsers(conf, channel, new[] { user });
|
||||
|
||||
await _greetQueue.Writer.WriteAsync((conf, user, channel));
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -166,98 +163,62 @@ public class GreetService : INService, IReadyExecutor
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string? GetDmGreetMsg(ulong id)
|
||||
private TypedKey<GreetSettings?> GreetSettingsKey(GreetType type)
|
||||
=> new($"greet_settings:{type}");
|
||||
|
||||
public async Task<GreetSettings?> GetGreetSettingsAsync(ulong gid, GreetType type)
|
||||
=> await _cache.GetOrAddAsync<GreetSettings?>(GreetSettingsKey(type),
|
||||
() => InternalGetGreetSettingsAsync(gid, type),
|
||||
TimeSpan.FromSeconds(3));
|
||||
|
||||
private async Task<GreetSettings?> InternalGetGreetSettingsAsync(ulong gid, GreetType type)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.GuildConfigsForId(id, set => set).DmGreetMessageText;
|
||||
await using var uow = _db.GetDbContext();
|
||||
var res = await uow.GetTable<GreetSettings>()
|
||||
.Where(x => x.GuildId == gid && x.GreetType == type)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (res is not null)
|
||||
res.MessageText ??= GetDefaultGreet(type);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public string? GetGreetMsg(ulong gid)
|
||||
private async Task GreetUsers(GreetSettings conf, ITextChannel? channel, IUser user)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.GuildConfigsForId(gid, set => set).ChannelGreetMessageText;
|
||||
}
|
||||
if (conf.GreetType == GreetType.GreetDm)
|
||||
{
|
||||
if (user is not IGuildUser gu)
|
||||
return;
|
||||
|
||||
public string? GetBoostMessage(ulong gid)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.GuildConfigsForId(gid, set => set).BoostMessage;
|
||||
}
|
||||
await GreetDmUserInternal(conf, gu);
|
||||
return;
|
||||
}
|
||||
|
||||
public GreetSettings GetGreetSettings(ulong gid)
|
||||
{
|
||||
if (_guildConfigsCache.TryGetValue(gid, out var gs))
|
||||
return gs;
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
return GreetSettings.Create(uow.GuildConfigsForId(gid, set => set));
|
||||
}
|
||||
|
||||
private Task ByeUsers(GreetSettings conf, ITextChannel channel, IUser user)
|
||||
=> ByeUsers(conf, channel, new[] { user });
|
||||
|
||||
private async Task ByeUsers(GreetSettings conf, ITextChannel channel, IReadOnlyCollection<IUser> users)
|
||||
{
|
||||
if (!users.Any())
|
||||
if (channel is null)
|
||||
return;
|
||||
|
||||
var repCtx = new ReplacementContext(client: _client,
|
||||
guild: channel.Guild,
|
||||
channel: channel,
|
||||
users: users.ToArray());
|
||||
user: user);
|
||||
|
||||
var text = SmartText.CreateFrom(conf.ChannelByeMessageText);
|
||||
var text = SmartText.CreateFrom(conf.MessageText);
|
||||
text = await _repSvc.ReplaceAsync(text, repCtx);
|
||||
try
|
||||
{
|
||||
var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
|
||||
if (conf.AutoDeleteByeMessagesTimer > 0)
|
||||
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer);
|
||||
if (conf.AutoDeleteTimer > 0)
|
||||
toDelete.DeleteAfter(conf.AutoDeleteTimer);
|
||||
}
|
||||
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
|
||||
|| ex.DiscordCode == DiscordErrorCode.MissingPermissions
|
||||
|| ex.DiscordCode == DiscordErrorCode.UnknownChannel)
|
||||
{
|
||||
Log.Warning(ex,
|
||||
"Missing permissions to send a bye message, the bye message will be disabled on server: {GuildId}",
|
||||
channel.GuildId);
|
||||
await SetBye(channel.GuildId, channel.Id, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error embeding bye message");
|
||||
}
|
||||
}
|
||||
|
||||
private Task GreetUsers(GreetSettings conf, ITextChannel channel, IGuildUser user)
|
||||
=> GreetUsers(conf, channel, new[] { user });
|
||||
|
||||
private async Task GreetUsers(GreetSettings conf, ITextChannel channel, IReadOnlyCollection<IGuildUser> users)
|
||||
{
|
||||
if (users.Count == 0)
|
||||
return;
|
||||
|
||||
var repCtx = new ReplacementContext(client: _client,
|
||||
guild: channel.Guild,
|
||||
channel: channel,
|
||||
users: users.ToArray());
|
||||
|
||||
var text = SmartText.CreateFrom(conf.ChannelGreetMessageText);
|
||||
text = await _repSvc.ReplaceAsync(text, repCtx);
|
||||
try
|
||||
{
|
||||
var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
|
||||
if (conf.AutoDeleteGreetMessagesTimer > 0)
|
||||
toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer);
|
||||
}
|
||||
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions
|
||||
|| ex.DiscordCode == DiscordErrorCode.MissingPermissions
|
||||
|| ex.DiscordCode == DiscordErrorCode.UnknownChannel)
|
||||
catch (HttpException ex) when (ex.DiscordCode is DiscordErrorCode.InsufficientPermissions
|
||||
or DiscordErrorCode.MissingPermissions
|
||||
or DiscordErrorCode.UnknownChannel)
|
||||
{
|
||||
Log.Warning(ex,
|
||||
"Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}",
|
||||
channel.GuildId);
|
||||
await SetGreet(channel.GuildId, channel.Id, false);
|
||||
await SetGreet(channel.GuildId, channel.Id, GreetType.Greet, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -265,33 +226,12 @@ public class GreetService : INService, IReadyExecutor
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Channel<(GreetSettings, IGuildUser, TaskCompletionSource<bool>)> _greetDmQueue =
|
||||
Channel.CreateBounded<(GreetSettings, IGuildUser, TaskCompletionSource<bool>)>(new BoundedChannelOptions(60)
|
||||
{
|
||||
// The limit of 60 users should be only hit when there's a raid. In that case
|
||||
// probably the best thing to do is to drop newest (raiding) users
|
||||
FullMode = BoundedChannelFullMode.DropNewest
|
||||
});
|
||||
|
||||
|
||||
private async Task<bool> GreetDmUser(GreetSettings conf, IGuildUser user)
|
||||
{
|
||||
var completionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
await _greetDmQueue.Writer.WriteAsync((conf, user, completionSource));
|
||||
return await completionSource.Task;
|
||||
}
|
||||
|
||||
private async Task<bool> GreetDmUserInternal(GreetSettings conf, IGuildUser user)
|
||||
{
|
||||
try
|
||||
{
|
||||
// var rep = new ReplacementBuilder()
|
||||
// .WithUser(user)
|
||||
// .WithServer(_client, (SocketGuild)user.Guild)
|
||||
// .Build();
|
||||
|
||||
var repCtx = new ReplacementContext(client: _client, guild: user.Guild, users: user);
|
||||
var smartText = SmartText.CreateFrom(conf.DmGreetMessageText);
|
||||
var repCtx = new ReplacementContext(client: _client, guild: user.Guild, user: user);
|
||||
var smartText = SmartText.CreateFrom(conf.MessageText);
|
||||
smartText = await _repSvc.ReplaceAsync(smartText, repCtx);
|
||||
|
||||
if (smartText is SmartPlainText pt)
|
||||
@@ -351,8 +291,9 @@ public class GreetService : INService, IReadyExecutor
|
||||
|
||||
await _sender.Response(user).Text(smartText).Sanitize(false).SendAsync();
|
||||
}
|
||||
catch
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error sending greet dm");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -372,297 +313,187 @@ public class GreetService : INService, IReadyExecutor
|
||||
{
|
||||
try
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
|
||||
if (conf.SendChannelGreetMessage)
|
||||
if (_enabled[GreetType.Greet].Contains(user.GuildId))
|
||||
{
|
||||
var channel = await user.Guild.GetTextChannelAsync(conf.GreetMessageChannelId);
|
||||
if (channel is not null)
|
||||
var conf = await GetGreetSettingsAsync(user.GuildId, GreetType.Greet);
|
||||
if (conf?.ChannelId is ulong cid)
|
||||
{
|
||||
if (GroupGreets)
|
||||
var channel = await user.Guild.GetTextChannelAsync(cid);
|
||||
if (channel is not null)
|
||||
{
|
||||
// if group is newly created, greet that user right away,
|
||||
// but any user which joins in the next 5 seconds will
|
||||
// be greeted in a group greet
|
||||
if (_greets.CreateOrAdd(user.GuildId, user))
|
||||
{
|
||||
// greet single user
|
||||
await GreetUsers(conf, channel, new[] { user });
|
||||
var groupClear = false;
|
||||
while (!groupClear)
|
||||
{
|
||||
await Task.Delay(5000);
|
||||
groupClear = _greets.ClearGroup(user.GuildId, 5, out var toGreet);
|
||||
await GreetUsers(conf, channel, toGreet);
|
||||
}
|
||||
}
|
||||
await _greetQueue.Writer.WriteAsync((conf, user, channel));
|
||||
}
|
||||
else
|
||||
await GreetUsers(conf, channel, new[] { user });
|
||||
}
|
||||
}
|
||||
|
||||
if (conf.SendDmGreetMessage)
|
||||
await GreetDmUser(conf, user);
|
||||
|
||||
if (_enabled[GreetType.GreetDm].Contains(user.GuildId))
|
||||
{
|
||||
var confDm = await GetGreetSettingsAsync(user.GuildId, GreetType.GreetDm);
|
||||
if (confDm is not null)
|
||||
{
|
||||
await _greetQueue.Writer.WriteAsync((confDm, user, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch(Exception ex)
|
||||
{
|
||||
// ignored
|
||||
Log.Error(ex, "Error in GreetService.OnUserJoined. This should not happen. Please report it");
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string? GetByeMessage(ulong gid)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.GuildConfigsForId(gid, set => set).ChannelByeMessageText;
|
||||
}
|
||||
|
||||
public GreetSettings GetOrAddSettingsForGuild(ulong guildId)
|
||||
{
|
||||
if (_guildConfigsCache.TryGetValue(guildId, out var settings))
|
||||
return settings;
|
||||
|
||||
using (var uow = _db.GetDbContext())
|
||||
public static string GetDefaultGreet(GreetType greetType)
|
||||
=> greetType switch
|
||||
{
|
||||
var gc = uow.GuildConfigsForId(guildId, set => set);
|
||||
settings = GreetSettings.Create(gc);
|
||||
GreetType.Boost => "%user.mention% has boosted the server!",
|
||||
GreetType.Greet => "%user.mention% has joined the server!",
|
||||
GreetType.Bye => "%user.name% has left the server!",
|
||||
GreetType.GreetDm => "Welcome to the server %user.name%",
|
||||
_ => "%user.name% did something new!"
|
||||
};
|
||||
|
||||
public async Task<bool> SetGreet(
|
||||
ulong guildId,
|
||||
ulong? channelId,
|
||||
GreetType greetType,
|
||||
bool? value = null)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var q = uow.GetTable<GreetSettings>();
|
||||
|
||||
if (value is null)
|
||||
value = !_enabled[greetType].Contains(guildId);
|
||||
|
||||
if (value is { } v)
|
||||
{
|
||||
await q
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
GreetType = greetType,
|
||||
IsEnabled = v,
|
||||
ChannelId = channelId,
|
||||
},
|
||||
(old) => new()
|
||||
{
|
||||
IsEnabled = v,
|
||||
ChannelId = channelId,
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
GreetType = greetType,
|
||||
});
|
||||
}
|
||||
|
||||
_guildConfigsCache.TryAdd(guildId, settings);
|
||||
return settings;
|
||||
if (value is true)
|
||||
{
|
||||
_enabled[greetType].Add(guildId);
|
||||
return true;
|
||||
}
|
||||
|
||||
_enabled[greetType].TryRemove(guildId);
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
|
||||
|
||||
public async Task<bool> SetMessage(ulong guildId, GreetType greetType, string? message)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
var enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage;
|
||||
conf.GreetMessageChannelId = channelId;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
await uow.GetTable<GreetSettings>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
GreetType = greetType,
|
||||
MessageText = message
|
||||
},
|
||||
x => new()
|
||||
{
|
||||
MessageText = message
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
GreetType = greetType
|
||||
});
|
||||
}
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
var conf = await GetGreetSettingsAsync(guildId, greetType);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
return enabled;
|
||||
return conf?.IsEnabled ?? false;
|
||||
}
|
||||
|
||||
public bool SetGreetMessage(ulong guildId, ref string message)
|
||||
public async Task<bool> SetDeleteTimer(ulong guildId, GreetType greetType, int timer)
|
||||
{
|
||||
message = message.SanitizeMentions();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
conf.ChannelGreetMessageText = message;
|
||||
var greetMsgEnabled = conf.SendChannelGreetMessage;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache.AddOrUpdate(guildId, toAdd, (_, _) => toAdd);
|
||||
|
||||
uow.SaveChanges();
|
||||
return greetMsgEnabled;
|
||||
}
|
||||
|
||||
public async Task<bool> SetGreetDm(ulong guildId, bool? value = null)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
var enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public bool SetGreetDmMessage(ulong guildId, ref string? message)
|
||||
{
|
||||
message = message?.SanitizeMentions();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
conf.DmGreetMessageText = message;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
|
||||
uow.SaveChanges();
|
||||
return conf.SendDmGreetMessage;
|
||||
}
|
||||
|
||||
public async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
var enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage;
|
||||
conf.ByeMessageChannelId = channelId;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public bool SetByeMessage(ulong guildId, ref string? message)
|
||||
{
|
||||
message = message?.SanitizeMentions();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
conf.ChannelByeMessageText = message;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
|
||||
uow.SaveChanges();
|
||||
return conf.SendChannelByeMessage;
|
||||
}
|
||||
|
||||
public async Task SetByeDel(ulong guildId, int timer)
|
||||
{
|
||||
if (timer is < 0 or > 600)
|
||||
return;
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
conf.AutoDeleteByeMessagesTimer = timer;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task SetGreetDel(ulong guildId, int timer)
|
||||
{
|
||||
if (timer is < 0 or > 600)
|
||||
return;
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
conf.AutoDeleteGreetMessagesTimer = timer;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public bool SetBoostMessage(ulong guildId, ref string message)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
conf.BoostMessage = message;
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
|
||||
uow.SaveChanges();
|
||||
return conf.SendBoostMessage;
|
||||
}
|
||||
|
||||
public async Task SetBoostDel(ulong guildId, int timer)
|
||||
{
|
||||
if (timer is < 0 or > 600)
|
||||
if (timer < 0 || timer > 3600)
|
||||
throw new ArgumentOutOfRangeException(nameof(timer));
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
conf.BoostMessageDeleteAfter = timer;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
await uow.GetTable<GreetSettings>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
GreetType = greetType,
|
||||
AutoDeleteTimer = timer,
|
||||
},
|
||||
x => new()
|
||||
{
|
||||
AutoDeleteTimer = timer
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
GreetType = greetType
|
||||
});
|
||||
}
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
var conf = await GetGreetSettingsAsync(guildId, greetType);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
return conf?.IsEnabled ?? false;
|
||||
}
|
||||
|
||||
public async Task<bool> ToggleBoost(ulong guildId, ulong channelId, bool? forceState = null)
|
||||
|
||||
public async Task<bool> Test(
|
||||
ulong guildId,
|
||||
GreetType type,
|
||||
IMessageChannel channel,
|
||||
IGuildUser user)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
var conf = await GetGreetSettingsAsync(guildId, type);
|
||||
if (conf is null)
|
||||
{
|
||||
conf = new GreetSettings()
|
||||
{
|
||||
ChannelId = channel.Id,
|
||||
GreetType = type,
|
||||
IsEnabled = false,
|
||||
GuildId = guildId,
|
||||
AutoDeleteTimer = 30,
|
||||
MessageText = GetDefaultGreet(type)
|
||||
};
|
||||
}
|
||||
|
||||
if (forceState is not bool fs)
|
||||
conf.SendBoostMessage = !conf.SendBoostMessage;
|
||||
else
|
||||
conf.SendBoostMessage = fs;
|
||||
|
||||
conf.BoostMessageChannelId = channelId;
|
||||
await uow.SaveChangesAsync();
|
||||
|
||||
var toAdd = GreetSettings.Create(conf);
|
||||
_guildConfigsCache[guildId] = toAdd;
|
||||
return conf.SendBoostMessage;
|
||||
await SendMessage(conf, channel, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Get Enabled Status
|
||||
|
||||
public bool GetGreetDmEnabled(ulong guildId)
|
||||
public async Task<bool> SendMessage(GreetSettings conf, IMessageChannel channel, IGuildUser user)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
return conf.SendDmGreetMessage;
|
||||
if (conf.GreetType == GreetType.GreetDm)
|
||||
{
|
||||
await _greetQueue.Writer.WriteAsync((conf, user, null));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (channel is not ITextChannel ch)
|
||||
return false;
|
||||
|
||||
await GreetUsers(conf, ch, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool GetGreetEnabled(ulong guildId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
return conf.SendChannelGreetMessage;
|
||||
}
|
||||
|
||||
public bool GetByeEnabled(ulong guildId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
return conf.SendChannelByeMessage;
|
||||
}
|
||||
|
||||
public bool GetBoostEnabled(ulong guildId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
return conf.SendBoostMessage;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Test Messages
|
||||
|
||||
public Task ByeTest(ITextChannel channel, IGuildUser user)
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
return ByeUsers(conf, channel, user);
|
||||
}
|
||||
|
||||
public Task GreetTest(ITextChannel channel, IGuildUser user)
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
return GreetUsers(conf, channel, user);
|
||||
}
|
||||
|
||||
public Task<bool> GreetDmTest(IGuildUser user)
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
return GreetDmUser(conf, user);
|
||||
}
|
||||
|
||||
public Task<bool> BoostTest(ITextChannel channel, IGuildUser user)
|
||||
{
|
||||
var conf = GetOrAddSettingsForGuild(user.GuildId);
|
||||
return SendBoostMessage(conf, user, channel);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@@ -1,45 +1,21 @@
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Services;
|
||||
|
||||
public enum GreetType
|
||||
{
|
||||
Greet,
|
||||
GreetDm,
|
||||
Bye,
|
||||
Boost,
|
||||
}
|
||||
|
||||
public class GreetSettings
|
||||
{
|
||||
public int AutoDeleteGreetMessagesTimer { get; set; }
|
||||
public int AutoDeleteByeMessagesTimer { get; set; }
|
||||
|
||||
public ulong GreetMessageChannelId { get; set; }
|
||||
public ulong ByeMessageChannelId { get; set; }
|
||||
|
||||
public bool SendDmGreetMessage { get; set; }
|
||||
public string? DmGreetMessageText { get; set; }
|
||||
|
||||
public bool SendChannelGreetMessage { get; set; }
|
||||
public string? ChannelGreetMessageText { get; set; }
|
||||
|
||||
public bool SendChannelByeMessage { get; set; }
|
||||
public string? ChannelByeMessageText { get; set; }
|
||||
|
||||
public bool SendBoostMessage { get; set; }
|
||||
public string? BoostMessage { get; set; }
|
||||
public int BoostMessageDeleteAfter { get; set; }
|
||||
public ulong BoostMessageChannelId { get; set; }
|
||||
|
||||
public static GreetSettings Create(GuildConfig g)
|
||||
=> new()
|
||||
{
|
||||
AutoDeleteByeMessagesTimer = g.AutoDeleteByeMessagesTimer,
|
||||
AutoDeleteGreetMessagesTimer = g.AutoDeleteGreetMessagesTimer,
|
||||
GreetMessageChannelId = g.GreetMessageChannelId,
|
||||
ByeMessageChannelId = g.ByeMessageChannelId,
|
||||
SendDmGreetMessage = g.SendDmGreetMessage,
|
||||
DmGreetMessageText = g.DmGreetMessageText,
|
||||
SendChannelGreetMessage = g.SendChannelGreetMessage,
|
||||
ChannelGreetMessageText = g.ChannelGreetMessageText,
|
||||
SendChannelByeMessage = g.SendChannelByeMessage,
|
||||
ChannelByeMessageText = g.ChannelByeMessageText,
|
||||
SendBoostMessage = g.SendBoostMessage,
|
||||
BoostMessage = g.BoostMessage,
|
||||
BoostMessageDeleteAfter = g.BoostMessageDeleteAfter,
|
||||
BoostMessageChannelId = g.BoostMessageChannelId
|
||||
};
|
||||
public int Id { get; set; }
|
||||
|
||||
public ulong GuildId { get; set; }
|
||||
public GreetType GreetType { get; set; }
|
||||
public string? MessageText { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
public ulong? ChannelId { get; set; }
|
||||
public int AutoDeleteTimer { get; set; }
|
||||
}
|
@@ -18,11 +18,17 @@ public partial class Administration
|
||||
await Response().Confirm(strs.ropl_disabled).SendAsync();
|
||||
}
|
||||
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task AddPlaying(ActivityType t, [Leftover] string status)
|
||||
public Task AddPlaying([Leftover] string status)
|
||||
=> AddPlaying(ActivityType.CustomStatus, status);
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task AddPlaying(ActivityType statusType, [Leftover] string status)
|
||||
{
|
||||
await _service.AddPlaying(t, status);
|
||||
await _service.AddPlaying(statusType, status);
|
||||
|
||||
await Response().Confirm(strs.ropl_added).SendAsync();
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
||||
: rotatingStatuses[index++];
|
||||
|
||||
var statusText = await _repService.ReplaceAsync(playingStatus.Status, new (client: _client));
|
||||
await _selfService.SetGameAsync(statusText, (ActivityType)playingStatus.Type);
|
||||
await _selfService.SetActivityAsync(statusText, (ActivityType)playingStatus.Type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -459,7 +459,7 @@ public partial class Administration
|
||||
|
||||
await Response().Confirm(strs.bot_name(Format.Bold(newName))).SendAsync();
|
||||
}
|
||||
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task SetStatus([Leftover] SettableUserStatus status)
|
||||
@@ -491,14 +491,25 @@ public partial class Administration
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task SetGame(ActivityType type, [Leftover] string game = null)
|
||||
public async Task SetActivity(ActivityType? type, [Leftover] string game = null)
|
||||
{
|
||||
// var rep = new ReplacementBuilder().WithDefault(Context).Build();
|
||||
|
||||
var repCtx = new ReplacementContext(ctx);
|
||||
await _service.SetGameAsync(game is null ? game : await repSvc.ReplaceAsync(game, repCtx), type);
|
||||
await _service.SetActivityAsync(game is null ? game : await repSvc.ReplaceAsync(game, repCtx), type);
|
||||
|
||||
await Response().Confirm(strs.set_game).SendAsync();
|
||||
await Response().Confirm(strs.set_activity).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public Task SetActivity([Leftover] string game = null)
|
||||
=> SetActivity(null, game);
|
||||
|
||||
public class SetActivityOptions
|
||||
{
|
||||
public ActivityType? Type { get; set; }
|
||||
public string Game { get; set; }
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -541,11 +552,11 @@ public partial class Administration
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var repCtx = new ReplacementContext(ctx);
|
||||
text = await repSvc.ReplaceAsync(text, repCtx);
|
||||
await Response().Channel(ch).Text(text).SendAsync();
|
||||
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
|
@@ -85,13 +85,14 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
|
||||
autoCommands = uow.Set<AutoCommand>().AsNoTracking()
|
||||
.Where(x => x.Interval >= 5)
|
||||
.AsEnumerable()
|
||||
.GroupBy(x => x.GuildId)
|
||||
.ToDictionary(x => x.Key,
|
||||
y => y.ToDictionary(x => x.Id, TimerFromAutoCommand).ToConcurrent())
|
||||
.ToConcurrent();
|
||||
autoCommands = uow.Set<AutoCommand>()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Interval >= 5)
|
||||
.AsEnumerable()
|
||||
.GroupBy(x => x.GuildId)
|
||||
.ToDictionary(x => x.Key,
|
||||
y => y.ToDictionary(x => x.Id, TimerFromAutoCommand).ToConcurrent())
|
||||
.ToConcurrent();
|
||||
|
||||
var startupCommands = uow.Set<AutoCommand>().AsNoTracking().Where(x => x.Interval == 0);
|
||||
foreach (var cmd in startupCommands)
|
||||
@@ -170,18 +171,18 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
||||
private async Task LoadOwnerChannels()
|
||||
{
|
||||
var channels = await _creds.OwnerIds.Select(id =>
|
||||
{
|
||||
var user = _client.GetUser(id);
|
||||
if (user is null)
|
||||
return Task.FromResult<IDMChannel>(null);
|
||||
{
|
||||
var user = _client.GetUser(id);
|
||||
if (user is null)
|
||||
return Task.FromResult<IDMChannel>(null);
|
||||
|
||||
return user.CreateDMChannelAsync();
|
||||
})
|
||||
.WhenAll();
|
||||
return user.CreateDMChannelAsync();
|
||||
})
|
||||
.WhenAll();
|
||||
|
||||
ownerChannels = channels.Where(x => x is not null)
|
||||
.ToDictionary(x => x.Recipient.Id, x => x)
|
||||
.ToImmutableDictionary();
|
||||
.ToDictionary(x => x.Recipient.Id, x => x)
|
||||
.ToImmutableDictionary();
|
||||
|
||||
if (!ownerChannels.Any())
|
||||
{
|
||||
@@ -400,7 +401,10 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
||||
{
|
||||
try
|
||||
{
|
||||
await _client.SetGameAsync(data.Name, data.Link, data.Type);
|
||||
if (data.Type is { } activityType)
|
||||
await _client.SetGameAsync(data.Name, data.Link, activityType);
|
||||
else
|
||||
await _client.SetCustomStatusAsync(data.Name);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -408,7 +412,7 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
||||
}
|
||||
});
|
||||
|
||||
public Task SetGameAsync(string game, ActivityType type)
|
||||
public Task SetActivityAsync(string game, ActivityType? type)
|
||||
=> _pubSub.Pub(_activitySetKey,
|
||||
new()
|
||||
{
|
||||
@@ -430,10 +434,10 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
||||
{
|
||||
public string Name { get; init; }
|
||||
public string Link { get; init; }
|
||||
public ActivityType Type { get; init; }
|
||||
public ActivityType? Type { get; init; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified <paramref name="users"/> to the database. If a database user with placeholder name
|
||||
/// and discriminator is present in <paramref name="users"/>, their name and discriminator get updated accordingly.
|
||||
@@ -443,41 +447,46 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
||||
/// <returns>A tuple with the amount of new users added and old users updated.</returns>
|
||||
public async Task<(long UsersAdded, long UsersUpdated)> RefreshUsersAsync(List<IUser> users)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
await using var ctx = _db.GetDbContext();
|
||||
var presentDbUsers = await ctx.GetTable<DiscordUser>()
|
||||
.Select(x => new { x.UserId, x.Username, x.Discriminator })
|
||||
.Where(x => users.Select(y => y.Id).Contains(x.UserId))
|
||||
.ToArrayAsyncEF();
|
||||
.Select(x => new
|
||||
{
|
||||
x.UserId,
|
||||
x.Username,
|
||||
x.Discriminator
|
||||
})
|
||||
.Where(x => users.Select(y => y.Id).Contains(x.UserId))
|
||||
.ToArrayAsyncEF();
|
||||
|
||||
var usersToAdd = users
|
||||
.Where(x => !presentDbUsers.Select(x => x.UserId).Contains(x.Id))
|
||||
.Select(x => new DiscordUser()
|
||||
{
|
||||
UserId = x.Id,
|
||||
AvatarId = x.AvatarId,
|
||||
Username = x.Username,
|
||||
Discriminator = x.Discriminator
|
||||
});
|
||||
.Where(x => !presentDbUsers.Select(x => x.UserId).Contains(x.Id))
|
||||
.Select(x => new DiscordUser()
|
||||
{
|
||||
UserId = x.Id,
|
||||
AvatarId = x.AvatarId,
|
||||
Username = x.Username,
|
||||
Discriminator = x.Discriminator
|
||||
});
|
||||
|
||||
var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied;
|
||||
var toUpdateUserIds = presentDbUsers
|
||||
.Where(x => x.Username == "Unknown" && x.Discriminator == "????")
|
||||
.Select(x => x.UserId)
|
||||
.ToArray();
|
||||
.Where(x => x.Username == "Unknown" && x.Discriminator == "????")
|
||||
.Select(x => x.UserId)
|
||||
.ToArray();
|
||||
|
||||
foreach (var user in users.Where(x => toUpdateUserIds.Contains(x.Id)))
|
||||
{
|
||||
await ctx.GetTable<DiscordUser>()
|
||||
.Where(x => x.UserId == user.Id)
|
||||
.UpdateAsync(x => new DiscordUser()
|
||||
{
|
||||
Username = user.Username,
|
||||
Discriminator = user.Discriminator,
|
||||
.Where(x => x.UserId == user.Id)
|
||||
.UpdateAsync(x => new DiscordUser()
|
||||
{
|
||||
Username = user.Username,
|
||||
Discriminator = user.Discriminator,
|
||||
|
||||
// .award tends to set AvatarId and DateAdded to NULL, so account for that.
|
||||
AvatarId = user.AvatarId,
|
||||
DateAdded = x.DateAdded ?? DateTime.UtcNow
|
||||
});
|
||||
// .award tends to set AvatarId and DateAdded to NULL, so account for that.
|
||||
AvatarId = user.AvatarId,
|
||||
DateAdded = x.DateAdded ?? DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
return (added, toUpdateUserIds.Length);
|
||||
|
@@ -243,43 +243,54 @@ public class UserPunishService : INService, IReadyExecutor
|
||||
public async Task CheckAllWarnExpiresAsync()
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var cleared = await uow.Set<Warning>()
|
||||
.Where(x => uow.Set<GuildConfig>()
|
||||
.Any(y => y.GuildId == x.GuildId
|
||||
&& y.WarnExpireHours > 0
|
||||
&& y.WarnExpireAction == WarnExpireAction.Clear)
|
||||
|
||||
var toClear = await uow.GetTable<Warning>()
|
||||
.Where(x => uow.GetTable<GuildConfig>()
|
||||
.Count(y => y.GuildId == x.GuildId
|
||||
&& y.WarnExpireHours > 0
|
||||
&& y.WarnExpireAction == WarnExpireAction.Clear)
|
||||
> 0
|
||||
&& x.Forgiven == false
|
||||
&& x.DateAdded
|
||||
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>()
|
||||
< DateTime.UtcNow.AddHours(-uow.GetTable<GuildConfig>()
|
||||
.Where(y => x.GuildId == y.GuildId)
|
||||
.Select(y => y.WarnExpireHours)
|
||||
.First()))
|
||||
.UpdateAsync(_ => new()
|
||||
{
|
||||
Forgiven = true,
|
||||
ForgivenBy = "expiry"
|
||||
});
|
||||
.Select(x => x.Id)
|
||||
.ToListAsyncLinqToDB();
|
||||
|
||||
var deleted = await uow.Set<Warning>()
|
||||
.Where(x => uow.Set<GuildConfig>()
|
||||
.Any(y => y.GuildId == x.GuildId
|
||||
&& y.WarnExpireHours > 0
|
||||
&& y.WarnExpireAction == WarnExpireAction.Delete)
|
||||
&& x.DateAdded
|
||||
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>()
|
||||
.Where(y => x.GuildId == y.GuildId)
|
||||
.Select(y => y.WarnExpireHours)
|
||||
.First()))
|
||||
.DeleteAsync();
|
||||
var cleared = await uow.GetTable<Warning>()
|
||||
.Where(x => toClear.Contains(x.Id))
|
||||
.UpdateAsync(_ => new()
|
||||
{
|
||||
Forgiven = true,
|
||||
ForgivenBy = "expiry"
|
||||
});
|
||||
|
||||
var toDelete = await uow.GetTable<Warning>()
|
||||
.Where(x => uow.GetTable<GuildConfig>()
|
||||
.Count(y => y.GuildId == x.GuildId
|
||||
&& y.WarnExpireHours > 0
|
||||
&& y.WarnExpireAction == WarnExpireAction.Delete)
|
||||
> 0
|
||||
&& x.DateAdded
|
||||
< DateTime.UtcNow.AddHours(-uow.GetTable<GuildConfig>()
|
||||
.Where(y => x.GuildId == y.GuildId)
|
||||
.Select(y => y.WarnExpireHours)
|
||||
.First()))
|
||||
.Select(x => x.Id)
|
||||
.ToListAsyncLinqToDB();
|
||||
|
||||
var deleted = await uow.GetTable<Warning>()
|
||||
.Where(x => toDelete.Contains(x.Id))
|
||||
.DeleteAsync();
|
||||
|
||||
if (cleared > 0 || deleted > 0)
|
||||
{
|
||||
Log.Information("Cleared {ClearedWarnings} warnings and deleted {DeletedWarnings} warnings due to expiry",
|
||||
cleared,
|
||||
deleted);
|
||||
toDelete.Count);
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task CheckWarnExpiresAsync(ulong guildId)
|
||||
|
@@ -36,7 +36,7 @@ public static class NadekoExpressionExtensions
|
||||
var repCtx = new ReplacementContext(client: client,
|
||||
guild: (ctx.Channel as ITextChannel)?.Guild as SocketGuild,
|
||||
channel: ctx.Channel,
|
||||
users: ctx.Author
|
||||
user: ctx.Author
|
||||
)
|
||||
.WithOverride("%target%",
|
||||
() => canMentionEveryone
|
||||
|
@@ -58,14 +58,14 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task ExprAddServer(string key, [Leftover] string message)
|
||||
public async Task ExprAddServer(string trigger, [Leftover] string response)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
||||
if (string.IsNullOrWhiteSpace(response) || string.IsNullOrWhiteSpace(trigger))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ExprAddInternalAsync(key, message);
|
||||
await ExprAddInternalAsync(trigger, response);
|
||||
}
|
||||
|
||||
|
||||
@@ -402,10 +402,9 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[Ratelimit(300)]
|
||||
public async Task ExprsImport([Leftover] string input = null)
|
||||
{
|
||||
// todo cooldown on public bot for 1 day, limit 100
|
||||
|
||||
if (!AdminInGuildOrOwnerInDm())
|
||||
{
|
||||
await Response().Error(strs.expr_insuff_perms).SendAsync();
|
||||
|
@@ -27,17 +27,13 @@ public class GameStatusEvent : ICurrencyEvent
|
||||
|
||||
private readonly string _code;
|
||||
|
||||
private readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10)
|
||||
.Concat(Enumerable.Range(65, 26))
|
||||
.Concat(Enumerable.Range(97, 26))
|
||||
.Select(x => (char)x)
|
||||
.ToArray();
|
||||
|
||||
private readonly object _stopLock = new();
|
||||
|
||||
private readonly object _potLock = new();
|
||||
private readonly IMessageSenderService _sender;
|
||||
|
||||
private static readonly NadekoRandom _rng = new NadekoRandom();
|
||||
|
||||
public GameStatusEvent(
|
||||
DiscordSocketClient client,
|
||||
ICurrencyService cs,
|
||||
@@ -58,7 +54,8 @@ public class GameStatusEvent : ICurrencyEvent
|
||||
_opts = opt;
|
||||
_sender = sender;
|
||||
// generate code
|
||||
_code = new(_sneakyGameStatusChars.Shuffle().Take(5).ToArray());
|
||||
|
||||
_code = new kwum(_rng.Next(1_000_000, 10_000_000)).ToString();
|
||||
|
||||
_t = new(OnTimerTick, null, Timeout.InfiniteTimeSpan, TimeSpan.FromSeconds(2));
|
||||
if (_opts.Hours > 0)
|
||||
@@ -88,9 +85,9 @@ public class GameStatusEvent : ICurrencyEvent
|
||||
if (_isPotLimited)
|
||||
{
|
||||
await msg.ModifyAsync(m =>
|
||||
{
|
||||
m.Embed = GetEmbed(PotSize).Build();
|
||||
});
|
||||
{
|
||||
m.Embed = GetEmbed(PotSize).Build();
|
||||
});
|
||||
}
|
||||
|
||||
Log.Information("Game status event awarded {Count} users {Amount} currency.{Remaining}",
|
||||
|
@@ -11,7 +11,7 @@ namespace NadekoBot.Modules.Gambling.Common;
|
||||
public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
||||
{
|
||||
[Comment("""DO NOT CHANGE""")]
|
||||
public int Version { get; set; } = 2;
|
||||
public int Version { get; set; } = 8;
|
||||
|
||||
[Comment("""Currency settings""")]
|
||||
public CurrencyConfig Currency { get; set; }
|
||||
@@ -20,9 +20,9 @@ public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
||||
public int MinBet { get; set; } = 0;
|
||||
|
||||
[Comment("""
|
||||
Maximum amount users can bet
|
||||
Set 0 for unlimited
|
||||
""")]
|
||||
Maximum amount users can bet
|
||||
Set 0 for unlimited
|
||||
""")]
|
||||
public int MaxBet { get; set; } = 0;
|
||||
|
||||
[Comment("""Settings for betflip command""")]
|
||||
@@ -35,14 +35,14 @@ public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
||||
public GenerationConfig Generation { get; set; }
|
||||
|
||||
[Comment("""
|
||||
Settings for timely command
|
||||
(letting people claim X amount of currency every Y hours)
|
||||
""")]
|
||||
Settings for timely command
|
||||
(letting people claim X amount of currency every Y hours)
|
||||
""")]
|
||||
public TimelyConfig Timely { get; set; }
|
||||
|
||||
[Comment("""How much will each user's owned currency decay over time.""")]
|
||||
public DecayConfig Decay { get; set; }
|
||||
|
||||
|
||||
[Comment("""What is the bot's cut on some transactions""")]
|
||||
public BotCutConfig BotCuts { get; set; }
|
||||
|
||||
@@ -53,15 +53,15 @@ public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
||||
public WaifuConfig Waifu { get; set; }
|
||||
|
||||
[Comment("""
|
||||
Amount of currency selfhosters will get PER pledged dollar CENT.
|
||||
1 = 100 currency per $. Used almost exclusively on public nadeko.
|
||||
""")]
|
||||
Amount of currency selfhosters will get PER pledged dollar CENT.
|
||||
1 = 100 currency per $. Used almost exclusively on public nadeko.
|
||||
""")]
|
||||
public decimal PatreonCurrencyPerCent { get; set; } = 1;
|
||||
|
||||
[Comment("""
|
||||
Currency reward per vote.
|
||||
This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting
|
||||
""")]
|
||||
Currency reward per vote.
|
||||
This will work only if you've set up VotesApi and correct credentials for topgg and/or discords voting
|
||||
""")]
|
||||
public long VoteReward { get; set; } = 100;
|
||||
|
||||
[Comment("""Slot config""")]
|
||||
@@ -91,9 +91,9 @@ public class CurrencyConfig
|
||||
public string Name { get; set; } = "Nadeko Flower";
|
||||
|
||||
[Comment("""
|
||||
For how long (in days) will the transactions be kept in the database (curtrs)
|
||||
Set 0 to disable cleanup (keep transactions forever)
|
||||
""")]
|
||||
For how long (in days) will the transactions be kept in the database (curtrs)
|
||||
Set 0 to disable cleanup (keep transactions forever)
|
||||
""")]
|
||||
public int TransactionsLifetime { get; set; } = 0;
|
||||
}
|
||||
|
||||
@@ -101,15 +101,15 @@ public class CurrencyConfig
|
||||
public partial class TimelyConfig
|
||||
{
|
||||
[Comment("""
|
||||
How much currency will the users get every time they run .timely command
|
||||
setting to 0 or less will disable this feature
|
||||
""")]
|
||||
How much currency will the users get every time they run .timely command
|
||||
setting to 0 or less will disable this feature
|
||||
""")]
|
||||
public int Amount { get; set; } = 0;
|
||||
|
||||
[Comment("""
|
||||
How often (in hours) can users claim currency with .timely command
|
||||
setting to 0 or less will disable this feature
|
||||
""")]
|
||||
How often (in hours) can users claim currency with .timely command
|
||||
setting to 0 or less will disable this feature
|
||||
""")]
|
||||
public int Cooldown { get; set; } = 24;
|
||||
}
|
||||
|
||||
@@ -124,10 +124,10 @@ public partial class BetFlipConfig
|
||||
public partial class BetRollConfig
|
||||
{
|
||||
[Comment("""
|
||||
When betroll is played, user will roll a number 0-100.
|
||||
This setting will describe which multiplier is used for when the roll is higher than the given number.
|
||||
Doesn't have to be ordered.
|
||||
""")]
|
||||
When betroll is played, user will roll a number 0-100.
|
||||
This setting will describe which multiplier is used for when the roll is higher than the given number.
|
||||
Doesn't have to be ordered.
|
||||
""")]
|
||||
public BetRollPair[] Pairs { get; set; } = Array.Empty<BetRollPair>();
|
||||
|
||||
public BetRollConfig()
|
||||
@@ -155,17 +155,17 @@ public partial class BetRollConfig
|
||||
public partial class GenerationConfig
|
||||
{
|
||||
[Comment("""
|
||||
when currency is generated, should it also have a random password
|
||||
associated with it which users have to type after the .pick command
|
||||
in order to get it
|
||||
""")]
|
||||
when currency is generated, should it also have a random password
|
||||
associated with it which users have to type after the .pick command
|
||||
in order to get it
|
||||
""")]
|
||||
public bool HasPassword { get; set; } = true;
|
||||
|
||||
[Comment("""
|
||||
Every message sent has a certain % chance to generate the currency
|
||||
specify the percentage here (1 being 100%, 0 being 0% - for example
|
||||
default is 0.02, which is 2%
|
||||
""")]
|
||||
Every message sent has a certain % chance to generate the currency
|
||||
specify the percentage here (1 being 100%, 0 being 0% - for example
|
||||
default is 0.02, which is 2%
|
||||
""")]
|
||||
public decimal Chance { get; set; } = 0.02M;
|
||||
|
||||
[Comment("""How many seconds have to pass for the next message to have a chance to spawn currency""")]
|
||||
@@ -175,9 +175,9 @@ public partial class GenerationConfig
|
||||
public int MinAmount { get; set; } = 1;
|
||||
|
||||
[Comment("""
|
||||
Maximum amount of currency that can spawn.
|
||||
Set to the same value as MinAmount to always spawn the same amount
|
||||
""")]
|
||||
Maximum amount of currency that can spawn.
|
||||
Set to the same value as MinAmount to always spawn the same amount
|
||||
""")]
|
||||
public int MaxAmount { get; set; } = 1;
|
||||
}
|
||||
|
||||
@@ -185,9 +185,9 @@ public partial class GenerationConfig
|
||||
public partial class DecayConfig
|
||||
{
|
||||
[Comment("""
|
||||
Percentage of user's current currency which will be deducted every 24h.
|
||||
0 - 1 (1 is 100%, 0.5 50%, 0 disabled)
|
||||
""")]
|
||||
Percentage of user's current currency which will be deducted every 24h.
|
||||
0 - 1 (1 is 100%, 0.5 50%, 0 disabled)
|
||||
""")]
|
||||
public decimal Percent { get; set; } = 0;
|
||||
|
||||
[Comment("""Maximum amount of user's currency that can decay at each interval. 0 for unlimited.""")]
|
||||
@@ -219,15 +219,15 @@ public sealed partial class WaifuConfig
|
||||
public MultipliersData Multipliers { get; set; } = new();
|
||||
|
||||
[Comment("""
|
||||
Settings for periodic waifu price decay.
|
||||
Waifu price decays only if the waifu has no claimer.
|
||||
""")]
|
||||
Settings for periodic waifu price decay.
|
||||
Waifu price decays only if the waifu has no claimer.
|
||||
""")]
|
||||
public WaifuDecayConfig Decay { get; set; } = new();
|
||||
|
||||
[Comment("""
|
||||
List of items available for gifting.
|
||||
If negative is true, gift will instead reduce waifu value.
|
||||
""")]
|
||||
List of items available for gifting.
|
||||
If negative is true, gift will instead reduce waifu value.
|
||||
""")]
|
||||
public List<WaifuItemModel> Items { get; set; } = [];
|
||||
|
||||
public WaifuConfig()
|
||||
@@ -274,19 +274,26 @@ public sealed partial class WaifuConfig
|
||||
public class WaifuDecayConfig
|
||||
{
|
||||
[Comment("""
|
||||
Percentage (0 - 100) of the waifu value to reduce.
|
||||
Set 0 to disable
|
||||
For example if a waifu has a price of 500$, setting this value to 10 would reduce the waifu value by 10% (50$)
|
||||
""")]
|
||||
public int Percent { get; set; } = 0;
|
||||
Unclaimed waifus will decay by this percentage (0 - 100).
|
||||
Default is 0 (disabled)
|
||||
For example if a waifu has a price of 500$, setting this value to 10 would reduce the waifu value by 10% (50$)
|
||||
""")]
|
||||
public int UnclaimedDecayPercent { get; set; } = 0;
|
||||
|
||||
[Comment("""
|
||||
Claimed waifus will decay by this percentage (0 - 100).
|
||||
Default is 0 (disabled)
|
||||
For example if a waifu has a price of 500$, setting this value to 10 would reduce the waifu value by 10% (50$)
|
||||
""")]
|
||||
public int ClaimedDecayPercent { get; set; } = 0;
|
||||
|
||||
[Comment("""How often to decay waifu values, in hours""")]
|
||||
public int HourInterval { get; set; } = 24;
|
||||
|
||||
[Comment("""
|
||||
Minimum waifu price required for the decay to be applied.
|
||||
For example if this value is set to 300, any waifu with the price 300 or less will not experience decay.
|
||||
""")]
|
||||
Minimum waifu price required for the decay to be applied.
|
||||
For example if this value is set to 300, any waifu with the price 300 or less will not experience decay.
|
||||
""")]
|
||||
public long MinPrice { get; set; } = 300;
|
||||
}
|
||||
}
|
||||
@@ -295,54 +302,54 @@ public sealed partial class WaifuConfig
|
||||
public sealed partial class MultipliersData
|
||||
{
|
||||
[Comment("""
|
||||
Multiplier for waifureset. Default 150.
|
||||
Formula (at the time of writing this):
|
||||
price = (waifu_price * 1.25f) + ((number_of_divorces + changes_of_heart + 2) * WaifuReset) rounded up
|
||||
""")]
|
||||
Multiplier for waifureset. Default 150.
|
||||
Formula (at the time of writing this):
|
||||
price = (waifu_price * 1.25f) + ((number_of_divorces + changes_of_heart + 2) * WaifuReset) rounded up
|
||||
""")]
|
||||
public int WaifuReset { get; set; } = 150;
|
||||
|
||||
[Comment("""
|
||||
The minimum amount of currency that you have to pay
|
||||
in order to buy a waifu who doesn't have a crush on you.
|
||||
Default is 1.1
|
||||
Example: If a waifu is worth 100, you will have to pay at least 100 * NormalClaim currency to claim her.
|
||||
(100 * 1.1 = 110)
|
||||
""")]
|
||||
The minimum amount of currency that you have to pay
|
||||
in order to buy a waifu who doesn't have a crush on you.
|
||||
Default is 1.1
|
||||
Example: If a waifu is worth 100, you will have to pay at least 100 * NormalClaim currency to claim her.
|
||||
(100 * 1.1 = 110)
|
||||
""")]
|
||||
public decimal NormalClaim { get; set; } = 1.1m;
|
||||
|
||||
[Comment("""
|
||||
The minimum amount of currency that you have to pay
|
||||
in order to buy a waifu that has a crush on you.
|
||||
Default is 0.88
|
||||
Example: If a waifu is worth 100, you will have to pay at least 100 * CrushClaim currency to claim her.
|
||||
(100 * 0.88 = 88)
|
||||
""")]
|
||||
The minimum amount of currency that you have to pay
|
||||
in order to buy a waifu that has a crush on you.
|
||||
Default is 0.88
|
||||
Example: If a waifu is worth 100, you will have to pay at least 100 * CrushClaim currency to claim her.
|
||||
(100 * 0.88 = 88)
|
||||
""")]
|
||||
public decimal CrushClaim { get; set; } = 0.88M;
|
||||
|
||||
[Comment("""
|
||||
When divorcing a waifu, her new value will be her current value multiplied by this number.
|
||||
Default 0.75 (meaning will lose 25% of her value)
|
||||
""")]
|
||||
When divorcing a waifu, her new value will be her current value multiplied by this number.
|
||||
Default 0.75 (meaning will lose 25% of her value)
|
||||
""")]
|
||||
public decimal DivorceNewValue { get; set; } = 0.75M;
|
||||
|
||||
[Comment("""
|
||||
All gift prices will be multiplied by this number.
|
||||
Default 1 (meaning no effect)
|
||||
""")]
|
||||
All gift prices will be multiplied by this number.
|
||||
Default 1 (meaning no effect)
|
||||
""")]
|
||||
public decimal AllGiftPrices { get; set; } = 1.0M;
|
||||
|
||||
[Comment("""
|
||||
What percentage of the value of the gift will a waifu gain when she's gifted.
|
||||
Default 0.95 (meaning 95%)
|
||||
Example: If a waifu is worth 1000, and she receives a gift worth 100, her new value will be 1095)
|
||||
""")]
|
||||
What percentage of the value of the gift will a waifu gain when she's gifted.
|
||||
Default 0.95 (meaning 95%)
|
||||
Example: If a waifu is worth 1000, and she receives a gift worth 100, her new value will be 1095)
|
||||
""")]
|
||||
public decimal GiftEffect { get; set; } = 0.95M;
|
||||
|
||||
[Comment("""
|
||||
What percentage of the value of the gift will a waifu lose when she's gifted a gift marked as 'negative'.
|
||||
Default 0.5 (meaning 50%)
|
||||
Example: If a waifu is worth 1000, and she receives a negative gift worth 100, her new value will be 950)
|
||||
""")]
|
||||
What percentage of the value of the gift will a waifu lose when she's gifted a gift marked as 'negative'.
|
||||
Default 0.5 (meaning 50%)
|
||||
Example: If a waifu is worth 1000, and she receives a negative gift worth 100, her new value will be 950)
|
||||
""")]
|
||||
public decimal NegativeGiftEffect { get; set; } = 0.50M;
|
||||
}
|
||||
|
||||
|
@@ -174,7 +174,7 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
c.Version = 5;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (data.Version < 6)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
@@ -190,5 +190,14 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
||||
c.Version = 7;
|
||||
});
|
||||
}
|
||||
|
||||
if (data.Version < 8)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 8;
|
||||
c.Waifu.Decay.UnclaimedDecayPercent = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -48,7 +48,7 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
_rng = new();
|
||||
_client = client;
|
||||
_gss = gss;
|
||||
|
||||
|
||||
using var uow = db.GetDbContext();
|
||||
var guildIds = client.Guilds.Select(x => x.Id).ToList();
|
||||
var configs = uow.Set<GuildConfig>()
|
||||
@@ -111,13 +111,17 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
{
|
||||
var curImg = await _images.GetCurrencyImageAsync();
|
||||
|
||||
if (curImg is null)
|
||||
return (new MemoryStream(), null);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(pass))
|
||||
{
|
||||
// determine the extension
|
||||
using var load = _ = Image.Load(curImg, out var format);
|
||||
using var load = Image.Load(curImg);
|
||||
|
||||
var format = load.Metadata.DecodedImageFormat;
|
||||
// return the image
|
||||
return (curImg.ToStream(), format.FileExtensions.FirstOrDefault() ?? "png");
|
||||
return (curImg.ToStream(), format?.FileExtensions.FirstOrDefault() ?? "png");
|
||||
}
|
||||
|
||||
// get the image stream and extension
|
||||
@@ -134,16 +138,17 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
{
|
||||
// draw lower, it looks better
|
||||
pass = pass.TrimTo(10, true).ToLowerInvariant();
|
||||
using var img = Image.Load<Rgba32>(curImg, out var format);
|
||||
using var img = Image.Load<Rgba32>(curImg);
|
||||
// choose font size based on the image height, so that it's visible
|
||||
var font = _fonts.NotoSans.CreateFont(img.Height / 12.0f, FontStyle.Bold);
|
||||
img.Mutate(x =>
|
||||
{
|
||||
// measure the size of the text to be drawing
|
||||
var size = TextMeasurer.Measure(pass, new TextOptions(font)
|
||||
{
|
||||
Origin = new PointF(0, 0)
|
||||
});
|
||||
var size = TextMeasurer.MeasureSize(pass,
|
||||
new RichTextOptions(font)
|
||||
{
|
||||
Origin = new PointF(0, 0)
|
||||
});
|
||||
|
||||
// fill the background with black, add 5 pixels on each side to make it look better
|
||||
x.FillPolygon(Color.ParseHex("00000080"),
|
||||
@@ -156,7 +161,8 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
x.DrawText(pass, font, Color.White, new(0, 0));
|
||||
});
|
||||
// return image as a stream for easy sending
|
||||
return (img.ToStream(format), format.FileExtensions.FirstOrDefault() ?? "png");
|
||||
var format = img.Metadata.DecodedImageFormat;
|
||||
return (img.ToStream(format), format?.FileExtensions.FirstOrDefault() ?? "png");
|
||||
}
|
||||
|
||||
private Task PotentialFlowerGeneration(IUserMessage imsg)
|
||||
@@ -256,7 +262,8 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
|
||||
pass = pass?.Trim().TrimTo(10, true).ToUpperInvariant();
|
||||
// gets all plants in this channel with the same password
|
||||
var entries = uow.Set<PlantedCurrency>().AsQueryable()
|
||||
var entries = uow.Set<PlantedCurrency>()
|
||||
.AsQueryable()
|
||||
.Where(x => x.ChannelId == ch.Id && pass == x.Password)
|
||||
.ToList();
|
||||
// sum how much currency that is, and get all of the message ids (so that i can delete them)
|
||||
@@ -368,15 +375,16 @@ public class PlantPickService : INService, IExecNoCommand
|
||||
string pass)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
uow.Set<PlantedCurrency>().Add(new()
|
||||
{
|
||||
Amount = amount,
|
||||
GuildId = gid,
|
||||
ChannelId = cid,
|
||||
Password = pass,
|
||||
UserId = uid,
|
||||
MessageId = mid
|
||||
});
|
||||
uow.Set<PlantedCurrency>()
|
||||
.Add(new()
|
||||
{
|
||||
Amount = amount,
|
||||
GuildId = gid,
|
||||
ChannelId = cid,
|
||||
Password = pass,
|
||||
UserId = uid,
|
||||
MessageId = mid
|
||||
});
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
}
|
@@ -167,18 +167,19 @@ public partial class Gambling
|
||||
long ownedAmount;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
ownedAmount = uow.Set<DiscordUser>().FirstOrDefault(x => x.UserId == ctx.User.Id)?.CurrencyAmount
|
||||
ownedAmount = uow.Set<DiscordUser>()
|
||||
.FirstOrDefault(x => x.UserId == ctx.User.Id)?.CurrencyAmount
|
||||
?? 0;
|
||||
}
|
||||
|
||||
var slotBg = await _images.GetSlotBgAsync();
|
||||
var bgImage = Image.Load<Rgba32>(slotBg, out _);
|
||||
var bgImage = Image.Load<Rgba32>(slotBg);
|
||||
var numbers = new int[3];
|
||||
result.Rolls.CopyTo(numbers, 0);
|
||||
|
||||
Color fontColor = Config.Slots.CurrencyFontColor;
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new TextOptions(_fonts.DottyFont.CreateFont(65))
|
||||
bgImage.Mutate<Rgba32>(x => x.DrawText(new RichTextOptions(_fonts.DottyFont.CreateFont(65))
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
@@ -190,7 +191,7 @@ public partial class Gambling
|
||||
|
||||
var bottomFont = _fonts.DottyFont.CreateFont(50);
|
||||
|
||||
bgImage.Mutate(x => x.DrawText(new TextOptions(bottomFont)
|
||||
bgImage.Mutate(x => x.DrawText(new RichTextOptions(bottomFont)
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
|
@@ -70,7 +70,11 @@ public partial class Gambling
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = GetText(strs.waifu_claimed(Format.Bold(target.ToString()), N(amount)));
|
||||
var msg = GetText(strs.waifu_claimed(
|
||||
Format.Bold(ctx.User.ToString()),
|
||||
Format.Bold(target.ToString()),
|
||||
N(amount)));
|
||||
|
||||
if (w.Affinity?.UserId == ctx.User.Id)
|
||||
msg += "\n" + GetText(strs.waifu_fulfilled(target, N(w.Price)));
|
||||
else
|
||||
@@ -191,13 +195,21 @@ public partial class Gambling
|
||||
}
|
||||
|
||||
if (user is null)
|
||||
{
|
||||
await Response().Confirm(strs.waifu_affinity_reset).SendAsync();
|
||||
}
|
||||
else if (oldAff is null)
|
||||
await Response().Confirm(strs.waifu_affinity_set(Format.Bold(user.ToString()))).SendAsync();
|
||||
{
|
||||
await Response()
|
||||
.Confirm(strs.waifu_affinity_set(Format.Bold(ctx.User.ToString()), Format.Bold(user.ToString())))
|
||||
.SendAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await Response()
|
||||
.Confirm(strs.waifu_affinity_changed(Format.Bold(oldAff.ToString()),
|
||||
.Confirm(strs.waifu_affinity_changed(
|
||||
Format.Bold(ctx.User.ToString()),
|
||||
Format.Bold(oldAff.ToString()),
|
||||
Format.Bold(user.ToString())))
|
||||
.SendAsync();
|
||||
}
|
||||
|
@@ -531,11 +531,18 @@ public class WaifuService : INService, IReadyExecutor
|
||||
{
|
||||
try
|
||||
{
|
||||
var multi = _gss.Data.Waifu.Decay.Percent / 100f;
|
||||
var minPrice = _gss.Data.Waifu.Decay.MinPrice;
|
||||
var decayInterval = _gss.Data.Waifu.Decay.HourInterval;
|
||||
var decay = _gss.Data.Waifu.Decay;
|
||||
|
||||
if (multi is < 0f or > 1f || decayInterval < 0)
|
||||
var unclaimedMulti = 1 - (decay.UnclaimedDecayPercent / 100f);
|
||||
var claimedMulti = 1 - (decay.ClaimedDecayPercent / 100f);
|
||||
|
||||
var minPrice = decay.MinPrice;
|
||||
var decayInterval = decay.HourInterval;
|
||||
|
||||
if (decayInterval <= 0)
|
||||
continue;
|
||||
|
||||
if ((unclaimedMulti < 0 || unclaimedMulti > 1) && (claimedMulti < 0 || claimedMulti > 1))
|
||||
continue;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
@@ -554,14 +561,28 @@ public class WaifuService : INService, IReadyExecutor
|
||||
|
||||
await _cache.AddAsync(_waifuDecayKey, nowB);
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
if (unclaimedMulti is > 0 and <= 1)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
|
||||
await uow.GetTable<WaifuInfo>()
|
||||
.Where(x => x.Price > minPrice && x.ClaimerId == null)
|
||||
.UpdateAsync(old => new()
|
||||
{
|
||||
Price = (long)(old.Price * multi)
|
||||
});
|
||||
await uow.GetTable<WaifuInfo>()
|
||||
.Where(x => x.Price > minPrice && x.ClaimerId == null)
|
||||
.UpdateAsync(old => new()
|
||||
{
|
||||
Price = (long)(old.Price * unclaimedMulti)
|
||||
});
|
||||
}
|
||||
|
||||
if (claimedMulti is > 0 and <= 1)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
await uow.GetTable<WaifuInfo>()
|
||||
.Where(x => x.Price > minPrice && x.ClaimerId != null)
|
||||
.UpdateAsync(old => new()
|
||||
{
|
||||
Price = (long)(old.Price * claimedMulti)
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -63,6 +63,8 @@ public static class WaifuExtensions
|
||||
|
||||
public static async Task<WaifuInfoStats> GetWaifuInfoAsync(this DbContext ctx, ulong userId)
|
||||
{
|
||||
await ctx.EnsureUserCreatedAsync(userId);
|
||||
|
||||
await ctx.Set<WaifuInfo>()
|
||||
.ToLinqToDBTable()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
@@ -78,7 +80,8 @@ public static class WaifuExtensions
|
||||
WaifuId = ctx.Set<DiscordUser>().Where(x => x.UserId == userId).Select(x => x.Id).First()
|
||||
});
|
||||
|
||||
var toReturn = ctx.Set<WaifuInfo>().AsQueryable()
|
||||
var toReturn = ctx.Set<WaifuInfo>()
|
||||
.AsQueryable()
|
||||
.Where(w => w.WaifuId
|
||||
== ctx.Set<DiscordUser>()
|
||||
.AsQueryable()
|
||||
|
@@ -175,8 +175,6 @@ public sealed partial class Help : NadekoModule<HelpService>
|
||||
return strs.module_description_gambling;
|
||||
case "music":
|
||||
return strs.module_description_music;
|
||||
case "nsfw":
|
||||
return strs.module_description_nsfw;
|
||||
case "permissions":
|
||||
return strs.module_description_permissions;
|
||||
case "xp":
|
||||
@@ -211,8 +209,6 @@ public sealed partial class Help : NadekoModule<HelpService>
|
||||
return "💰";
|
||||
case "music":
|
||||
return "🎶";
|
||||
case "nsfw":
|
||||
return "😳";
|
||||
case "permissions":
|
||||
return "🚓";
|
||||
case "xp":
|
||||
|
@@ -31,7 +31,7 @@ public class HelpService : IExecNoCommand, INService
|
||||
return;
|
||||
}
|
||||
|
||||
var repCtx = new ReplacementContext(guild: guild, channel: msg.Channel, users: msg.Author)
|
||||
var repCtx = new ReplacementContext(guild: guild, channel: msg.Channel, user: msg.Author)
|
||||
.WithOverride("%prefix%", () => _bss.Data.Prefix)
|
||||
.WithOverride("%bot.prefix%", () => _bss.Data.Prefix);
|
||||
|
||||
|
@@ -136,7 +136,7 @@ public sealed partial class Music
|
||||
Provider = s.Platform.ToString(),
|
||||
ProviderType = (MusicType)s.Platform,
|
||||
Title = s.Title,
|
||||
Query = s.Platform == MusicPlatform.Local ? s.GetStreamUrl().Result!.Trim('"') : s.Url
|
||||
Query = s.Url
|
||||
})
|
||||
.ToList();
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Modules.Music.Resolvers;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Services;
|
||||
@@ -8,7 +9,7 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
||||
private readonly AyuVoiceStateService _voiceStateService;
|
||||
private readonly ITrackResolveProvider _trackResolveProvider;
|
||||
private readonly DbService _db;
|
||||
private readonly IYoutubeResolver _ytResolver;
|
||||
private readonly IYoutubeResolverFactory _ytResolver;
|
||||
private readonly ILocalTrackResolver _localResolver;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotStrings _strings;
|
||||
@@ -24,7 +25,7 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
||||
AyuVoiceStateService voiceStateService,
|
||||
ITrackResolveProvider trackResolveProvider,
|
||||
DbService db,
|
||||
IYoutubeResolver ytResolver,
|
||||
IYoutubeResolverFactory ytResolver,
|
||||
ILocalTrackResolver localResolver,
|
||||
DiscordSocketClient client,
|
||||
IBotStrings strings,
|
||||
@@ -93,7 +94,7 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
||||
public async Task<int> EnqueueYoutubePlaylistAsync(IMusicPlayer mp, string query, string queuer)
|
||||
{
|
||||
var count = 0;
|
||||
await foreach (var track in _ytResolver.ResolveTracksFromPlaylistAsync(query))
|
||||
await foreach (var track in _ytResolver.GetYoutubeResolver().ResolveTracksFromPlaylistAsync(query))
|
||||
{
|
||||
if (mp.IsKilled)
|
||||
break;
|
||||
@@ -139,6 +140,7 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
||||
|
||||
var mp = new MusicPlayer(queue,
|
||||
resolver,
|
||||
_ytResolver,
|
||||
proxy,
|
||||
_googleApiService,
|
||||
settings.QualityPreset,
|
||||
|
@@ -8,5 +8,4 @@ public interface ITrackInfo
|
||||
public string Thumbnail { get; }
|
||||
public TimeSpan Duration { get; }
|
||||
public MusicPlatform Platform { get; }
|
||||
public ValueTask<string?> GetStreamUrl();
|
||||
}
|
@@ -4,8 +4,8 @@ namespace NadekoBot.Modules.Music;
|
||||
|
||||
public interface IYoutubeResolver : IPlatformQueryResolver
|
||||
{
|
||||
public Regex YtVideoIdRegex { get; }
|
||||
public Task<ITrackInfo?> ResolveByIdAsync(string id);
|
||||
IAsyncEnumerable<ITrackInfo> ResolveTracksFromPlaylistAsync(string query);
|
||||
Task<ITrackInfo?> ResolveByQueryAsync(string query, bool tryExtractingId);
|
||||
Task<string?> GetStreamUrl(string query);
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using NadekoBot.Voice;
|
||||
using NadekoBot.Db.Models;
|
||||
using NadekoBot.Modules.Music.Resolvers;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -27,6 +28,7 @@ public sealed class MusicPlayer : IMusicPlayer
|
||||
|
||||
private readonly IMusicQueue _queue;
|
||||
private readonly ITrackResolveProvider _trackResolveProvider;
|
||||
private readonly IYoutubeResolverFactory _ytResolverFactory;
|
||||
private readonly IVoiceProxy _proxy;
|
||||
private readonly IGoogleApiService _googleApiService;
|
||||
private readonly ISongBuffer _songBuffer;
|
||||
@@ -41,6 +43,7 @@ public sealed class MusicPlayer : IMusicPlayer
|
||||
public MusicPlayer(
|
||||
IMusicQueue queue,
|
||||
ITrackResolveProvider trackResolveProvider,
|
||||
IYoutubeResolverFactory ytResolverFactory,
|
||||
IVoiceProxy proxy,
|
||||
IGoogleApiService googleApiService,
|
||||
QualityPreset qualityPreset,
|
||||
@@ -48,6 +51,7 @@ public sealed class MusicPlayer : IMusicPlayer
|
||||
{
|
||||
_queue = queue;
|
||||
_trackResolveProvider = trackResolveProvider;
|
||||
_ytResolverFactory = ytResolverFactory;
|
||||
_proxy = proxy;
|
||||
_googleApiService = googleApiService;
|
||||
AutoPlay = autoPlay;
|
||||
@@ -118,7 +122,7 @@ public sealed class MusicPlayer : IMusicPlayer
|
||||
// make sure song buffer is ready to be (re)used
|
||||
_songBuffer.Reset();
|
||||
|
||||
var streamUrl = await track.GetStreamUrl();
|
||||
var streamUrl = await GetStreamUrl(track);
|
||||
// start up the data source
|
||||
using var source = FfmpegTrackDataSource.CreateAsync(
|
||||
_vc.BitDepth,
|
||||
@@ -256,6 +260,7 @@ public sealed class MusicPlayer : IMusicPlayer
|
||||
IsStopped = true;
|
||||
Log.Error("Please install ffmpeg and make sure it's added to your "
|
||||
+ "PATH environment variable before trying again");
|
||||
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -264,6 +269,7 @@ public sealed class MusicPlayer : IMusicPlayer
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Unknown error in music loop: {ErrorMessage}", ex.Message);
|
||||
await Task.Delay(3_000);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -303,6 +309,14 @@ public sealed class MusicPlayer : IMusicPlayer
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> GetStreamUrl(IQueuedTrackInfo track)
|
||||
{
|
||||
if (track.TrackInfo is SimpleTrackInfo sti)
|
||||
return sti.StreamUrl;
|
||||
|
||||
return await _ytResolverFactory.GetYoutubeResolver().GetStreamUrl(track.TrackInfo.Id);
|
||||
}
|
||||
|
||||
private bool? CopyChunkToOutput(ISongBuffer sb, VoiceClient vc)
|
||||
{
|
||||
var data = sb.Read(vc.InputLength, out var length);
|
||||
|
@@ -28,9 +28,6 @@ public sealed partial class MusicQueue
|
||||
TrackInfo = trackInfo;
|
||||
Queuer = queuer;
|
||||
}
|
||||
|
||||
public ValueTask<string?> GetStreamUrl()
|
||||
=> TrackInfo.GetStreamUrl();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -6,11 +6,4 @@ public sealed record RemoteTrackInfo(
|
||||
string Url,
|
||||
string Thumbnail,
|
||||
TimeSpan Duration,
|
||||
MusicPlatform Platform,
|
||||
Func<Task<string?>> _streamFactory) : ITrackInfo
|
||||
{
|
||||
private readonly Func<Task<string?>> _streamFactory = _streamFactory;
|
||||
|
||||
public async ValueTask<string?> GetStreamUrl()
|
||||
=> await _streamFactory();
|
||||
}
|
||||
MusicPlatform Platform) : ITrackInfo;
|
@@ -24,7 +24,4 @@ public sealed class SimpleTrackInfo : ITrackInfo
|
||||
Platform = platform;
|
||||
StreamUrl = streamUrl;
|
||||
}
|
||||
|
||||
public ValueTask<string?> GetStreamUrl()
|
||||
=> new(StreamUrl);
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
namespace NadekoBot.Modules.Music;
|
||||
|
||||
public sealed class InvTrackInfo : ITrackInfo
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string Title { get; init; }
|
||||
public required string Url { get; init; }
|
||||
public required string Thumbnail { get; init; }
|
||||
public required TimeSpan Duration { get; init; }
|
||||
public required MusicPlatform Platform { get; init; }
|
||||
public required string? StreamUrl { get; init; }
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
using NadekoBot.Modules.Searches;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace NadekoBot.Modules.Music;
|
||||
|
||||
public sealed class InvidiousYoutubeResolver : IYoutubeResolver
|
||||
{
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly SearchesConfigService _sc;
|
||||
private readonly NadekoRandom _rng;
|
||||
|
||||
private string InvidiousApiUrl
|
||||
=> _sc.Data.InvidiousInstances[_rng.Next(0, _sc.Data.InvidiousInstances.Count)];
|
||||
|
||||
public InvidiousYoutubeResolver(IHttpClientFactory httpFactory, SearchesConfigService sc)
|
||||
{
|
||||
_rng = new NadekoRandom();
|
||||
_httpFactory = httpFactory;
|
||||
_sc = sc;
|
||||
}
|
||||
|
||||
public async Task<ITrackInfo?> ResolveByQueryAsync(string query)
|
||||
{
|
||||
using var http = _httpFactory.CreateClient();
|
||||
|
||||
var items = await http.GetFromJsonAsync<List<InvidiousSearchResponse>>(
|
||||
$"{InvidiousApiUrl}/api/v1/search"
|
||||
+ $"?q={query}"
|
||||
+ $"&type=video");
|
||||
|
||||
if (items is null || items.Count == 0)
|
||||
return null;
|
||||
|
||||
|
||||
var res = items.First();
|
||||
|
||||
return new InvTrackInfo()
|
||||
{
|
||||
Id = res.VideoId,
|
||||
Title = res.Title,
|
||||
Url = $"https://youtube.com/watch?v={res.VideoId}",
|
||||
Thumbnail = res.Thumbnails?.Select(x => x.Url).FirstOrDefault() ?? string.Empty,
|
||||
Duration = TimeSpan.FromSeconds(res.LengthSeconds),
|
||||
Platform = MusicPlatform.Youtube,
|
||||
StreamUrl = null,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ITrackInfo?> ResolveByIdAsync(string id)
|
||||
=> await InternalResolveByIdAsync(id);
|
||||
|
||||
private async Task<InvTrackInfo?> InternalResolveByIdAsync(string id)
|
||||
{
|
||||
using var http = _httpFactory.CreateClient();
|
||||
|
||||
var res = await http.GetFromJsonAsync<InvidiousVideoResponse>(
|
||||
$"{InvidiousApiUrl}/api/v1/videos/{id}");
|
||||
|
||||
if (res is null)
|
||||
return null;
|
||||
|
||||
return new InvTrackInfo()
|
||||
{
|
||||
Id = res.VideoId,
|
||||
Title = res.Title,
|
||||
Url = $"https://youtube.com/watch?v={res.VideoId}",
|
||||
Thumbnail = res.Thumbnails?.Select(x => x.Url).FirstOrDefault() ?? string.Empty,
|
||||
Duration = TimeSpan.FromSeconds(res.LengthSeconds),
|
||||
Platform = MusicPlatform.Youtube,
|
||||
StreamUrl = res.AdaptiveFormats.FirstOrDefault(x => x.AudioQuality == "AUDIO_QUALITY_HIGH")?.Url
|
||||
?? res.AdaptiveFormats.FirstOrDefault(x => x.AudioQuality == "AUDIO_QUALITY_MEDIUM")?.Url
|
||||
?? res.AdaptiveFormats.FirstOrDefault(x => x.AudioQuality == "AUDIO_QUALITY_LOW")?.Url
|
||||
};
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<ITrackInfo> ResolveTracksFromPlaylistAsync(string query)
|
||||
{
|
||||
using var http = _httpFactory.CreateClient();
|
||||
var res = await http.GetFromJsonAsync<InvidiousPlaylistResponse>(
|
||||
$"{InvidiousApiUrl}/api/v1/search?type=video&q={query}");
|
||||
|
||||
if (res is null)
|
||||
yield break;
|
||||
|
||||
foreach (var video in res.Videos)
|
||||
{
|
||||
yield return new InvTrackInfo()
|
||||
{
|
||||
Id = video.VideoId,
|
||||
Title = video.Title,
|
||||
Url = $"https://youtube.com/watch?v={video.VideoId}",
|
||||
Thumbnail = video.Thumbnails?.Select(x => x.Url).FirstOrDefault() ?? string.Empty,
|
||||
Duration = TimeSpan.FromSeconds(video.LengthSeconds),
|
||||
Platform = MusicPlatform.Youtube,
|
||||
StreamUrl = null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public Task<ITrackInfo?> ResolveByQueryAsync(string query, bool tryExtractingId)
|
||||
=> ResolveByQueryAsync(query);
|
||||
|
||||
public async Task<string?> GetStreamUrl(string videoId)
|
||||
{
|
||||
var video = await InternalResolveByIdAsync(videoId);
|
||||
return video?.StreamUrl;
|
||||
}
|
||||
}
|
@@ -1,13 +1,15 @@
|
||||
namespace NadekoBot.Modules.Music;
|
||||
using NadekoBot.Modules.Music.Resolvers;
|
||||
|
||||
namespace NadekoBot.Modules.Music;
|
||||
|
||||
public sealed class TrackResolveProvider : ITrackResolveProvider
|
||||
{
|
||||
private readonly IYoutubeResolver _ytResolver;
|
||||
private readonly IYoutubeResolverFactory _ytResolver;
|
||||
private readonly ILocalTrackResolver _localResolver;
|
||||
private readonly IRadioResolver _radioResolver;
|
||||
|
||||
public TrackResolveProvider(
|
||||
IYoutubeResolver ytResolver,
|
||||
IYoutubeResolverFactory ytResolver,
|
||||
ILocalTrackResolver localResolver,
|
||||
IRadioResolver radioResolver)
|
||||
{
|
||||
@@ -23,19 +25,22 @@ public sealed class TrackResolveProvider : ITrackResolveProvider
|
||||
case MusicPlatform.Radio:
|
||||
return _radioResolver.ResolveByQueryAsync(query);
|
||||
case MusicPlatform.Youtube:
|
||||
return _ytResolver.ResolveByQueryAsync(query);
|
||||
return _ytResolver.GetYoutubeResolver().ResolveByQueryAsync(query);
|
||||
case MusicPlatform.Local:
|
||||
return _localResolver.ResolveByQueryAsync(query);
|
||||
case null:
|
||||
var match = _ytResolver.YtVideoIdRegex.Match(query);
|
||||
var match = YoutubeHelpers.YtVideoIdRegex.Match(query);
|
||||
|
||||
if (match.Success)
|
||||
return _ytResolver.ResolveByIdAsync(match.Groups["id"].Value);
|
||||
else if (Uri.TryCreate(query, UriKind.Absolute, out var uri) && uri.IsFile)
|
||||
return _ytResolver.GetYoutubeResolver().ResolveByIdAsync(match.Groups["id"].Value);
|
||||
|
||||
if (Uri.TryCreate(query, UriKind.Absolute, out var uri) && uri.IsFile)
|
||||
return _localResolver.ResolveByQueryAsync(uri.AbsolutePath);
|
||||
else if (IsRadioLink(query))
|
||||
|
||||
if (IsRadioLink(query))
|
||||
return _radioResolver.ResolveByQueryAsync(query);
|
||||
else
|
||||
return _ytResolver.ResolveByQueryAsync(query, false);
|
||||
|
||||
return _ytResolver.GetYoutubeResolver().ResolveByQueryAsync(query, false);
|
||||
default:
|
||||
Log.Error("Unsupported platform: {MusicPlatform}", forcePlatform);
|
||||
return Task.FromResult<ITrackInfo?>(null);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user