1
Fork 0
mirror of https://gitlab.com/Kwoth/nadekobot.git synced 2024-10-02 20:13:13 +00:00

Merge branch 'greet-rework' into 'v5'

Greet reworked under the hood

See merge request Kwoth/nadekobot!333
This commit is contained in:
Kwoth 2024-09-15 22:44:37 +00:00
commit e4202b33f5
86 changed files with 4899 additions and 82742 deletions

View file

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

View file

@ -1,5 +1,6 @@
#nullable disable #nullable disable
using DryIoc; using DryIoc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using NadekoBot.Common.Configs; using NadekoBot.Common.Configs;
@ -92,14 +93,14 @@ public sealed class Bot : IBot
private async Task AddServices() private async Task AddServices()
{ {
var startingGuildIdList = GetCurrentGuildIds(); var startingGuildIdList = GetCurrentGuildIds().ToList();
var startTime = Stopwatch.GetTimestamp(); var startTime = Stopwatch.GetTimestamp();
var bot = Client.CurrentUser; var bot = Client.CurrentUser;
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
uow.EnsureUserCreated(bot.Id, bot.Username, bot.Discriminator, bot.AvatarId);
AllGuildConfigs = await uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList); AllGuildConfigs = await uow.GuildConfigs.GetAllGuildConfigs(startingGuildIdList);
uow.EnsureUserCreated(bot.Id, bot.Username, bot.Discriminator, bot.AvatarId);
} }
// var svcs = new StandardKernel(new NinjectSettings() // var svcs = new StandardKernel(new NinjectSettings()
@ -161,7 +162,8 @@ public sealed class Bot : IBot
LoadTypeReaders(a); 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) private void LoadTypeReaders(Assembly assembly)
@ -273,7 +275,9 @@ public sealed class Bot : IBot
Helpers.ReadErrorAndExit(9); 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>(); var commandHandler = Services.GetRequiredService<CommandHandler>();
// start handling messages received in commandhandler // start handling messages received in commandhandler

View file

@ -50,16 +50,21 @@ public static class GuildConfigExtensions
.Include(gc => gc.StreamRole) .Include(gc => gc.StreamRole)
.Include(gc => gc.DelMsgOnCmdChannels) .Include(gc => gc.DelMsgOnCmdChannels)
.Include(gc => gc.XpSettings) .Include(gc => gc.XpSettings)
.ThenInclude(x => x.ExclusionList) .ThenInclude(x => x.ExclusionList);
;
public static Task<GuildConfig[]> GetAllGuildConfigs( public static async Task<GuildConfig[]> GetAllGuildConfigs(
this DbSet<GuildConfig> configs, this DbSet<GuildConfig> configs,
IReadOnlyList<ulong> availableGuilds) List<ulong> availableGuilds)
=> configs.IncludeEverything() {
var result = await configs
.AsQueryable()
.Include(x => x.CommandCooldowns)
.Where(x => availableGuilds.Contains(x.GuildId)) .Where(x => availableGuilds.Contains(x.GuildId))
.AsNoTracking() .AsNoTracking()
.ToArrayAsyncEF(); .ToArrayAsync();
return result;
}
/// <summary> /// <summary>
/// Gets and creates if it doesn't exist a config for a guild. /// Gets and creates if it doesn't exist a config for a guild.
@ -85,7 +90,8 @@ public static class GuildConfigExtensions
if (config is null) if (config is null)
{ {
ctx.Set<GuildConfig>().Add(config = new() ctx.Set<GuildConfig>()
.Add(config = new()
{ {
GuildId = guildId, GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist, Permissions = Permissionv2.GetDefaultPermlist,
@ -154,14 +160,16 @@ public static class GuildConfigExtensions
public static GuildConfig GcWithPermissionsFor(this DbContext ctx, ulong guildId) public static GuildConfig GcWithPermissionsFor(this DbContext ctx, ulong guildId)
{ {
var config = ctx.Set<GuildConfig>().AsQueryable() var config = ctx.Set<GuildConfig>()
.AsQueryable()
.Where(gc => gc.GuildId == guildId) .Where(gc => gc.GuildId == guildId)
.Include(gc => gc.Permissions) .Include(gc => gc.Permissions)
.FirstOrDefault(); .FirstOrDefault();
if (config is null) // if there is no guildconfig, create new one if (config is null) // if there is no guildconfig, create new one
{ {
ctx.Set<GuildConfig>().Add(config = new() ctx.Set<GuildConfig>()
.Add(config = new()
{ {
GuildId = guildId, GuildId = guildId,
Permissions = Permissionv2.GetDefaultPermlist Permissions = Permissionv2.GetDefaultPermlist

View file

@ -13,21 +13,23 @@ public class GuildConfig : DbEntity
public string AutoAssignRoleIds { get; set; } public string AutoAssignRoleIds { get; set; }
//greet stuff // //greet stuff
public int AutoDeleteGreetMessagesTimer { get; set; } = 30; // public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
public int AutoDeleteByeMessagesTimer { get; set; } = 30; // public int AutoDeleteByeMessagesTimer { get; set; } = 30;
//
public ulong GreetMessageChannelId { get; set; } // public ulong GreetMessageChannelId { get; set; }
public ulong ByeMessageChannelId { get; set; } // public ulong ByeMessageChannelId { get; set; }
//
public bool SendDmGreetMessage { get; set; } // public bool SendDmGreetMessage { get; set; }
public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!"; // public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
//
public bool SendChannelGreetMessage { get; set; } // public bool SendChannelGreetMessage { get; set; }
public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!"; // public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
//
public bool SendChannelByeMessage { get; set; } // public bool SendChannelByeMessage { get; set; }
public string ChannelByeMessageText { get; set; } = "%user% has left!"; // public string ChannelByeMessageText { get; set; } = "%user% has left!";
// public bool SendBoostMessage { get; set; }
// pulic int BoostMessageDeleteAfter { get; set; }
//self assignable roles //self assignable roles
public bool ExclusiveSelfAssignedRoles { get; set; } public bool ExclusiveSelfAssignedRoles { get; set; }
@ -98,10 +100,6 @@ public class GuildConfig : DbEntity
#region Boost Message #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; } public bool StickyRoles { get; set; }
#endregion #endregion

View file

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

View file

@ -10,6 +10,7 @@ namespace NadekoBot.Db;
public abstract class NadekoContext : DbContext public abstract class NadekoContext : DbContext
{ {
public DbSet<GuildConfig> GuildConfigs { get; set; } public DbSet<GuildConfig> GuildConfigs { get; set; }
public DbSet<GreetSettings> GreetSettings { get; set; }
public DbSet<Quote> Quotes { get; set; } public DbSet<Quote> Quotes { get; set; }
public DbSet<Reminder> Reminders { get; set; } public DbSet<Reminder> Reminders { get; set; }
@ -678,6 +679,29 @@ public abstract class NadekoContext : DbContext
.OnDelete(DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
#endregion #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 #if DEBUG

View file

@ -44,8 +44,6 @@ public sealed class NadekoDbService : DbService
case "postgres": case "postgres":
case "pgsql": case "pgsql":
return new PostgreSqlContext(connString); return new PostgreSqlContext(connString);
case "mysql":
return new MysqlContext(connString);
case "sqlite": case "sqlite":
return new SqliteContext(connString); return new SqliteContext(connString);
default: default:

View file

@ -7,16 +7,7 @@ public static class MigrationQueries
{ {
public static void MigrateRero(MigrationBuilder migrationBuilder) public static void MigrateRero(MigrationBuilder migrationBuilder)
{ {
if (migrationBuilder.IsMySql()) if (migrationBuilder.IsSqlite())
{
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())
{ {
migrationBuilder.Sql( migrationBuilder.Sql(
@"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded) @"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()) 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 select guildid, channelid, messageid, emotename, roleid, exclusive::int, 0, reactionrolemessage.dateadded
from reactionrole from reactionrole
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
@ -43,15 +35,34 @@ left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;")
public static void GuildConfigCleanup(MigrationBuilder builder) public static void GuildConfigCleanup(MigrationBuilder builder)
{ {
builder.Sql($""" builder.Sql($"""
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL;
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs");
DELETE FROM "StreamRoleBlacklistedUser" WHERE "StreamRoleSettingsId" is NULL; DELETE FROM "StreamRoleBlacklistedUser" WHERE "StreamRoleSettingsId" is NULL;
"""); """);
}
builder.Sql($""" public static void GreetSettingsCopy(MigrationBuilder builder)
DELETE FROM "DelMsgOnCmdChannel" WHERE "GuildConfigId" is NULL; {
""");
builder.Sql(""" builder.Sql("""
DELETE FROM "WarningPunishment" WHERE "GuildConfigId" NOT IN (SELECT "Id" from "GuildConfigs"); 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

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

View file

@ -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

View file

@ -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

View file

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

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

View file

@ -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

View file

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

View file

@ -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

View file

@ -9,12 +9,6 @@ namespace NadekoBot.Migrations.PostgreSql
{ {
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.AddColumn<decimal>(
name: "rolerequirement",
table: "shopentry",
type: "numeric(20,0)",
nullable: true);
migrationBuilder.CreateTable( migrationBuilder.CreateTable(
name: "autopublishchannel", name: "autopublishchannel",
columns: table => new columns: table => new
@ -41,10 +35,6 @@ namespace NadekoBot.Migrations.PostgreSql
{ {
migrationBuilder.DropTable( migrationBuilder.DropTable(
name: "autopublishchannel"); name: "autopublishchannel");
migrationBuilder.DropColumn(
name: "rolerequirement",
table: "shopentry");
} }
} }
} }

View file

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

View file

@ -17,7 +17,7 @@ namespace NadekoBot.Migrations.PostgreSql
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "8.0.4") .HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63); .HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -1220,42 +1220,10 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("autoassignroleids"); .HasColumnName("autoassignroleids");
b.Property<int>("AutoDeleteByeMessagesTimer")
.HasColumnType("integer")
.HasColumnName("autodeletebyemessagestimer");
b.Property<int>("AutoDeleteGreetMessagesTimer")
.HasColumnType("integer")
.HasColumnName("autodeletegreetmessagestimer");
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages") b.Property<bool>("AutoDeleteSelfAssignedRoleMessages")
.HasColumnType("boolean") .HasColumnType("boolean")
.HasColumnName("autodeleteselfassignedrolemessages"); .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") b.Property<bool>("CleverbotEnabled")
.HasColumnType("boolean") .HasColumnType("boolean")
.HasColumnName("cleverbotenabled"); .HasColumnName("cleverbotenabled");
@ -1276,10 +1244,6 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("boolean") .HasColumnType("boolean")
.HasColumnName("disableglobalexpressions"); .HasColumnName("disableglobalexpressions");
b.Property<string>("DmGreetMessageText")
.HasColumnType("text")
.HasColumnName("dmgreetmessagetext");
b.Property<bool>("ExclusiveSelfAssignedRoles") b.Property<bool>("ExclusiveSelfAssignedRoles")
.HasColumnType("boolean") .HasColumnType("boolean")
.HasColumnName("exclusiveselfassignedroles"); .HasColumnName("exclusiveselfassignedroles");
@ -1300,10 +1264,6 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("numeric(20,0)") .HasColumnType("numeric(20,0)")
.HasColumnName("gamevoicechannel"); .HasColumnName("gamevoicechannel");
b.Property<decimal>("GreetMessageChannelId")
.HasColumnType("numeric(20,0)")
.HasColumnName("greetmessagechannelid");
b.Property<decimal>("GuildId") b.Property<decimal>("GuildId")
.HasColumnType("numeric(20,0)") .HasColumnType("numeric(20,0)")
.HasColumnName("guildid"); .HasColumnName("guildid");
@ -1328,22 +1288,6 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("text") .HasColumnType("text")
.HasColumnName("prefix"); .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") b.Property<bool>("StickyRoles")
.HasColumnType("boolean") .HasColumnType("boolean")
.HasColumnName("stickyroles"); .HasColumnName("stickyroles");
@ -3163,6 +3107,53 @@ namespace NadekoBot.Migrations.PostgreSql
b.ToTable("xpshopowneditem", (string)null); 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 => modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
{ {
b.HasOne("NadekoBot.Db.Models.GuildConfig", null) b.HasOne("NadekoBot.Db.Models.GuildConfig", null)

File diff suppressed because it is too large Load diff

View 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);
}
}
}

View file

@ -15,7 +15,7 @@ namespace NadekoBot.Migrations
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.4"); modelBuilder.HasAnnotation("ProductVersion", "8.0.8");
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b => modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
{ {
@ -907,33 +907,9 @@ namespace NadekoBot.Migrations
b.Property<string>("AutoAssignRoleIds") b.Property<string>("AutoAssignRoleIds")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("AutoDeleteByeMessagesTimer")
.HasColumnType("INTEGER");
b.Property<int>("AutoDeleteGreetMessagesTimer")
.HasColumnType("INTEGER");
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages") b.Property<bool>("AutoDeleteSelfAssignedRoleMessages")
.HasColumnType("INTEGER"); .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") b.Property<bool>("CleverbotEnabled")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -949,9 +925,6 @@ namespace NadekoBot.Migrations
b.Property<bool>("DisableGlobalExpressions") b.Property<bool>("DisableGlobalExpressions")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("DmGreetMessageText")
.HasColumnType("TEXT");
b.Property<bool>("ExclusiveSelfAssignedRoles") b.Property<bool>("ExclusiveSelfAssignedRoles")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -967,9 +940,6 @@ namespace NadekoBot.Migrations
b.Property<ulong?>("GameVoiceChannel") b.Property<ulong?>("GameVoiceChannel")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<ulong>("GreetMessageChannelId")
.HasColumnType("INTEGER");
b.Property<ulong>("GuildId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -988,18 +958,6 @@ namespace NadekoBot.Migrations
b.Property<string>("Prefix") b.Property<string>("Prefix")
.HasColumnType("TEXT"); .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") b.Property<bool>("StickyRoles")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -2351,6 +2309,42 @@ namespace NadekoBot.Migrations
b.ToTable("XpShopOwnedItem"); 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 => modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
{ {
b.HasOne("NadekoBot.Db.Models.GuildConfig", null) b.HasOne("NadekoBot.Db.Models.GuildConfig", null)

View file

@ -46,7 +46,7 @@ public partial class Administration
[Cmd] [Cmd]
[OwnerOnly] [OwnerOnly]
public async Task LeaveUnkeptServers(int startShardId) public async Task LeaveUnkeptServers(int startShardId, int shardMultiplier = 3000)
{ {
var keptGuildCount = await _svc.GetKeptGuildCount(); var keptGuildCount = await _svc.GetKeptGuildCount();
@ -65,7 +65,7 @@ public partial class Administration
for (var shardId = startShardId; shardId < _creds.GetCreds().TotalShards; shardId++) for (var shardId = startShardId; shardId < _creds.GetCreds().TotalShards; shardId++)
{ {
await _svc.StartLeavingUnkeptServers(shardId); await _svc.StartLeavingUnkeptServers(shardId);
await Task.Delay(3000 * 1000); await Task.Delay(shardMultiplier * 1000);
} }
await ctx.OkAsync(); await ctx.OkAsync();

View file

@ -72,10 +72,10 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
dontDelete = dontDeleteList.ToHashSet(); dontDelete = dontDeleteList.ToHashSet();
} }
Log.Information("Leaving {RemainingCount} guilds every {Delay} seconds, {DontDeleteCount} will remain", Log.Information("Leaving {RemainingCount} guilds, 1 every second. {DontDeleteCount} will remain",
allGuildIds.Length - dontDelete.Count, allGuildIds.Length - dontDelete.Count,
shardId,
dontDelete.Count); dontDelete.Count);
foreach (var guildId in allGuildIds) foreach (var guildId in allGuildIds)
{ {
if (dontDelete.Contains(guildId)) if (dontDelete.Contains(guildId))

View file

@ -8,235 +8,218 @@ public partial class Administration
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)] [UserPerm(GuildPerm.ManageGuild)]
public async Task Boost() public Task Boost()
{ => Toggle(GreetType.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();
}
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)] [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) if (timer is < 0 or > 600)
return; return;
await _service.SetBoostDel(ctx.Guild.Id, timer); await _service.SetDeleteTimer(ctx.Guild.Id, type, timer);
if (timer > 0) 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 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)] public async Task SetMsg(GreetType type, string? text = null)
[UserPerm(GuildPerm.ManageGuild)]
public async Task BoostMsg([Leftover] string? text = null)
{ {
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
{ {
var boostMessage = _service.GetBoostMessage(ctx.Guild.Id); var conf = await _service.GetGreetSettingsAsync(ctx.Guild.Id, type);
await Response().Confirm(strs.boostmsg_cur(boostMessage?.SanitizeMentions())).SendAsync(); 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; 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(); await Response()
if (!sendBoostEnabled) .Confirm(type switch
await Response().Pending(strs.boostmsg_enable($"`{prefix}boost`")).SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task GreetDel(int timer = 30)
{ {
if (timer is < 0 or > 600) GreetType.Boost => strs.boostmsg_new,
return; GreetType.Greet => strs.greetmsg_new,
GreetType.Bye => strs.byemsg_new,
GreetType.GreetDm => strs.greetdmmsg_new,
_ => strs.error
})
.SendAsync();
await _service.SetGreetDel(ctx.Guild.Id, timer);
if (timer > 0) if (!isEnabled)
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); var cmdName = type switch
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)) GreetType.Greet => "greet",
{ GreetType.Bye => "bye",
var greetMsg = _service.GetGreetMsg(ctx.Guild.Id); GreetType.Boost => "boost",
await Response().Confirm(strs.greetmsg_cur(greetMsg?.SanitizeMentions())).SendAsync(); GreetType.GreetDm => "greetdm",
return; _ => "unknown_command"
};
await Response().Pending(strs.boostmsg_enable($"`{prefix}{cmdName}`")).SendAsync();
}
} }
var sendGreetEnabled = _service.SetGreetMessage(ctx.Guild.Id, ref text); public async Task Test(GreetType type, IGuildUser? user = null)
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()
{
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 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();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task Bye()
{
var enabled = await _service.SetBye(ctx.Guild.Id, ctx.Channel.Id);
if (enabled)
await Response().Confirm(strs.bye_on).SendAsync();
else
await Response().Confirm(strs.bye_off).SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageGuild)]
public async Task ByeMsg([Leftover] string? text = null)
{
if (string.IsNullOrWhiteSpace(text))
{
var byeMsg = _service.GetByeMessage(ctx.Guild.Id);
await Response().Confirm(strs.byemsg_cur(byeMsg?.SanitizeMentions())).SendAsync();
return;
}
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; user ??= (IGuildUser)ctx.User;
await _service.ByeTest((ITextChannel)ctx.Channel, user); await _service.Test(ctx.Guild.Id, type, (ITextChannel)ctx.Channel, user);
var enabled = _service.GetByeEnabled(ctx.Guild.Id); var conf = await _service.GetGreetSettingsAsync(ctx.Guild.Id, type);
if (!enabled) if (conf?.IsEnabled is not true)
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(); await Response().Pending(strs.boostmsg_enable($"`{prefix}boost`")).SendAsync();
} }
} }

View file

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

View file

@ -1,58 +1,85 @@
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using LinqToDB.Tools;
using NadekoBot.Common.ModuleBehaviors; using NadekoBot.Common.ModuleBehaviors;
using NadekoBot.Db.Models;
using System.Threading.Channels; using System.Threading.Channels;
namespace NadekoBot.Services; namespace NadekoBot.Services;
public class GreetService : INService, IReadyExecutor public class GreetService : INService, IReadyExecutor
{ {
public bool GroupGreets
=> _bss.Data.GroupGreets;
private readonly DbService _db; private readonly DbService _db;
private readonly ConcurrentDictionary<ulong, GreetSettings> _guildConfigsCache; private ConcurrentDictionary<GreetType, ConcurrentHashSet<ulong>> _enabled = new();
private readonly DiscordSocketClient _client; 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 IReplacementService _repSvc;
private readonly IBotCache _cache;
private readonly IMessageSenderService _sender; private readonly IMessageSenderService _sender;
private readonly Channel<(GreetSettings, IUser, ITextChannel?)> _greetQueue =
Channel.CreateBounded<(GreetSettings, IUser, ITextChannel?)>(
new BoundedChannelOptions(60)
{
FullMode = BoundedChannelFullMode.DropOldest
});
public GreetService( public GreetService(
DiscordSocketClient client, DiscordSocketClient client,
IBot bot,
DbService db, DbService db,
BotConfigService bss,
IMessageSenderService sender, IMessageSenderService sender,
IReplacementService repSvc) IReplacementService repSvc,
IBotCache cache
)
{ {
_db = db; _db = db;
_client = client; _client = client;
_bss = bss;
_repSvc = repSvc; _repSvc = repSvc;
_cache = cache;
_sender = sender; _sender = sender;
_guildConfigsCache = new(bot.AllGuildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
_client.UserJoined += OnUserJoined; foreach (var type in Enum.GetValues<GreetType>())
_client.UserLeft += OnUserLeft; {
_enabled[type] = new();
bot.JoinedGuild += OnBotJoinedGuild; }
_client.LeftGuild += OnClientLeftGuild;
_client.GuildMemberUpdated += ClientOnGuildMemberUpdated;
} }
public async Task OnReadyAsync() public async Task OnReadyAsync()
{ {
while (true) // cache all enabled guilds
await using (var uow = _db.GetDbContext())
{ {
var (conf, user, compl) = await _greetDmQueue.Reader.ReadAsync(); var guilds = _client.Guilds.Select(x => x.Id).ToList();
var res = await GreetDmUserInternal(conf, user); var enabled = await uow.GetTable<GreetSettings>()
compl.TrySetResult(res); .Where(x => x.GuildId.In(guilds))
await Task.Delay(2000); .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;
var timer = new PeriodicTimer(TimeSpan.FromSeconds(2));
while (await timer.WaitForNextTickAsync())
{
var (conf, user, ch) = await _greetQueue.Reader.ReadAsync();
await GreetUsers(conf, ch, user);
} }
} }
@ -65,61 +92,38 @@ public class GreetService : INService, IReadyExecutor
&& newUser.PremiumSince is { } newDate && newUser.PremiumSince is { } newDate
&& newDate > oldDate)) && newDate > oldDate))
{ {
var conf = GetOrAddSettingsForGuild(newUser.Guild.Id); _ = Task.Run(async () =>
if (!conf.SendBoostMessage)
return Task.CompletedTask;
_ = Task.Run(TriggerBoostMessage(conf, newUser));
}
return Task.CompletedTask;
}
private Func<Task> TriggerBoostMessage(GreetSettings conf, SocketGuildUser user)
=> async () =>
{ {
var channel = user.Guild.GetTextChannel(conf.BoostMessageChannelId); var conf = await GetGreetSettingsAsync(newUser.Guild.Id, GreetType.Boost);
if (conf is null || !conf.IsEnabled)
return;
ITextChannel? channel = null;
if (conf.ChannelId is { } cid)
channel = newUser.Guild.GetTextChannel(cid);
if (channel is null) if (channel is null)
return; return;
await SendBoostMessage(conf, user, channel); await GreetUsers(conf, channel, newUser);
}; });
private async Task<bool> SendBoostMessage(GreetSettings conf, IGuildUser user, ITextChannel channel)
{
if (string.IsNullOrWhiteSpace(conf.BoostMessage))
return false;
var toSend = SmartText.CreateFrom(conf.BoostMessage);
try
{
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");
} }
return false;
}
private Task OnClientLeftGuild(SocketGuild arg)
{
_guildConfigsCache.TryRemove(arg.Id, out _);
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task OnBotJoinedGuild(GuildConfig gc) private async Task OnClientLeftGuild(SocketGuild guild)
{ {
_guildConfigsCache[gc.GuildId] = GreetSettings.Create(gc); foreach (var gt in Enum.GetValues<GreetType>())
return Task.CompletedTask; {
_enabled[gt].TryRemove(guild.Id);
}
await using var uow = _db.GetDbContext();
await uow.GetTable<GreetSettings>()
.Where(x => x.GuildId == guild.Id)
.DeleteAsync();
} }
private Task OnUserLeft(SocketGuild guild, SocketUser user) private Task OnUserLeft(SocketGuild guild, SocketUser user)
@ -128,35 +132,20 @@ public class GreetService : INService, IReadyExecutor
{ {
try try
{ {
var conf = GetOrAddSettingsForGuild(guild.Id); var conf = await GetGreetSettingsAsync(guild.Id, GreetType.Bye);
if (!conf.SendChannelByeMessage) if (conf is null)
return; 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 if (channel is null) //maybe warn the server owner that the channel is missing
{
await SetGreet(guild.Id, null, GreetType.Bye, false);
return; return;
}
if (GroupGreets) await _greetQueue.Writer.WriteAsync((conf, user, channel));
{
// 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);
}
}
}
else
await ByeUsers(conf, channel, new[] { user });
} }
catch catch
{ {
@ -166,98 +155,61 @@ public class GreetService : INService, IReadyExecutor
return Task.CompletedTask; return Task.CompletedTask;
} }
public string? GetDmGreetMsg(ulong id) private readonly TypedKey<GreetSettings?> _greetSettingsKey = new("greet_settings");
public async Task<GreetSettings?> GetGreetSettingsAsync(ulong gid, GreetType type)
=> await _cache.GetOrAddAsync<GreetSettings?>(_greetSettingsKey,
() => InternalGetGreetSettingsAsync(gid, type),
TimeSpan.FromSeconds(3));
private async Task<GreetSettings?> InternalGetGreetSettingsAsync(ulong gid, GreetType type)
{ {
using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(id, set => set).DmGreetMessageText; 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(); if (conf.GreetType == GreetType.GreetDm)
return uow.GuildConfigsForId(gid, set => set).ChannelGreetMessageText; {
if (user is not IGuildUser gu)
return;
await GreetDmUserInternal(conf, gu);
return;
} }
public string? GetBoostMessage(ulong gid) if (channel is null)
{
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(gid, set => set).BoostMessage;
}
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())
return; return;
var repCtx = new ReplacementContext(client: _client, var repCtx = new ReplacementContext(client: _client,
guild: channel.Guild, guild: channel.Guild,
channel: channel, 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); text = await _repSvc.ReplaceAsync(text, repCtx);
try try
{ {
var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync(); var toDelete = await _sender.Response(channel).Text(text).Sanitize(false).SendAsync();
if (conf.AutoDeleteByeMessagesTimer > 0) if (conf.AutoDeleteTimer > 0)
toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); toDelete.DeleteAfter(conf.AutoDeleteTimer);
} }
catch (HttpException ex) when (ex.DiscordCode == DiscordErrorCode.InsufficientPermissions catch (HttpException ex) when (ex.DiscordCode is DiscordErrorCode.InsufficientPermissions
|| ex.DiscordCode == DiscordErrorCode.MissingPermissions or DiscordErrorCode.MissingPermissions
|| ex.DiscordCode == DiscordErrorCode.UnknownChannel) or 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)
{ {
Log.Warning(ex, Log.Warning(ex,
"Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}", "Missing permissions to send a bye message, the greet message will be disabled on server: {GuildId}",
channel.GuildId); channel.GuildId);
await SetGreet(channel.GuildId, channel.Id, false); await SetGreet(channel.GuildId, channel.Id, GreetType.Greet, false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -265,19 +217,11 @@ 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) private async Task<bool> GreetDmUser(GreetSettings conf, IGuildUser user)
{ {
var completionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously); var completionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
await _greetDmQueue.Writer.WriteAsync((conf, user, completionSource)); await _greetQueue.Writer.WriteAsync((conf, user, null));
return await completionSource.Task; return await completionSource.Task;
} }
@ -285,13 +229,8 @@ public class GreetService : INService, IReadyExecutor
{ {
try try
{ {
// var rep = new ReplacementBuilder() var repCtx = new ReplacementContext(client: _client, guild: user.Guild, user: user);
// .WithUser(user) var smartText = SmartText.CreateFrom(conf.MessageText);
// .WithServer(_client, (SocketGuild)user.Guild)
// .Build();
var repCtx = new ReplacementContext(client: _client, guild: user.Guild, users: user);
var smartText = SmartText.CreateFrom(conf.DmGreetMessageText);
smartText = await _repSvc.ReplaceAsync(smartText, repCtx); smartText = await _repSvc.ReplaceAsync(smartText, repCtx);
if (smartText is SmartPlainText pt) if (smartText is SmartPlainText pt)
@ -372,38 +311,21 @@ public class GreetService : INService, IReadyExecutor
{ {
try try
{ {
var conf = GetOrAddSettingsForGuild(user.GuildId); var conf = await GetGreetSettingsAsync(user.GuildId, GreetType.Greet);
if (conf.SendChannelGreetMessage) if (conf is not null && conf.IsEnabled && conf.ChannelId is { } channelId)
{ {
var channel = await user.Guild.GetTextChannelAsync(conf.GreetMessageChannelId); var channel = await user.Guild.GetTextChannelAsync(channelId);
if (channel is not null) if (channel is not null)
{ {
if (GroupGreets) await _greetQueue.Writer.WriteAsync((conf, user, channel));
{
// 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);
}
}
}
else
await GreetUsers(conf, channel, new[] { user });
} }
} }
if (conf.SendDmGreetMessage) var confDm = await GetGreetSettingsAsync(user.GuildId, GreetType.GreetDm);
await GreetDmUser(conf, user);
if (confDm?.IsEnabled ?? false)
await GreetDmUser(confDm, user);
} }
catch catch
{ {
@ -413,256 +335,146 @@ public class GreetService : INService, IReadyExecutor
return Task.CompletedTask; return Task.CompletedTask;
} }
public string? GetByeMessage(ulong gid)
public static string GetDefaultGreet(GreetType greetType)
=> greetType switch
{ {
using var uow = _db.GetDbContext(); GreetType.Boost => "%user.mention% has boosted the server!",
return uow.GuildConfigsForId(gid, set => set).ChannelByeMessageText; 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 GreetSettings GetOrAddSettingsForGuild(ulong guildId) public async Task<bool> SetGreet(
{ ulong guildId,
if (_guildConfigsCache.TryGetValue(guildId, out var settings)) ulong? channelId,
return settings; GreetType greetType,
bool? value = null)
using (var uow = _db.GetDbContext())
{
var gc = uow.GuildConfigsForId(guildId, set => set);
settings = GreetSettings.Create(gc);
}
_guildConfigsCache.TryAdd(guildId, settings);
return settings;
}
public async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var conf = uow.GuildConfigsForId(guildId, set => set); var q = uow.GetTable<GreetSettings>();
var enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage;
conf.GreetMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf); if(value is null)
_guildConfigsCache[guildId] = toAdd; value = !_enabled[greetType].Contains(guildId);
await uow.SaveChangesAsync(); if (value is { } v)
return enabled; {
await q
.InsertOrUpdateAsync(() => new()
{
GuildId = guildId,
GreetType = greetType,
IsEnabled = v,
ChannelId = channelId,
},
(old) => new()
{
IsEnabled = v,
ChannelId = channelId,
},
() => new()
{
GuildId = guildId,
GreetType = greetType,
});
} }
public bool SetGreetMessage(ulong guildId, ref string message) if (value is true)
{ {
message = message.SanitizeMentions(); _enabled[greetType].Add(guildId);
return true;
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) _enabled[greetType].TryRemove(guildId);
{ return false;
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)
public async Task<bool> SetMessage(ulong guildId, GreetType greetType, string? message)
{ {
message = message?.SanitizeMentions(); await using (var uow = _db.GetDbContext())
{
if (string.IsNullOrWhiteSpace(message)) await uow.GetTable<GreetSettings>()
throw new ArgumentNullException(nameof(message)); .InsertOrUpdateAsync(() => new()
{
using var uow = _db.GetDbContext(); GuildId = guildId,
var conf = uow.GuildConfigsForId(guildId, set => set); GreetType = greetType,
conf.DmGreetMessageText = message; MessageText = message
},
var toAdd = GreetSettings.Create(conf); x => new()
_guildConfigsCache[guildId] = toAdd; {
MessageText = message
uow.SaveChanges(); },
return conf.SendDmGreetMessage; () => new()
{
GuildId = guildId,
GreetType = greetType
});
} }
public async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null) var conf = await GetGreetSettingsAsync(guildId, greetType);
{
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); return conf?.IsEnabled ?? false;
_guildConfigsCache[guildId] = toAdd;
await uow.SaveChangesAsync();
return enabled;
} }
public bool SetByeMessage(ulong guildId, ref string? message) public async Task<bool> SetDeleteTimer(ulong guildId, GreetType greetType, int timer)
{ {
message = message?.SanitizeMentions(); if (timer < 0 || timer > 3600)
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)
throw new ArgumentOutOfRangeException(nameof(timer)); throw new ArgumentOutOfRangeException(nameof(timer));
await using var uow = _db.GetDbContext(); await using (var uow = _db.GetDbContext())
var conf = uow.GuildConfigsForId(guildId, set => set);
conf.BoostMessageDeleteAfter = timer;
var toAdd = GreetSettings.Create(conf);
_guildConfigsCache[guildId] = toAdd;
await uow.SaveChangesAsync();
}
public async Task<bool> ToggleBoost(ulong guildId, ulong channelId, bool? forceState = null)
{ {
await using var uow = _db.GetDbContext(); await uow.GetTable<GreetSettings>()
var conf = uow.GuildConfigsForId(guildId, set => set); .InsertOrUpdateAsync(() => new()
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;
}
#region Get Enabled Status
public bool GetGreetDmEnabled(ulong guildId)
{ {
using var uow = _db.GetDbContext(); GuildId = guildId,
var conf = uow.GuildConfigsForId(guildId, set => set); GreetType = greetType,
return conf.SendDmGreetMessage; AutoDeleteTimer = timer,
} },
x => new()
public bool GetGreetEnabled(ulong guildId)
{ {
using var uow = _db.GetDbContext(); AutoDeleteTimer = timer
var conf = uow.GuildConfigsForId(guildId, set => set); },
return conf.SendChannelGreetMessage; () => new()
}
public bool GetByeEnabled(ulong guildId)
{ {
using var uow = _db.GetDbContext(); GuildId = guildId,
var conf = uow.GuildConfigsForId(guildId, set => set); GreetType = greetType
return conf.SendChannelByeMessage; });
} }
public bool GetBoostEnabled(ulong guildId) var conf = await GetGreetSettingsAsync(guildId, greetType);
return conf?.IsEnabled ?? false;
}
public async Task<bool> Test(
ulong guildId,
GreetType type,
IMessageChannel channel,
IGuildUser user)
{ {
using var uow = _db.GetDbContext(); var conf = await GetGreetSettingsAsync(guildId, type);
var conf = uow.GuildConfigsForId(guildId, set => set); if (conf is null)
return conf.SendBoostMessage; return false;
await SendMessage(conf, channel, user);
return true;
} }
#endregion public async Task<bool> SendMessage(GreetSettings conf, IMessageChannel channel, IGuildUser user)
#region Test Messages
public Task ByeTest(ITextChannel channel, IGuildUser user)
{ {
var conf = GetOrAddSettingsForGuild(user.GuildId); if (conf.GreetType == GreetType.GreetDm)
return ByeUsers(conf, channel, user);
}
public Task GreetTest(ITextChannel channel, IGuildUser user)
{ {
var conf = GetOrAddSettingsForGuild(user.GuildId); await _greetQueue.Writer.WriteAsync((conf, user, channel as ITextChannel));
return GreetUsers(conf, channel, user); return await GreetDmUser(conf, user);
} }
public Task<bool> GreetDmTest(IGuildUser user) if (channel is not ITextChannel ch)
{ return false;
var conf = GetOrAddSettingsForGuild(user.GuildId);
return GreetDmUser(conf, user);
}
public Task<bool> BoostTest(ITextChannel channel, IGuildUser user) await GreetUsers(conf, ch, user);
{ return true;
var conf = GetOrAddSettingsForGuild(user.GuildId);
return SendBoostMessage(conf, user, channel);
} }
#endregion
} }

View file

@ -1,45 +1,21 @@
using NadekoBot.Db.Models;
namespace NadekoBot.Services; namespace NadekoBot.Services;
public enum GreetType
{
Greet,
GreetDm,
Bye,
Boost,
}
public class GreetSettings public class GreetSettings
{ {
public int AutoDeleteGreetMessagesTimer { get; set; } public int Id { get; set; }
public int AutoDeleteByeMessagesTimer { get; set; }
public ulong GreetMessageChannelId { get; set; } public ulong GuildId { get; set; }
public ulong ByeMessageChannelId { get; set; } public GreetType GreetType { get; set; }
public string? MessageText { get; set; }
public bool SendDmGreetMessage { get; set; } public bool IsEnabled { get; set; }
public string? DmGreetMessageText { get; set; } public ulong? ChannelId { get; set; }
public int AutoDeleteTimer { 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
};
} }

View file

@ -18,11 +18,17 @@ public partial class Administration
await Response().Confirm(strs.ropl_disabled).SendAsync(); await Response().Confirm(strs.ropl_disabled).SendAsync();
} }
[Cmd] [Cmd]
[OwnerOnly] [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(); await Response().Confirm(strs.ropl_added).SendAsync();
} }

View file

@ -243,43 +243,54 @@ public class UserPunishService : INService, IReadyExecutor
public async Task CheckAllWarnExpiresAsync() public async Task CheckAllWarnExpiresAsync()
{ {
await using var uow = _db.GetDbContext(); await using var uow = _db.GetDbContext();
var cleared = await uow.Set<Warning>()
.Where(x => uow.Set<GuildConfig>() var toClear = await uow.GetTable<Warning>()
.Any(y => y.GuildId == x.GuildId .Where(x => uow.GetTable<GuildConfig>()
.Count(y => y.GuildId == x.GuildId
&& y.WarnExpireHours > 0 && y.WarnExpireHours > 0
&& y.WarnExpireAction == WarnExpireAction.Clear) && y.WarnExpireAction == WarnExpireAction.Clear)
> 0
&& x.Forgiven == false && x.Forgiven == false
&& x.DateAdded && x.DateAdded
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>() < DateTime.UtcNow.AddHours(-uow.GetTable<GuildConfig>()
.Where(y => x.GuildId == y.GuildId) .Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours) .Select(y => y.WarnExpireHours)
.First())) .First()))
.Select(x => x.Id)
.ToListAsyncLinqToDB();
var cleared = await uow.GetTable<Warning>()
.Where(x => toClear.Contains(x.Id))
.UpdateAsync(_ => new() .UpdateAsync(_ => new()
{ {
Forgiven = true, Forgiven = true,
ForgivenBy = "expiry" ForgivenBy = "expiry"
}); });
var deleted = await uow.Set<Warning>() var toDelete = await uow.GetTable<Warning>()
.Where(x => uow.Set<GuildConfig>() .Where(x => uow.GetTable<GuildConfig>()
.Any(y => y.GuildId == x.GuildId .Count(y => y.GuildId == x.GuildId
&& y.WarnExpireHours > 0 && y.WarnExpireHours > 0
&& y.WarnExpireAction == WarnExpireAction.Delete) && y.WarnExpireAction == WarnExpireAction.Delete)
> 0
&& x.DateAdded && x.DateAdded
< DateTime.UtcNow.AddHours(-uow.Set<GuildConfig>() < DateTime.UtcNow.AddHours(-uow.GetTable<GuildConfig>()
.Where(y => x.GuildId == y.GuildId) .Where(y => x.GuildId == y.GuildId)
.Select(y => y.WarnExpireHours) .Select(y => y.WarnExpireHours)
.First())) .First()))
.Select(x => x.Id)
.ToListAsyncLinqToDB();
var deleted = await uow.GetTable<Warning>()
.Where(x => toDelete.Contains(x.Id))
.DeleteAsync(); .DeleteAsync();
if (cleared > 0 || deleted > 0) if (cleared > 0 || deleted > 0)
{ {
Log.Information("Cleared {ClearedWarnings} warnings and deleted {DeletedWarnings} warnings due to expiry", Log.Information("Cleared {ClearedWarnings} warnings and deleted {DeletedWarnings} warnings due to expiry",
cleared, cleared,
deleted); toDelete.Count);
} }
await uow.SaveChangesAsync();
} }
public async Task CheckWarnExpiresAsync(ulong guildId) public async Task CheckWarnExpiresAsync(ulong guildId)

View file

@ -36,7 +36,7 @@ public static class NadekoExpressionExtensions
var repCtx = new ReplacementContext(client: client, var repCtx = new ReplacementContext(client: client,
guild: (ctx.Channel as ITextChannel)?.Guild as SocketGuild, guild: (ctx.Channel as ITextChannel)?.Guild as SocketGuild,
channel: ctx.Channel, channel: ctx.Channel,
users: ctx.Author user: ctx.Author
) )
.WithOverride("%target%", .WithOverride("%target%",
() => canMentionEveryone () => canMentionEveryone

View file

@ -58,14 +58,14 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
[Cmd] [Cmd]
[UserPerm(GuildPerm.Administrator)] [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; return;
} }
await ExprAddInternalAsync(key, message); await ExprAddInternalAsync(trigger, response);
} }

View file

@ -167,7 +167,8 @@ public partial class Gambling
long ownedAmount; long ownedAmount;
await using (var uow = _db.GetDbContext()) 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; ?? 0;
} }

View file

@ -175,8 +175,6 @@ public sealed partial class Help : NadekoModule<HelpService>
return strs.module_description_gambling; return strs.module_description_gambling;
case "music": case "music":
return strs.module_description_music; return strs.module_description_music;
case "nsfw":
return strs.module_description_nsfw;
case "permissions": case "permissions":
return strs.module_description_permissions; return strs.module_description_permissions;
case "xp": case "xp":
@ -211,8 +209,6 @@ public sealed partial class Help : NadekoModule<HelpService>
return "💰"; return "💰";
case "music": case "music":
return "🎶"; return "🎶";
case "nsfw":
return "😳";
case "permissions": case "permissions":
return "🚓"; return "🚓";
case "xp": case "xp":

View file

@ -31,7 +31,7 @@ public class HelpService : IExecNoCommand, INService
return; 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("%prefix%", () => _bss.Data.Prefix)
.WithOverride("%bot.prefix%", () => _bss.Data.Prefix); .WithOverride("%bot.prefix%", () => _bss.Data.Prefix);

View file

@ -72,7 +72,7 @@ public partial class Searches
} }
[Cmd] [Cmd]
public async Task Image([Leftover] string? query = null) public async Task Image([Leftover] string? query)
{ {
query = query?.Trim(); query = query?.Trim();

View file

@ -262,7 +262,7 @@ public sealed class RepeaterService : IReadyExecutor, INService
var repCtx = new ReplacementContext(client: _client, var repCtx = new ReplacementContext(client: _client,
guild: guild, guild: guild,
channel: channel, channel: channel,
users: guild.CurrentUser); user: guild.CurrentUser);
try try
{ {

View file

@ -91,7 +91,6 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4"/>
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2"/>
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/> <PackageReference Include="EFCore.NamingConventions" Version="8.0.3"/>

View file

@ -1,6 +1,5 @@
#nullable disable #nullable disable
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using NadekoBot.Common.Yml; using NadekoBot.Common.Yml;
namespace NadekoBot.Services; namespace NadekoBot.Services;
@ -22,7 +21,6 @@ public sealed class BotCredsProvider : IBotCredsProvider
private readonly object _reloadLock = new(); private readonly object _reloadLock = new();
private readonly IDisposable _changeToken;
public BotCredsProvider(int? totalShards = null, string credPath = null) public BotCredsProvider(int? totalShards = null, string credPath = null)
{ {
@ -71,7 +69,6 @@ public sealed class BotCredsProvider : IBotCredsProvider
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
} }
_changeToken = ChangeToken.OnChange(() => _config.GetReloadToken(), Reload);
Reload(); Reload();
} }

View file

@ -72,6 +72,22 @@ public static class EnumerableExtensions
where TKey : notnull where TKey : notnull
=> new(dict); => new(dict);
/// <summary>
/// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}" /> class
/// that contains elements copied from the specified <see cref="IEnumerable{T}" />
/// has the default concurrency level, has the default initial capacity,
/// and uses the default comparer for the key type.
/// </summary>
/// <param name="dict">
/// The <see cref="IEnumerable{T}" /> whose elements are copied to the new
/// <see cref="ConcurrentDictionary{TKey,TValue}" />.
/// </param>
/// <returns>A new instance of the <see cref="ConcurrentDictionary{TKey,TValue}" /> class</returns>
public static ConcurrentHashSet<TValue> ToConcurrentSet<TValue>(
this IReadOnlyCollection<TValue> dict)
where TValue : notnull
=> new(dict);
public static IndexedCollection<T> ToIndexed<T>(this IEnumerable<T> enumerable) public static IndexedCollection<T> ToIndexed<T>(this IEnumerable<T> enumerable)
where T : class, IIndexed where T : class, IIndexed
=> new(enumerable); => new(enumerable);

View file

@ -13,7 +13,7 @@ namespace NadekoBot.Common.Configs;
public sealed partial class BotConfig : ICloneable<BotConfig> public sealed partial class BotConfig : ICloneable<BotConfig>
{ {
[Comment("""DO NOT CHANGE""")] [Comment("""DO NOT CHANGE""")]
public int Version { get; set; } = 7; public int Version { get; set; } = 8;
[Comment(""" [Comment("""
Most commands, when executed, have a small colored line Most commands, when executed, have a small colored line
@ -85,19 +85,6 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
[Comment("""Which string will be used to recognize the commands""")] [Comment("""Which string will be used to recognize the commands""")]
public string Prefix { get; set; } public string Prefix { get; set; }
[Comment("""
Toggles whether your bot will group greet/bye messages into a single message every 5 seconds.
1st user who joins will get greeted immediately
If more users join within the next 5 seconds, they will be greeted in groups of 5.
This will cause %user.mention% and other placeholders to be replaced with multiple users.
Keep in mind this might break some of your embeds - for example if you have %user.avatar% in the thumbnail,
it will become invalid, as it will resolve to a list of avatars of grouped users.
note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some
servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited,
and (slightly) reduce the greet spam in those servers.
""")]
public bool GroupGreets { get; set; }
[Comment(""" [Comment("""
Whether the bot will rotate through all specified statuses. Whether the bot will rotate through all specified statuses.
This setting can be changed via .ropl command. This setting can be changed via .ropl command.
@ -145,7 +132,6 @@ public sealed partial class BotConfig : ICloneable<BotConfig>
Blocked = blocked; Blocked = blocked;
Prefix = "."; Prefix = ".";
RotateStatuses = false; RotateStatuses = false;
GroupGreets = false;
DmHelpTextKeywords = DmHelpTextKeywords =
[ [
"help", "help",

View file

@ -1,5 +1,6 @@
#nullable disable #nullable disable
using LinqToDB; using LinqToDB;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions; using System.Linq.Expressions;
namespace NadekoBot.Common; namespace NadekoBot.Common;

View file

@ -7,7 +7,7 @@ public sealed class ReplacementContext
public DiscordSocketClient? Client { get; } public DiscordSocketClient? Client { get; }
public IGuild? Guild { get; } public IGuild? Guild { get; }
public IMessageChannel? Channel { get; } public IMessageChannel? Channel { get; }
public IUser[]? Users { get; } public IUser? User { get; }
private readonly List<ReplacementInfo> _overrides = new(); private readonly List<ReplacementInfo> _overrides = new();
private readonly HashSet<string> _tokens = new(); private readonly HashSet<string> _tokens = new();
@ -21,7 +21,8 @@ public sealed class ReplacementContext
public IReadOnlyList<RegexReplacementInfo> RegexOverrides public IReadOnlyList<RegexReplacementInfo> RegexOverrides
=> _regexOverrides.AsReadOnly(); => _regexOverrides.AsReadOnly();
public ReplacementContext(ICommandContext cmdContext) : this(cmdContext.Client as DiscordSocketClient, public ReplacementContext(ICommandContext cmdContext)
: this(cmdContext.Client as DiscordSocketClient,
cmdContext.Guild, cmdContext.Guild,
cmdContext.Channel, cmdContext.Channel,
cmdContext.User) cmdContext.User)
@ -32,12 +33,12 @@ public sealed class ReplacementContext
DiscordSocketClient? client = null, DiscordSocketClient? client = null,
IGuild? guild = null, IGuild? guild = null,
IMessageChannel? channel = null, IMessageChannel? channel = null,
params IUser[]? users) IUser? user = null)
{ {
Client = client; Client = client;
Guild = guild; Guild = guild;
Channel = channel; Channel = channel;
Users = users; User = user;
} }
public ReplacementContext WithOverride(string key, Func<ValueTask<string>> repFactory) public ReplacementContext WithOverride(string key, Func<ValueTask<string>> repFactory)

View file

@ -61,6 +61,18 @@ public sealed partial class ReplacementPatternStore
private void WithUsers() private void WithUsers()
{ {
Register("%user%", static (IUser user) => user.Mention);
Register("%user.mention%", static (IUser user) => user.Mention);
Register("%user.fullname%", static (IUser user) => user.ToString()!);
Register("%user.name%", static (IUser user) => user.Username);
Register("%user.discrim%", static (IUser user) => user.Discriminator);
Register("%user.avatar%", static (IUser user) => user.RealAvatarUrl().ToString());
Register("%user.id%", static (IUser user) => user.Id.ToString());
Register("%user.created_time%", static (IUser user) => user.CreatedAt.ToString("HH:mm"));
Register("%user.created_date%", static (IUser user) => user.CreatedAt.ToString("dd.MM.yyyy"));
Register("%user.joined_time%", static (IGuildUser user) => user.JoinedAt?.ToString("HH:mm"));
Register("%user.joined_date%", static (IGuildUser user) => user.JoinedAt?.ToString("dd.MM.yyyy"));
Register("%user%", Register("%user%",
static (IUser[] users) => string.Join(" ", users.Select(user => user.Mention))); static (IUser[] users) => string.Join(" ", users.Select(user => user.Mention)));
Register("%user.mention%", Register("%user.mention%",

View file

@ -40,8 +40,8 @@ public sealed class ReplacementService : IReplacementService, INService
if (repCtx.Guild is not null) if (repCtx.Guild is not null)
obj.Add(repCtx.Guild); obj.Add(repCtx.Guild);
if (repCtx.Users is not null) if (repCtx.User is not null)
obj.Add(repCtx.Users); obj.Add(repCtx.User);
if (repCtx.Channel is not null) if (repCtx.Channel is not null)
obj.Add(repCtx.Channel); obj.Add(repCtx.Channel);
@ -86,9 +86,9 @@ public sealed class ReplacementService : IReplacementService, INService
objs.Add(repCtx.Channel); objs.Add(repCtx.Channel);
} }
if (repCtx.Users is not null) if (repCtx.User is not null)
{ {
objs.Add(repCtx.Users); objs.Add(repCtx.User);
} }
if (repCtx.Guild is not null) if (repCtx.Guild is not null)
@ -117,9 +117,9 @@ public sealed class ReplacementService : IReplacementService, INService
objs.Add(repCtx.Channel); objs.Add(repCtx.Channel);
} }
if (repCtx.Users is not null) if (repCtx.User is not null)
{ {
objs.Add(repCtx.Users); objs.Add(repCtx.User);
} }
if (repCtx.Guild is not null) if (repCtx.Guild is not null)

View file

@ -9,8 +9,8 @@ public sealed partial class Replacer
private readonly IEnumerable<RegexReplacementInfo> _regexReps; private readonly IEnumerable<RegexReplacementInfo> _regexReps;
private readonly object[] _inputData; private readonly object[] _inputData;
[GeneratedRegex(@"\%[\p{L}\p{N}\._]*[\p{L}\p{N}]+[\p{L}\p{N}\._]*\%")] // [GeneratedRegex(@"\%[\p{L}\p{N}\._]*[\p{L}\p{N}]+[\p{L}\p{N}\._]*\%")]
private static partial Regex TokenExtractionRegex(); // private static partial Regex TokenExtractionRegex();
public Replacer(IEnumerable<ReplacementInfo> reps, IEnumerable<RegexReplacementInfo> regexReps, object[] inputData) public Replacer(IEnumerable<ReplacementInfo> reps, IEnumerable<RegexReplacementInfo> regexReps, object[] inputData)
{ {
@ -24,10 +24,10 @@ public sealed partial class Replacer
if (string.IsNullOrWhiteSpace(input)) if (string.IsNullOrWhiteSpace(input))
return input; return input;
var matches = TokenExtractionRegex().IsMatch(input); // var matches = TokenExtractionRegex().IsMatch(input);
if (matches) // if (matches)
{ // {
foreach (var rep in _reps) foreach (var rep in _reps)
{ {
if (input.Contains(rep.Token, StringComparison.InvariantCulture)) if (input.Contains(rep.Token, StringComparison.InvariantCulture))
@ -36,7 +36,7 @@ public sealed partial class Replacer
input = input.Replace(rep.Token, await rep.GetValueAsync(objs), StringComparison.InvariantCulture); input = input.Replace(rep.Token, await rep.GetValueAsync(objs), StringComparison.InvariantCulture);
} }
} }
} // }
foreach (var rep in _regexReps) foreach (var rep in _regexReps)
{ {
@ -93,14 +93,16 @@ public sealed partial class Replacer
Embeds = await embedArr.Embeds.Map(async e => await ReplaceAsync(e) with Embeds = await embedArr.Embeds.Map(async e => await ReplaceAsync(e) with
{ {
Color = e.Color Color = e.Color
}).WhenAll(), })
.WhenAll(),
Content = await ReplaceAsync(embedArr.Content) Content = await ReplaceAsync(embedArr.Content)
}; };
private async ValueTask<SmartPlainText> ReplaceAsync(SmartPlainText plain) private async ValueTask<SmartPlainText> ReplaceAsync(SmartPlainText plain)
=> await ReplaceAsync(plain.Text); => await ReplaceAsync(plain.Text);
private async Task<T> ReplaceAsync<T>(T embedData) where T : SmartEmbedTextBase, new() private async Task<T> ReplaceAsync<T>(T embedData)
where T : SmartEmbedTextBase, new()
{ {
var newEmbedData = new T var newEmbedData = new T
{ {
@ -123,7 +125,8 @@ public sealed partial class Replacer
Name = await ReplaceAsync(f.Name), Name = await ReplaceAsync(f.Name),
Value = await ReplaceAsync(f.Value), Value = await ReplaceAsync(f.Value),
Inline = f.Inline Inline = f.Inline
}) ?? []), })
?? []),
Footer = embedData.Footer is null Footer = embedData.Footer is null
? null ? null
: new() : new()

View file

@ -69,5 +69,11 @@ public sealed class BotConfigService : ConfigServiceBase<BotConfig>
c.Version = 7; c.Version = 7;
c.IgnoreOtherBots = true; c.IgnoreOtherBots = true;
}); });
if(data.Version < 8)
ModifyConfig(c =>
{
c.Version = 8;
});
} }
} }

View file

@ -1,5 +1,5 @@
# DO NOT CHANGE # DO NOT CHANGE
version: 7 version: 8
# Most commands, when executed, have a small colored line # Most commands, when executed, have a small colored line
# next to the response. The color depends whether the command # next to the response. The color depends whether the command
# is completed, errored or in progress (pending) # is completed, errored or in progress (pending)
@ -80,16 +80,6 @@ blocked:
modules: [] modules: []
# Which string will be used to recognize the commands # Which string will be used to recognize the commands
prefix: . prefix: .
# Toggles whether your bot will group greet/bye messages into a single message every 5 seconds.
# 1st user who joins will get greeted immediately
# If more users join within the next 5 seconds, they will be greeted in groups of 5.
# This will cause %user.mention% and other placeholders to be replaced with multiple users.
# Keep in mind this might break some of your embeds - for example if you have %user.avatar% in the thumbnail,
# it will become invalid, as it will resolve to a list of avatars of grouped users.
# note: This setting is primarily used if you're afraid of raids, or you're running medium/large bots where some
# servers might get hundreds of people join at once. This is used to prevent the bot from getting ratelimited,
# and (slightly) reduce the greet spam in those servers.
groupGreets: false
# Whether the bot will rotate through all specified statuses. # Whether the bot will rotate through all specified statuses.
# This setting can be changed via .ropl command. # This setting can be changed via .ropl command.
# See RotatingStatuses submodule in Administration. # See RotatingStatuses submodule in Administration.

View file

@ -140,13 +140,13 @@
imageUrl: https://cdn.nadeko.bot/flags/gt-flag.gif imageUrl: https://cdn.nadeko.bot/flags/gt-flag.gif
- word: Guinea - word: Guinea
imageUrl: https://cdn.nadeko.bot/flags/gv-flag.gif imageUrl: https://cdn.nadeko.bot/flags/gv-flag.gif
- word: Guinea-Bissau - word: Guinea Bissau
imageUrl: https://cdn.nadeko.bot/flags/pu-flag.gif imageUrl: https://cdn.nadeko.bot/flags/pu-flag.gif
- word: Guyana - word: Guyana
imageUrl: https://cdn.nadeko.bot/flags/gy-flag.gif imageUrl: https://cdn.nadeko.bot/flags/gy-flag.gif
- word: Haiti - word: Haiti
imageUrl: https://cdn.nadeko.bot/flags/ha-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ha-flag.gif
- word: Holy See - word: Vatican
imageUrl: https://cdn.nadeko.bot/flags/vt-flag.gif imageUrl: https://cdn.nadeko.bot/flags/vt-flag.gif
- word: Honduras - word: Honduras
imageUrl: https://cdn.nadeko.bot/flags/ho-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ho-flag.gif
@ -184,7 +184,7 @@
imageUrl: https://cdn.nadeko.bot/flags/ku-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ku-flag.gif
- word: Kyrgyzstan - word: Kyrgyzstan
imageUrl: https://cdn.nadeko.bot/flags/kg-flag.gif imageUrl: https://cdn.nadeko.bot/flags/kg-flag.gif
- word: Laos - word: Lao People's Democratic Republic
imageUrl: https://cdn.nadeko.bot/flags/la-flag.gif imageUrl: https://cdn.nadeko.bot/flags/la-flag.gif
- word: Latvia - word: Latvia
imageUrl: https://cdn.nadeko.bot/flags/lg-flag.gif imageUrl: https://cdn.nadeko.bot/flags/lg-flag.gif
@ -282,7 +282,7 @@
imageUrl: https://cdn.nadeko.bot/flags/qa-flag.gif imageUrl: https://cdn.nadeko.bot/flags/qa-flag.gif
- word: Romania - word: Romania
imageUrl: https://cdn.nadeko.bot/flags/ro-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ro-flag.gif
- word: Russia - word: Russian Federation
imageUrl: https://cdn.nadeko.bot/flags/rs-flag.gif imageUrl: https://cdn.nadeko.bot/flags/rs-flag.gif
- word: Rwanda - word: Rwanda
imageUrl: https://cdn.nadeko.bot/flags/rw-flag.gif imageUrl: https://cdn.nadeko.bot/flags/rw-flag.gif
@ -318,7 +318,7 @@
imageUrl: https://cdn.nadeko.bot/flags/so-flag.gif imageUrl: https://cdn.nadeko.bot/flags/so-flag.gif
- word: South Africa - word: South Africa
imageUrl: https://cdn.nadeko.bot/flags/sf-flag.gif imageUrl: https://cdn.nadeko.bot/flags/sf-flag.gif
- word: South Korea - word: Republic of Korea
imageUrl: https://cdn.nadeko.bot/flags/ks-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ks-flag.gif
- word: South Sudan - word: South Sudan
imageUrl: https://cdn.nadeko.bot/flags/od-flag.gif imageUrl: https://cdn.nadeko.bot/flags/od-flag.gif
@ -326,9 +326,9 @@
imageUrl: https://cdn.nadeko.bot/flags/sp-flag.gif imageUrl: https://cdn.nadeko.bot/flags/sp-flag.gif
- word: Sri Lanka - word: Sri Lanka
imageUrl: https://cdn.nadeko.bot/flags/ce-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ce-flag.gif
- word: St. Vincent Grenadines - word: Saint Vincent and the Grenadines
imageUrl: https://cdn.nadeko.bot/flags/vc-flag.gif imageUrl: https://cdn.nadeko.bot/flags/vc-flag.gif
- word: State of Palestine - word: Palestine
imageUrl: https://cdn.nadeko.bot/flags/palestine-flag.gif imageUrl: https://cdn.nadeko.bot/flags/palestine-flag.gif
- word: Sudan - word: Sudan
imageUrl: https://cdn.nadeko.bot/flags/su-flag.gif imageUrl: https://cdn.nadeko.bot/flags/su-flag.gif
@ -338,11 +338,11 @@
imageUrl: https://cdn.nadeko.bot/flags/sw-flag.gif imageUrl: https://cdn.nadeko.bot/flags/sw-flag.gif
- word: Switzerland - word: Switzerland
imageUrl: https://cdn.nadeko.bot/flags/sz-flag.gif imageUrl: https://cdn.nadeko.bot/flags/sz-flag.gif
- word: Syria - word: Syrian Arab Republic
imageUrl: https://cdn.nadeko.bot/flags/sy-flag.gif imageUrl: https://cdn.nadeko.bot/flags/sy-flag.gif
- word: Tajikistan - word: Tajikistan
imageUrl: https://cdn.nadeko.bot/flags/ti-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ti-flag.gif
- word: Tanzania - word: United Republic of Tanzania
imageUrl: https://cdn.nadeko.bot/flags/tz-flag.gif imageUrl: https://cdn.nadeko.bot/flags/tz-flag.gif
- word: Thailand - word: Thailand
imageUrl: https://cdn.nadeko.bot/flags/th-flag.gif imageUrl: https://cdn.nadeko.bot/flags/th-flag.gif
@ -380,7 +380,7 @@
imageUrl: https://cdn.nadeko.bot/flags/nh-flag.gif imageUrl: https://cdn.nadeko.bot/flags/nh-flag.gif
- word: Venezuela - word: Venezuela
imageUrl: https://cdn.nadeko.bot/flags/ve-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ve-flag.gif
- word: Vietnam - word: Viet Nam
imageUrl: https://cdn.nadeko.bot/flags/vm-flag.gif imageUrl: https://cdn.nadeko.bot/flags/vm-flag.gif
- word: Yemen - word: Yemen
imageUrl: https://cdn.nadeko.bot/flags/ym-flag.gif imageUrl: https://cdn.nadeko.bot/flags/ym-flag.gif

View file

@ -175,9 +175,9 @@ logignore:
params: params:
- { } - { }
- target: - target:
desc: "The channel to ignore or show the list of ignored channels for." desc: "The channel to ignore."
- target: - target:
desc: "The user or channel being targeted for logging ignore or inclusion." desc: "The user to ignore."
repeatlist: repeatlist:
desc: Lists currently repeating messages and their indexes. desc: Lists currently repeating messages and their indexes.
ex: ex:
@ -283,10 +283,12 @@ addplaying:
- Playing with you - Playing with you
- Watching you sleep - Watching you sleep
params: params:
- t: - statusType:
desc: "The type of status, allowed values are `Playing`, `Watching`, or `Listening`." desc: "The type of status, allowed values are `Playing`, `Watching`, or `Listening`."
status: status:
desc: "The status text." desc: "The status text."
- status:
desc: "The status text."
listplaying: listplaying:
desc: Lists all playing statuses and their indexes. desc: Lists all playing statuses and their indexes.
ex: ex:
@ -387,18 +389,18 @@ expradd:
- '"hello" Hi there %user.mention%' - '"hello" Hi there %user.mention%'
params: params:
- trigger: - trigger:
desc: "The trigger word or phrase for the bot to respond to users typing." desc: "The trigger word or phrase (phrase must be in quotes)."
response: response:
desc: "The text of the message that shows up when a user types the trigger word." desc: "The text of the message that shows up when a user types the trigger word or phrase."
expraddserver: expraddserver:
desc: 'Add an expression with a trigger and a response in this server. Bot will post a response whenever someone types the trigger word. This command is useful if you want to lower the permission requirement for managing expressions by using `{0}dpo`. Guide [here](<https://nadekobot.readthedocs.io/en/latest/custom-reactions/>).' desc: 'Add an expression with a trigger and a response in this server. Bot will post a response whenever someone types the trigger word. This command is useful if you want to lower the permission requirement for managing expressions by using `{0}dpo`. Guide [here](<https://nadekobot.readthedocs.io/en/latest/custom-reactions/>).'
ex: ex:
- '"hello" Hi there %user.mention%' - '"hello" Hi there %user.mention%'
params: params:
- key: - trigger:
desc: "The trigger word or phrase for the bot to respond to users typing." desc: "The trigger word or phrase (phrase must be in quotes)."
message: response:
desc: "The text of the message that shows up when a user types the trigger word." desc: "The text of the message that shows up when a user types the trigger word or phrase."
exprlist: exprlist:
desc: |- desc: |-
Lists global or server expressions (20 expressions per page). Lists global or server expressions (20 expressions per page).
@ -496,21 +498,21 @@ delmsgoncmd:
- list - list
params: params:
- _: - _:
desc: "The list of channels or servers where the automatic deletion is enabled, disabled, or inherited." desc: "The word 'list'"
- _: - _:
desc: "The server where the command is being executed or monitored for chat flood prevention." desc: "The word 'server'"
- _: - _:
desc: "The channel where the automatic deletion of successful command messages should be toggled." desc: "The word 'channel'"
s: s:
desc: "The state of whether automatic deletion is enabled or disabled for a specific channel." desc: "The state to set. One of 'enable', 'disable', or 'inherit'."
ch: ch:
desc: "The channel where the automatic deletion of successful command messages should be toggled for." desc: "Channel in which to set"
- _: - _:
desc: "The channel where the automatic deletion of successful command messages should be toggled." desc: "The word 'channel'"
s: s:
desc: "The state of whether automatic deletion is enabled or disabled for a specific channel." desc: "The state to set. One of 'enable', 'disable', or 'inherit'."
chId: chId:
desc: "The ID of a channel where the automatic deletion should be toggled or inherited." desc: "Optional channel id. If omitted, defaults to the current channel."
restart: restart:
desc: Restarts the bot. Might not work. desc: Restarts the bot. Might not work.
ex: ex:
@ -1623,12 +1625,12 @@ gencurlist:
- page: - page:
desc: "The current page number for pagination." desc: "The current page number for pagination."
choose: choose:
desc: Chooses a thing from a list of things desc: Chooses a thing from a list of things. Separate items with a semicolon ;
ex: ex:
- Get up;Sleep;Sleep more - Get up;Sleep;Sleep more
params: params:
- list: - list:
desc: "The type of items in the collection being searched." desc: "The items separated by ;"
rps: rps:
desc: |- desc: |-
Play a game of Rocket-Paperclip-Scissors with Nadeko. Play a game of Rocket-Paperclip-Scissors with Nadeko.
@ -3341,7 +3343,7 @@ globalcommand:
globalmodule: globalmodule:
desc: Toggles whether a module can be used on any server. desc: Toggles whether a module can be used on any server.
ex: ex:
- nsfw - 'Gambling'
params: params:
- module: - module:
desc: "The type of module or configuration information being toggled." desc: "The type of module or configuration information being toggled."

View file

@ -991,7 +991,6 @@
"module_description_help": "Get command help, descriptions and usage examples", "module_description_help": "Get command help, descriptions and usage examples",
"module_description_gambling": "Bet on dice rolls, blackjack, slots, coinflips and others", "module_description_gambling": "Bet on dice rolls, blackjack, slots, coinflips and others",
"module_description_games": "Play trivia, nunchi, hangman, connect4 and other games", "module_description_games": "Play trivia, nunchi, hangman, connect4 and other games",
"module_description_nsfw": "NSFW commands.",
"module_description_music": "Play music from youtube, local files and radio streams", "module_description_music": "Play music from youtube, local files and radio streams",
"module_description_utility": "Manage custom quotes, repeating messages and check facts about the server", "module_description_utility": "Manage custom quotes, repeating messages and check facts about the server",
"module_description_administration": "Moderation, punish users, setup self assignable roles and greet messages", "module_description_administration": "Moderation, punish users, setup self assignable roles and greet messages",