mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 01:38:27 -04:00
Restructured the project structure back to the way it was, there's no reasonable way to split the modules
This commit is contained in:
34
src/NadekoBot/Db/Extensions/ClubExtensions.cs
Normal file
34
src/NadekoBot/Db/Extensions/ClubExtensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class ClubExtensions
|
||||
{
|
||||
private static IQueryable<ClubInfo> Include(this DbSet<ClubInfo> clubs)
|
||||
=> clubs.Include(x => x.Owner)
|
||||
.Include(x => x.Applicants)
|
||||
.ThenInclude(x => x.User)
|
||||
.Include(x => x.Bans)
|
||||
.ThenInclude(x => x.User)
|
||||
.Include(x => x.Members)
|
||||
.AsQueryable();
|
||||
|
||||
public static ClubInfo GetByOwner(this DbSet<ClubInfo> clubs, ulong userId)
|
||||
=> Include(clubs).FirstOrDefault(c => c.Owner.UserId == userId);
|
||||
|
||||
public static ClubInfo GetByOwnerOrAdmin(this DbSet<ClubInfo> clubs, ulong userId)
|
||||
=> Include(clubs)
|
||||
.FirstOrDefault(c => c.Owner.UserId == userId || c.Members.Any(u => u.UserId == userId && u.IsClubAdmin));
|
||||
|
||||
public static ClubInfo GetByMember(this DbSet<ClubInfo> clubs, ulong userId)
|
||||
=> Include(clubs).FirstOrDefault(c => c.Members.Any(u => u.UserId == userId));
|
||||
|
||||
public static ClubInfo GetByName(this DbSet<ClubInfo> clubs, string name)
|
||||
=> Include(clubs)
|
||||
.FirstOrDefault(c => c.Name == name);
|
||||
|
||||
public static List<ClubInfo> GetClubLeaderboardPage(this DbSet<ClubInfo> clubs, int page)
|
||||
=> clubs.AsNoTracking().OrderByDescending(x => x.Xp).Skip(page * 9).Take(9).ToList();
|
||||
}
|
20
src/NadekoBot/Db/Extensions/CurrencyTransactionExtensions.cs
Normal file
20
src/NadekoBot/Db/Extensions/CurrencyTransactionExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
#nullable disable
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class CurrencyTransactionExtensions
|
||||
{
|
||||
public static Task<List<CurrencyTransaction>> GetPageFor(
|
||||
this DbSet<CurrencyTransaction> set,
|
||||
ulong userId,
|
||||
int page)
|
||||
=> set.ToLinqToDBTable()
|
||||
.Where(x => x.UserId == userId)
|
||||
.OrderByDescending(x => x.DateAdded)
|
||||
.Skip(15 * page)
|
||||
.Take(15)
|
||||
.ToListAsyncLinqToDB();
|
||||
}
|
12
src/NadekoBot/Db/Extensions/DbExtensions.cs
Normal file
12
src/NadekoBot/Db/Extensions/DbExtensions.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class DbExtensions
|
||||
{
|
||||
public static T GetById<T>(this DbSet<T> set, int id)
|
||||
where T : DbEntity
|
||||
=> set.FirstOrDefault(x => x.Id == id);
|
||||
}
|
125
src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs
Normal file
125
src/NadekoBot/Db/Extensions/DiscordUserExtensions.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class DiscordUserExtensions
|
||||
{
|
||||
public static Task<DiscordUser> GetByUserIdAsync(
|
||||
this IQueryable<DiscordUser> set,
|
||||
ulong userId)
|
||||
=> set.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId);
|
||||
|
||||
public static void EnsureUserCreated(
|
||||
this DbContext ctx,
|
||||
ulong userId,
|
||||
string username,
|
||||
string discrim,
|
||||
string avatarId)
|
||||
=> ctx.GetTable<DiscordUser>()
|
||||
.InsertOrUpdate(
|
||||
() => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Username = username,
|
||||
Discriminator = discrim,
|
||||
AvatarId = avatarId,
|
||||
TotalXp = 0,
|
||||
CurrencyAmount = 0
|
||||
},
|
||||
old => new()
|
||||
{
|
||||
Username = username,
|
||||
Discriminator = discrim,
|
||||
AvatarId = avatarId
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
public static Task EnsureUserCreatedAsync(
|
||||
this DbContext ctx,
|
||||
ulong userId)
|
||||
=> ctx.GetTable<DiscordUser>()
|
||||
.InsertOrUpdateAsync(
|
||||
() => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Username = "Unknown",
|
||||
Discriminator = "????",
|
||||
AvatarId = string.Empty,
|
||||
TotalXp = 0,
|
||||
CurrencyAmount = 0
|
||||
},
|
||||
old => new()
|
||||
{
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
//temp is only used in updatecurrencystate, so that i don't overwrite real usernames/discrims with Unknown
|
||||
public static DiscordUser GetOrCreateUser(
|
||||
this DbContext ctx,
|
||||
ulong userId,
|
||||
string username,
|
||||
string discrim,
|
||||
string avatarId,
|
||||
Func<IQueryable<DiscordUser>, IQueryable<DiscordUser>> includes = null)
|
||||
{
|
||||
ctx.EnsureUserCreated(userId, username, discrim, avatarId);
|
||||
|
||||
IQueryable<DiscordUser> queryable = ctx.Set<DiscordUser>();
|
||||
if (includes is not null)
|
||||
queryable = includes(queryable);
|
||||
return queryable.First(u => u.UserId == userId);
|
||||
}
|
||||
|
||||
|
||||
public static int GetUserGlobalRank(this DbSet<DiscordUser> users, ulong id)
|
||||
=> users.AsQueryable()
|
||||
.Where(x => x.TotalXp
|
||||
> users.AsQueryable().Where(y => y.UserId == id).Select(y => y.TotalXp).FirstOrDefault())
|
||||
.Count()
|
||||
+ 1;
|
||||
|
||||
public static DiscordUser[] GetUsersXpLeaderboardFor(this DbSet<DiscordUser> users, int page, int perPage)
|
||||
=> users.AsQueryable().OrderByDescending(x => x.TotalXp).Skip(page * perPage).Take(perPage).AsEnumerable()
|
||||
.ToArray();
|
||||
|
||||
public static Task<List<DiscordUser>> GetTopRichest(
|
||||
this DbSet<DiscordUser> users,
|
||||
ulong botId,
|
||||
int page = 0, int perPage = 9)
|
||||
=> users.AsQueryable()
|
||||
.Where(c => c.CurrencyAmount > 0 && botId != c.UserId)
|
||||
.OrderByDescending(c => c.CurrencyAmount)
|
||||
.Skip(page * perPage)
|
||||
.Take(perPage)
|
||||
.ToListAsyncLinqToDB();
|
||||
|
||||
public static async Task<long> GetUserCurrencyAsync(this DbSet<DiscordUser> users, ulong userId)
|
||||
=> (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0;
|
||||
|
||||
public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
|
||||
{
|
||||
var items = users.AsQueryable().Where(x => ids.Contains(x.UserId));
|
||||
foreach (var item in items)
|
||||
item.CurrencyAmount = 0;
|
||||
}
|
||||
|
||||
public static decimal GetTotalCurrency(this DbSet<DiscordUser> users)
|
||||
=> users.Sum((Func<DiscordUser, decimal>)(x => x.CurrencyAmount));
|
||||
|
||||
public static decimal GetTopOnePercentCurrency(this DbSet<DiscordUser> users, ulong botId)
|
||||
=> users.AsQueryable()
|
||||
.Where(x => x.UserId != botId)
|
||||
.OrderByDescending(x => x.CurrencyAmount)
|
||||
.Take(users.Count() / 100 == 0 ? 1 : users.Count() / 100)
|
||||
.Sum(x => x.CurrencyAmount);
|
||||
}
|
229
src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs
Normal file
229
src/NadekoBot/Db/Extensions/GuildConfigExtensions.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class GuildConfigExtensions
|
||||
{
|
||||
private static List<WarningPunishment> DefaultWarnPunishments
|
||||
=> new()
|
||||
{
|
||||
new()
|
||||
{
|
||||
Count = 3,
|
||||
Punishment = PunishmentAction.Kick
|
||||
},
|
||||
new()
|
||||
{
|
||||
Count = 5,
|
||||
Punishment = PunishmentAction.Ban
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets full stream role settings for the guild with the specified id.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Db Context</param>
|
||||
/// <param name="guildId">Id of the guild to get stream role settings for.</param>
|
||||
/// <returns>Guild'p stream role settings</returns>
|
||||
public static StreamRoleSettings GetStreamRoleSettings(this DbContext ctx, ulong guildId)
|
||||
{
|
||||
var conf = ctx.GuildConfigsForId(guildId,
|
||||
set => set.Include(y => y.StreamRole)
|
||||
.Include(y => y.StreamRole.Whitelist)
|
||||
.Include(y => y.StreamRole.Blacklist));
|
||||
|
||||
if (conf.StreamRole is null)
|
||||
conf.StreamRole = new();
|
||||
|
||||
return conf.StreamRole;
|
||||
}
|
||||
|
||||
private static IQueryable<GuildConfig> IncludeEverything(this DbSet<GuildConfig> configs)
|
||||
=> configs.AsQueryable()
|
||||
.AsSplitQuery()
|
||||
.Include(gc => gc.CommandCooldowns)
|
||||
.Include(gc => gc.FollowedStreams)
|
||||
.Include(gc => gc.StreamRole)
|
||||
.Include(gc => gc.XpSettings)
|
||||
.ThenInclude(x => x.ExclusionList)
|
||||
.Include(gc => gc.DelMsgOnCmdChannels);
|
||||
|
||||
public static IEnumerable<GuildConfig> GetAllGuildConfigs(
|
||||
this DbSet<GuildConfig> configs,
|
||||
IReadOnlyList<ulong> availableGuilds)
|
||||
=> configs.IncludeEverything().AsNoTracking().Where(x => availableGuilds.Contains(x.GuildId)).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets and creates if it doesn't exist a config for a guild.
|
||||
/// </summary>
|
||||
/// <param name="ctx">Context</param>
|
||||
/// <param name="guildId">Id of the guide</param>
|
||||
/// <param name="includes">Use to manipulate the set however you want. Pass null to include everything</param>
|
||||
/// <returns>Config for the guild</returns>
|
||||
public static GuildConfig GuildConfigsForId(
|
||||
this DbContext ctx,
|
||||
ulong guildId,
|
||||
Func<DbSet<GuildConfig>, IQueryable<GuildConfig>> includes)
|
||||
{
|
||||
GuildConfig config;
|
||||
|
||||
if (includes is null)
|
||||
config = ctx.Set<GuildConfig>().IncludeEverything().FirstOrDefault(c => c.GuildId == guildId);
|
||||
else
|
||||
{
|
||||
var set = includes(ctx.Set<GuildConfig>());
|
||||
config = set.FirstOrDefault(c => c.GuildId == guildId);
|
||||
}
|
||||
|
||||
if (config is null)
|
||||
{
|
||||
ctx.Set<GuildConfig>().Add(config = new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Permissions = Permissionv2.GetDefaultPermlist,
|
||||
WarningsInitialized = true,
|
||||
WarnPunishments = DefaultWarnPunishments
|
||||
});
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
if (!config.WarningsInitialized)
|
||||
{
|
||||
config.WarningsInitialized = true;
|
||||
config.WarnPunishments = DefaultWarnPunishments;
|
||||
}
|
||||
|
||||
return config;
|
||||
|
||||
// ctx.GuildConfigs
|
||||
// .ToLinqToDBTable()
|
||||
// .InsertOrUpdate(() => new()
|
||||
// {
|
||||
// GuildId = guildId,
|
||||
// Permissions = Permissionv2.GetDefaultPermlist,
|
||||
// WarningsInitialized = true,
|
||||
// WarnPunishments = DefaultWarnPunishments
|
||||
// },
|
||||
// _ => new(),
|
||||
// () => new()
|
||||
// {
|
||||
// GuildId = guildId
|
||||
// });
|
||||
//
|
||||
// if(includes is null)
|
||||
// return ctx.GuildConfigs
|
||||
// .ToLinqToDBTable()
|
||||
// .First(x => x.GuildId == guildId);
|
||||
}
|
||||
|
||||
public static LogSetting LogSettingsFor(this DbContext ctx, ulong guildId)
|
||||
{
|
||||
var logSetting = ctx.Set<LogSetting>()
|
||||
.AsQueryable()
|
||||
.Include(x => x.LogIgnores)
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (logSetting is null)
|
||||
{
|
||||
ctx.Set<LogSetting>()
|
||||
.Add(logSetting = new()
|
||||
{
|
||||
GuildId = guildId
|
||||
});
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
return logSetting;
|
||||
}
|
||||
|
||||
public static IEnumerable<GuildConfig> PermissionsForAll(this DbSet<GuildConfig> configs, List<ulong> include)
|
||||
{
|
||||
var query = configs.AsQueryable().Where(x => include.Contains(x.GuildId)).Include(gc => gc.Permissions);
|
||||
|
||||
return query.ToList();
|
||||
}
|
||||
|
||||
public static GuildConfig GcWithPermissionsFor(this DbContext ctx, ulong guildId)
|
||||
{
|
||||
var config = ctx.Set<GuildConfig>().AsQueryable()
|
||||
.Where(gc => gc.GuildId == guildId)
|
||||
.Include(gc => gc.Permissions)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (config is null) // if there is no guildconfig, create new one
|
||||
{
|
||||
ctx.Set<GuildConfig>().Add(config = new()
|
||||
{
|
||||
GuildId = guildId,
|
||||
Permissions = Permissionv2.GetDefaultPermlist
|
||||
});
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
else if (config.Permissions is null || !config.Permissions.Any()) // if no perms, add default ones
|
||||
{
|
||||
config.Permissions = Permissionv2.GetDefaultPermlist;
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs)
|
||||
=> configs.AsQueryable().Include(x => x.FollowedStreams).SelectMany(gc => gc.FollowedStreams).ToArray();
|
||||
|
||||
public static IEnumerable<FollowedStream> GetFollowedStreams(this DbSet<GuildConfig> configs, List<ulong> included)
|
||||
=> configs.AsQueryable()
|
||||
.Where(gc => included.Contains(gc.GuildId))
|
||||
.Include(gc => gc.FollowedStreams)
|
||||
.SelectMany(gc => gc.FollowedStreams)
|
||||
.ToList();
|
||||
|
||||
public static void SetCleverbotEnabled(this DbSet<GuildConfig> configs, ulong id, bool cleverbotEnabled)
|
||||
{
|
||||
var conf = configs.FirstOrDefault(gc => gc.GuildId == id);
|
||||
|
||||
if (conf is null)
|
||||
return;
|
||||
|
||||
conf.CleverbotEnabled = cleverbotEnabled;
|
||||
}
|
||||
|
||||
public static XpSettings XpSettingsFor(this DbContext ctx, ulong guildId)
|
||||
{
|
||||
var gc = ctx.GuildConfigsForId(guildId,
|
||||
set => set.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.RoleRewards)
|
||||
.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.CurrencyRewards)
|
||||
.Include(x => x.XpSettings)
|
||||
.ThenInclude(x => x.ExclusionList));
|
||||
|
||||
if (gc.XpSettings is null)
|
||||
gc.XpSettings = new();
|
||||
|
||||
return gc.XpSettings;
|
||||
}
|
||||
|
||||
public static IEnumerable<GeneratingChannel> GetGeneratingChannels(this DbSet<GuildConfig> configs)
|
||||
=> configs.AsQueryable()
|
||||
.Include(x => x.GenerateCurrencyChannelIds)
|
||||
.Where(x => x.GenerateCurrencyChannelIds.Any())
|
||||
.SelectMany(x => x.GenerateCurrencyChannelIds)
|
||||
.Select(x => new GeneratingChannel
|
||||
{
|
||||
ChannelId = x.ChannelId,
|
||||
GuildId = x.GuildConfig.GuildId
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
public class GeneratingChannel
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
||||
}
|
15
src/NadekoBot/Db/Extensions/NadekoExpressionExtensions.cs
Normal file
15
src/NadekoBot/Db/Extensions/NadekoExpressionExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class NadekoExpressionExtensions
|
||||
{
|
||||
public static int ClearFromGuild(this DbSet<NadekoExpression> exprs, ulong guildId)
|
||||
=> exprs.Delete(x => x.GuildId == guildId);
|
||||
|
||||
public static IEnumerable<NadekoExpression> ForId(this DbSet<NadekoExpression> exprs, ulong id)
|
||||
=> exprs.AsNoTracking().AsQueryable().Where(x => x.GuildId == id).ToList();
|
||||
}
|
53
src/NadekoBot/Db/Extensions/QuoteExtensions.cs
Normal file
53
src/NadekoBot/Db/Extensions/QuoteExtensions.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class QuoteExtensions
|
||||
{
|
||||
public static IEnumerable<Quote> GetForGuild(this DbSet<Quote> quotes, ulong guildId)
|
||||
=> quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||
|
||||
public static IReadOnlyCollection<Quote> GetGroup(
|
||||
this DbSet<Quote> quotes,
|
||||
ulong guildId,
|
||||
int page,
|
||||
OrderType order)
|
||||
{
|
||||
var q = quotes.AsQueryable().Where(x => x.GuildId == guildId);
|
||||
if (order == OrderType.Keyword)
|
||||
q = q.OrderBy(x => x.Keyword);
|
||||
else
|
||||
q = q.OrderBy(x => x.Id);
|
||||
|
||||
return q.Skip(15 * page).Take(15).ToArray();
|
||||
}
|
||||
|
||||
public static async Task<Quote> GetRandomQuoteByKeywordAsync(
|
||||
this DbSet<Quote> quotes,
|
||||
ulong guildId,
|
||||
string keyword)
|
||||
{
|
||||
return (await quotes.AsQueryable().Where(q => q.GuildId == guildId && q.Keyword == keyword).ToArrayAsync())
|
||||
.RandomOrDefault();
|
||||
}
|
||||
|
||||
public static async Task<Quote> SearchQuoteKeywordTextAsync(
|
||||
this DbSet<Quote> quotes,
|
||||
ulong guildId,
|
||||
string keyword,
|
||||
string text)
|
||||
{
|
||||
return (await quotes.AsQueryable()
|
||||
.Where(q => q.GuildId == guildId
|
||||
&& (keyword == null || q.Keyword == keyword)
|
||||
&& (EF.Functions.Like(q.Text.ToUpper(), $"%{text.ToUpper()}%")
|
||||
|| EF.Functions.Like(q.AuthorName, text)))
|
||||
.ToArrayAsync())
|
||||
.RandomOrDefault();
|
||||
}
|
||||
|
||||
public static void RemoveAllByKeyword(this DbSet<Quote> quotes, ulong guildId, string keyword)
|
||||
=> quotes.RemoveRange(quotes.AsQueryable().Where(x => x.GuildId == guildId && x.Keyword.ToUpper() == keyword));
|
||||
}
|
23
src/NadekoBot/Db/Extensions/ReminderExtensions.cs
Normal file
23
src/NadekoBot/Db/Extensions/ReminderExtensions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class ReminderExtensions
|
||||
{
|
||||
public static IEnumerable<Reminder> GetIncludedReminders(
|
||||
this DbSet<Reminder> reminders,
|
||||
IEnumerable<ulong> guildIds)
|
||||
=> reminders.AsQueryable().Where(x => guildIds.Contains(x.ServerId) || x.ServerId == 0).ToList();
|
||||
|
||||
public static IEnumerable<Reminder> RemindersFor(this DbSet<Reminder> reminders, ulong userId, int page)
|
||||
=> reminders.AsQueryable().Where(x => x.UserId == userId).OrderBy(x => x.DateAdded).Skip(page * 10).Take(10);
|
||||
|
||||
public static IEnumerable<Reminder> RemindersForServer(this DbSet<Reminder> reminders, ulong serverId, int page)
|
||||
=> reminders.AsQueryable()
|
||||
.Where(x => x.ServerId == serverId)
|
||||
.OrderBy(x => x.DateAdded)
|
||||
.Skip(page * 10)
|
||||
.Take(10);
|
||||
}
|
22
src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs
Normal file
22
src/NadekoBot/Db/Extensions/SelfAssignableRolesExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class SelfAssignableRolesExtensions
|
||||
{
|
||||
public static bool DeleteByGuildAndRoleId(this DbSet<SelfAssignedRole> roles, ulong guildId, ulong roleId)
|
||||
{
|
||||
var role = roles.FirstOrDefault(s => s.GuildId == guildId && s.RoleId == roleId);
|
||||
|
||||
if (role is null)
|
||||
return false;
|
||||
|
||||
roles.Remove(role);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<SelfAssignedRole> GetFromGuild(this DbSet<SelfAssignedRole> roles, ulong guildId)
|
||||
=> roles.AsQueryable().Where(s => s.GuildId == guildId).ToArray();
|
||||
}
|
72
src/NadekoBot/Db/Extensions/UserXpExtensions.cs
Normal file
72
src/NadekoBot/Db/Extensions/UserXpExtensions.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class UserXpExtensions
|
||||
{
|
||||
public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
|
||||
{
|
||||
var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
||||
|
||||
if (usr is null)
|
||||
{
|
||||
ctx.Add(usr = new()
|
||||
{
|
||||
Xp = 0,
|
||||
UserId = userId,
|
||||
NotifyOnLevelUp = XpNotificationLocation.None,
|
||||
GuildId = guildId
|
||||
});
|
||||
}
|
||||
|
||||
return usr;
|
||||
}
|
||||
|
||||
public static List<UserXpStats> GetUsersFor(this DbSet<UserXpStats> xps, ulong guildId, int page)
|
||||
=> xps.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
||||
.Skip(page * 9)
|
||||
.Take(9)
|
||||
.ToList();
|
||||
|
||||
public static List<UserXpStats> GetTopUserXps(this DbSet<UserXpStats> xps, ulong guildId, int count)
|
||||
=> xps.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.OrderByDescending(x => x.Xp + x.AwardedXp)
|
||||
.Take(count)
|
||||
.ToList();
|
||||
|
||||
public static int GetUserGuildRanking(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
||||
=> xps.AsQueryable()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.GuildId == guildId
|
||||
&& x.Xp + x.AwardedXp
|
||||
> xps.AsQueryable()
|
||||
.Where(y => y.UserId == userId && y.GuildId == guildId)
|
||||
.Select(y => y.Xp + y.AwardedXp)
|
||||
.FirstOrDefault())
|
||||
.Count()
|
||||
+ 1;
|
||||
|
||||
public static void ResetGuildUserXp(this DbSet<UserXpStats> xps, ulong userId, ulong guildId)
|
||||
=> xps.Delete(x => x.UserId == userId && x.GuildId == guildId);
|
||||
|
||||
public static void ResetGuildXp(this DbSet<UserXpStats> xps, ulong guildId)
|
||||
=> xps.Delete(x => x.GuildId == guildId);
|
||||
|
||||
public static async Task<LevelStats> GetLevelDataFor(this ITable<UserXpStats> userXp, ulong guildId, ulong userId)
|
||||
=> await userXp
|
||||
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||
.FirstOrDefaultAsyncLinqToDB() is UserXpStats uxs
|
||||
? new(uxs.Xp + uxs.AwardedXp)
|
||||
: new(0);
|
||||
|
||||
}
|
60
src/NadekoBot/Db/Extensions/WarningExtensions.cs
Normal file
60
src/NadekoBot/Db/Extensions/WarningExtensions.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public static class WarningExtensions
|
||||
{
|
||||
public static Warning[] ForId(this DbSet<Warning> warnings, ulong guildId, ulong userId)
|
||||
{
|
||||
var query = warnings.AsQueryable()
|
||||
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||
.OrderByDescending(x => x.DateAdded);
|
||||
|
||||
return query.ToArray();
|
||||
}
|
||||
|
||||
public static bool Forgive(
|
||||
this DbSet<Warning> warnings,
|
||||
ulong guildId,
|
||||
ulong userId,
|
||||
string mod,
|
||||
int index)
|
||||
{
|
||||
if (index < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
|
||||
var warn = warnings.AsQueryable()
|
||||
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||
.OrderByDescending(x => x.DateAdded)
|
||||
.Skip(index)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (warn is null || warn.Forgiven)
|
||||
return false;
|
||||
|
||||
warn.Forgiven = true;
|
||||
warn.ForgivenBy = mod;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task ForgiveAll(
|
||||
this DbSet<Warning> warnings,
|
||||
ulong guildId,
|
||||
ulong userId,
|
||||
string mod)
|
||||
=> await warnings.AsQueryable()
|
||||
.Where(x => x.GuildId == guildId && x.UserId == userId)
|
||||
.ForEachAsync(x =>
|
||||
{
|
||||
if (x.Forgiven != true)
|
||||
{
|
||||
x.Forgiven = true;
|
||||
x.ForgivenBy = mod;
|
||||
}
|
||||
});
|
||||
|
||||
public static Warning[] GetForGuild(this DbSet<Warning> warnings, ulong id)
|
||||
=> warnings.AsQueryable().Where(x => x.GuildId == id).ToArray();
|
||||
}
|
6
src/NadekoBot/Db/Helpers/ActivityType.cs
Normal file
6
src/NadekoBot/Db/Helpers/ActivityType.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Nadeko.Bot.Db;
|
||||
|
||||
public enum ActivityType
|
||||
{
|
||||
|
||||
}
|
47
src/NadekoBot/Db/Helpers/GuildPerm.cs
Normal file
47
src/NadekoBot/Db/Helpers/GuildPerm.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace Nadeko.Bot.Db;
|
||||
|
||||
[Flags]
|
||||
public enum GuildPerm : ulong
|
||||
{
|
||||
CreateInstantInvite = 1,
|
||||
KickMembers = 2,
|
||||
BanMembers = 4,
|
||||
Administrator = 8,
|
||||
ManageChannels = 16, // 0x0000000000000010
|
||||
ManageGuild = 32, // 0x0000000000000020
|
||||
ViewGuildInsights = 524288, // 0x0000000000080000
|
||||
AddReactions = 64, // 0x0000000000000040
|
||||
ViewAuditLog = 128, // 0x0000000000000080
|
||||
ViewChannel = 1024, // 0x0000000000000400
|
||||
SendMessages = 2048, // 0x0000000000000800
|
||||
SendTTSMessages = 4096, // 0x0000000000001000
|
||||
ManageMessages = 8192, // 0x0000000000002000
|
||||
EmbedLinks = 16384, // 0x0000000000004000
|
||||
AttachFiles = 32768, // 0x0000000000008000
|
||||
ReadMessageHistory = 65536, // 0x0000000000010000
|
||||
MentionEveryone = 131072, // 0x0000000000020000
|
||||
UseExternalEmojis = 262144, // 0x0000000000040000
|
||||
Connect = 1048576, // 0x0000000000100000
|
||||
Speak = 2097152, // 0x0000000000200000
|
||||
MuteMembers = 4194304, // 0x0000000000400000
|
||||
DeafenMembers = 8388608, // 0x0000000000800000
|
||||
MoveMembers = 16777216, // 0x0000000001000000
|
||||
UseVAD = 33554432, // 0x0000000002000000
|
||||
PrioritySpeaker = 256, // 0x0000000000000100
|
||||
Stream = 512, // 0x0000000000000200
|
||||
ChangeNickname = 67108864, // 0x0000000004000000
|
||||
ManageNicknames = 134217728, // 0x0000000008000000
|
||||
ManageRoles = 268435456, // 0x0000000010000000
|
||||
ManageWebhooks = 536870912, // 0x0000000020000000
|
||||
ManageEmojisAndStickers = 1073741824, // 0x0000000040000000
|
||||
UseApplicationCommands = 2147483648, // 0x0000000080000000
|
||||
RequestToSpeak = 4294967296, // 0x0000000100000000
|
||||
ManageEvents = 8589934592, // 0x0000000200000000
|
||||
ManageThreads = 17179869184, // 0x0000000400000000
|
||||
CreatePublicThreads = 34359738368, // 0x0000000800000000
|
||||
CreatePrivateThreads = 68719476736, // 0x0000001000000000
|
||||
UseExternalStickers = 137438953472, // 0x0000002000000000
|
||||
SendMessagesInThreads = 274877906944, // 0x0000004000000000
|
||||
StartEmbeddedActivities = 549755813888, // 0x0000008000000000
|
||||
ModerateMembers = 1099511627776, // 0x0000010000000000
|
||||
}
|
41
src/NadekoBot/Db/LevelStats.cs
Normal file
41
src/NadekoBot/Db/LevelStats.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Db;
|
||||
|
||||
public readonly struct LevelStats
|
||||
{
|
||||
public const int XP_REQUIRED_LVL_1 = 36;
|
||||
|
||||
public long Level { get; }
|
||||
public long LevelXp { get; }
|
||||
public long RequiredXp { get; }
|
||||
public long TotalXp { get; }
|
||||
|
||||
public LevelStats(long xp)
|
||||
{
|
||||
if (xp < 0)
|
||||
xp = 0;
|
||||
|
||||
TotalXp = xp;
|
||||
|
||||
const int baseXp = XP_REQUIRED_LVL_1;
|
||||
|
||||
var required = baseXp;
|
||||
var totalXp = 0;
|
||||
var lvl = 1;
|
||||
while (true)
|
||||
{
|
||||
required = (int)(baseXp + (baseXp / 4.0 * (lvl - 1)));
|
||||
|
||||
if (required + totalXp > xp)
|
||||
break;
|
||||
|
||||
totalXp += required;
|
||||
lvl++;
|
||||
}
|
||||
|
||||
Level = lvl - 1;
|
||||
LevelXp = xp - totalXp;
|
||||
RequiredXp = required;
|
||||
}
|
||||
}
|
14
src/NadekoBot/Db/Models/AutoCommand.cs
Normal file
14
src/NadekoBot/Db/Models/AutoCommand.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class AutoCommand : DbEntity
|
||||
{
|
||||
public string CommandText { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public string ChannelName { get; set; }
|
||||
public ulong? GuildId { get; set; }
|
||||
public string GuildName { get; set; }
|
||||
public ulong? VoiceChannelId { get; set; }
|
||||
public string VoiceChannelName { get; set; }
|
||||
public int Interval { get; set; }
|
||||
}
|
9
src/NadekoBot/Db/Models/AutoPublishChannel.cs
Normal file
9
src/NadekoBot/Db/Models/AutoPublishChannel.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
public class AutoPublishChannel : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
10
src/NadekoBot/Db/Models/AutoTranslateChannel.cs
Normal file
10
src/NadekoBot/Db/Models/AutoTranslateChannel.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class AutoTranslateChannel : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public bool AutoDelete { get; set; }
|
||||
public IList<AutoTranslateUser> Users { get; set; } = new List<AutoTranslateUser>();
|
||||
}
|
11
src/NadekoBot/Db/Models/AutoTranslateUser.cs
Normal file
11
src/NadekoBot/Db/Models/AutoTranslateUser.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class AutoTranslateUser : DbEntity
|
||||
{
|
||||
public int ChannelId { get; set; }
|
||||
public AutoTranslateChannel Channel { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public string Source { get; set; }
|
||||
public string Target { get; set; }
|
||||
}
|
15
src/NadekoBot/Db/Models/BlacklistEntry.cs
Normal file
15
src/NadekoBot/Db/Models/BlacklistEntry.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class BlacklistEntry : DbEntity
|
||||
{
|
||||
public ulong ItemId { get; set; }
|
||||
public BlacklistType Type { get; set; }
|
||||
}
|
||||
|
||||
public enum BlacklistType
|
||||
{
|
||||
Server,
|
||||
Channel,
|
||||
User
|
||||
}
|
8
src/NadekoBot/Db/Models/CommandAlias.cs
Normal file
8
src/NadekoBot/Db/Models/CommandAlias.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class CommandAlias : DbEntity
|
||||
{
|
||||
public string Trigger { get; set; }
|
||||
public string Mapping { get; set; }
|
||||
}
|
8
src/NadekoBot/Db/Models/CommandCooldown.cs
Normal file
8
src/NadekoBot/Db/Models/CommandCooldown.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class CommandCooldown : DbEntity
|
||||
{
|
||||
public int Seconds { get; set; }
|
||||
public string CommandName { get; set; }
|
||||
}
|
12
src/NadekoBot/Db/Models/CurrencyTransaction.cs
Normal file
12
src/NadekoBot/Db/Models/CurrencyTransaction.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class CurrencyTransaction : DbEntity
|
||||
{
|
||||
public long Amount { get; set; }
|
||||
public string Note { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Extra { get; set; }
|
||||
public ulong? OtherId { get; set; }
|
||||
}
|
12
src/NadekoBot/Db/Models/DbEntity.cs
Normal file
12
src/NadekoBot/Db/Models/DbEntity.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
#nullable disable
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class DbEntity
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
|
||||
public DateTime? DateAdded { get; set; } = DateTime.UtcNow;
|
||||
}
|
14
src/NadekoBot/Db/Models/DelMsgOnCmdChannel.cs
Normal file
14
src/NadekoBot/Db/Models/DelMsgOnCmdChannel.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class DelMsgOnCmdChannel : DbEntity
|
||||
{
|
||||
public ulong ChannelId { get; set; }
|
||||
public bool State { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is DelMsgOnCmdChannel x && x.ChannelId == ChannelId;
|
||||
}
|
12
src/NadekoBot/Db/Models/DiscordPemOverride.cs
Normal file
12
src/NadekoBot/Db/Models/DiscordPemOverride.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db;
|
||||
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class DiscordPermOverride : DbEntity
|
||||
{
|
||||
public GuildPerm Perm { get; set; }
|
||||
|
||||
public ulong? GuildId { get; set; }
|
||||
public string Command { get; set; }
|
||||
}
|
37
src/NadekoBot/Db/Models/DiscordUser.cs
Normal file
37
src/NadekoBot/Db/Models/DiscordUser.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
|
||||
// FUTURE remove LastLevelUp from here and UserXpStats
|
||||
public class DiscordUser : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Discriminator { get; set; }
|
||||
public string AvatarId { get; set; }
|
||||
|
||||
public int? ClubId { get; set; }
|
||||
public ClubInfo Club { get; set; }
|
||||
public bool IsClubAdmin { get; set; }
|
||||
|
||||
public long TotalXp { get; set; }
|
||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||
|
||||
public long CurrencyAmount { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is DiscordUser du ? du.UserId == UserId : false;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Discriminator) || Discriminator == "0000")
|
||||
return Username;
|
||||
|
||||
return Username + "#" + Discriminator;
|
||||
}
|
||||
}
|
49
src/NadekoBot/Db/Models/Event.cs
Normal file
49
src/NadekoBot/Db/Models/Event.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class CurrencyEvent
|
||||
{
|
||||
public enum Type
|
||||
{
|
||||
Reaction,
|
||||
|
||||
GameStatus
|
||||
//NotRaid,
|
||||
}
|
||||
|
||||
public ulong ServerId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public ulong MessageId { get; set; }
|
||||
public Type EventType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Amount of currency that the user will be rewarded.
|
||||
/// </summary>
|
||||
public long Amount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of currency that can be handed out.
|
||||
/// </summary>
|
||||
public long PotSize { get; set; }
|
||||
|
||||
public List<AwardedUser> AwardedUsers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used as extra data storage for events which need it.
|
||||
/// </summary>
|
||||
public ulong ExtraId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// May be used for some future event.
|
||||
/// </summary>
|
||||
public ulong ExtraId2 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// May be used for some future event.
|
||||
/// </summary>
|
||||
public string ExtraString { get; set; }
|
||||
}
|
||||
|
||||
public class AwardedUser
|
||||
{
|
||||
}
|
19
src/NadekoBot/Db/Models/FeedSub.cs
Normal file
19
src/NadekoBot/Db/Models/FeedSub.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class FeedSub : DbEntity
|
||||
{
|
||||
public int GuildConfigId { get; set; }
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
|
||||
public ulong ChannelId { get; set; }
|
||||
public string Url { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Url.GetHashCode(StringComparison.InvariantCulture) ^ GuildConfigId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FeedSub s && s.Url.ToLower() == Url.ToLower() && s.GuildConfigId == GuildConfigId;
|
||||
}
|
35
src/NadekoBot/Db/Models/FollowedStream.cs
Normal file
35
src/NadekoBot/Db/Models/FollowedStream.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
public class FollowedStream : DbEntity
|
||||
{
|
||||
public enum FType
|
||||
{
|
||||
Twitch = 0,
|
||||
Picarto = 3,
|
||||
Youtube = 4,
|
||||
Facebook = 5,
|
||||
Trovo = 6
|
||||
}
|
||||
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public string Username { get; set; }
|
||||
public FType Type { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
protected bool Equals(FollowedStream other)
|
||||
=> ChannelId == other.ChannelId
|
||||
&& Username.Trim().ToUpperInvariant() == other.Username.Trim().ToUpperInvariant()
|
||||
&& Type == other.Type;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> HashCode.Combine(ChannelId, Username, (int)Type);
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FollowedStream fs && Equals(fs);
|
||||
|
||||
|
||||
}
|
14
src/NadekoBot/Db/Models/GCChannelId.cs
Normal file
14
src/NadekoBot/Db/Models/GCChannelId.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class GCChannelId : DbEntity
|
||||
{
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is GCChannelId gc && gc.ChannelId == ChannelId;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
}
|
9
src/NadekoBot/Db/Models/GamblingStats.cs
Normal file
9
src/NadekoBot/Db/Models/GamblingStats.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class GamblingStats : DbEntity
|
||||
{
|
||||
public string Feature { get; set; }
|
||||
public decimal Bet { get; set; }
|
||||
public decimal PaidOut { get; set; }
|
||||
}
|
11
src/NadekoBot/Db/Models/GroupName.cs
Normal file
11
src/NadekoBot/Db/Models/GroupName.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class GroupName : DbEntity
|
||||
{
|
||||
public int GuildConfigId { get; set; }
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
|
||||
public int Number { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
109
src/NadekoBot/Db/Models/GuildConfig.cs
Normal file
109
src/NadekoBot/Db/Models/GuildConfig.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Db.Models;
|
||||
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class GuildConfig : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
|
||||
public string Prefix { get; set; }
|
||||
|
||||
public bool DeleteMessageOnCommand { get; set; }
|
||||
public HashSet<DelMsgOnCmdChannel> DelMsgOnCmdChannels { get; set; } = new();
|
||||
|
||||
public string AutoAssignRoleIds { get; set; }
|
||||
|
||||
//greet stuff
|
||||
public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
|
||||
public int AutoDeleteByeMessagesTimer { get; set; } = 30;
|
||||
|
||||
public ulong GreetMessageChannelId { get; set; }
|
||||
public ulong ByeMessageChannelId { get; set; }
|
||||
|
||||
public bool SendDmGreetMessage { get; set; }
|
||||
public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
||||
|
||||
public bool SendChannelGreetMessage { get; set; }
|
||||
public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
||||
|
||||
public bool SendChannelByeMessage { get; set; }
|
||||
public string ChannelByeMessageText { get; set; } = "%user% has left!";
|
||||
|
||||
//self assignable roles
|
||||
public bool ExclusiveSelfAssignedRoles { get; set; }
|
||||
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
|
||||
|
||||
//stream notifications
|
||||
public HashSet<FollowedStream> FollowedStreams { get; set; } = new();
|
||||
|
||||
//currencyGeneration
|
||||
public HashSet<GCChannelId> GenerateCurrencyChannelIds { get; set; } = new();
|
||||
|
||||
public List<Permissionv2> Permissions { get; set; }
|
||||
public bool VerbosePermissions { get; set; } = true;
|
||||
public string PermissionRole { get; set; }
|
||||
|
||||
public HashSet<CommandCooldown> CommandCooldowns { get; set; } = new();
|
||||
|
||||
//filtering
|
||||
public bool FilterInvites { get; set; }
|
||||
public bool FilterLinks { get; set; }
|
||||
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; } = new();
|
||||
public HashSet<FilterLinksChannelId> FilterLinksChannelIds { get; set; } = new();
|
||||
|
||||
//public bool FilterLinks { get; set; }
|
||||
//public HashSet<FilterLinksChannelId> FilterLinksChannels { get; set; } = new HashSet<FilterLinksChannelId>();
|
||||
|
||||
public bool FilterWords { get; set; }
|
||||
public HashSet<FilteredWord> FilteredWords { get; set; } = new();
|
||||
public HashSet<FilterWordsChannelId> FilterWordsChannelIds { get; set; } = new();
|
||||
|
||||
public HashSet<MutedUserId> MutedUsers { get; set; } = new();
|
||||
|
||||
public string MuteRoleName { get; set; }
|
||||
public bool CleverbotEnabled { get; set; }
|
||||
|
||||
public AntiRaidSetting AntiRaidSetting { get; set; }
|
||||
public AntiSpamSetting AntiSpamSetting { get; set; }
|
||||
public AntiAltSetting AntiAltSetting { get; set; }
|
||||
|
||||
public string Locale { get; set; }
|
||||
public string TimeZoneId { get; set; }
|
||||
|
||||
public HashSet<UnmuteTimer> UnmuteTimers { get; set; } = new();
|
||||
public HashSet<UnbanTimer> UnbanTimer { get; set; } = new();
|
||||
public HashSet<UnroleTimer> UnroleTimer { get; set; } = new();
|
||||
public HashSet<VcRoleInfo> VcRoleInfos { get; set; }
|
||||
public HashSet<CommandAlias> CommandAliases { get; set; } = new();
|
||||
public List<WarningPunishment> WarnPunishments { get; set; } = new();
|
||||
public bool WarningsInitialized { get; set; }
|
||||
public HashSet<SlowmodeIgnoredUser> SlowmodeIgnoredUsers { get; set; }
|
||||
public HashSet<SlowmodeIgnoredRole> SlowmodeIgnoredRoles { get; set; }
|
||||
|
||||
public List<ShopEntry> ShopEntries { get; set; }
|
||||
public ulong? GameVoiceChannel { get; set; }
|
||||
public bool VerboseErrors { get; set; } = true;
|
||||
|
||||
public StreamRoleSettings StreamRole { get; set; }
|
||||
|
||||
public XpSettings XpSettings { get; set; }
|
||||
public List<FeedSub> FeedSubs { get; set; } = new();
|
||||
public bool NotifyStreamOffline { get; set; }
|
||||
public bool DeleteStreamOnlineMessage { get; set; }
|
||||
public List<GroupName> SelfAssignableRoleGroupNames { get; set; }
|
||||
public int WarnExpireHours { get; set; }
|
||||
public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
|
||||
|
||||
public bool DisableGlobalExpressions { get; set; } = false;
|
||||
|
||||
#region Boost Message
|
||||
|
||||
public bool SendBoostMessage { get; set; }
|
||||
public string BoostMessage { get; set; } = "%user% just boosted this server!";
|
||||
public ulong BoostMessageChannelId { get; set; }
|
||||
public int BoostMessageDeleteAfter { get; set; }
|
||||
public bool StickyRoles { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
16
src/NadekoBot/Db/Models/IgnoredLogItem.cs
Normal file
16
src/NadekoBot/Db/Models/IgnoredLogItem.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class IgnoredLogItem : DbEntity
|
||||
{
|
||||
public int LogSettingId { get; set; }
|
||||
public LogSetting LogSetting { get; set; }
|
||||
public ulong LogItemId { get; set; }
|
||||
public IgnoredItemType ItemType { get; set; }
|
||||
}
|
||||
|
||||
public enum IgnoredItemType
|
||||
{
|
||||
Channel,
|
||||
User
|
||||
}
|
8
src/NadekoBot/Db/Models/IgnoredVoicePresenceChannel.cs
Normal file
8
src/NadekoBot/Db/Models/IgnoredVoicePresenceChannel.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class IgnoredVoicePresenceChannel : DbEntity
|
||||
{
|
||||
public LogSetting LogSetting { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
}
|
15
src/NadekoBot/Db/Models/ImageOnlyChannel.cs
Normal file
15
src/NadekoBot/Db/Models/ImageOnlyChannel.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class ImageOnlyChannel : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public OnlyChannelType Type { get; set; }
|
||||
}
|
||||
|
||||
public enum OnlyChannelType
|
||||
{
|
||||
Image,
|
||||
Link
|
||||
}
|
37
src/NadekoBot/Db/Models/LogSetting.cs
Normal file
37
src/NadekoBot/Db/Models/LogSetting.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class LogSetting : DbEntity
|
||||
{
|
||||
public List<IgnoredLogItem> LogIgnores { get; set; } = new();
|
||||
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong? LogOtherId { get; set; }
|
||||
public ulong? MessageUpdatedId { get; set; }
|
||||
public ulong? MessageDeletedId { get; set; }
|
||||
|
||||
public ulong? UserJoinedId { get; set; }
|
||||
public ulong? UserLeftId { get; set; }
|
||||
public ulong? UserBannedId { get; set; }
|
||||
public ulong? UserUnbannedId { get; set; }
|
||||
public ulong? UserUpdatedId { get; set; }
|
||||
|
||||
public ulong? ChannelCreatedId { get; set; }
|
||||
public ulong? ChannelDestroyedId { get; set; }
|
||||
public ulong? ChannelUpdatedId { get; set; }
|
||||
|
||||
|
||||
public ulong? ThreadDeletedId { get; set; }
|
||||
public ulong? ThreadCreatedId { get; set; }
|
||||
|
||||
public ulong? UserMutedId { get; set; }
|
||||
|
||||
//userpresence
|
||||
public ulong? LogUserPresenceId { get; set; }
|
||||
|
||||
//voicepresence
|
||||
|
||||
public ulong? LogVoicePresenceId { get; set; }
|
||||
public ulong? LogVoicePresenceTTSId { get; set; }
|
||||
public ulong? LogWarnsId { get; set; }
|
||||
}
|
55
src/NadekoBot/Db/Models/Permission.cs
Normal file
55
src/NadekoBot/Db/Models/Permission.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
#nullable disable
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
[DebuggerDisplay("{PrimaryTarget}{SecondaryTarget} {SecondaryTargetName} {State} {PrimaryTargetId}")]
|
||||
public class Permissionv2 : DbEntity, IIndexed
|
||||
{
|
||||
public int? GuildConfigId { get; set; }
|
||||
public int Index { get; set; }
|
||||
|
||||
public PrimaryPermissionType PrimaryTarget { get; set; }
|
||||
public ulong PrimaryTargetId { get; set; }
|
||||
|
||||
public SecondaryPermissionType SecondaryTarget { get; set; }
|
||||
public string SecondaryTargetName { get; set; }
|
||||
|
||||
public bool IsCustomCommand { get; set; }
|
||||
|
||||
public bool State { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public static Permissionv2 AllowAllPerm
|
||||
=> new()
|
||||
{
|
||||
PrimaryTarget = PrimaryPermissionType.Server,
|
||||
PrimaryTargetId = 0,
|
||||
SecondaryTarget = SecondaryPermissionType.AllModules,
|
||||
SecondaryTargetName = "*",
|
||||
State = true,
|
||||
Index = 0
|
||||
};
|
||||
|
||||
public static List<Permissionv2> GetDefaultPermlist
|
||||
=> new()
|
||||
{
|
||||
AllowAllPerm
|
||||
};
|
||||
}
|
||||
|
||||
public enum PrimaryPermissionType
|
||||
{
|
||||
User,
|
||||
Channel,
|
||||
Role,
|
||||
Server
|
||||
}
|
||||
|
||||
public enum SecondaryPermissionType
|
||||
{
|
||||
Module,
|
||||
Command,
|
||||
AllModules
|
||||
}
|
12
src/NadekoBot/Db/Models/PlantedCurrency.cs
Normal file
12
src/NadekoBot/Db/Models/PlantedCurrency.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class PlantedCurrency : DbEntity
|
||||
{
|
||||
public long Amount { get; set; }
|
||||
public string Password { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public ulong MessageId { get; set; }
|
||||
}
|
18
src/NadekoBot/Db/Models/PlaylistSong.cs
Normal file
18
src/NadekoBot/Db/Models/PlaylistSong.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class PlaylistSong : DbEntity
|
||||
{
|
||||
public string Provider { get; set; }
|
||||
public MusicType ProviderType { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public string Query { get; set; }
|
||||
}
|
||||
|
||||
public enum MusicType
|
||||
{
|
||||
Radio,
|
||||
YouTube,
|
||||
Local,
|
||||
}
|
19
src/NadekoBot/Db/Models/Reminder.cs
Normal file
19
src/NadekoBot/Db/Models/Reminder.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class Reminder : DbEntity
|
||||
{
|
||||
public DateTime When { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public ulong ServerId { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public string Message { get; set; }
|
||||
public bool IsPrivate { get; set; }
|
||||
public ReminderType Type { get; set; }
|
||||
}
|
||||
|
||||
public enum ReminderType
|
||||
{
|
||||
User,
|
||||
Timely
|
||||
}
|
15
src/NadekoBot/Db/Models/Repeater.cs
Normal file
15
src/NadekoBot/Db/Models/Repeater.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class Repeater
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public ulong? LastMessageId { get; set; }
|
||||
public string Message { get; set; }
|
||||
public TimeSpan Interval { get; set; }
|
||||
public TimeSpan? StartTimeOfDay { get; set; }
|
||||
public bool NoRedundant { get; set; }
|
||||
public DateTime DateAdded { get; set; }
|
||||
}
|
10
src/NadekoBot/Db/Models/RotatingPlayingStatus.cs
Normal file
10
src/NadekoBot/Db/Models/RotatingPlayingStatus.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db;
|
||||
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class RotatingPlayingStatus : DbEntity
|
||||
{
|
||||
public string Status { get; set; }
|
||||
public ActivityType Type { get; set; }
|
||||
}
|
46
src/NadekoBot/Db/Models/ShopEntry.cs
Normal file
46
src/NadekoBot/Db/Models/ShopEntry.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public enum ShopEntryType
|
||||
{
|
||||
Role,
|
||||
|
||||
List,
|
||||
Command
|
||||
}
|
||||
|
||||
public class ShopEntry : DbEntity, IIndexed
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public int Price { get; set; }
|
||||
public string Name { get; set; }
|
||||
public ulong AuthorId { get; set; }
|
||||
|
||||
public ShopEntryType Type { get; set; }
|
||||
|
||||
//role
|
||||
public string RoleName { get; set; }
|
||||
public ulong RoleId { get; set; }
|
||||
|
||||
//list
|
||||
public HashSet<ShopEntryItem> Items { get; set; } = new();
|
||||
public ulong? RoleRequirement { get; set; }
|
||||
|
||||
// command
|
||||
public string Command { get; set; }
|
||||
}
|
||||
|
||||
public class ShopEntryItem : DbEntity
|
||||
{
|
||||
public string Text { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null || GetType() != obj.GetType())
|
||||
return false;
|
||||
return ((ShopEntryItem)obj).Text == Text;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Text.GetHashCode(StringComparison.InvariantCulture);
|
||||
}
|
13
src/NadekoBot/Db/Models/StreamOnlineMessage.cs
Normal file
13
src/NadekoBot/Db/Models/StreamOnlineMessage.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
public class StreamOnlineMessage : DbEntity
|
||||
{
|
||||
public ulong ChannelId { get; set; }
|
||||
public ulong MessageId { get; set; }
|
||||
|
||||
public FollowedStream.FType Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
68
src/NadekoBot/Db/Models/StreamRoleSettings.cs
Normal file
68
src/NadekoBot/Db/Models/StreamRoleSettings.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class StreamRoleSettings : DbEntity
|
||||
{
|
||||
public int GuildConfigId { get; set; }
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the feature is enabled in the guild.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id of the role to give to the users in the role 'FromRole' when they start streaming
|
||||
/// </summary>
|
||||
public ulong AddRoleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Id of the role whose users are eligible to get the 'AddRole'
|
||||
/// </summary>
|
||||
public ulong FromRoleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set, feature will only apply to users who have this keyword in their streaming status.
|
||||
/// </summary>
|
||||
public string Keyword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A collection of whitelisted users' IDs. Whitelisted users don't require 'keyword' in
|
||||
/// order to get the stream role.
|
||||
/// </summary>
|
||||
public HashSet<StreamRoleWhitelistedUser> Whitelist { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// A collection of blacklisted users' IDs. Blacklisted useres will never get the stream role.
|
||||
/// </summary>
|
||||
public HashSet<StreamRoleBlacklistedUser> Blacklist { get; set; } = new();
|
||||
}
|
||||
|
||||
public class StreamRoleBlacklistedUser : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public string Username { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is not StreamRoleBlacklistedUser x)
|
||||
return false;
|
||||
|
||||
return x.UserId == UserId;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
}
|
||||
|
||||
public class StreamRoleWhitelistedUser : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public string Username { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is StreamRoleWhitelistedUser x ? x.UserId == UserId : false;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
}
|
8
src/NadekoBot/Db/Models/VcRoleInfo.cs
Normal file
8
src/NadekoBot/Db/Models/VcRoleInfo.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class VcRoleInfo : DbEntity
|
||||
{
|
||||
public ulong VoiceChannelId { get; set; }
|
||||
public ulong RoleId { get; set; }
|
||||
}
|
11
src/NadekoBot/Db/Models/anti/AntiAltSetting.cs
Normal file
11
src/NadekoBot/Db/Models/anti/AntiAltSetting.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class AntiAltSetting
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int GuildConfigId { get; set; }
|
||||
public TimeSpan MinAge { get; set; }
|
||||
public PunishmentAction Action { get; set; }
|
||||
public int ActionDurationMinutes { get; set; }
|
||||
public ulong? RoleId { get; set; }
|
||||
}
|
19
src/NadekoBot/Db/Models/anti/AntiRaidSetting.cs
Normal file
19
src/NadekoBot/Db/Models/anti/AntiRaidSetting.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
|
||||
public class AntiRaidSetting : DbEntity
|
||||
{
|
||||
public int GuildConfigId { get; set; }
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
|
||||
public int UserThreshold { get; set; }
|
||||
public int Seconds { get; set; }
|
||||
public PunishmentAction Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Duration of the punishment, in minutes. This works only for supported Actions, like:
|
||||
/// Mute, Chatmute, Voicemute, etc...
|
||||
/// </summary>
|
||||
public int PunishDuration { get; set; }
|
||||
}
|
12
src/NadekoBot/Db/Models/anti/AntiSpamIgnore.cs
Normal file
12
src/NadekoBot/Db/Models/anti/AntiSpamIgnore.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class AntiSpamIgnore : DbEntity
|
||||
{
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
=> obj is AntiSpamIgnore inst && inst.ChannelId == ChannelId;
|
||||
}
|
14
src/NadekoBot/Db/Models/anti/AntiSpamSetting.cs
Normal file
14
src/NadekoBot/Db/Models/anti/AntiSpamSetting.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
#nullable disable
|
||||
public class AntiSpamSetting : DbEntity
|
||||
{
|
||||
public int GuildConfigId { get; set; }
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
|
||||
public PunishmentAction Action { get; set; }
|
||||
public int MessageThreshold { get; set; } = 3;
|
||||
public int MuteTime { get; set; }
|
||||
public ulong? RoleId { get; set; }
|
||||
public HashSet<AntiSpamIgnore> IgnoredChannels { get; set; } = new();
|
||||
}
|
42
src/NadekoBot/Db/Models/club/ClubInfo.cs
Normal file
42
src/NadekoBot/Db/Models/club/ClubInfo.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
public class ClubInfo : DbEntity
|
||||
{
|
||||
[MaxLength(20)]
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string ImageUrl { get; set; } = string.Empty;
|
||||
|
||||
public int Xp { get; set; } = 0;
|
||||
public int? OwnerId { get; set; }
|
||||
public DiscordUser Owner { get; set; }
|
||||
|
||||
public List<DiscordUser> Members { get; set; } = new();
|
||||
public List<ClubApplicants> Applicants { get; set; } = new();
|
||||
public List<ClubBans> Bans { get; set; } = new();
|
||||
|
||||
public override string ToString()
|
||||
=> Name;
|
||||
}
|
||||
|
||||
public class ClubApplicants
|
||||
{
|
||||
public int ClubId { get; set; }
|
||||
public ClubInfo Club { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
public DiscordUser User { get; set; }
|
||||
}
|
||||
|
||||
public class ClubBans
|
||||
{
|
||||
public int ClubId { get; set; }
|
||||
public ClubInfo Club { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
public DiscordUser User { get; set; }
|
||||
}
|
9
src/NadekoBot/Db/Models/currency/BankUser.cs
Normal file
9
src/NadekoBot/Db/Models/currency/BankUser.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
public class BankUser : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public long Balance { get; set; }
|
||||
}
|
27
src/NadekoBot/Db/Models/expr/NadekoExpression.cs
Normal file
27
src/NadekoBot/Db/Models/expr/NadekoExpression.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class NadekoExpression : DbEntity
|
||||
{
|
||||
public ulong? GuildId { get; set; }
|
||||
public string Response { get; set; }
|
||||
public string Trigger { get; set; }
|
||||
|
||||
public bool AutoDeleteTrigger { get; set; }
|
||||
public bool DmResponse { get; set; }
|
||||
public bool ContainsAnywhere { get; set; }
|
||||
public bool AllowTarget { get; set; }
|
||||
public string Reactions { get; set; }
|
||||
|
||||
public string[] GetReactions()
|
||||
=> string.IsNullOrWhiteSpace(Reactions) ? Array.Empty<string>() : Reactions.Split("@@@");
|
||||
|
||||
public bool IsGlobal()
|
||||
=> GuildId is null or 0;
|
||||
}
|
||||
|
||||
public class ReactionResponse : DbEntity
|
||||
{
|
||||
public bool OwnerOnly { get; set; }
|
||||
public string Text { get; set; }
|
||||
}
|
26
src/NadekoBot/Db/Models/expr/Quote.cs
Normal file
26
src/NadekoBot/Db/Models/expr/Quote.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
#nullable disable
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class Quote : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Keyword { get; set; }
|
||||
|
||||
[Required]
|
||||
public string AuthorName { get; set; }
|
||||
|
||||
public ulong AuthorId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Text { get; set; }
|
||||
}
|
||||
|
||||
public enum OrderType
|
||||
{
|
||||
Id = -1,
|
||||
Keyword = -2
|
||||
}
|
30
src/NadekoBot/Db/Models/filter/FilterChannelId.cs
Normal file
30
src/NadekoBot/Db/Models/filter/FilterChannelId.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class FilterChannelId : DbEntity
|
||||
{
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public bool Equals(FilterChannelId other)
|
||||
=> ChannelId == other.ChannelId;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FilterChannelId fci && Equals(fci);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
}
|
||||
|
||||
public class FilterWordsChannelId : DbEntity
|
||||
{
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public bool Equals(FilterWordsChannelId other)
|
||||
=> ChannelId == other.ChannelId;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FilterWordsChannelId fci && Equals(fci);
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
}
|
13
src/NadekoBot/Db/Models/filter/FilterLinksChannelId.cs
Normal file
13
src/NadekoBot/Db/Models/filter/FilterLinksChannelId.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class FilterLinksChannelId : DbEntity
|
||||
{
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is FilterLinksChannelId f && f.ChannelId == ChannelId;
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ChannelId.GetHashCode();
|
||||
}
|
7
src/NadekoBot/Db/Models/filter/FilteredWord.cs
Normal file
7
src/NadekoBot/Db/Models/filter/FilteredWord.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class FilteredWord : DbEntity
|
||||
{
|
||||
public string Word { get; set; }
|
||||
}
|
14
src/NadekoBot/Db/Models/giveaway/GiveawayModel.cs
Normal file
14
src/NadekoBot/Db/Models/giveaway/GiveawayModel.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
#nullable disable
|
||||
public sealed class GiveawayModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong MessageId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public IList<GiveawayUser> Participants { get; set; } = new List<GiveawayUser>();
|
||||
public DateTime EndsAt { get; set; }
|
||||
}
|
10
src/NadekoBot/Db/Models/giveaway/GiveawayUser.cs
Normal file
10
src/NadekoBot/Db/Models/giveaway/GiveawayUser.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
#nullable disable
|
||||
public sealed class GiveawayUser
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int GiveawayId { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
9
src/NadekoBot/Db/Models/punish/BanTemplate.cs
Normal file
9
src/NadekoBot/Db/Models/punish/BanTemplate.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class BanTemplate : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public string Text { get; set; }
|
||||
public int? PruneDays { get; set; }
|
||||
}
|
13
src/NadekoBot/Db/Models/punish/MutedUserId.cs
Normal file
13
src/NadekoBot/Db/Models/punish/MutedUserId.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class MutedUserId : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is MutedUserId mui ? mui.UserId == UserId : false;
|
||||
}
|
15
src/NadekoBot/Db/Models/punish/PunishmentAction.cs
Normal file
15
src/NadekoBot/Db/Models/punish/PunishmentAction.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public enum PunishmentAction
|
||||
{
|
||||
Mute,
|
||||
Kick,
|
||||
Ban,
|
||||
Softban,
|
||||
RemoveRoles,
|
||||
ChatMute,
|
||||
VoiceMute,
|
||||
AddRole,
|
||||
Warn,
|
||||
TimeOut
|
||||
}
|
8
src/NadekoBot/Db/Models/punish/WarnExpireAction.cs
Normal file
8
src/NadekoBot/Db/Models/punish/WarnExpireAction.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public enum WarnExpireAction
|
||||
{
|
||||
Clear,
|
||||
Delete
|
||||
}
|
13
src/NadekoBot/Db/Models/punish/Warning.cs
Normal file
13
src/NadekoBot/Db/Models/punish/Warning.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class Warning : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public string Reason { get; set; }
|
||||
public bool Forgiven { get; set; }
|
||||
public string ForgivenBy { get; set; }
|
||||
public string Moderator { get; set; }
|
||||
public long Weight { get; set; }
|
||||
}
|
10
src/NadekoBot/Db/Models/punish/WarningPunishment.cs
Normal file
10
src/NadekoBot/Db/Models/punish/WarningPunishment.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class WarningPunishment : DbEntity
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public PunishmentAction Punishment { get; set; }
|
||||
public int Time { get; set; }
|
||||
public ulong? RoleId { get; set; }
|
||||
}
|
18
src/NadekoBot/Db/Models/roles/ReactionRole.cs
Normal file
18
src/NadekoBot/Db/Models/roles/ReactionRole.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
#nullable disable
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class ReactionRoleV2 : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
public ulong MessageId { get; set; }
|
||||
|
||||
[MaxLength(100)]
|
||||
public string Emote { get; set; }
|
||||
public ulong RoleId { get; set; }
|
||||
public int Group { get; set; }
|
||||
public int LevelReq { get; set; }
|
||||
}
|
11
src/NadekoBot/Db/Models/roles/SelfAssignableRole.cs
Normal file
11
src/NadekoBot/Db/Models/roles/SelfAssignableRole.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class SelfAssignedRole : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public ulong RoleId { get; set; }
|
||||
|
||||
public int Group { get; set; }
|
||||
public int LevelRequirement { get; set; }
|
||||
}
|
14
src/NadekoBot/Db/Models/roles/StickyRoles.cs
Normal file
14
src/NadekoBot/Db/Models/roles/StickyRoles.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
#nullable disable
|
||||
public class StickyRole : DbEntity
|
||||
{
|
||||
public ulong GuildId { get; set; }
|
||||
public string RoleIds { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
|
||||
public ulong[] GetRoleIds()
|
||||
=> string.IsNullOrWhiteSpace(RoleIds)
|
||||
? []
|
||||
: RoleIds.Split(',').Select(ulong.Parse).ToArray();
|
||||
}
|
20
src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredRole.cs
Normal file
20
src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredRole.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class SlowmodeIgnoredRole : DbEntity
|
||||
{
|
||||
public ulong RoleId { get; set; }
|
||||
|
||||
// override object.Equals
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null || GetType() != obj.GetType())
|
||||
return false;
|
||||
|
||||
return ((SlowmodeIgnoredRole)obj).RoleId == RoleId;
|
||||
}
|
||||
|
||||
// override object.GetHashCode
|
||||
public override int GetHashCode()
|
||||
=> RoleId.GetHashCode();
|
||||
}
|
20
src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredUser.cs
Normal file
20
src/NadekoBot/Db/Models/slowmode/SlowmodeIgnoredUser.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class SlowmodeIgnoredUser : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
|
||||
// override object.Equals
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null || GetType() != obj.GetType())
|
||||
return false;
|
||||
|
||||
return ((SlowmodeIgnoredUser)obj).UserId == UserId;
|
||||
}
|
||||
|
||||
// override object.GetHashCode
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
}
|
48
src/NadekoBot/Db/Models/support/PatronQuota.cs
Normal file
48
src/NadekoBot/Db/Models/support/PatronQuota.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Contains data about usage of Patron-Only commands per user
|
||||
/// in order to provide support for quota limitations
|
||||
/// (allow user x who is pledging amount y to use the specified command only
|
||||
/// x amount of times in the specified time period)
|
||||
/// </summary>
|
||||
public class PatronQuota
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public FeatureType FeatureType { get; set; }
|
||||
public string Feature { get; set; }
|
||||
public uint HourlyCount { get; set; }
|
||||
public uint DailyCount { get; set; }
|
||||
public uint MonthlyCount { get; set; }
|
||||
}
|
||||
|
||||
public enum FeatureType
|
||||
{
|
||||
Command,
|
||||
Group,
|
||||
Module,
|
||||
Limit
|
||||
}
|
||||
|
||||
public class PatronUser
|
||||
{
|
||||
public string UniquePlatformUserId { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public int AmountCents { get; set; }
|
||||
|
||||
public DateTime LastCharge { get; set; }
|
||||
|
||||
// Date Only component
|
||||
public DateTime ValidThru { get; set; }
|
||||
|
||||
public PatronUser Clone()
|
||||
=> new PatronUser()
|
||||
{
|
||||
UniquePlatformUserId = this.UniquePlatformUserId,
|
||||
UserId = this.UserId,
|
||||
AmountCents = this.AmountCents,
|
||||
LastCharge = this.LastCharge,
|
||||
ValidThru = this.ValidThru
|
||||
};
|
||||
}
|
10
src/NadekoBot/Db/Models/support/RewardedUser.cs
Normal file
10
src/NadekoBot/Db/Models/support/RewardedUser.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class RewardedUser : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public string PlatformUserId { get; set; }
|
||||
public long AmountRewardedThisMonth { get; set; }
|
||||
public DateTime LastReward { get; set; }
|
||||
}
|
10
src/NadekoBot/Db/Models/todo/ArchivedTodoListModel.cs
Normal file
10
src/NadekoBot/Db/Models/todo/ArchivedTodoListModel.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
#nullable disable
|
||||
public sealed class ArchivedTodoListModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public string Name { get; set; }
|
||||
public List<TodoModel> Items { get; set; }
|
||||
}
|
13
src/NadekoBot/Db/Models/todo/TodoModel.cs
Normal file
13
src/NadekoBot/Db/Models/todo/TodoModel.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace NadekoBot.Db.Models;
|
||||
|
||||
#nullable disable
|
||||
public sealed class TodoModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public ulong UserId { get; set; }
|
||||
public string Todo { get; set; }
|
||||
|
||||
public DateTime DateAdded { get; set; }
|
||||
public bool IsDone { get; set; }
|
||||
public int? ArchiveId { get; set; }
|
||||
}
|
14
src/NadekoBot/Db/Models/untimer/UnbanTimer.cs
Normal file
14
src/NadekoBot/Db/Models/untimer/UnbanTimer.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class UnbanTimer : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public DateTime UnbanAt { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is UnbanTimer ut ? ut.UserId == UserId : false;
|
||||
}
|
14
src/NadekoBot/Db/Models/untimer/UnmuteTimer.cs
Normal file
14
src/NadekoBot/Db/Models/untimer/UnmuteTimer.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class UnmuteTimer : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public DateTime UnmuteAt { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is UnmuteTimer ut ? ut.UserId == UserId : false;
|
||||
}
|
15
src/NadekoBot/Db/Models/untimer/UnroleTimer.cs
Normal file
15
src/NadekoBot/Db/Models/untimer/UnroleTimer.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class UnroleTimer : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public ulong RoleId { get; set; }
|
||||
public DateTime UnbanAt { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> UserId.GetHashCode() ^ RoleId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is UnroleTimer ut ? ut.UserId == UserId && ut.RoleId == RoleId : false;
|
||||
}
|
13
src/NadekoBot/Db/Models/xp/UserXpStats.cs
Normal file
13
src/NadekoBot/Db/Models/xp/UserXpStats.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class UserXpStats : DbEntity
|
||||
{
|
||||
public ulong UserId { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
public long Xp { get; set; }
|
||||
public long AwardedXp { get; set; }
|
||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||
}
|
||||
|
||||
public enum XpNotificationLocation { None, Dm, Channel }
|
62
src/NadekoBot/Db/Models/xp/XpSettings.cs
Normal file
62
src/NadekoBot/Db/Models/xp/XpSettings.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
#nullable disable
|
||||
namespace Nadeko.Bot.Db.Models;
|
||||
|
||||
public class XpSettings : DbEntity
|
||||
{
|
||||
public int GuildConfigId { get; set; }
|
||||
public GuildConfig GuildConfig { get; set; }
|
||||
|
||||
public HashSet<XpRoleReward> RoleRewards { get; set; } = new();
|
||||
public HashSet<XpCurrencyReward> CurrencyRewards { get; set; } = new();
|
||||
public HashSet<ExcludedItem> ExclusionList { get; set; } = new();
|
||||
public bool ServerExcluded { get; set; }
|
||||
}
|
||||
|
||||
public enum ExcludedItemType { Channel, Role }
|
||||
|
||||
public class XpRoleReward : DbEntity
|
||||
{
|
||||
public int XpSettingsId { get; set; }
|
||||
public XpSettings XpSettings { get; set; }
|
||||
|
||||
public int Level { get; set; }
|
||||
public ulong RoleId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the role should be removed (true) or added (false)
|
||||
/// </summary>
|
||||
public bool Remove { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Level.GetHashCode() ^ XpSettingsId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is XpRoleReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId;
|
||||
}
|
||||
|
||||
public class XpCurrencyReward : DbEntity
|
||||
{
|
||||
public int XpSettingsId { get; set; }
|
||||
public XpSettings XpSettings { get; set; }
|
||||
|
||||
public int Level { get; set; }
|
||||
public int Amount { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> Level.GetHashCode() ^ XpSettingsId.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is XpCurrencyReward xrr && xrr.Level == Level && xrr.XpSettingsId == XpSettingsId;
|
||||
}
|
||||
|
||||
public class ExcludedItem : DbEntity
|
||||
{
|
||||
public ulong ItemId { get; set; }
|
||||
public ExcludedItemType ItemType { get; set; }
|
||||
|
||||
public override int GetHashCode()
|
||||
=> ItemId.GetHashCode() ^ ItemType.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj)
|
||||
=> obj is ExcludedItem ei && ei.ItemId == ItemId && ei.ItemType == ItemType;
|
||||
}
|
27
src/NadekoBot/Expressions/ExportedExpr.cs
Normal file
27
src/NadekoBot/Expressions/ExportedExpr.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Modules.NadekoExpressions;
|
||||
|
||||
public class ExportedExpr
|
||||
{
|
||||
public string Res { get; set; }
|
||||
public string Id { get; set; }
|
||||
public bool Ad { get; set; }
|
||||
public bool Dm { get; set; }
|
||||
public bool At { get; set; }
|
||||
public bool Ca { get; set; }
|
||||
public string[] React;
|
||||
|
||||
public static ExportedExpr FromModel(NadekoExpression cr)
|
||||
=> new()
|
||||
{
|
||||
Res = cr.Response,
|
||||
Id = ((kwum)cr.Id).ToString(),
|
||||
Ad = cr.AutoDeleteTrigger,
|
||||
At = cr.AllowTarget,
|
||||
Ca = cr.ContainsAnywhere,
|
||||
Dm = cr.DmResponse,
|
||||
React = string.IsNullOrWhiteSpace(cr.Reactions) ? null : cr.GetReactions()
|
||||
};
|
||||
}
|
10
src/NadekoBot/Expressions/ExprField.cs
Normal file
10
src/NadekoBot/Expressions/ExprField.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace NadekoBot.Modules.NadekoExpressions;
|
||||
|
||||
public enum ExprField
|
||||
{
|
||||
AutoDelete,
|
||||
DmResponse,
|
||||
AllowTarget,
|
||||
ContainsAnywhere,
|
||||
Message
|
||||
}
|
92
src/NadekoBot/Expressions/NadekoExpressionExtensions.cs
Normal file
92
src/NadekoBot/Expressions/NadekoExpressionExtensions.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
#nullable disable
|
||||
using Nadeko.Bot.Db.Models;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NadekoBot.Common;
|
||||
|
||||
namespace NadekoBot.Modules.NadekoExpressions;
|
||||
|
||||
public static class NadekoExpressionExtensions
|
||||
{
|
||||
private static string ResolveTriggerString(this string str, DiscordSocketClient client)
|
||||
=> str.Replace("%bot.mention%", client.CurrentUser.Mention, StringComparison.Ordinal);
|
||||
|
||||
public static async Task<IUserMessage> Send(
|
||||
this NadekoExpression cr,
|
||||
IUserMessage ctx,
|
||||
IReplacementService repSvc,
|
||||
DiscordSocketClient client,
|
||||
bool sanitize)
|
||||
{
|
||||
var channel = cr.DmResponse ? await ctx.Author.CreateDMChannelAsync() : ctx.Channel;
|
||||
|
||||
var trigger = cr.Trigger.ResolveTriggerString(client);
|
||||
var substringIndex = trigger.Length;
|
||||
if (cr.ContainsAnywhere)
|
||||
{
|
||||
var pos = ctx.Content.AsSpan().GetWordPosition(trigger);
|
||||
if (pos == WordPosition.Start)
|
||||
substringIndex += 1;
|
||||
else if (pos == WordPosition.End)
|
||||
substringIndex = ctx.Content.Length;
|
||||
else if (pos == WordPosition.Middle)
|
||||
substringIndex += ctx.Content.IndexOf(trigger, StringComparison.InvariantCulture);
|
||||
}
|
||||
|
||||
var canMentionEveryone = (ctx.Author as IGuildUser)?.GuildPermissions.MentionEveryone ?? true;
|
||||
|
||||
var repCtx = new ReplacementContext(client: client,
|
||||
guild: (ctx.Channel as ITextChannel)?.Guild as SocketGuild,
|
||||
channel: ctx.Channel,
|
||||
users: ctx.Author
|
||||
)
|
||||
.WithOverride("%target%",
|
||||
() => canMentionEveryone
|
||||
? ctx.Content[substringIndex..].Trim()
|
||||
: ctx.Content[substringIndex..].Trim().SanitizeMentions(true));
|
||||
|
||||
var text = SmartText.CreateFrom(cr.Response);
|
||||
text = await repSvc.ReplaceAsync(text, repCtx);
|
||||
|
||||
return await channel.SendAsync(text, sanitize);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static WordPosition GetWordPosition(this ReadOnlySpan<char> str, in ReadOnlySpan<char> word)
|
||||
{
|
||||
var wordIndex = str.IndexOf(word, StringComparison.OrdinalIgnoreCase);
|
||||
if (wordIndex == -1)
|
||||
return WordPosition.None;
|
||||
|
||||
if (wordIndex == 0)
|
||||
{
|
||||
if (word.Length < str.Length && str.IsValidWordDivider(word.Length))
|
||||
return WordPosition.Start;
|
||||
}
|
||||
else if (wordIndex + word.Length == str.Length)
|
||||
{
|
||||
if (str.IsValidWordDivider(wordIndex - 1))
|
||||
return WordPosition.End;
|
||||
}
|
||||
else if (str.IsValidWordDivider(wordIndex - 1) && str.IsValidWordDivider(wordIndex + word.Length))
|
||||
return WordPosition.Middle;
|
||||
|
||||
return WordPosition.None;
|
||||
}
|
||||
|
||||
private static bool IsValidWordDivider(this in ReadOnlySpan<char> str, int index)
|
||||
{
|
||||
var ch = str[index];
|
||||
if (ch is >= 'a' and <= 'z' or >= 'A' and <= 'Z' or >= '1' and <= '9')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public enum WordPosition
|
||||
{
|
||||
None,
|
||||
Start,
|
||||
Middle,
|
||||
End
|
||||
}
|
395
src/NadekoBot/Expressions/NadekoExpressions.cs
Normal file
395
src/NadekoBot/Expressions/NadekoExpressions.cs
Normal file
@@ -0,0 +1,395 @@
|
||||
#nullable disable
|
||||
|
||||
using NadekoBot.Common.Attributes;
|
||||
|
||||
namespace NadekoBot.Modules.NadekoExpressions;
|
||||
|
||||
[Name("Expressions")]
|
||||
public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
||||
{
|
||||
public enum All
|
||||
{
|
||||
All
|
||||
}
|
||||
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly IHttpClientFactory _clientFactory;
|
||||
|
||||
public NadekoExpressions(IBotCredentials creds, IHttpClientFactory clientFactory)
|
||||
{
|
||||
_creds = creds;
|
||||
_clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
private bool AdminInGuildOrOwnerInDm()
|
||||
=> (ctx.Guild is null && _creds.IsOwner(ctx.User))
|
||||
|| (ctx.Guild is not null && ((IGuildUser)ctx.User).GuildPermissions.Administrator);
|
||||
|
||||
private async Task ExprAddInternalAsync(string key, string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
|
||||
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.expr_new))
|
||||
.WithDescription($"#{new kwum(ex.Id)}")
|
||||
.AddField(GetText(strs.trigger), key)
|
||||
.AddField(GetText(strs.response),
|
||||
message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task ExprToggleGlobal()
|
||||
{
|
||||
var result = await _service.ToggleGlobalExpressionsAsync(ctx.Guild.Id);
|
||||
if (result)
|
||||
await ReplyConfirmLocalizedAsync(strs.expr_global_disabled);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.expr_global_enabled);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task ExprAddServer(string key, [Leftover] string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ExprAddInternalAsync(key, message);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task ExprAdd(string key, [Leftover] string message)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AdminInGuildOrOwnerInDm())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||
return;
|
||||
}
|
||||
|
||||
await ExprAddInternalAsync(key, message);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task ExprEdit(kwum id, [Leftover] string message)
|
||||
{
|
||||
var channel = ctx.Channel as ITextChannel;
|
||||
if (string.IsNullOrWhiteSpace(message) || id < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((channel is null && !_creds.IsOwner(ctx.User))
|
||||
|| (channel is not null && !((IGuildUser)ctx.User).GuildPermissions.Administrator))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||
return;
|
||||
}
|
||||
|
||||
var ex = await _service.EditAsync(ctx.Guild?.Id, id, message);
|
||||
if (ex is not null)
|
||||
{
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.expr_edited))
|
||||
.WithDescription($"#{id}")
|
||||
.AddField(GetText(strs.trigger), ex.Trigger)
|
||||
.AddField(GetText(strs.response),
|
||||
message.Length > 1024 ? GetText(strs.redacted_too_long) : message));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[Priority(1)]
|
||||
public async Task ExprList(int page = 1)
|
||||
{
|
||||
if (--page < 0 || page > 999)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var expressions = _service.GetExpressionsFor(ctx.Guild?.Id);
|
||||
|
||||
if (expressions is null || !expressions.Any())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_no_found);
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.SendPaginatedConfirmAsync(page,
|
||||
curPage =>
|
||||
{
|
||||
var desc = expressions.OrderBy(ex => ex.Trigger)
|
||||
.Skip(curPage * 20)
|
||||
.Take(20)
|
||||
.Select(ex => $"{(ex.ContainsAnywhere ? "🗯" : "◾")}"
|
||||
+ $"{(ex.DmResponse ? "✉" : "◾")}"
|
||||
+ $"{(ex.AutoDeleteTrigger ? "❌" : "◾")}"
|
||||
+ $"`{(kwum)ex.Id}` {ex.Trigger}"
|
||||
+ (string.IsNullOrWhiteSpace(ex.Reactions)
|
||||
? string.Empty
|
||||
: " // " + string.Join(" ", ex.GetReactions())))
|
||||
.Join('\n');
|
||||
|
||||
return _eb.Create().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
|
||||
},
|
||||
expressions.Length,
|
||||
20);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task ExprShow(kwum id)
|
||||
{
|
||||
var found = _service.GetExpression(ctx.Guild?.Id, id);
|
||||
|
||||
if (found is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithDescription($"#{id}")
|
||||
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
|
||||
.AddField(GetText(strs.response),
|
||||
found.Response.TrimTo(1000).Replace("](", "]\\(")));
|
||||
}
|
||||
|
||||
public async Task ExprDeleteInternalAsync(kwum id)
|
||||
{
|
||||
var ex = await _service.DeleteAsync(ctx.Guild?.Id, id);
|
||||
|
||||
if (ex is not null)
|
||||
{
|
||||
await ctx.Channel.EmbedAsync(_eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.expr_deleted))
|
||||
.WithDescription($"#{id}")
|
||||
.AddField(GetText(strs.trigger), ex.Trigger.TrimTo(1024))
|
||||
.AddField(GetText(strs.response), ex.Response.TrimTo(1024)));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task ExprDeleteServer(kwum id)
|
||||
=> await ExprDeleteInternalAsync(id);
|
||||
|
||||
[Cmd]
|
||||
public async Task ExprDelete(kwum id)
|
||||
{
|
||||
if (!AdminInGuildOrOwnerInDm())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||
return;
|
||||
}
|
||||
|
||||
await ExprDeleteInternalAsync(id);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task ExprReact(kwum id, params string[] emojiStrs)
|
||||
{
|
||||
if (!AdminInGuildOrOwnerInDm())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||
return;
|
||||
}
|
||||
|
||||
var ex = _service.GetExpression(ctx.Guild?.Id, id);
|
||||
if (ex is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (emojiStrs.Length == 0)
|
||||
{
|
||||
await _service.ResetExprReactions(ctx.Guild?.Id, id);
|
||||
await ReplyConfirmLocalizedAsync(strs.expr_reset(Format.Bold(id.ToString())));
|
||||
return;
|
||||
}
|
||||
|
||||
var succ = new List<string>();
|
||||
foreach (var emojiStr in emojiStrs)
|
||||
{
|
||||
var emote = emojiStr.ToIEmote();
|
||||
|
||||
// i should try adding these emojis right away to the message, to make sure the bot can react with these emojis. If it fails, skip that emoji
|
||||
try
|
||||
{
|
||||
await ctx.Message.AddReactionAsync(emote);
|
||||
await Task.Delay(100);
|
||||
succ.Add(emojiStr);
|
||||
|
||||
if (succ.Count >= 3)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (succ.Count == 0)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.invalid_emojis);
|
||||
return;
|
||||
}
|
||||
|
||||
await _service.SetExprReactions(ctx.Guild?.Id, id, succ);
|
||||
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.expr_set(Format.Bold(id.ToString()),
|
||||
succ.Select(static x => x.ToString()).Join(", ")));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public Task ExprCa(kwum id)
|
||||
=> InternalExprEdit(id, ExprField.ContainsAnywhere);
|
||||
|
||||
[Cmd]
|
||||
public Task ExprDm(kwum id)
|
||||
=> InternalExprEdit(id, ExprField.DmResponse);
|
||||
|
||||
[Cmd]
|
||||
public Task ExprAd(kwum id)
|
||||
=> InternalExprEdit(id, ExprField.AutoDelete);
|
||||
|
||||
[Cmd]
|
||||
public Task ExprAt(kwum id)
|
||||
=> InternalExprEdit(id, ExprField.AllowTarget);
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task ExprsReload()
|
||||
{
|
||||
await _service.TriggerReloadExpressions();
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
private async Task InternalExprEdit(kwum id, ExprField option)
|
||||
{
|
||||
if (!AdminInGuildOrOwnerInDm())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||
return;
|
||||
}
|
||||
|
||||
var (success, newVal) = await _service.ToggleExprOptionAsync(ctx.Guild?.Id, id, option);
|
||||
if (!success)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_no_found_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newVal)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.option_enabled(Format.Code(option.ToString()),
|
||||
Format.Code(id.ToString())));
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.option_disabled(Format.Code(option.ToString()),
|
||||
Format.Code(id.ToString())));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task ExprClear()
|
||||
{
|
||||
if (await PromptUserConfirmAsync(_eb.Create()
|
||||
.WithTitle("Expression clear")
|
||||
.WithDescription("This will delete all expressions on this server.")))
|
||||
{
|
||||
var count = _service.DeleteAllExpressions(ctx.Guild.Id);
|
||||
await ReplyConfirmLocalizedAsync(strs.exprs_cleared(count));
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task ExprsExport()
|
||||
{
|
||||
if (!AdminInGuildOrOwnerInDm())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||
return;
|
||||
}
|
||||
|
||||
_ = ctx.Channel.TriggerTypingAsync();
|
||||
|
||||
var serialized = _service.ExportExpressions(ctx.Guild?.Id);
|
||||
await using var stream = await serialized.ToStream();
|
||||
await ctx.Channel.SendFileAsync(stream, "exprs-export.yml");
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
#if GLOBAL_NADEKO
|
||||
[OwnerOnly]
|
||||
#endif
|
||||
public async Task ExprsImport([Leftover] string input = null)
|
||||
{
|
||||
if (!AdminInGuildOrOwnerInDm())
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_insuff_perms);
|
||||
return;
|
||||
}
|
||||
|
||||
input = input?.Trim();
|
||||
|
||||
_ = ctx.Channel.TriggerTypingAsync();
|
||||
|
||||
if (input is null)
|
||||
{
|
||||
var attachment = ctx.Message.Attachments.FirstOrDefault();
|
||||
if (attachment is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_import_no_input);
|
||||
return;
|
||||
}
|
||||
|
||||
using var client = _clientFactory.CreateClient();
|
||||
input = await client.GetStringAsync(attachment.Url);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_import_no_input);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var succ = await _service.ImportExpressionsAsync(ctx.Guild?.Id, input);
|
||||
if (!succ)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.expr_import_invalid_data);
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
}
|
777
src/NadekoBot/Expressions/NadekoExpressionsService.cs
Normal file
777
src/NadekoBot/Expressions/NadekoExpressionsService.cs
Normal file
@@ -0,0 +1,777 @@
|
||||
#nullable disable
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Common.Yml;
|
||||
using NadekoBot.Db;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using NadekoBot.Services;
|
||||
using Serilog;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
||||
namespace NadekoBot.Modules.NadekoExpressions;
|
||||
|
||||
public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
|
||||
{
|
||||
private const string MENTION_PH = "%bot.mention%";
|
||||
|
||||
private const string PREPEND_EXPORT =
|
||||
"""
|
||||
# Keys are triggers, Each key has a LIST of expressions in the following format:
|
||||
# - res: Response string
|
||||
# id: Alphanumeric id used for commands related to the expression. (Note, when using .exprsimport, a new id will be generated.)
|
||||
# react:
|
||||
# - <List
|
||||
# - of
|
||||
# - reactions>
|
||||
# at: Whether expression allows targets (see .h .exprat)
|
||||
# ca: Whether expression expects trigger anywhere (see .h .exprca)
|
||||
# dm: Whether expression DMs the response (see .h .exprdm)
|
||||
# ad: Whether expression automatically deletes triggering message (see .h .exprad)
|
||||
|
||||
|
||||
""";
|
||||
|
||||
private static readonly ISerializer _exportSerializer = new SerializerBuilder()
|
||||
.WithEventEmitter(args
|
||||
=> new MultilineScalarFlowStyleEmitter(args))
|
||||
.WithNamingConvention(CamelCaseNamingConvention.Instance)
|
||||
.WithIndentedSequences()
|
||||
.ConfigureDefaultValuesHandling(DefaultValuesHandling
|
||||
.OmitDefaults)
|
||||
.DisableAliases()
|
||||
.Build();
|
||||
|
||||
public int Priority
|
||||
=> 0;
|
||||
|
||||
private readonly object _gexprWriteLock = new();
|
||||
|
||||
private readonly TypedKey<NadekoExpression> _gexprAddedKey = new("gexpr.added");
|
||||
private readonly TypedKey<int> _gexprDeletedkey = new("gexpr.deleted");
|
||||
private readonly TypedKey<NadekoExpression> _gexprEditedKey = new("gexpr.edited");
|
||||
private readonly TypedKey<bool> _exprsReloadedKey = new("exprs.reloaded");
|
||||
|
||||
// it is perfectly fine to have global expressions as an array
|
||||
// 1. expressions are almost never added (compared to how many times they are being looped through)
|
||||
// 2. only need write locks for this as we'll rebuild+replace the array on every edit
|
||||
// 3. there's never many of them (at most a thousand, usually < 100)
|
||||
private NadekoExpression[] globalExpressions = Array.Empty<NadekoExpression>();
|
||||
private ConcurrentDictionary<ulong, NadekoExpression[]> newguildExpressions = new();
|
||||
|
||||
private readonly DbService _db;
|
||||
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
// private readonly PermissionService _perms;
|
||||
// private readonly GlobalPermissionService _gperm;
|
||||
// private readonly CmdCdService _cmdCds;
|
||||
private readonly IPermissionChecker _permChecker;
|
||||
private readonly ICommandHandler _cmd;
|
||||
private readonly IBotStrings _strings;
|
||||
private readonly IBot _bot;
|
||||
private readonly IPubSub _pubSub;
|
||||
private readonly IEmbedBuilderService _eb;
|
||||
private readonly IReplacementService _repSvc;
|
||||
private readonly Random _rng;
|
||||
|
||||
private bool ready;
|
||||
private ConcurrentHashSet<ulong> _disabledGlobalExpressionGuilds;
|
||||
|
||||
public NadekoExpressionsService(
|
||||
DbService db,
|
||||
IBotStrings strings,
|
||||
IBot bot,
|
||||
DiscordSocketClient client,
|
||||
ICommandHandler cmd,
|
||||
IPubSub pubSub,
|
||||
IEmbedBuilderService eb,
|
||||
IReplacementService repSvc,
|
||||
IPermissionChecker permChecker)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
_cmd = cmd;
|
||||
_strings = strings;
|
||||
_bot = bot;
|
||||
_pubSub = pubSub;
|
||||
_eb = eb;
|
||||
_repSvc = repSvc;
|
||||
_permChecker = permChecker;
|
||||
_rng = new NadekoRandom();
|
||||
|
||||
_pubSub.Sub(_exprsReloadedKey, OnExprsShouldReload);
|
||||
pubSub.Sub(_gexprAddedKey, OnGexprAdded);
|
||||
pubSub.Sub(_gexprDeletedkey, OnGexprDeleted);
|
||||
pubSub.Sub(_gexprEditedKey, OnGexprEdited);
|
||||
|
||||
bot.JoinedGuild += OnJoinedGuild;
|
||||
_client.LeftGuild += OnLeftGuild;
|
||||
}
|
||||
|
||||
private async Task ReloadInternal(IReadOnlyList<ulong> allGuildIds)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var guildItems = await uow.Set<NadekoExpression>().AsNoTracking()
|
||||
.Where(x => allGuildIds.Contains(x.GuildId.Value))
|
||||
.ToListAsync();
|
||||
|
||||
newguildExpressions = guildItems.GroupBy(k => k.GuildId!.Value)
|
||||
.ToDictionary(g => g.Key,
|
||||
g => g.Select(x =>
|
||||
{
|
||||
x.Trigger = x.Trigger.Replace(MENTION_PH, _client.CurrentUser.Mention);
|
||||
return x;
|
||||
})
|
||||
.ToArray())
|
||||
.ToConcurrent();
|
||||
|
||||
_disabledGlobalExpressionGuilds = new(await uow.Set<GuildConfig>()
|
||||
.Where(x => x.DisableGlobalExpressions)
|
||||
.Select(x => x.GuildId)
|
||||
.ToListAsyncLinqToDB());
|
||||
|
||||
lock (_gexprWriteLock)
|
||||
{
|
||||
var globalItems = uow.Set<NadekoExpression>().AsNoTracking()
|
||||
.Where(x => x.GuildId == null || x.GuildId == 0)
|
||||
.AsEnumerable()
|
||||
.Select(x =>
|
||||
{
|
||||
x.Trigger = x.Trigger.Replace(MENTION_PH, _client.CurrentUser.Mention);
|
||||
return x;
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
globalExpressions = globalItems;
|
||||
}
|
||||
|
||||
ready = true;
|
||||
}
|
||||
|
||||
private NadekoExpression TryGetExpression(IUserMessage umsg)
|
||||
{
|
||||
if (!ready)
|
||||
return null;
|
||||
|
||||
if (umsg.Channel is not SocketTextChannel channel)
|
||||
return null;
|
||||
|
||||
var content = umsg.Content.Trim().ToLowerInvariant();
|
||||
|
||||
if (newguildExpressions.TryGetValue(channel.Guild.Id, out var expressions) && expressions.Length > 0)
|
||||
{
|
||||
var expr = MatchExpressions(content, expressions);
|
||||
if (expr is not null)
|
||||
return expr;
|
||||
}
|
||||
|
||||
if (_disabledGlobalExpressionGuilds.Contains(channel.Guild.Id))
|
||||
return null;
|
||||
|
||||
var localGrs = globalExpressions;
|
||||
|
||||
return MatchExpressions(content, localGrs);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private NadekoExpression MatchExpressions(in ReadOnlySpan<char> content, NadekoExpression[] exprs)
|
||||
{
|
||||
var result = new List<NadekoExpression>(1);
|
||||
for (var i = 0; i < exprs.Length; i++)
|
||||
{
|
||||
var expr = exprs[i];
|
||||
var trigger = expr.Trigger;
|
||||
if (content.Length > trigger.Length)
|
||||
{
|
||||
// if input is greater than the trigger, it can only work if:
|
||||
// it has CA enabled
|
||||
if (expr.ContainsAnywhere)
|
||||
{
|
||||
// if ca is enabled, we have to check if it is a word within the content
|
||||
var wp = content.GetWordPosition(trigger);
|
||||
|
||||
// if it is, then that's valid
|
||||
if (wp != WordPosition.None)
|
||||
result.Add(expr);
|
||||
|
||||
// if it's not, then it cant' work under any circumstance,
|
||||
// because content is greater than the trigger length
|
||||
// so it can't be equal, and it's not contained as a word
|
||||
continue;
|
||||
}
|
||||
|
||||
// if CA is disabled, and expr has AllowTarget, then the
|
||||
// content has to start with the trigger followed by a space
|
||||
if (expr.AllowTarget
|
||||
&& content.StartsWith(trigger, StringComparison.OrdinalIgnoreCase)
|
||||
&& content[trigger.Length] == ' ')
|
||||
result.Add(expr);
|
||||
}
|
||||
else if (content.Length < expr.Trigger.Length)
|
||||
{
|
||||
// if input length is less than trigger length, it means
|
||||
// that the reaction can never be triggered
|
||||
}
|
||||
else
|
||||
{
|
||||
// if input length is the same as trigger length
|
||||
// reaction can only trigger if the strings are equal
|
||||
if (content.SequenceEqual(expr.Trigger))
|
||||
result.Add(expr);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.Count == 0)
|
||||
return null;
|
||||
|
||||
var cancelled = result.FirstOrDefault(x => x.Response == "-");
|
||||
if (cancelled is not null)
|
||||
return cancelled;
|
||||
|
||||
return result[_rng.Next(0, result.Count)];
|
||||
}
|
||||
|
||||
public async Task<bool> ExecOnMessageAsync(IGuild guild, IUserMessage msg)
|
||||
{
|
||||
// maybe this message is an expression
|
||||
var expr = TryGetExpression(msg);
|
||||
|
||||
if (expr is null || expr.Response == "-")
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (guild is SocketGuild sg)
|
||||
{
|
||||
var result = await _permChecker.CheckPermsAsync(
|
||||
guild,
|
||||
msg.Channel,
|
||||
msg.Author,
|
||||
"ACTUALEXPRESSIONS",
|
||||
expr.Trigger
|
||||
);
|
||||
|
||||
if (!result.IsAllowed)
|
||||
{
|
||||
if (result.TryPickT3(out var disallowed, out _))
|
||||
{
|
||||
var permissionMessage = _strings.GetText(strs.perm_prevent(disallowed.PermIndex + 1,
|
||||
Format.Bold(disallowed.PermText)),
|
||||
sg.Id);
|
||||
|
||||
try
|
||||
{
|
||||
await msg.Channel.SendErrorAsync(_eb, permissionMessage);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
Log.Information("{PermissionMessage}", permissionMessage);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var sentMsg = await expr.Send(msg, _repSvc, _client, false);
|
||||
|
||||
var reactions = expr.GetReactions();
|
||||
foreach (var reaction in reactions)
|
||||
{
|
||||
try
|
||||
{
|
||||
await sentMsg.AddReactionAsync(reaction.ToIEmote());
|
||||
}
|
||||
catch
|
||||
{
|
||||
Log.Warning("Unable to add reactions to message {Message} in server {GuildId}",
|
||||
sentMsg.Id,
|
||||
expr.GuildId);
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
if (expr.AutoDeleteTrigger)
|
||||
{
|
||||
try
|
||||
{
|
||||
await msg.DeleteAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Log.Information("s: {GuildId} c: {ChannelId} u: {UserId} | {UserName} executed expression {Expr}",
|
||||
guild.Id,
|
||||
msg.Channel.Id,
|
||||
msg.Author.Id,
|
||||
msg.Author.ToString(),
|
||||
expr.Trigger);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error in Expression RunBehavior: {ErrorMessage}", ex.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task ResetExprReactions(ulong? maybeGuildId, int id)
|
||||
{
|
||||
NadekoExpression expr;
|
||||
await using var uow = _db.GetDbContext();
|
||||
expr = uow.Set<NadekoExpression>().GetById(id);
|
||||
if (expr is null)
|
||||
return;
|
||||
|
||||
expr.Reactions = string.Empty;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private Task UpdateInternalAsync(ulong? maybeGuildId, NadekoExpression expr)
|
||||
{
|
||||
if (maybeGuildId is { } guildId)
|
||||
UpdateInternal(guildId, expr);
|
||||
else
|
||||
return _pubSub.Pub(_gexprEditedKey, expr);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void UpdateInternal(ulong? maybeGuildId, NadekoExpression expr)
|
||||
{
|
||||
if (maybeGuildId is { } guildId)
|
||||
{
|
||||
newguildExpressions.AddOrUpdate(guildId,
|
||||
new[] { expr },
|
||||
(_, old) =>
|
||||
{
|
||||
var newArray = old.ToArray();
|
||||
for (var i = 0; i < newArray.Length; i++)
|
||||
{
|
||||
if (newArray[i].Id == expr.Id)
|
||||
newArray[i] = expr;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (_gexprWriteLock)
|
||||
{
|
||||
var exprs = globalExpressions;
|
||||
for (var i = 0; i < exprs.Length; i++)
|
||||
{
|
||||
if (exprs[i].Id == expr.Id)
|
||||
exprs[i] = expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task AddInternalAsync(ulong? maybeGuildId, NadekoExpression expr)
|
||||
{
|
||||
// only do this for perf purposes
|
||||
expr.Trigger = expr.Trigger.Replace(MENTION_PH, _client.CurrentUser.Mention);
|
||||
|
||||
if (maybeGuildId is { } guildId)
|
||||
newguildExpressions.AddOrUpdate(guildId, new[] { expr }, (_, old) => old.With(expr));
|
||||
else
|
||||
return _pubSub.Pub(_gexprAddedKey, expr);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task DeleteInternalAsync(ulong? maybeGuildId, int id)
|
||||
{
|
||||
if (maybeGuildId is { } guildId)
|
||||
{
|
||||
newguildExpressions.AddOrUpdate(guildId,
|
||||
Array.Empty<NadekoExpression>(),
|
||||
(key, old) => DeleteInternal(old, id, out _));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
lock (_gexprWriteLock)
|
||||
{
|
||||
var expr = Array.Find(globalExpressions, item => item.Id == id);
|
||||
if (expr is not null)
|
||||
return _pubSub.Pub(_gexprDeletedkey, expr.Id);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private NadekoExpression[] DeleteInternal(
|
||||
IReadOnlyList<NadekoExpression> exprs,
|
||||
int id,
|
||||
out NadekoExpression deleted)
|
||||
{
|
||||
deleted = null;
|
||||
if (exprs is null || exprs.Count == 0)
|
||||
return exprs as NadekoExpression[] ?? exprs?.ToArray();
|
||||
|
||||
var newExprs = new NadekoExpression[exprs.Count - 1];
|
||||
for (int i = 0, k = 0; i < exprs.Count; i++, k++)
|
||||
{
|
||||
if (exprs[i].Id == id)
|
||||
{
|
||||
deleted = exprs[i];
|
||||
k--;
|
||||
continue;
|
||||
}
|
||||
|
||||
newExprs[k] = exprs[i];
|
||||
}
|
||||
|
||||
return newExprs;
|
||||
}
|
||||
|
||||
public async Task SetExprReactions(ulong? guildId, int id, IEnumerable<string> emojis)
|
||||
{
|
||||
NadekoExpression expr;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
expr = uow.Set<NadekoExpression>().GetById(id);
|
||||
if (expr is null)
|
||||
return;
|
||||
|
||||
expr.Reactions = string.Join("@@@", emojis);
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await UpdateInternalAsync(guildId, expr);
|
||||
}
|
||||
|
||||
public async Task<(bool Sucess, bool NewValue)> ToggleExprOptionAsync(ulong? guildId, int id, ExprField field)
|
||||
{
|
||||
var newVal = false;
|
||||
NadekoExpression expr;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
expr = uow.Set<NadekoExpression>().GetById(id);
|
||||
|
||||
if (expr is null || expr.GuildId != guildId)
|
||||
return (false, false);
|
||||
if (field == ExprField.AutoDelete)
|
||||
newVal = expr.AutoDeleteTrigger = !expr.AutoDeleteTrigger;
|
||||
else if (field == ExprField.ContainsAnywhere)
|
||||
newVal = expr.ContainsAnywhere = !expr.ContainsAnywhere;
|
||||
else if (field == ExprField.DmResponse)
|
||||
newVal = expr.DmResponse = !expr.DmResponse;
|
||||
else if (field == ExprField.AllowTarget)
|
||||
newVal = expr.AllowTarget = !expr.AllowTarget;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await UpdateInternalAsync(guildId, expr);
|
||||
|
||||
return (true, newVal);
|
||||
}
|
||||
|
||||
public NadekoExpression GetExpression(ulong? guildId, int id)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var expr = uow.Set<NadekoExpression>().GetById(id);
|
||||
if (expr is null || expr.GuildId != guildId)
|
||||
return null;
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
public int DeleteAllExpressions(ulong guildId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var count = uow.Set<NadekoExpression>().ClearFromGuild(guildId);
|
||||
uow.SaveChanges();
|
||||
|
||||
newguildExpressions.TryRemove(guildId, out _);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public bool ExpressionExists(ulong? guildId, string input)
|
||||
{
|
||||
input = input.ToLowerInvariant();
|
||||
|
||||
var gexprs = globalExpressions;
|
||||
foreach (var t in gexprs)
|
||||
{
|
||||
if (t.Trigger == input)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (guildId is ulong gid && newguildExpressions.TryGetValue(gid, out var guildExprs))
|
||||
{
|
||||
foreach (var t in guildExprs)
|
||||
{
|
||||
if (t.Trigger == input)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string ExportExpressions(ulong? guildId)
|
||||
{
|
||||
var exprs = GetExpressionsFor(guildId);
|
||||
|
||||
var exprsDict = exprs.GroupBy(x => x.Trigger).ToDictionary(x => x.Key, x => x.Select(ExportedExpr.FromModel));
|
||||
|
||||
return PREPEND_EXPORT + _exportSerializer.Serialize(exprsDict).UnescapeUnicodeCodePoints();
|
||||
}
|
||||
|
||||
public async Task<bool> ImportExpressionsAsync(ulong? guildId, string input)
|
||||
{
|
||||
Dictionary<string, List<ExportedExpr>> data;
|
||||
try
|
||||
{
|
||||
data = Yaml.Deserializer.Deserialize<Dictionary<string, List<ExportedExpr>>>(input);
|
||||
if (data.Sum(x => x.Value.Count) == 0)
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
foreach (var entry in data)
|
||||
{
|
||||
var trigger = entry.Key;
|
||||
await uow.Set<NadekoExpression>().AddRangeAsync(entry.Value
|
||||
.Where(expr => !string.IsNullOrWhiteSpace(expr.Res))
|
||||
.Select(expr => new NadekoExpression
|
||||
{
|
||||
GuildId = guildId,
|
||||
Response = expr.Res,
|
||||
Reactions = expr.React?.Join("@@@"),
|
||||
Trigger = trigger,
|
||||
AllowTarget = expr.At,
|
||||
ContainsAnywhere = expr.Ca,
|
||||
DmResponse = expr.Dm,
|
||||
AutoDeleteTrigger = expr.Ad
|
||||
}));
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
await TriggerReloadExpressions();
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
=> await OnExprsShouldReload(true);
|
||||
|
||||
private ValueTask OnExprsShouldReload(bool _)
|
||||
=> new(ReloadInternal(_bot.GetCurrentGuildIds()));
|
||||
|
||||
private ValueTask OnGexprAdded(NadekoExpression c)
|
||||
{
|
||||
lock (_gexprWriteLock)
|
||||
{
|
||||
var newGlobalReactions = new NadekoExpression[globalExpressions.Length + 1];
|
||||
Array.Copy(globalExpressions, newGlobalReactions, globalExpressions.Length);
|
||||
newGlobalReactions[globalExpressions.Length] = c;
|
||||
globalExpressions = newGlobalReactions;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private ValueTask OnGexprEdited(NadekoExpression c)
|
||||
{
|
||||
lock (_gexprWriteLock)
|
||||
{
|
||||
for (var i = 0; i < globalExpressions.Length; i++)
|
||||
{
|
||||
if (globalExpressions[i].Id == c.Id)
|
||||
{
|
||||
globalExpressions[i] = c;
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
// if edited expr is not found?!
|
||||
// add it
|
||||
OnGexprAdded(c);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private ValueTask OnGexprDeleted(int id)
|
||||
{
|
||||
lock (_gexprWriteLock)
|
||||
{
|
||||
var newGlobalReactions = DeleteInternal(globalExpressions, id, out _);
|
||||
globalExpressions = newGlobalReactions;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task TriggerReloadExpressions()
|
||||
=> _pubSub.Pub(_exprsReloadedKey, true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Client Event Handlers
|
||||
|
||||
private Task OnLeftGuild(SocketGuild arg)
|
||||
{
|
||||
newguildExpressions.TryRemove(arg.Id, out _);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnJoinedGuild(GuildConfig gc)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var exprs = await uow.Set<NadekoExpression>().AsNoTracking().Where(x => x.GuildId == gc.GuildId).ToArrayAsync();
|
||||
|
||||
newguildExpressions[gc.GuildId] = exprs;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Basic Operations
|
||||
|
||||
public async Task<NadekoExpression> AddAsync(ulong? guildId, string key, string message,
|
||||
bool ca = false, bool ad = false, bool dm = false)
|
||||
{
|
||||
key = key.ToLowerInvariant();
|
||||
var expr = new NadekoExpression
|
||||
{
|
||||
GuildId = guildId,
|
||||
Trigger = key,
|
||||
Response = message,
|
||||
ContainsAnywhere = ca,
|
||||
AutoDeleteTrigger = ad,
|
||||
DmResponse = dm
|
||||
};
|
||||
|
||||
if (expr.Response.Contains("%target%", StringComparison.OrdinalIgnoreCase))
|
||||
expr.AllowTarget = true;
|
||||
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
uow.Set<NadekoExpression>().Add(expr);
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await AddInternalAsync(guildId, expr);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
public async Task<NadekoExpression> EditAsync(ulong? guildId, int id, string message,
|
||||
bool? ca = null, bool? ad = null, bool? dm = null)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var expr = uow.Set<NadekoExpression>().GetById(id);
|
||||
|
||||
if (expr is null || expr.GuildId != guildId)
|
||||
return null;
|
||||
|
||||
// disable allowtarget if message had target, but it was removed from it
|
||||
if (!message.Contains("%target%", StringComparison.OrdinalIgnoreCase)
|
||||
&& expr.Response.Contains("%target%", StringComparison.OrdinalIgnoreCase))
|
||||
expr.AllowTarget = false;
|
||||
|
||||
expr.Response = message;
|
||||
|
||||
// enable allow target if message is edited to contain target
|
||||
if (expr.Response.Contains("%target%", StringComparison.OrdinalIgnoreCase))
|
||||
expr.AllowTarget = true;
|
||||
|
||||
expr.ContainsAnywhere = ca ?? expr.ContainsAnywhere;
|
||||
expr.AutoDeleteTrigger = ad ?? expr.AutoDeleteTrigger;
|
||||
expr.DmResponse = dm ?? expr.DmResponse;
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
await UpdateInternalAsync(guildId, expr);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
|
||||
public async Task<NadekoExpression> DeleteAsync(ulong? guildId, int id)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
var toDelete = uow.Set<NadekoExpression>().GetById(id);
|
||||
|
||||
if (toDelete is null)
|
||||
return null;
|
||||
|
||||
if ((toDelete.IsGlobal() && guildId is null) || guildId == toDelete.GuildId)
|
||||
{
|
||||
uow.Set<NadekoExpression>().Remove(toDelete);
|
||||
await uow.SaveChangesAsync();
|
||||
await DeleteInternalAsync(guildId, id);
|
||||
return toDelete;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public NadekoExpression[] GetExpressionsFor(ulong? maybeGuildId)
|
||||
{
|
||||
if (maybeGuildId is { } guildId)
|
||||
return newguildExpressions.TryGetValue(guildId, out var exprs) ? exprs : Array.Empty<NadekoExpression>();
|
||||
|
||||
return globalExpressions;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task<bool> ToggleGlobalExpressionsAsync(ulong guildId)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
var gc = ctx.GuildConfigsForId(guildId, set => set);
|
||||
var toReturn = gc.DisableGlobalExpressions = !gc.DisableGlobalExpressions;
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
if (toReturn)
|
||||
_disabledGlobalExpressionGuilds.Add(guildId);
|
||||
else
|
||||
_disabledGlobalExpressionGuilds.TryRemove(guildId);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
|
||||
public async Task<(IReadOnlyCollection<NadekoExpression> Exprs, int TotalCount)> FindExpressionsAsync(ulong guildId,
|
||||
string query, int page)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
if (newguildExpressions.TryGetValue(guildId, out var exprs))
|
||||
{
|
||||
return (exprs.Where(x => x.Trigger.Contains(query))
|
||||
.Skip(page * 9)
|
||||
.Take(9)
|
||||
.ToArray(), exprs.Length);
|
||||
}
|
||||
|
||||
return ([], 0);
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.NadekoExpressions;
|
||||
using NadekoBot.Services;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class CommandOrExprTypeReader : NadekoTypeReader<CommandOrExprInfo>
|
||||
{
|
||||
private readonly CommandService _cmds;
|
||||
private readonly ICommandHandler _commandHandler;
|
||||
private readonly NadekoExpressionsService _exprs;
|
||||
|
||||
public CommandOrExprTypeReader(CommandService cmds, NadekoExpressionsService exprs, ICommandHandler commandHandler)
|
||||
{
|
||||
_cmds = cmds;
|
||||
_exprs = exprs;
|
||||
_commandHandler = commandHandler;
|
||||
}
|
||||
|
||||
public override async ValueTask<TypeReaderResult<CommandOrExprInfo>> ReadAsync(ICommandContext ctx, string input)
|
||||
{
|
||||
if (_exprs.ExpressionExists(ctx.Guild?.Id, input))
|
||||
return TypeReaderResult.FromSuccess(new CommandOrExprInfo(input, CommandOrExprInfo.Type.Custom));
|
||||
|
||||
var cmd = await new CommandTypeReader(_commandHandler, _cmds).ReadAsync(ctx, input);
|
||||
if (cmd.IsSuccess)
|
||||
{
|
||||
return TypeReaderResult.FromSuccess(new CommandOrExprInfo(((CommandInfo)cmd.Values.First().Value).Name,
|
||||
CommandOrExprInfo.Type.Normal));
|
||||
}
|
||||
|
||||
return TypeReaderResult.FromError<CommandOrExprInfo>(CommandError.ParseFailed, "No such command or expression found.");
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ global using Nadeko.Common; // new project
|
||||
global using NadekoBot.Common; // old + nadekobot specific things
|
||||
global using NadekoBot.Common.Attributes;
|
||||
global using NadekoBot.Extensions;
|
||||
global using Nadeko.Snake;
|
||||
// global using Nadeko.Snake;
|
||||
|
||||
// discord
|
||||
global using Discord;
|
||||
|
@@ -1,5 +1,7 @@
|
||||
#nullable enable
|
||||
|
||||
using Nadeko.Snake;
|
||||
|
||||
[DIIgnore]
|
||||
public sealed class BehaviorAdapter : ICustomBehavior
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using Nadeko.Medusa;
|
||||
using Nadeko.Snake;
|
||||
|
||||
internal class ContextAdapterFactory
|
||||
{
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Nadeko.Snake;
|
||||
|
||||
public sealed class DmContextAdapter : DmContext
|
||||
{
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Nadeko.Medusa.Adapters;
|
||||
using Nadeko.Snake;
|
||||
|
||||
namespace Nadeko.Medusa.Adapters;
|
||||
|
||||
public class FilterAdapter : PreconditionAttribute
|
||||
{
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Nadeko.Snake;
|
||||
|
||||
public sealed class GuildContextAdapter : GuildContext
|
||||
{
|
||||
|
@@ -1,4 +1,6 @@
|
||||
public sealed class ParamParserAdapter<T> : TypeReader
|
||||
using Nadeko.Snake;
|
||||
|
||||
public sealed class ParamParserAdapter<T> : TypeReader
|
||||
{
|
||||
private readonly ParamParser<T> _parser;
|
||||
private readonly IMedusaStrings _strings;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using Nadeko.Snake;
|
||||
using System.Reflection;
|
||||
using Ninject;
|
||||
using Ninject.Activation;
|
||||
using Ninject.Activation.Caching;
|
||||
|
@@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Nadeko.Snake;
|
||||
|
||||
namespace Nadeko.Medusa;
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using Ninject.Modules;
|
||||
using Nadeko.Snake;
|
||||
using Ninject.Modules;
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace Nadeko.Medusa;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Reflection;
|
||||
using Nadeko.Snake;
|
||||
using System.Reflection;
|
||||
using CommandStrings = Nadeko.Snake.CommandStrings;
|
||||
|
||||
namespace Nadeko.Medusa;
|
||||
|
@@ -1,4 +1,6 @@
|
||||
namespace Nadeko.Medusa;
|
||||
using Nadeko.Snake;
|
||||
|
||||
namespace Nadeko.Medusa;
|
||||
|
||||
public sealed record SnekInfo(
|
||||
string Name,
|
||||
|
405
src/NadekoBot/Modules/Administration/Administration.cs
Normal file
405
src/NadekoBot/Modules/Administration/Administration.cs
Normal file
@@ -0,0 +1,405 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Common.TypeReaders.Models;
|
||||
using NadekoBot.Modules.Administration.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Administration;
|
||||
|
||||
public partial class Administration : NadekoModule<AdministrationService>
|
||||
{
|
||||
public enum Channel
|
||||
{
|
||||
Channel,
|
||||
Ch,
|
||||
Chnl,
|
||||
Chan
|
||||
}
|
||||
|
||||
public enum List
|
||||
{
|
||||
List = 0,
|
||||
Ls = 0
|
||||
}
|
||||
|
||||
public enum Server
|
||||
{
|
||||
Server
|
||||
}
|
||||
|
||||
public enum State
|
||||
{
|
||||
Enable,
|
||||
Disable,
|
||||
Inherit
|
||||
}
|
||||
|
||||
private readonly SomethingOnlyChannelService _somethingOnly;
|
||||
private readonly AutoPublishService _autoPubService;
|
||||
|
||||
public Administration(SomethingOnlyChannelService somethingOnly, AutoPublishService autoPubService)
|
||||
{
|
||||
_somethingOnly = somethingOnly;
|
||||
_autoPubService = autoPubService;
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageGuild)]
|
||||
public async Task ImageOnlyChannel(StoopidTime time = null)
|
||||
{
|
||||
var newValue = await _somethingOnly.ToggleImageOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
||||
if (newValue)
|
||||
await ReplyConfirmLocalizedAsync(strs.imageonly_enable);
|
||||
else
|
||||
await ReplyPendingLocalizedAsync(strs.imageonly_disable);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageGuild)]
|
||||
public async Task LinkOnlyChannel(StoopidTime time = null)
|
||||
{
|
||||
var newValue = await _somethingOnly.ToggleLinkOnlyChannelAsync(ctx.Guild.Id, ctx.Channel.Id);
|
||||
if (newValue)
|
||||
await ReplyConfirmLocalizedAsync(strs.linkonly_enable);
|
||||
else
|
||||
await ReplyPendingLocalizedAsync(strs.linkonly_disable);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageChannels)]
|
||||
[BotPerm(ChannelPerm.ManageChannels)]
|
||||
public async Task Slowmode(StoopidTime time = null)
|
||||
{
|
||||
var seconds = (int?)time?.Time.TotalSeconds ?? 0;
|
||||
if (time is not null && (time.Time < TimeSpan.FromSeconds(0) || time.Time > TimeSpan.FromHours(6)))
|
||||
return;
|
||||
|
||||
await ((ITextChannel)ctx.Channel).ModifyAsync(tcp =>
|
||||
{
|
||||
tcp.SlowModeInterval = seconds;
|
||||
});
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(2)]
|
||||
public async Task Delmsgoncmd(List _)
|
||||
{
|
||||
var guild = (SocketGuild)ctx.Guild;
|
||||
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
|
||||
|
||||
var embed = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.server_delmsgoncmd))
|
||||
.WithDescription(enabled ? "✅" : "❌");
|
||||
|
||||
var str = string.Join("\n",
|
||||
channels.Select(x =>
|
||||
{
|
||||
var ch = guild.GetChannel(x.ChannelId)?.ToString() ?? x.ChannelId.ToString();
|
||||
var prefixSign = x.State ? "✅ " : "❌ ";
|
||||
return prefixSign + ch;
|
||||
}));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
str = "-";
|
||||
|
||||
embed.AddField(GetText(strs.channel_delmsgoncmd), str);
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(1)]
|
||||
public async Task Delmsgoncmd(Server _ = Server.Server)
|
||||
{
|
||||
if (_service.ToggleDeleteMessageOnCommand(ctx.Guild.Id))
|
||||
{
|
||||
_service.DeleteMessagesOnCommand.Add(ctx.Guild.Id);
|
||||
await ReplyConfirmLocalizedAsync(strs.delmsg_on);
|
||||
}
|
||||
else
|
||||
{
|
||||
_service.DeleteMessagesOnCommand.TryRemove(ctx.Guild.Id);
|
||||
await ReplyConfirmLocalizedAsync(strs.delmsg_off);
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public Task Delmsgoncmd(Channel _, State s, ITextChannel ch)
|
||||
=> Delmsgoncmd(_, s, ch.Id);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[BotPerm(GuildPerm.ManageMessages)]
|
||||
[Priority(1)]
|
||||
public async Task Delmsgoncmd(Channel _, State s, ulong? chId = null)
|
||||
{
|
||||
var actualChId = chId ?? ctx.Channel.Id;
|
||||
await _service.SetDelMsgOnCmdState(ctx.Guild.Id, actualChId, s);
|
||||
|
||||
if (s == State.Disable)
|
||||
await ReplyConfirmLocalizedAsync(strs.delmsg_channel_off);
|
||||
else if (s == State.Enable)
|
||||
await ReplyConfirmLocalizedAsync(strs.delmsg_channel_on);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.delmsg_channel_inherit);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.DeafenMembers)]
|
||||
[BotPerm(GuildPerm.DeafenMembers)]
|
||||
public async Task Deafen(params IGuildUser[] users)
|
||||
{
|
||||
await _service.DeafenUsers(true, users);
|
||||
await ReplyConfirmLocalizedAsync(strs.deafen);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.DeafenMembers)]
|
||||
[BotPerm(GuildPerm.DeafenMembers)]
|
||||
public async Task UnDeafen(params IGuildUser[] users)
|
||||
{
|
||||
await _service.DeafenUsers(false, users);
|
||||
await ReplyConfirmLocalizedAsync(strs.undeafen);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task DelVoiChanl([Leftover] IVoiceChannel voiceChannel)
|
||||
{
|
||||
await voiceChannel.DeleteAsync();
|
||||
await ReplyConfirmLocalizedAsync(strs.delvoich(Format.Bold(voiceChannel.Name)));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task CreatVoiChanl([Leftover] string channelName)
|
||||
{
|
||||
var ch = await ctx.Guild.CreateVoiceChannelAsync(channelName);
|
||||
await ReplyConfirmLocalizedAsync(strs.createvoich(Format.Bold(ch.Name)));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task DelTxtChanl([Leftover] ITextChannel toDelete)
|
||||
{
|
||||
await toDelete.DeleteAsync();
|
||||
await ReplyConfirmLocalizedAsync(strs.deltextchan(Format.Bold(toDelete.Name)));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task CreaTxtChanl([Leftover] string channelName)
|
||||
{
|
||||
var txtCh = await ctx.Guild.CreateTextChannelAsync(channelName);
|
||||
await ReplyConfirmLocalizedAsync(strs.createtextchan(Format.Bold(txtCh.Name)));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task SetTopic([Leftover] string topic = null)
|
||||
{
|
||||
var channel = (ITextChannel)ctx.Channel;
|
||||
topic ??= "";
|
||||
await channel.ModifyAsync(c => c.Topic = topic);
|
||||
await ReplyConfirmLocalizedAsync(strs.set_topic);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task SetChanlName([Leftover] string name)
|
||||
{
|
||||
var channel = (ITextChannel)ctx.Channel;
|
||||
await channel.ModifyAsync(c => c.Name = name);
|
||||
await ReplyConfirmLocalizedAsync(strs.set_channel_name);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageChannels)]
|
||||
[BotPerm(GuildPerm.ManageChannels)]
|
||||
public async Task AgeRestrictToggle()
|
||||
{
|
||||
var channel = (ITextChannel)ctx.Channel;
|
||||
var isEnabled = channel.IsNsfw;
|
||||
|
||||
await channel.ModifyAsync(c => c.IsNsfw = !isEnabled);
|
||||
|
||||
if (isEnabled)
|
||||
await ReplyConfirmLocalizedAsync(strs.nsfw_set_false);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.nsfw_set_true);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[Priority(0)]
|
||||
public Task Edit(ulong messageId, [Leftover] string text)
|
||||
=> Edit((ITextChannel)ctx.Channel, messageId, text);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[Priority(1)]
|
||||
public async Task Edit(ITextChannel channel, ulong messageId, [Leftover] string text)
|
||||
{
|
||||
var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
|
||||
var botPerms = ((SocketGuild)ctx.Guild).CurrentUser.GetPermissions(channel);
|
||||
if (!userPerms.Has(ChannelPermission.ManageMessages))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!botPerms.Has(ChannelPermission.ViewChannel))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.insuf_perms_i);
|
||||
return;
|
||||
}
|
||||
|
||||
await _service.EditMessage(ctx, channel, messageId, text);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
[BotPerm(ChannelPerm.ManageMessages)]
|
||||
public Task Delete(ulong messageId, StoopidTime time = null)
|
||||
=> Delete((ITextChannel)ctx.Channel, messageId, time);
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Delete(ITextChannel channel, ulong messageId, StoopidTime time = null)
|
||||
=> await InternalMessageAction(channel, messageId, time, msg => msg.DeleteAsync());
|
||||
|
||||
private async Task InternalMessageAction(
|
||||
ITextChannel channel,
|
||||
ulong messageId,
|
||||
StoopidTime time,
|
||||
Func<IMessage, Task> func)
|
||||
{
|
||||
var userPerms = ((SocketGuildUser)ctx.User).GetPermissions(channel);
|
||||
var botPerms = ((SocketGuild)ctx.Guild).CurrentUser.GetPermissions(channel);
|
||||
if (!userPerms.Has(ChannelPermission.ManageMessages))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.insuf_perms_u);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!botPerms.Has(ChannelPermission.ManageMessages))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.insuf_perms_i);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var msg = await channel.GetMessageAsync(messageId);
|
||||
if (msg is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.msg_not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
if (time is null)
|
||||
await msg.DeleteAsync();
|
||||
else if (time.Time <= TimeSpan.FromDays(7))
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(time.Time);
|
||||
await msg.DeleteAsync();
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.time_too_long);
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(ChannelPermission.CreatePublicThreads)]
|
||||
[UserPerm(ChannelPermission.CreatePublicThreads)]
|
||||
public async Task ThreadCreate([Leftover] string name)
|
||||
{
|
||||
if (ctx.Channel is not SocketTextChannel stc)
|
||||
return;
|
||||
|
||||
await stc.CreateThreadAsync(name, message: ctx.Message.ReferencedMessage);
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(ChannelPermission.ManageThreads)]
|
||||
[UserPerm(ChannelPermission.ManageThreads)]
|
||||
public async Task ThreadDelete([Leftover] string name)
|
||||
{
|
||||
if (ctx.Channel is not SocketTextChannel stc)
|
||||
return;
|
||||
|
||||
var t = stc.Threads.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (t is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
await t.DeleteAsync();
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(ChannelPerm.ManageMessages)]
|
||||
public async Task AutoPublish()
|
||||
{
|
||||
if (ctx.Channel.GetChannelType() != ChannelType.News)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.req_announcement_channel);
|
||||
return;
|
||||
}
|
||||
|
||||
var newState = await _autoPubService.ToggleAutoPublish(ctx.Guild.Id, ctx.Channel.Id);
|
||||
|
||||
if (newState)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.autopublish_enable);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.autopublish_disable);
|
||||
}
|
||||
}
|
||||
}
|
162
src/NadekoBot/Modules/Administration/AdministrationService.cs
Normal file
162
src/NadekoBot/Modules/Administration/AdministrationService.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
#nullable disable
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Db;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Administration.Services;
|
||||
|
||||
public class AdministrationService : INService
|
||||
{
|
||||
public ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; }
|
||||
public ConcurrentDictionary<ulong, bool> DeleteMessagesOnCommandChannels { get; }
|
||||
|
||||
private readonly DbService _db;
|
||||
private readonly IReplacementService _repSvc;
|
||||
private readonly ILogCommandService _logService;
|
||||
|
||||
public AdministrationService(
|
||||
IBot bot,
|
||||
CommandHandler cmdHandler,
|
||||
DbService db,
|
||||
IReplacementService repSvc,
|
||||
ILogCommandService logService)
|
||||
{
|
||||
_db = db;
|
||||
_repSvc = repSvc;
|
||||
_logService = logService;
|
||||
|
||||
DeleteMessagesOnCommand = new(bot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
|
||||
|
||||
DeleteMessagesOnCommandChannels = new(bot.AllGuildConfigs.SelectMany(x => x.DelMsgOnCmdChannels)
|
||||
.ToDictionary(x => x.ChannelId, x => x.State)
|
||||
.ToConcurrent());
|
||||
|
||||
cmdHandler.CommandExecuted += DelMsgOnCmd_Handler;
|
||||
}
|
||||
|
||||
public (bool DelMsgOnCmd, IEnumerable<DelMsgOnCmdChannel> channels) GetDelMsgOnCmdData(ulong guildId)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.DelMsgOnCmdChannels));
|
||||
|
||||
return (conf.DeleteMessageOnCommand, conf.DelMsgOnCmdChannels);
|
||||
}
|
||||
|
||||
private Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd)
|
||||
{
|
||||
if (msg.Channel is not ITextChannel channel)
|
||||
return Task.CompletedTask;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
//wat ?!
|
||||
if (DeleteMessagesOnCommandChannels.TryGetValue(channel.Id, out var state))
|
||||
{
|
||||
if (state && cmd.Name != "prune" && cmd.Name != "pick")
|
||||
{
|
||||
_logService.AddDeleteIgnore(msg.Id);
|
||||
try { await msg.DeleteAsync(); }
|
||||
catch { }
|
||||
}
|
||||
//if state is false, that means do not do it
|
||||
}
|
||||
else if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick")
|
||||
{
|
||||
_logService.AddDeleteIgnore(msg.Id);
|
||||
try { await msg.DeleteAsync(); }
|
||||
catch { }
|
||||
}
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public bool ToggleDeleteMessageOnCommand(ulong guildId)
|
||||
{
|
||||
bool enabled;
|
||||
using var uow = _db.GetDbContext();
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set);
|
||||
enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand;
|
||||
|
||||
uow.SaveChanges();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public async Task SetDelMsgOnCmdState(ulong guildId, ulong chId, Administration.State newState)
|
||||
{
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var conf = uow.GuildConfigsForId(guildId, set => set.Include(x => x.DelMsgOnCmdChannels));
|
||||
|
||||
var old = conf.DelMsgOnCmdChannels.FirstOrDefault(x => x.ChannelId == chId);
|
||||
if (newState == Administration.State.Inherit)
|
||||
{
|
||||
if (old is not null)
|
||||
{
|
||||
conf.DelMsgOnCmdChannels.Remove(old);
|
||||
uow.Remove(old);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (old is null)
|
||||
{
|
||||
old = new()
|
||||
{
|
||||
ChannelId = chId
|
||||
};
|
||||
conf.DelMsgOnCmdChannels.Add(old);
|
||||
}
|
||||
|
||||
old.State = newState == Administration.State.Enable;
|
||||
DeleteMessagesOnCommandChannels[chId] = newState == Administration.State.Enable;
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (newState == Administration.State.Disable)
|
||||
{
|
||||
}
|
||||
else if (newState == Administration.State.Enable)
|
||||
DeleteMessagesOnCommandChannels[chId] = true;
|
||||
else
|
||||
DeleteMessagesOnCommandChannels.TryRemove(chId, out _);
|
||||
}
|
||||
|
||||
public async Task DeafenUsers(bool value, params IGuildUser[] users)
|
||||
{
|
||||
if (!users.Any())
|
||||
return;
|
||||
foreach (var u in users)
|
||||
{
|
||||
try
|
||||
{
|
||||
await u.ModifyAsync(usr => usr.Deaf = value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task EditMessage(
|
||||
ICommandContext context,
|
||||
ITextChannel chanl,
|
||||
ulong messageId,
|
||||
string input)
|
||||
{
|
||||
var msg = await chanl.GetMessageAsync(messageId);
|
||||
|
||||
if (msg is not IUserMessage umsg || msg.Author.Id != context.Client.CurrentUser.Id)
|
||||
return;
|
||||
|
||||
var repCtx = new ReplacementContext(context);
|
||||
|
||||
var text = SmartText.CreateFrom(input);
|
||||
text = await _repSvc.ReplaceAsync(text, repCtx);
|
||||
|
||||
await umsg.EditAsync(text);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user