mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 17:58:26 -04:00
Compare commits
60 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
231451f978 | ||
|
2bed4e7eac | ||
|
22eea3fa7b | ||
|
8b1767078e | ||
|
6654ccdad5 | ||
|
99827b0fa1 | ||
|
0451551ddf | ||
|
75d0eb631d | ||
|
96ce7e23bb | ||
|
aac475be62 | ||
|
55fae53f6d | ||
|
879f196f8b | ||
|
796086538a | ||
|
3178074828 | ||
|
0a9d53d380 | ||
|
d7fbf44d53 | ||
|
cdd07d0559 | ||
|
80a41c1f38 | ||
|
1631394638 | ||
|
7eb4895570 | ||
|
9b55028ba6 | ||
|
53363d2840 | ||
|
ca5a870bf8 | ||
|
7f5e065c4d | ||
|
86b214163a | ||
|
9dbb08d85f | ||
|
89ca56c77c | ||
|
9e96679099 | ||
|
1280d2b397 | ||
|
c731127607 | ||
|
c15930306a | ||
|
701501d678 | ||
|
fa12fcea58 | ||
|
274219c40b | ||
|
96c9b47da2 | ||
|
b5d1469df1 | ||
|
d7747bd25a | ||
|
7d162d1f04 | ||
|
704d061d46 | ||
|
c39c9061fd | ||
|
619ddba4f8 | ||
|
3acef04b32 | ||
|
83a1d959b1 | ||
|
a1632722bc | ||
|
ee0a28afab | ||
|
2b301c0aab | ||
|
b6b6b4e19e | ||
|
32fc8b6e03 | ||
|
297e2fde0e | ||
|
729f26caab | ||
|
4b12e4e923 | ||
|
12f4ce7f2a | ||
|
00944e08c3 | ||
|
569abd7194 | ||
|
474a1db41d | ||
|
0f6255947e | ||
|
f68f219a25 | ||
|
8f16b11d02 | ||
|
df5eced904 | ||
|
1dcd158f43 |
797
CHANGELOG.md
797
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
|||||||
dotnet ef migrations remove -c SqliteContext -f -p src/NadekoBot/NadekoBot.csproj
|
|
||||||
dotnet ef migrations remove -c PostgreSqlContext -f -p src/NadekoBot/NadekoBot.csproj
|
|
||||||
|
|
60
src/NadekoBot.GrpcApiBase/protos/fin.proto
Normal file
60
src/NadekoBot.GrpcApiBase/protos/fin.proto
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option csharp_namespace = "NadekoBot.GrpcApi";
|
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto";
|
||||||
|
|
||||||
|
package fin;
|
||||||
|
|
||||||
|
service GrpcFin {
|
||||||
|
rpc GetTransactions(GetTransactionsRequest) returns (GetTransactionsReply);
|
||||||
|
rpc GetHoldings(GetHoldingsRequest) returns (GetHoldingsReply);
|
||||||
|
rpc Withdraw(WithdrawRequest) returns (WithdrawReply);
|
||||||
|
rpc Deposit(DepositRequest) returns (DepositReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTransactionsRequest {
|
||||||
|
int32 page = 1;
|
||||||
|
uint64 userId = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTransactionsReply {
|
||||||
|
repeated TransactionReply transactions = 1;
|
||||||
|
int32 total = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TransactionReply {
|
||||||
|
int64 amount = 1;
|
||||||
|
string note = 2;
|
||||||
|
string type = 3;
|
||||||
|
string extra = 4;
|
||||||
|
google.protobuf.Timestamp timestamp = 5;
|
||||||
|
string id = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetHoldingsRequest {
|
||||||
|
uint64 userId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetHoldingsReply {
|
||||||
|
int64 cash = 1;
|
||||||
|
int64 bank = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WithdrawRequest {
|
||||||
|
uint64 userId = 1;
|
||||||
|
int64 amount = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WithdrawReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DepositRequest {
|
||||||
|
uint64 userId = 1;
|
||||||
|
int64 amount = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DepositReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
120
src/NadekoBot.GrpcApiBase/protos/xp.proto
Normal file
120
src/NadekoBot.GrpcApiBase/protos/xp.proto
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option csharp_namespace = "NadekoBot.GrpcApi";
|
||||||
|
|
||||||
|
package xp;
|
||||||
|
|
||||||
|
service GrpcXp {
|
||||||
|
rpc GetXpLb(GetXpLbRequest) returns (GetXpLbReply);
|
||||||
|
rpc ResetUserXp(ResetUserXpRequest) returns (ResetUserXpReply);
|
||||||
|
|
||||||
|
rpc GetXpSettings(GetXpSettingsRequest) returns (GetXpSettingsReply);
|
||||||
|
|
||||||
|
rpc AddExclusion(AddExclusionRequest) returns (AddExclusionReply);
|
||||||
|
rpc DeleteExclusion(DeleteExclusionRequest) returns (DeleteExclusionReply);
|
||||||
|
|
||||||
|
rpc AddReward(AddRewardRequest) returns (AddRewardReply);
|
||||||
|
rpc DeleteReward(DeleteRewardRequest) returns (DeleteRewardReply);
|
||||||
|
|
||||||
|
rpc SetServerExclusion(SetServerExclusionRequest) returns (SetServerExclusionReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetServerExclusionRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
bool serverExcluded = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SetServerExclusionReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetXpLbRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
int32 page = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetXpLbReply {
|
||||||
|
repeated XpLbUserReply users = 1;
|
||||||
|
int32 total = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message XpLbUserReply {
|
||||||
|
uint64 userId = 1;
|
||||||
|
string username = 2;
|
||||||
|
int64 xp = 3;
|
||||||
|
int64 level = 4;
|
||||||
|
int64 levelPercent = 5;
|
||||||
|
string avatar = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResetUserXpRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
uint64 userId = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResetUserXpReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetXpSettingsReply {
|
||||||
|
repeated ExclItemReply exclusions = 1;
|
||||||
|
repeated RewItemReply rewards = 2;
|
||||||
|
bool serverExcluded = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetXpSettingsRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ExclItemReply {
|
||||||
|
string type = 1;
|
||||||
|
uint64 id = 2;
|
||||||
|
string name = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RewItemReply {
|
||||||
|
int32 level = 1;
|
||||||
|
string type = 2;
|
||||||
|
string value = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddExclusionRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
string type = 2;
|
||||||
|
uint64 id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddExclusionReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteExclusionRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
string type = 2;
|
||||||
|
uint64 id = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteExclusionReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddRewardRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
int32 level = 2;
|
||||||
|
string type = 3;
|
||||||
|
string value = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddRewardReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteRewardRequest {
|
||||||
|
uint64 guildId = 1;
|
||||||
|
int32 level = 2;
|
||||||
|
string type = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DeleteRewardReply {
|
||||||
|
bool success = 1;
|
||||||
|
}
|
@@ -25,7 +25,6 @@ public static class DiscordUserExtensions
|
|||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
Username = username,
|
Username = username,
|
||||||
Discriminator = discrim,
|
|
||||||
AvatarId = avatarId,
|
AvatarId = avatarId,
|
||||||
TotalXp = 0,
|
TotalXp = 0,
|
||||||
CurrencyAmount = 0
|
CurrencyAmount = 0
|
||||||
@@ -33,7 +32,6 @@ public static class DiscordUserExtensions
|
|||||||
old => new()
|
old => new()
|
||||||
{
|
{
|
||||||
Username = username,
|
Username = username,
|
||||||
Discriminator = discrim,
|
|
||||||
AvatarId = avatarId
|
AvatarId = avatarId
|
||||||
},
|
},
|
||||||
() => new()
|
() => new()
|
||||||
@@ -49,8 +47,7 @@ public static class DiscordUserExtensions
|
|||||||
() => new()
|
() => new()
|
||||||
{
|
{
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
Username = "Unknown",
|
Username = "??Unknown",
|
||||||
Discriminator = "????",
|
|
||||||
AvatarId = string.Empty,
|
AvatarId = string.Empty,
|
||||||
TotalXp = 0,
|
TotalXp = 0,
|
||||||
CurrencyAmount = 0
|
CurrencyAmount = 0
|
||||||
|
@@ -1,22 +0,0 @@
|
|||||||
#nullable disable
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using NadekoBot.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();
|
|
||||||
}
|
|
@@ -1,5 +1,4 @@
|
|||||||
#nullable disable
|
using LinqToDB;
|
||||||
using LinqToDB;
|
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
@@ -8,6 +7,9 @@ namespace NadekoBot.Db;
|
|||||||
|
|
||||||
public static class UserXpExtensions
|
public static class UserXpExtensions
|
||||||
{
|
{
|
||||||
|
public static async Task<UserXpStats?> GetGuildUserXp(this ITable<UserXpStats> table, ulong guildId, ulong userId)
|
||||||
|
=> await table.FirstOrDefaultAsyncLinqToDB(x => x.GuildId == guildId && x.UserId == userId);
|
||||||
|
|
||||||
public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
|
public static UserXpStats GetOrCreateUserXpStats(this DbContext ctx, ulong guildId, ulong userId)
|
||||||
{
|
{
|
||||||
var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
var usr = ctx.Set<UserXpStats>().FirstOrDefault(x => x.UserId == userId && x.GuildId == guildId);
|
||||||
@@ -44,9 +46,6 @@ public static class UserXpExtensions
|
|||||||
.CountAsyncLinqToDB()
|
.CountAsyncLinqToDB()
|
||||||
+ 1;
|
+ 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)
|
public static void ResetGuildXp(this DbSet<UserXpStats> xps, ulong guildId)
|
||||||
=> xps.Delete(x => x.GuildId == guildId);
|
=> xps.Delete(x => x.GuildId == guildId);
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ public class DiscordUser : DbEntity
|
|||||||
{
|
{
|
||||||
public ulong UserId { get; set; }
|
public ulong UserId { get; set; }
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Discriminator { get; set; }
|
// public string Discriminator { get; set; }
|
||||||
public string AvatarId { get; set; }
|
public string AvatarId { get; set; }
|
||||||
|
|
||||||
public int? ClubId { get; set; }
|
public int? ClubId { get; set; }
|
||||||
@@ -27,9 +27,6 @@ public class DiscordUser : DbEntity
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(Discriminator) || Discriminator == "0000")
|
return Username;
|
||||||
return Username;
|
|
||||||
|
|
||||||
return Username + "#" + Discriminator;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
8
src/NadekoBot/Db/Models/FlagTranslateChannel.cs
Normal file
8
src/NadekoBot/Db/Models/FlagTranslateChannel.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#nullable disable
|
||||||
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
public class FlagTranslateChannel : DbEntity
|
||||||
|
{
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong ChannelId { get; set; }
|
||||||
|
}
|
@@ -9,7 +9,8 @@ public class FollowedStream : DbEntity
|
|||||||
Picarto = 3,
|
Picarto = 3,
|
||||||
Youtube = 4,
|
Youtube = 4,
|
||||||
Facebook = 5,
|
Facebook = 5,
|
||||||
Trovo = 6
|
Trovo = 6,
|
||||||
|
Kick = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
#nullable disable
|
|
||||||
namespace NadekoBot.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; }
|
|
||||||
}
|
|
@@ -5,14 +5,15 @@ namespace NadekoBot.Db.Models;
|
|||||||
public class GuildColors
|
public class GuildColors
|
||||||
{
|
{
|
||||||
[Key]
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
|
|
||||||
[Length(0, 9)]
|
[MaxLength(9)]
|
||||||
public string? OkColor { get; set; }
|
public string? OkColor { get; set; }
|
||||||
|
|
||||||
[Length(0, 9)]
|
[MaxLength(9)]
|
||||||
public string? ErrorColor { get; set; }
|
public string? ErrorColor { get; set; }
|
||||||
|
|
||||||
[Length(0, 9)]
|
[MaxLength(9)]
|
||||||
public string? PendingColor { get; set; }
|
public string? PendingColor { get; set; }
|
||||||
}
|
}
|
@@ -13,28 +13,11 @@ public class GuildConfig : DbEntity
|
|||||||
|
|
||||||
public string AutoAssignRoleIds { get; set; }
|
public string AutoAssignRoleIds { get; set; }
|
||||||
|
|
||||||
// //greet stuff
|
//todo FUTURE: DELETE, UNUSED
|
||||||
// public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
|
|
||||||
// public int AutoDeleteByeMessagesTimer { get; set; } = 30;
|
|
||||||
//
|
|
||||||
// public ulong GreetMessageChannelId { get; set; }
|
|
||||||
// public ulong ByeMessageChannelId { get; set; }
|
|
||||||
//
|
|
||||||
// public bool SendDmGreetMessage { get; set; }
|
|
||||||
// public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
|
||||||
//
|
|
||||||
// public bool SendChannelGreetMessage { get; set; }
|
|
||||||
// public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
|
|
||||||
//
|
|
||||||
// public bool SendChannelByeMessage { get; set; }
|
|
||||||
// public string ChannelByeMessageText { get; set; } = "%user% has left!";
|
|
||||||
// public bool SendBoostMessage { get; set; }
|
|
||||||
// pulic int BoostMessageDeleteAfter { get; set; }
|
|
||||||
|
|
||||||
//self assignable roles
|
|
||||||
public bool ExclusiveSelfAssignedRoles { get; set; }
|
public bool ExclusiveSelfAssignedRoles { get; set; }
|
||||||
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
|
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
|
||||||
|
|
||||||
|
|
||||||
//stream notifications
|
//stream notifications
|
||||||
public HashSet<FollowedStream> FollowedStreams { get; set; } = new();
|
public HashSet<FollowedStream> FollowedStreams { get; set; } = new();
|
||||||
|
|
||||||
@@ -91,15 +74,10 @@ public class GuildConfig : DbEntity
|
|||||||
public List<FeedSub> FeedSubs { get; set; } = new();
|
public List<FeedSub> FeedSubs { get; set; } = new();
|
||||||
public bool NotifyStreamOffline { get; set; }
|
public bool NotifyStreamOffline { get; set; }
|
||||||
public bool DeleteStreamOnlineMessage { get; set; }
|
public bool DeleteStreamOnlineMessage { get; set; }
|
||||||
public List<GroupName> SelfAssignableRoleGroupNames { get; set; }
|
|
||||||
public int WarnExpireHours { get; set; }
|
public int WarnExpireHours { get; set; }
|
||||||
public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
|
public WarnExpireAction WarnExpireAction { get; set; } = WarnExpireAction.Clear;
|
||||||
|
|
||||||
public bool DisableGlobalExpressions { get; set; } = false;
|
public bool DisableGlobalExpressions { get; set; } = false;
|
||||||
|
|
||||||
#region Boost Message
|
|
||||||
|
|
||||||
public bool StickyRoles { get; set; }
|
public bool StickyRoles { get; set; }
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
18
src/NadekoBot/Db/Models/SarGroup.cs
Normal file
18
src/NadekoBot/Db/Models/SarGroup.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
public sealed class SarGroup
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int GroupNumber { get; set; }
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong? RoleReq { get; set; }
|
||||||
|
public ICollection<Sar> Roles { get; set; } = [];
|
||||||
|
public bool IsExclusive { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
27
src/NadekoBot/Db/Models/btnrole/ButtonRole.cs
Normal file
27
src/NadekoBot/Db/Models/btnrole/ButtonRole.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
public sealed class ButtonRole
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(200)]
|
||||||
|
public string ButtonId { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public ulong ChannelId { get; set; }
|
||||||
|
public ulong MessageId { get; set; }
|
||||||
|
|
||||||
|
public int Position { get; set; }
|
||||||
|
public ulong RoleId { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string Emote { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
|
public string Label { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public bool Exclusive { get; set; }
|
||||||
|
}
|
@@ -1,11 +1,24 @@
|
|||||||
#nullable disable
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace NadekoBot.Db.Models;
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
public class SelfAssignedRole : DbEntity
|
public sealed class Sar
|
||||||
{
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
public ulong GuildId { get; set; }
|
public ulong GuildId { get; set; }
|
||||||
public ulong RoleId { get; set; }
|
public ulong RoleId { get; set; }
|
||||||
|
|
||||||
public int Group { get; set; }
|
public int SarGroupId { get; set; }
|
||||||
public int LevelRequirement { get; set; }
|
public int LevelReq { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SarAutoDelete
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public ulong GuildId { get; set; }
|
||||||
|
public bool IsEnabled { get; set; } = false;
|
||||||
}
|
}
|
@@ -1,8 +1,12 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace NadekoBot.Db.Models;
|
namespace NadekoBot.Db.Models;
|
||||||
|
|
||||||
public class PatronUser
|
public class PatronUser
|
||||||
{
|
{
|
||||||
|
// [Key]
|
||||||
|
// public int Id { get; set; }
|
||||||
public string UniquePlatformUserId { get; set; }
|
public string UniquePlatformUserId { get; set; }
|
||||||
public ulong UserId { get; set; }
|
public ulong UserId { get; set; }
|
||||||
public int AmountCents { get; set; }
|
public int AmountCents { get; set; }
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||||
|
|
||||||
@@ -14,7 +15,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
public DbSet<Quote> Quotes { get; set; }
|
public DbSet<Quote> Quotes { get; set; }
|
||||||
public DbSet<Reminder> Reminders { get; set; }
|
public DbSet<Reminder> Reminders { get; set; }
|
||||||
public DbSet<SelfAssignedRole> SelfAssignableRoles { get; set; }
|
|
||||||
public DbSet<MusicPlaylist> MusicPlaylists { get; set; }
|
public DbSet<MusicPlaylist> MusicPlaylists { get; set; }
|
||||||
public DbSet<NadekoExpression> Expressions { get; set; }
|
public DbSet<NadekoExpression> Expressions { get; set; }
|
||||||
public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; }
|
public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; }
|
||||||
@@ -62,6 +62,7 @@ public abstract class NadekoContext : DbContext
|
|||||||
public DbSet<ArchivedTodoListModel> TodosArchive { get; set; }
|
public DbSet<ArchivedTodoListModel> TodosArchive { get; set; }
|
||||||
public DbSet<HoneypotChannel> HoneyPotChannels { get; set; }
|
public DbSet<HoneypotChannel> HoneyPotChannels { get; set; }
|
||||||
|
|
||||||
|
|
||||||
// public DbSet<GuildColors> GuildColors { get; set; }
|
// public DbSet<GuildColors> GuildColors { get; set; }
|
||||||
|
|
||||||
|
|
||||||
@@ -73,6 +74,89 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
#region GuildColors
|
||||||
|
|
||||||
|
modelBuilder.Entity<GuildColors>()
|
||||||
|
.HasIndex(x => x.GuildId)
|
||||||
|
.IsUnique(true);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Button Roles
|
||||||
|
|
||||||
|
modelBuilder.Entity<ButtonRole>(br =>
|
||||||
|
{
|
||||||
|
br.HasIndex(x => x.GuildId)
|
||||||
|
.IsUnique(false);
|
||||||
|
|
||||||
|
br.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.RoleId,
|
||||||
|
x.MessageId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region New Sar
|
||||||
|
|
||||||
|
modelBuilder.Entity<SarGroup>(sg =>
|
||||||
|
{
|
||||||
|
sg.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.GroupNumber
|
||||||
|
});
|
||||||
|
|
||||||
|
sg.HasMany(x => x.Roles)
|
||||||
|
.WithOne()
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<Sar>()
|
||||||
|
.HasAlternateKey(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.RoleId
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<SarAutoDelete>()
|
||||||
|
.HasIndex(x => x.GuildId)
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Rakeback
|
||||||
|
|
||||||
|
modelBuilder.Entity<Rakeback>()
|
||||||
|
.HasKey(x => x.UserId);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region UserBetStats
|
||||||
|
|
||||||
|
modelBuilder.Entity<UserBetStats>()
|
||||||
|
.HasIndex(x => new
|
||||||
|
{
|
||||||
|
x.UserId,
|
||||||
|
x.Game
|
||||||
|
})
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Flag Translate
|
||||||
|
|
||||||
|
modelBuilder.Entity<FlagTranslateChannel>()
|
||||||
|
.HasIndex(x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.ChannelId
|
||||||
|
})
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region NCanvas
|
#region NCanvas
|
||||||
|
|
||||||
modelBuilder.Entity<NCPixel>()
|
modelBuilder.Entity<NCPixel>()
|
||||||
@@ -261,11 +345,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
.HasForeignKey(x => x.GuildConfigId)
|
.HasForeignKey(x => x.GuildConfigId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
modelBuilder.Entity<GuildConfig>()
|
|
||||||
.HasMany(x => x.SelfAssignableRoleGroupNames)
|
|
||||||
.WithOne()
|
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
|
||||||
|
|
||||||
modelBuilder.Entity<FeedSub>()
|
modelBuilder.Entity<FeedSub>()
|
||||||
.HasAlternateKey(x => new
|
.HasAlternateKey(x => new
|
||||||
{
|
{
|
||||||
@@ -294,21 +373,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Self Assignable Roles
|
|
||||||
|
|
||||||
var selfassignableRolesEntity = modelBuilder.Entity<SelfAssignedRole>();
|
|
||||||
|
|
||||||
selfassignableRolesEntity.HasIndex(s => new
|
|
||||||
{
|
|
||||||
s.GuildId,
|
|
||||||
s.RoleId
|
|
||||||
})
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
selfassignableRolesEntity.Property(x => x.Group).HasDefaultValue(0);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region MusicPlaylists
|
#region MusicPlaylists
|
||||||
|
|
||||||
var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>();
|
var musicPlaylistEntity = modelBuilder.Entity<MusicPlaylist>();
|
||||||
@@ -491,23 +555,6 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region GroupName
|
|
||||||
|
|
||||||
modelBuilder.Entity<GroupName>()
|
|
||||||
.HasIndex(x => new
|
|
||||||
{
|
|
||||||
x.GuildConfigId,
|
|
||||||
x.Number
|
|
||||||
})
|
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
modelBuilder.Entity<GroupName>()
|
|
||||||
.HasOne(x => x.GuildConfig)
|
|
||||||
.WithMany(x => x.SelfAssignableRoleGroupNames)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region BanTemplate
|
#region BanTemplate
|
||||||
|
|
||||||
modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
|
modelBuilder.Entity<BanTemplate>().HasIndex(x => x.GuildId).IsUnique();
|
||||||
|
@@ -5,6 +5,41 @@ namespace NadekoBot.Migrations;
|
|||||||
|
|
||||||
public static class MigrationQueries
|
public static class MigrationQueries
|
||||||
{
|
{
|
||||||
|
public static void MigrateSar(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
INSERT INTO GroupName (Number, GuildConfigId)
|
||||||
|
SELECT DISTINCT "Group", GC.Id
|
||||||
|
FROM SelfAssignableRoles as SAR
|
||||||
|
INNER JOIN GuildConfigs as GC
|
||||||
|
ON SAR.GuildId = GC.GuildId
|
||||||
|
WHERE SAR.GuildId not in (SELECT GuildConfigs.GuildId from GroupName LEFT JOIN GuildConfigs ON GroupName.GuildConfigId = GuildConfigs.Id);
|
||||||
|
|
||||||
|
INSERT INTO SarGroup (Id, GroupNumber, Name, IsExclusive, GuildId)
|
||||||
|
SELECT GN.Id, GN.Number, GN.Name, GC.ExclusiveSelfAssignedRoles, GC.GuildId
|
||||||
|
FROM GroupName as GN
|
||||||
|
INNER JOIN GuildConfigs as GC ON GN.GuildConfigId = GC.Id;
|
||||||
|
|
||||||
|
INSERT INTO Sar (GuildId, RoleId, SarGroupId, LevelReq)
|
||||||
|
SELECT SAR.GuildId, SAR.RoleId, (SELECT Id FROM SarGroup WHERE SG.Number = SarGroup.GroupNumber AND SG.GuildId = SarGroup.GuildId), MIN(SAR.LevelRequirement)
|
||||||
|
FROM SelfAssignableRoles as SAR
|
||||||
|
INNER JOIN (SELECT GuildId, gn.Number FROM GroupName as gn
|
||||||
|
INNER JOIN GuildConfigs as gc ON gn.GuildConfigId =gc.Id
|
||||||
|
) as SG
|
||||||
|
ON SG.GuildId = SAR.GuildId
|
||||||
|
WHERE SG.Number IN (SELECT GroupNumber FROM SarGroup WHERE Sar.GuildId = SarGroup.GuildId)
|
||||||
|
GROUP BY SAR.GuildId, SAR.RoleId;
|
||||||
|
|
||||||
|
INSERT INTO SarAutoDelete (GuildId, IsEnabled)
|
||||||
|
SELECT GuildId, AutoDeleteSelfAssignedRoleMessages FROM GuildConfigs WHERE AutoDeleteSelfAssignedRoleMessages = TRUE;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void UpdateUsernames(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("UPDATE DiscordUser SET Username = '??' || Username WHERE Discriminator = '????';");
|
||||||
|
}
|
||||||
|
|
||||||
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
if (migrationBuilder.IsSqlite())
|
if (migrationBuilder.IsSqlite())
|
||||||
|
3851
src/NadekoBot/Migrations/PostgreSql/20241102022956_no-discrim-and-flag-translate.Designer.cs
generated
Normal file
3851
src/NadekoBot/Migrations/PostgreSql/20241102022956_no-discrim-and-flag-translate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class nodiscrimandflagtranslate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "discriminator",
|
||||||
|
table: "discorduser");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "flagtranslatechannel",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_flagtranslatechannel", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_flagtranslatechannel_guildid_channelid",
|
||||||
|
table: "flagtranslatechannel",
|
||||||
|
columns: new[] { "guildid", "channelid" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "flagtranslatechannel");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "discriminator",
|
||||||
|
table: "discorduser",
|
||||||
|
type: "text",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3902
src/NadekoBot/Migrations/PostgreSql/20241104094232_betstats.Designer.cs
generated
Normal file
3902
src/NadekoBot/Migrations/PostgreSql/20241104094232_betstats.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class betstats : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "userbetstats",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
game = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
wincount = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
losecount = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
totalbet = table.Column<decimal>(type: "numeric", nullable: false),
|
||||||
|
paidout = table.Column<decimal>(type: "numeric", nullable: false),
|
||||||
|
maxwin = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
maxbet = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_userbetstats", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_userbetstats_userid_game",
|
||||||
|
table: "userbetstats",
|
||||||
|
columns: new[] { "userid", "game" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "userbetstats");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3919
src/NadekoBot/Migrations/PostgreSql/20241105073004_rakeback.Designer.cs
generated
Normal file
3919
src/NadekoBot/Migrations/PostgreSql/20241105073004_rakeback.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class rakeback : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "rakeback",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
userid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
amount = table.Column<decimal>(type: "numeric", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_rakeback", x => x.userid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "rakeback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3947
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.Designer.cs
generated
Normal file
3947
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
170
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.cs
Normal file
170
src/NadekoBot/Migrations/PostgreSql/20241115171647_sar-rework.cs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class sarrework : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "sarautodelete",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
isenabled = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_sarautodelete", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "sargroup",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
groupnumber = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
rolereq = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
|
||||||
|
isexclusive = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
name = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_sargroup", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_sargroup_guildid_groupnumber",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.guildid,
|
||||||
|
x.groupnumber
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "sar",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
sargroupid = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
levelreq = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_sar", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_sar_guildid_roleid",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.guildid,
|
||||||
|
x.roleid
|
||||||
|
});
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_sar_sargroup_sargroupid",
|
||||||
|
column: x => x.sargroupid,
|
||||||
|
principalTable: "sargroup",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_sar_sargroupid",
|
||||||
|
table: "sar",
|
||||||
|
column: "sargroupid");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_sarautodelete_guildid",
|
||||||
|
table: "sarautodelete",
|
||||||
|
column: "guildid",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
MigrationQueries.MigrateSar(migrationBuilder);
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "groupname");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "selfassignableroles");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "sar");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "sarautodelete");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "sargroup");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "groupname",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildconfigid = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||||
|
name = table.Column<string>(type: "text", nullable: true),
|
||||||
|
number = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_groupname", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_groupname_guildconfigs_guildconfigid",
|
||||||
|
column: x => x.guildconfigid,
|
||||||
|
principalTable: "guildconfigs",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "selfassignableroles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy",
|
||||||
|
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true),
|
||||||
|
group = table.Column<int>(type: "integer", nullable: false, defaultValue: 0),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
levelrequirement = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_selfassignableroles", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_groupname_guildconfigid_number",
|
||||||
|
table: "groupname",
|
||||||
|
columns: new[] { "guildconfigid", "number" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_selfassignableroles_guildid_roleid",
|
||||||
|
table: "selfassignableroles",
|
||||||
|
columns: new[] { "guildid", "roleid" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4048
src/NadekoBot/Migrations/PostgreSql/20241126033634_btnroles_guildcolors.Designer.cs
generated
Normal file
4048
src/NadekoBot/Migrations/PostgreSql/20241126033634_btnroles_guildcolors.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class btnroles_guildcolors : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "buttonrole",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
buttonid = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
messageid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
position = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
emote = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
label = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
||||||
|
exclusive = table.Column<bool>(type: "boolean", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_buttonrole", x => x.id);
|
||||||
|
table.UniqueConstraint("ak_buttonrole_roleid_messageid", x => new { x.roleid, x.messageid });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "guildcolors",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
|
||||||
|
okcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true),
|
||||||
|
errorcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true),
|
||||||
|
pendingcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_guildcolors", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_buttonrole_guildid",
|
||||||
|
table: "buttonrole",
|
||||||
|
column: "guildid");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_guildcolors_guildid",
|
||||||
|
table: "guildcolors",
|
||||||
|
column: "guildid",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "buttonrole");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "guildcolors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -451,6 +451,69 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("blacklist", (string)null);
|
b.ToTable("blacklist", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.ButtonRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ButtonId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("buttonid");
|
||||||
|
|
||||||
|
b.Property<decimal>("ChannelId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
|
b.Property<string>("Emote")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("emote");
|
||||||
|
|
||||||
|
b.Property<bool>("Exclusive")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("exclusive");
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<string>("Label")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("label");
|
||||||
|
|
||||||
|
b.Property<decimal>("MessageId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("messageid");
|
||||||
|
|
||||||
|
b.Property<int>("Position")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("position");
|
||||||
|
|
||||||
|
b.Property<decimal>("RoleId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("roleid");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_buttonrole");
|
||||||
|
|
||||||
|
b.HasAlternateKey("RoleId", "MessageId")
|
||||||
|
.HasName("ak_buttonrole_roleid_messageid");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId")
|
||||||
|
.HasDatabaseName("ix_buttonrole_guildid");
|
||||||
|
|
||||||
|
b.ToTable("buttonrole", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("ClubId")
|
b.Property<int>("ClubId")
|
||||||
@@ -751,10 +814,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasColumnType("timestamp without time zone")
|
.HasColumnType("timestamp without time zone")
|
||||||
.HasColumnName("dateadded");
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
b.Property<string>("Discriminator")
|
|
||||||
.HasColumnType("text")
|
|
||||||
.HasColumnName("discriminator");
|
|
||||||
|
|
||||||
b.Property<bool>("IsClubAdmin")
|
b.Property<bool>("IsClubAdmin")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("boolean")
|
.HasColumnType("boolean")
|
||||||
@@ -998,6 +1057,37 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("filteredword", (string)null);
|
b.ToTable("filteredword", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.FlagTranslateChannel", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<decimal>("ChannelId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("channelid");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded")
|
||||||
|
.HasColumnType("timestamp without time zone")
|
||||||
|
.HasColumnName("dateadded");
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_flagtranslatechannel");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId", "ChannelId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_flagtranslatechannel_guildid_channelid");
|
||||||
|
|
||||||
|
b.ToTable("flagtranslatechannel", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -1175,7 +1265,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("giveawayuser", (string)null);
|
b.ToTable("giveawayuser", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildColors", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -1184,30 +1274,33 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<string>("ErrorColor")
|
||||||
.HasColumnType("timestamp without time zone")
|
.HasMaxLength(9)
|
||||||
.HasColumnName("dateadded");
|
.HasColumnType("character varying(9)")
|
||||||
|
.HasColumnName("errorcolor");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<decimal>("GuildId")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildconfigid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("OkColor")
|
||||||
.HasColumnType("text")
|
.HasMaxLength(9)
|
||||||
.HasColumnName("name");
|
.HasColumnType("character varying(9)")
|
||||||
|
.HasColumnName("okcolor");
|
||||||
|
|
||||||
b.Property<int>("Number")
|
b.Property<string>("PendingColor")
|
||||||
.HasColumnType("integer")
|
.HasMaxLength(9)
|
||||||
.HasColumnName("number");
|
.HasColumnType("character varying(9)")
|
||||||
|
.HasColumnName("pendingcolor");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_groupname");
|
.HasName("pk_guildcolors");
|
||||||
|
|
||||||
b.HasIndex("GuildConfigId", "Number")
|
b.HasIndex("GuildId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_groupname_guildconfigid_number");
|
.HasDatabaseName("ix_guildcolors_guildid");
|
||||||
|
|
||||||
b.ToTable("groupname", (string)null);
|
b.ToTable("guildcolors", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
||||||
@@ -2173,7 +2266,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("rotatingstatus", (string)null);
|
b.ToTable("rotatingstatus", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.SelfAssignedRole", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -2182,36 +2275,98 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
|
||||||
.HasColumnType("timestamp without time zone")
|
|
||||||
.HasColumnName("dateadded");
|
|
||||||
|
|
||||||
b.Property<int>("Group")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasDefaultValue(0)
|
|
||||||
.HasColumnName("group");
|
|
||||||
|
|
||||||
b.Property<decimal>("GuildId")
|
b.Property<decimal>("GuildId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("guildid");
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
b.Property<int>("LevelRequirement")
|
b.Property<int>("LevelReq")
|
||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("levelrequirement");
|
.HasColumnName("levelreq");
|
||||||
|
|
||||||
b.Property<decimal>("RoleId")
|
b.Property<decimal>("RoleId")
|
||||||
.HasColumnType("numeric(20,0)")
|
.HasColumnType("numeric(20,0)")
|
||||||
.HasColumnName("roleid");
|
.HasColumnName("roleid");
|
||||||
|
|
||||||
|
b.Property<int>("SarGroupId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("sargroupid");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_selfassignableroles");
|
.HasName("pk_sar");
|
||||||
|
|
||||||
b.HasIndex("GuildId", "RoleId")
|
b.HasAlternateKey("GuildId", "RoleId")
|
||||||
|
.HasName("ak_sar_guildid_roleid");
|
||||||
|
|
||||||
|
b.HasIndex("SarGroupId")
|
||||||
|
.HasDatabaseName("ix_sar_sargroupid");
|
||||||
|
|
||||||
|
b.ToTable("sar", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarAutoDelete", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("isenabled");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_sarautodelete");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_selfassignableroles_guildid_roleid");
|
.HasDatabaseName("ix_sarautodelete_guildid");
|
||||||
|
|
||||||
b.ToTable("selfassignableroles", (string)null);
|
b.ToTable("sarautodelete", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("GroupNumber")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("groupnumber");
|
||||||
|
|
||||||
|
b.Property<decimal>("GuildId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("guildid");
|
||||||
|
|
||||||
|
b.Property<bool>("IsExclusive")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("isexclusive");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<decimal?>("RoleReq")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("rolereq");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_sargroup");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "GroupNumber")
|
||||||
|
.HasName("ak_sargroup_guildid_groupnumber");
|
||||||
|
|
||||||
|
b.ToTable("sargroup", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
@@ -3200,6 +3355,74 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.ToTable("greetsettings", (string)null);
|
b.ToTable("greetsettings", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Rakeback", b =>
|
||||||
|
{
|
||||||
|
b.Property<decimal>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("userid");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("numeric")
|
||||||
|
.HasColumnName("amount");
|
||||||
|
|
||||||
|
b.HasKey("UserId")
|
||||||
|
.HasName("pk_rakeback");
|
||||||
|
|
||||||
|
b.ToTable("rakeback", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.UserBetStats", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("Game")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("game");
|
||||||
|
|
||||||
|
b.Property<long>("LoseCount")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("losecount");
|
||||||
|
|
||||||
|
b.Property<long>("MaxBet")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("maxbet");
|
||||||
|
|
||||||
|
b.Property<long>("MaxWin")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("maxwin");
|
||||||
|
|
||||||
|
b.Property<decimal>("PaidOut")
|
||||||
|
.HasColumnType("numeric")
|
||||||
|
.HasColumnName("paidout");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalBet")
|
||||||
|
.HasColumnType("numeric")
|
||||||
|
.HasColumnName("totalbet");
|
||||||
|
|
||||||
|
b.Property<decimal>("UserId")
|
||||||
|
.HasColumnType("numeric(20,0)")
|
||||||
|
.HasColumnName("userid");
|
||||||
|
|
||||||
|
b.Property<long>("WinCount")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("wincount");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_userbetstats");
|
||||||
|
|
||||||
|
b.HasIndex("UserId", "Game")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_userbetstats_userid_game");
|
||||||
|
|
||||||
|
b.ToTable("userbetstats", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||||
@@ -3432,18 +3655,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasConstraintName("fk_giveawayuser_giveawaymodel_giveawayid");
|
.HasConstraintName("fk_giveawayuser_giveawaymodel_giveawayid");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
|
|
||||||
.WithMany("SelfAssignableRoleGroupNames")
|
|
||||||
.HasForeignKey("GuildConfigId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired()
|
|
||||||
.HasConstraintName("fk_groupname_guildconfigs_guildconfigid");
|
|
||||||
|
|
||||||
b.Navigation("GuildConfig");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
||||||
@@ -3483,6 +3694,16 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
.HasConstraintName("fk_playlistsong_musicplaylists_musicplaylistid");
|
.HasConstraintName("fk_playlistsong_musicplaylists_musicplaylistid");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Db.Models.SarGroup", null)
|
||||||
|
.WithMany("Roles")
|
||||||
|
.HasForeignKey("SarGroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_sar_sargroup_sargroupid");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||||
@@ -3759,8 +3980,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
|
|
||||||
b.Navigation("Permissions");
|
b.Navigation("Permissions");
|
||||||
|
|
||||||
b.Navigation("SelfAssignableRoleGroupNames");
|
|
||||||
|
|
||||||
b.Navigation("ShopEntries");
|
b.Navigation("ShopEntries");
|
||||||
|
|
||||||
b.Navigation("SlowmodeIgnoredRoles");
|
b.Navigation("SlowmodeIgnoredRoles");
|
||||||
@@ -3790,6 +4009,11 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.Navigation("Songs");
|
b.Navigation("Songs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Roles");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Items");
|
b.Navigation("Items");
|
||||||
|
2973
src/NadekoBot/Migrations/Sqlite/20241102022949_no-discrim-and-flag-translate.Designer.cs
generated
Normal file
2973
src/NadekoBot/Migrations/Sqlite/20241102022949_no-discrim-and-flag-translate.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class nodiscrimandflagtranslate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Discriminator",
|
||||||
|
table: "DiscordUser");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "FlagTranslateChannel",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_FlagTranslateChannel", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_FlagTranslateChannel_GuildId_ChannelId",
|
||||||
|
table: "FlagTranslateChannel",
|
||||||
|
columns: new[] { "GuildId", "ChannelId" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "FlagTranslateChannel");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Discriminator",
|
||||||
|
table: "DiscordUser",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3011
src/NadekoBot/Migrations/Sqlite/20241104094222_betstats.Designer.cs
generated
Normal file
3011
src/NadekoBot/Migrations/Sqlite/20241104094222_betstats.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
47
src/NadekoBot/Migrations/Sqlite/20241104094222_betstats.cs
Normal file
47
src/NadekoBot/Migrations/Sqlite/20241104094222_betstats.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class betstats : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "UserBetStats",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Game = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
WinCount = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
LoseCount = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
TotalBet = table.Column<decimal>(type: "TEXT", nullable: false),
|
||||||
|
PaidOut = table.Column<decimal>(type: "TEXT", nullable: false),
|
||||||
|
MaxWin = table.Column<long>(type: "INTEGER", nullable: false),
|
||||||
|
MaxBet = table.Column<long>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_UserBetStats", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserBetStats_UserId_Game",
|
||||||
|
table: "UserBetStats",
|
||||||
|
columns: new[] { "UserId", "Game" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "UserBetStats");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3025
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.Designer.cs
generated
Normal file
3025
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.cs
Normal file
34
src/NadekoBot/Migrations/Sqlite/20241105072953_rakeback.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class rakeback : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Rakeback",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<ulong>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Amount = table.Column<decimal>(type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Rakeback", x => x.UserId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Rakeback");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3045
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.Designer.cs
generated
Normal file
3045
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
163
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.cs
Normal file
163
src/NadekoBot/Migrations/Sqlite/20241115171638_sar-rework.cs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class sarrework : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SarAutoDelete",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SarAutoDelete", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SarGroup",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GroupNumber = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
RoleReq = table.Column<ulong>(type: "INTEGER", nullable: true),
|
||||||
|
IsExclusive = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", maxLength: 100, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SarGroup", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_SarGroup_GuildId_GroupNumber",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.GroupNumber
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Sar",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
SarGroupId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
LevelReq = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Sar", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_Sar_GuildId_RoleId",
|
||||||
|
x => new
|
||||||
|
{
|
||||||
|
x.GuildId,
|
||||||
|
x.RoleId
|
||||||
|
});
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Sar_SarGroup_SarGroupId",
|
||||||
|
column: x => x.SarGroupId,
|
||||||
|
principalTable: "SarGroup",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Sar_SarGroupId",
|
||||||
|
table: "Sar",
|
||||||
|
column: "SarGroupId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SarAutoDelete_GuildId",
|
||||||
|
table: "SarAutoDelete",
|
||||||
|
column: "GuildId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
MigrationQueries.MigrateSar(migrationBuilder);
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GroupName");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SelfAssignableRoles");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Sar");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SarAutoDelete");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SarGroup");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GroupName",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildConfigId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Number = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GroupName", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_GroupName_GuildConfigs_GuildConfigId",
|
||||||
|
column: x => x.GuildConfigId,
|
||||||
|
principalTable: "GuildConfigs",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SelfAssignableRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||||
|
Group = table.Column<int>(type: "INTEGER", nullable: false, defaultValue: 0),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
LevelRequirement = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SelfAssignableRoles", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GroupName_GuildConfigId_Number",
|
||||||
|
table: "GroupName",
|
||||||
|
columns: new[] { "GuildConfigId", "Number" },
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_SelfAssignableRoles_GuildId_RoleId",
|
||||||
|
table: "SelfAssignableRoles",
|
||||||
|
columns: new[] { "GuildId", "RoleId" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3122
src/NadekoBot/Migrations/Sqlite/20241126033626_btnroles_guildcolors.Designer.cs
generated
Normal file
3122
src/NadekoBot/Migrations/Sqlite/20241126033626_btnroles_guildcolors.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace NadekoBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class btnroles_guildcolors : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ButtonRole",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
ButtonId = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
MessageId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Position = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
Emote = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
Label = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
|
||||||
|
Exclusive = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ButtonRole", x => x.Id);
|
||||||
|
table.UniqueConstraint("AK_ButtonRole_RoleId_MessageId", x => new { x.RoleId, x.MessageId });
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "GuildColors",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
|
||||||
|
OkColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true),
|
||||||
|
ErrorColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true),
|
||||||
|
PendingColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_GuildColors", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ButtonRole_GuildId",
|
||||||
|
table: "ButtonRole",
|
||||||
|
column: "GuildId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_GuildColors_GuildId",
|
||||||
|
table: "GuildColors",
|
||||||
|
column: "GuildId",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ButtonRole");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "GuildColors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -335,6 +335,54 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("Blacklist");
|
b.ToTable("Blacklist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.ButtonRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ButtonId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Emote")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Exclusive")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Label")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("MessageId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Position")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("RoleId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("RoleId", "MessageId");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId");
|
||||||
|
|
||||||
|
b.ToTable("ButtonRole");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("ClubId")
|
b.Property<int>("ClubId")
|
||||||
@@ -560,9 +608,6 @@ namespace NadekoBot.Migrations
|
|||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<DateTime?>("DateAdded")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Discriminator")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<bool>("IsClubAdmin")
|
b.Property<bool>("IsClubAdmin")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER")
|
.HasColumnType("INTEGER")
|
||||||
@@ -743,6 +788,29 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("FilteredWord");
|
b.ToTable("FilteredWord");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.FlagTranslateChannel", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("ChannelId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("DateAdded")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId", "ChannelId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("FlagTranslateChannel");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.FollowedStream", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -874,30 +942,33 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("GiveawayUser");
|
b.ToTable("GiveawayUser");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildColors", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
b.Property<string>("ErrorColor")
|
||||||
|
.HasMaxLength(9)
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("GuildConfigId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("OkColor")
|
||||||
|
.HasMaxLength(9)
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("Number")
|
b.Property<string>("PendingColor")
|
||||||
.HasColumnType("INTEGER");
|
.HasMaxLength(9)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("GuildConfigId", "Number")
|
b.HasIndex("GuildId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("GroupName");
|
b.ToTable("GuildColors");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.GuildConfig", b =>
|
||||||
@@ -1620,35 +1691,80 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("RotatingStatus");
|
b.ToTable("RotatingStatus");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.SelfAssignedRole", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<DateTime?>("DateAdded")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<int>("Group")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER")
|
|
||||||
.HasDefaultValue(0);
|
|
||||||
|
|
||||||
b.Property<ulong>("GuildId")
|
b.Property<ulong>("GuildId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("LevelRequirement")
|
b.Property<int>("LevelReq")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<ulong>("RoleId")
|
b.Property<ulong>("RoleId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("SarGroupId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("GuildId", "RoleId")
|
b.HasAlternateKey("GuildId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("SarGroupId");
|
||||||
|
|
||||||
|
b.ToTable("Sar");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarAutoDelete", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("GuildId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("SelfAssignableRoles");
|
b.ToTable("SarAutoDelete");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("GroupNumber")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<ulong>("GuildId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("IsExclusive")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong?>("RoleReq")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasAlternateKey("GuildId", "GroupNumber");
|
||||||
|
|
||||||
|
b.ToTable("SarGroup");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
@@ -2379,6 +2495,58 @@ namespace NadekoBot.Migrations
|
|||||||
b.ToTable("GreetSettings");
|
b.ToTable("GreetSettings");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.Rakeback", b =>
|
||||||
|
{
|
||||||
|
b.Property<ulong>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("UserId");
|
||||||
|
|
||||||
|
b.ToTable("Rakeback");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Services.UserBetStats", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Game")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("LoseCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("MaxBet")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("MaxWin")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<decimal>("PaidOut")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalBet")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<ulong>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("WinCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId", "Game")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("UserBetStats");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.AntiAltSetting", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||||
@@ -2588,17 +2756,6 @@ namespace NadekoBot.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.GroupName", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", "GuildConfig")
|
|
||||||
.WithMany("SelfAssignableRoleGroupNames")
|
|
||||||
.HasForeignKey("GuildConfigId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("GuildConfig");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.IgnoredLogItem", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
b.HasOne("NadekoBot.Db.Models.LogSetting", "LogSetting")
|
||||||
@@ -2634,6 +2791,15 @@ namespace NadekoBot.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.Sar", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("NadekoBot.Db.Models.SarGroup", null)
|
||||||
|
.WithMany("Roles")
|
||||||
|
.HasForeignKey("SarGroupId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
b.HasOne("NadekoBot.Db.Models.GuildConfig", null)
|
||||||
@@ -2888,8 +3054,6 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.Navigation("Permissions");
|
b.Navigation("Permissions");
|
||||||
|
|
||||||
b.Navigation("SelfAssignableRoleGroupNames");
|
|
||||||
|
|
||||||
b.Navigation("ShopEntries");
|
b.Navigation("ShopEntries");
|
||||||
|
|
||||||
b.Navigation("SlowmodeIgnoredRoles");
|
b.Navigation("SlowmodeIgnoredRoles");
|
||||||
@@ -2919,6 +3083,11 @@ namespace NadekoBot.Migrations
|
|||||||
b.Navigation("Songs");
|
b.Navigation("Songs");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("NadekoBot.Db.Models.SarGroup", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Roles");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
modelBuilder.Entity("NadekoBot.Db.Models.ShopEntry", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Items");
|
b.Navigation("Items");
|
||||||
|
@@ -96,7 +96,7 @@ public partial class Administration : NadekoModule<AdministrationService>
|
|||||||
var guild = (SocketGuild)ctx.Guild;
|
var guild = (SocketGuild)ctx.Guild;
|
||||||
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
|
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.server_delmsgoncmd))
|
.WithTitle(GetText(strs.server_delmsgoncmd))
|
||||||
.WithDescription(enabled ? "✅" : "❌");
|
.WithDescription(enabled ? "✅" : "❌");
|
||||||
|
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using NadekoBot.Modules.Administration._common.results;
|
using NadekoBot.Modules.Administration._common.results;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration.Services;
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
public class AdministrationService : INService
|
public class AdministrationService : INService
|
||||||
{
|
{
|
||||||
|
@@ -25,6 +25,13 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the user can't aar the role which is greater or equal to the bot's highest role
|
||||||
|
if (role.Position >= ((SocketGuild)ctx.Guild).CurrentUser.GetRoles().Max(x => x.Position))
|
||||||
|
{
|
||||||
|
await Response().Error(strs.hierarchy).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var roles = await _service.ToggleAarAsync(ctx.Guild.Id, role.Id);
|
var roles = await _service.ToggleAarAsync(ctx.Guild.Id, role.Id);
|
||||||
if (roles.Count == 0)
|
if (roles.Count == 0)
|
||||||
await Response().Confirm(strs.aar_disabled).SendAsync();
|
await Response().Confirm(strs.aar_disabled).SendAsync();
|
||||||
|
@@ -136,7 +136,7 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
|||||||
await using var linqCtx = ctx.CreateLinqToDBContext();
|
await using var linqCtx = ctx.CreateLinqToDBContext();
|
||||||
await using var tempTable = linqCtx.CreateTempTable<CleanupId>();
|
await using var tempTable = linqCtx.CreateTempTable<CleanupId>();
|
||||||
|
|
||||||
foreach (var chunk in allIds.Chunk(20000))
|
foreach (var chunk in allIds.Chunk(10000))
|
||||||
{
|
{
|
||||||
await tempTable.BulkCopyAsync(chunk.Select(x => new CleanupId()
|
await tempTable.BulkCopyAsync(chunk.Select(x => new CleanupId()
|
||||||
{
|
{
|
||||||
@@ -187,13 +187,6 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
|||||||
.Contains(x.GuildId))
|
.Contains(x.GuildId))
|
||||||
.DeleteAsync();
|
.DeleteAsync();
|
||||||
|
|
||||||
// delete ignored users
|
|
||||||
await ctx.GetTable<DiscordPermOverride>()
|
|
||||||
.Where(x => x.GuildId != null
|
|
||||||
&& !tempTable.Select(x => x.GuildId)
|
|
||||||
.Contains(x.GuildId.Value))
|
|
||||||
.DeleteAsync();
|
|
||||||
|
|
||||||
// delete perm overrides
|
// delete perm overrides
|
||||||
await ctx.GetTable<DiscordPermOverride>()
|
await ctx.GetTable<DiscordPermOverride>()
|
||||||
.Where(x => x.GuildId != null
|
.Where(x => x.GuildId != null
|
||||||
@@ -219,6 +212,54 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, INService
|
|||||||
.Contains(x.GuildId))
|
.Contains(x.GuildId))
|
||||||
.DeleteAsync();
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete sar
|
||||||
|
await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete warnings
|
||||||
|
await ctx.GetTable<Warning>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete warn punishments
|
||||||
|
await ctx.GetTable<WarningPunishment>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete sticky roles
|
||||||
|
await ctx.GetTable<StickyRole>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete at channels
|
||||||
|
await ctx.GetTable<AutoTranslateChannel>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete ban templates
|
||||||
|
await ctx.GetTable<BanTemplate>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete reminders
|
||||||
|
await ctx.GetTable<Reminder>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.ServerId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
// delete button roles
|
||||||
|
await ctx.GetTable<ButtonRole>()
|
||||||
|
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||||
|
.Contains(x.GuildId))
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
GuildCount = guildIds.Keys.Count,
|
GuildCount = guildIds.Keys.Count,
|
||||||
|
@@ -42,9 +42,9 @@ public partial class Administration
|
|||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return _sender.CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
|
return CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
|
||||||
|
|
||||||
return _sender.CreateEmbed()
|
return CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithFooter(sql)
|
.WithFooter(sql)
|
||||||
.WithTitle(string.Join(" ║ ", result.ColumnNames))
|
.WithTitle(string.Join(" ║ ", result.ColumnNames))
|
||||||
@@ -99,7 +99,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithTitle(GetText(strs.sql_confirm_exec))
|
.WithTitle(GetText(strs.sql_confirm_exec))
|
||||||
.WithDescription(Format.Code(sql));
|
.WithDescription(Format.Code(sql));
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ public partial class Administration
|
|||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task PurgeUser(ulong userId)
|
public async Task PurgeUser(ulong userId)
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
|
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
|
||||||
|
|
||||||
if (!await PromptUserConfirmAsync(embed))
|
if (!await PromptUserConfirmAsync(embed))
|
||||||
|
@@ -339,7 +339,7 @@ public class GreetService : INService, IReadyExecutor
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Error(ex, "Error sending greet dm");
|
Log.Warning(ex, "Unable to send Greet DM. Probably the user has closed DMs");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -123,7 +123,7 @@ public partial class Administration
|
|||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task LanguagesList()
|
public async Task LanguagesList()
|
||||||
=> await Response().Embed(_sender.CreateEmbed()
|
=> await Response().Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.lang_list))
|
.WithTitle(GetText(strs.lang_list))
|
||||||
.WithDescription(string.Join("\n",
|
.WithDescription(string.Join("\n",
|
||||||
|
@@ -122,7 +122,7 @@ public class MuteService : INService
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_ = Task.Run(() => _sender.Response(user)
|
_ = Task.Run(() => _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(_sender.CreateEmbed(user?.GuildId)
|
||||||
.WithDescription($"You've been muted in {user.Guild} server")
|
.WithDescription($"You've been muted in {user.Guild} server")
|
||||||
.AddField("Mute Type", type.ToString())
|
.AddField("Mute Type", type.ToString())
|
||||||
.AddField("Moderator", mod.ToString())
|
.AddField("Moderator", mod.ToString())
|
||||||
@@ -140,7 +140,7 @@ public class MuteService : INService
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_ = Task.Run(() => _sender.Response(user)
|
_ = Task.Run(() => _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(_sender.CreateEmbed(user.GuildId)
|
||||||
.WithDescription($"You've been unmuted in {user.Guild} server")
|
.WithDescription($"You've been unmuted in {user.Guild} server")
|
||||||
.AddField("Unmute Type", type.ToString())
|
.AddField("Unmute Type", type.ToString())
|
||||||
.AddField("Moderator", mod.ToString())
|
.AddField("Moderator", mod.ToString())
|
||||||
|
@@ -36,7 +36,7 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task DiscordPermOverrideReset()
|
public async Task DiscordPermOverrideReset()
|
||||||
{
|
{
|
||||||
var result = await PromptUserConfirmAsync(_sender.CreateEmbed()
|
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(strs.perm_override_all_confirm)));
|
.WithDescription(GetText(strs.perm_override_all_confirm)));
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ public partial class Administration
|
|||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
var eb = _sender.CreateEmbed().WithTitle(GetText(strs.perm_overrides)).WithOkColor();
|
var eb = CreateEmbed().WithTitle(GetText(strs.perm_overrides)).WithOkColor();
|
||||||
|
|
||||||
if (items.Count == 0)
|
if (items.Count == 0)
|
||||||
eb.WithDescription(GetText(strs.perm_override_page_none));
|
eb.WithDescription(GetText(strs.perm_override_page_none));
|
||||||
|
@@ -9,7 +9,9 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
|||||||
{
|
{
|
||||||
private readonly BotConfigService _bss;
|
private readonly BotConfigService _bss;
|
||||||
private readonly SelfService _selfService;
|
private readonly SelfService _selfService;
|
||||||
|
|
||||||
private readonly IReplacementService _repService;
|
private readonly IReplacementService _repService;
|
||||||
|
|
||||||
// private readonly Replacer _rep;
|
// private readonly Replacer _rep;
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
@@ -27,7 +29,6 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
|||||||
_selfService = selfService;
|
_selfService = selfService;
|
||||||
_repService = repService;
|
_repService = repService;
|
||||||
_client = client;
|
_client = client;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
@@ -57,7 +58,7 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
|||||||
? rotatingStatuses[index = 0]
|
? rotatingStatuses[index = 0]
|
||||||
: rotatingStatuses[index++];
|
: rotatingStatuses[index++];
|
||||||
|
|
||||||
var statusText = await _repService.ReplaceAsync(playingStatus.Status, new (client: _client));
|
var statusText = await _repService.ReplaceAsync(playingStatus.Status, new(client: _client));
|
||||||
await _selfService.SetActivityAsync(statusText, (ActivityType)playingStatus.Type);
|
await _selfService.SetActivityAsync(statusText, (ActivityType)playingStatus.Type);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -72,7 +73,11 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
|||||||
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
ArgumentOutOfRangeException.ThrowIfNegative(index);
|
||||||
|
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var toRemove = await uow.Set<RotatingPlayingStatus>().AsQueryable().AsNoTracking().Skip(index).FirstOrDefaultAsync();
|
var toRemove = await uow.Set<RotatingPlayingStatus>()
|
||||||
|
.AsQueryable()
|
||||||
|
.AsNoTracking()
|
||||||
|
.Skip(index)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (toRemove is null)
|
if (toRemove is null)
|
||||||
return null;
|
return null;
|
||||||
@@ -94,6 +99,11 @@ public sealed class PlayingRotateService : INService, IReadyExecutor
|
|||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void DisableRotatePlaying()
|
||||||
|
{
|
||||||
|
_bss.ModifyConfig(bs => { bs.RotateStatuses = false; });
|
||||||
|
}
|
||||||
|
|
||||||
public bool ToggleRotatePlaying()
|
public bool ToggleRotatePlaying()
|
||||||
{
|
{
|
||||||
var enabled = false;
|
var enabled = false;
|
||||||
|
@@ -241,7 +241,7 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.prot_active));
|
var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.prot_active));
|
||||||
|
|
||||||
if (spam is not null)
|
if (spam is not null)
|
||||||
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);
|
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);
|
||||||
|
@@ -113,7 +113,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
await progressMsg.ModifyAsync(props =>
|
await progressMsg.ModifyAsync(props =>
|
||||||
{
|
{
|
||||||
props.Embed = _sender.CreateEmbed()
|
props.Embed = CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithDescription(GetText(strs.prune_progress(deleted, total)))
|
.WithDescription(GetText(strs.prune_progress(deleted, total)))
|
||||||
.Build();
|
.Build();
|
||||||
|
302
src/NadekoBot/Modules/Administration/Role/ButtonRolesCommands.cs
Normal file
302
src/NadekoBot/Modules/Administration/Role/ButtonRolesCommands.cs
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
using NadekoBot.Common.TypeReaders.Models;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration.Services;
|
||||||
|
using System.Text;
|
||||||
|
using ContextType = Discord.Commands.ContextType;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public partial class Administration
|
||||||
|
{
|
||||||
|
[Group("btr")]
|
||||||
|
public partial class ButtonRoleCommands : NadekoModule<ButtonRolesService>
|
||||||
|
{
|
||||||
|
private List<ActionRowBuilder> GetActionRows(IReadOnlyList<ButtonRole> roles)
|
||||||
|
{
|
||||||
|
var rows = roles.Select((x, i) => (Index: i, ButtonRole: x))
|
||||||
|
.GroupBy(x => x.Index / 5)
|
||||||
|
.Select(x => x.Select(y => y.ButtonRole))
|
||||||
|
.Select(x =>
|
||||||
|
{
|
||||||
|
var ab = new ActionRowBuilder()
|
||||||
|
.WithComponents(x.Select(y =>
|
||||||
|
{
|
||||||
|
var curRole = ctx.Guild.GetRole(y.RoleId);
|
||||||
|
var label = string.IsNullOrWhiteSpace(y.Label)
|
||||||
|
? curRole?.ToString() ?? "?missing " + y.RoleId
|
||||||
|
: y.Label;
|
||||||
|
|
||||||
|
var btnEmote = EmoteTypeReader.TryParse(y.Emote, out var e)
|
||||||
|
? e
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return new ButtonBuilder()
|
||||||
|
.WithCustomId(y.ButtonId)
|
||||||
|
.WithEmote(btnEmote)
|
||||||
|
.WithLabel(label)
|
||||||
|
.WithStyle(ButtonStyle.Secondary)
|
||||||
|
.Build() as IMessageComponent;
|
||||||
|
})
|
||||||
|
.ToList());
|
||||||
|
|
||||||
|
return ab;
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<MessageLink?> CreateMessageLinkAsync(ulong messageId)
|
||||||
|
{
|
||||||
|
var msg = await ctx.Channel.GetMessageAsync(messageId);
|
||||||
|
if (msg is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new MessageLink(ctx.Guild, ctx.Channel, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleAdd(ulong messageId, IEmote emote, [Leftover] IRole role)
|
||||||
|
{
|
||||||
|
var link = await CreateMessageLinkAsync(messageId);
|
||||||
|
|
||||||
|
if (link is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.invalid_message_id).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await BtnRoleAdd(link, emote, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleAdd(MessageLink link, IEmote emote, [Leftover] IRole role)
|
||||||
|
{
|
||||||
|
if (link.Message is not IUserMessage msg || !msg.IsAuthor(ctx.Client))
|
||||||
|
{
|
||||||
|
await Response().Error(strs.invalid_message_link).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await CheckRoleHierarchy(role))
|
||||||
|
{
|
||||||
|
await Response().Error(strs.hierarchy).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var success = await _service.AddButtonRole(ctx.Guild.Id, link.Channel.Id, role.Id, link.Message.Id, emote);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_message_max).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles = await _service.GetButtonRoles(ctx.Guild.Id, link.Message.Id);
|
||||||
|
|
||||||
|
var rows = GetActionRows(roles);
|
||||||
|
|
||||||
|
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().WithRows(rows).Build()));
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemove(ulong messageId, IRole role)
|
||||||
|
=> BtnRoleRemove(messageId, role.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemove(MessageLink link, IRole role)
|
||||||
|
=> BtnRoleRemove(link.Message.Id, role.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemove(MessageLink link, ulong roleId)
|
||||||
|
=> BtnRoleRemove(link.Message.Id, roleId);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleRemove(ulong messageId, ulong roleId)
|
||||||
|
{
|
||||||
|
var removed = await _service.RemoveButtonRole(ctx.Guild.Id, messageId, roleId);
|
||||||
|
if (removed is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles = await _service.GetButtonRoles(ctx.Guild.Id, messageId);
|
||||||
|
|
||||||
|
var ch = await ctx.Guild.GetTextChannelAsync(removed.ChannelId);
|
||||||
|
|
||||||
|
if (ch is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = await ch.GetMessageAsync(removed.MessageId) as IUserMessage;
|
||||||
|
|
||||||
|
if (msg is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows = GetActionRows(roles);
|
||||||
|
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().WithRows(rows).Build()));
|
||||||
|
await Response().Confirm(strs.btnrole_removed).SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleRemoveAll(MessageLink link)
|
||||||
|
=> BtnRoleRemoveAll(link.Message.Id);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleRemoveAll(ulong messageId)
|
||||||
|
{
|
||||||
|
var succ = await _service.RemoveButtonRoles(ctx.Guild.Id, messageId);
|
||||||
|
|
||||||
|
if (succ.Count == 0)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var info = succ[0];
|
||||||
|
|
||||||
|
var ch = await ctx.Guild.GetTextChannelAsync(info.ChannelId);
|
||||||
|
if (ch is null)
|
||||||
|
{
|
||||||
|
await Response().Pending(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = await ch.GetMessageAsync(info.MessageId) as IUserMessage;
|
||||||
|
if (msg is null)
|
||||||
|
{
|
||||||
|
await Response().Pending(strs.btnrole_removeall_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().Build()));
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleList()
|
||||||
|
{
|
||||||
|
var btnRoles = await _service.GetButtonRoles(ctx.Guild.Id, null);
|
||||||
|
|
||||||
|
var groups = btnRoles
|
||||||
|
.GroupBy(x => (x.ChannelId, x.MessageId))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.Items(groups)
|
||||||
|
.PageSize(1)
|
||||||
|
.AddFooter(false)
|
||||||
|
.Page(async (items, page) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
var item = items.FirstOrDefault();
|
||||||
|
if (item == default)
|
||||||
|
{
|
||||||
|
eb.WithPendingColor()
|
||||||
|
.WithDescription(GetText(strs.btnrole_none));
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (cid, msgId) = item.Key;
|
||||||
|
|
||||||
|
var str = new StringBuilder();
|
||||||
|
|
||||||
|
var ch = await ctx.Client.GetChannelAsync(cid) as IMessageChannel;
|
||||||
|
|
||||||
|
str.AppendLine($"Channel: {ch?.ToString() ?? cid.ToString()}");
|
||||||
|
str.AppendLine($"Message: {msgId}");
|
||||||
|
|
||||||
|
if (ch is not null)
|
||||||
|
{
|
||||||
|
var msg = await ch.GetMessageAsync(msgId);
|
||||||
|
if (msg is not null)
|
||||||
|
{
|
||||||
|
str.AppendLine(new MessageLink(ctx.Guild, ch, msg).ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str.AppendLine("---");
|
||||||
|
|
||||||
|
foreach (var x in item.AsEnumerable())
|
||||||
|
{
|
||||||
|
var role = ctx.Guild.GetRole(x.RoleId);
|
||||||
|
|
||||||
|
str.AppendLine($"{x.Emote} {(role?.ToString() ?? x.RoleId.ToString())}");
|
||||||
|
}
|
||||||
|
|
||||||
|
eb.WithDescription(str.ToString());
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public Task BtnRoleExclusive(MessageLink link, PermissionAction exclusive)
|
||||||
|
=> BtnRoleExclusive(link.Message.Id, exclusive);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
[RequireUserPermission(GuildPerm.ManageRoles)]
|
||||||
|
public async Task BtnRoleExclusive(ulong messageId, PermissionAction exclusive)
|
||||||
|
{
|
||||||
|
var res = await _service.SetExclusiveButtonRoles(ctx.Guild.Id, messageId, exclusive.Value);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.btnrole_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exclusive.Value)
|
||||||
|
{
|
||||||
|
await Response().Confirm(strs.btnrole_exclusive).SendAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Response().Confirm(strs.btnrole_multiple).SendAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
187
src/NadekoBot/Modules/Administration/Role/ButtonRolesService.cs
Normal file
187
src/NadekoBot/Modules/Administration/Role/ButtonRolesService.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using LinqToDB.SqlQuery;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NCalc;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
|
public sealed class ButtonRolesService : INService, IReadyExecutor
|
||||||
|
{
|
||||||
|
private const string BTN_PREFIX = "n:btnrole:";
|
||||||
|
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly IBotCreds _creds;
|
||||||
|
|
||||||
|
public ButtonRolesService(IBotCreds creds, DiscordSocketClient client, DbService db)
|
||||||
|
{
|
||||||
|
_creds = creds;
|
||||||
|
_client = client;
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnReadyAsync()
|
||||||
|
{
|
||||||
|
_client.InteractionCreated += OnInteraction;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnInteraction(SocketInteraction inter)
|
||||||
|
{
|
||||||
|
if (inter is not SocketMessageComponent smc)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!smc.Data.CustomId.StartsWith(BTN_PREFIX))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await inter.DeferAsync();
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var buttonRole = await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.ButtonId == smc.Data.CustomId && x.MessageId == smc.Message.Id)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
|
||||||
|
if (buttonRole is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var guild = _client.GetGuild(buttonRole.GuildId);
|
||||||
|
if (guild is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var role = guild.GetRole(buttonRole.RoleId);
|
||||||
|
if (role is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (smc.User is not IGuildUser user)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (user.GetRoles().Any(x => x.Id == role.Id))
|
||||||
|
{
|
||||||
|
await user.RemoveRoleAsync(role.Id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttonRole.Exclusive)
|
||||||
|
{
|
||||||
|
var otherRoles = await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == smc.GuildId && x.MessageId == smc.Message.Id)
|
||||||
|
.Select(x => x.RoleId)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
await user.RemoveRolesAsync(otherRoles);
|
||||||
|
}
|
||||||
|
|
||||||
|
await user.AddRoleAsync(role.Id);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, "Unable to handle button role interaction for user {UserId}", inter.User.Id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> AddButtonRole(
|
||||||
|
ulong guildId,
|
||||||
|
ulong channelId,
|
||||||
|
ulong roleId,
|
||||||
|
ulong messageId,
|
||||||
|
IEmote emote
|
||||||
|
)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
|
// up to 25 per message
|
||||||
|
if (await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.MessageId == messageId)
|
||||||
|
.CountAsyncLinqToDB()
|
||||||
|
>= 25)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
var emoteStr = emote.ToString()!;
|
||||||
|
var guid = Guid.NewGuid();
|
||||||
|
await uow.GetTable<ButtonRole>()
|
||||||
|
.InsertOrUpdateAsync(() => new ButtonRole()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
RoleId = roleId,
|
||||||
|
MessageId = messageId,
|
||||||
|
Position =
|
||||||
|
uow
|
||||||
|
.GetTable<ButtonRole>()
|
||||||
|
.Any(x => x.MessageId == messageId)
|
||||||
|
? uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.MessageId == messageId)
|
||||||
|
.Max(x => x.Position)
|
||||||
|
: 1,
|
||||||
|
Emote = emoteStr,
|
||||||
|
Label = string.Empty,
|
||||||
|
ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}",
|
||||||
|
Exclusive = uow.GetTable<ButtonRole>()
|
||||||
|
.Any(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
&& uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
.All(x => x.Exclusive)
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
Emote = emoteStr,
|
||||||
|
Label = string.Empty,
|
||||||
|
ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}"
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
RoleId = roleId,
|
||||||
|
MessageId = messageId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<ButtonRole>> RemoveButtonRoles(ulong guildId, ulong messageId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
return await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
.DeleteWithOutputAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ButtonRole?> RemoveButtonRole(ulong guildId, ulong messageId, ulong roleId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
var deleted = await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId && x.RoleId == roleId)
|
||||||
|
.DeleteWithOutputAsync();
|
||||||
|
|
||||||
|
return deleted.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<ButtonRole>> GetButtonRoles(ulong guildId, ulong? messageId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
return await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && (messageId == null || x.MessageId == messageId))
|
||||||
|
.OrderBy(x => x.Id)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetExclusiveButtonRoles(ulong guildId, ulong messageId, bool exclusive)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
return await uow.GetTable<ButtonRole>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.MessageId == messageId)
|
||||||
|
.UpdateAsync((_) => new()
|
||||||
|
{
|
||||||
|
Exclusive = exclusive
|
||||||
|
}) > 0;
|
||||||
|
}
|
||||||
|
}
|
@@ -80,7 +80,7 @@ public partial class Administration
|
|||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var content = string.Empty;
|
var content = string.Empty;
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
#nullable disable
|
using LinqToDB;
|
||||||
using LinqToDB;
|
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Modules.Patronage;
|
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using OneOf.Types;
|
using OneOf.Types;
|
||||||
using OneOf;
|
using OneOf;
|
||||||
@@ -18,18 +16,15 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache;
|
private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache;
|
||||||
private readonly object _cacheLock = new();
|
private readonly object _cacheLock = new();
|
||||||
private readonly SemaphoreSlim _assignementLock = new(1, 1);
|
private readonly SemaphoreSlim _assignementLock = new(1, 1);
|
||||||
private readonly IPatronageService _ps;
|
|
||||||
|
|
||||||
public ReactionRolesService(
|
public ReactionRolesService(
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
IPatronageService ps,
|
|
||||||
DbService db,
|
DbService db,
|
||||||
IBotCreds creds)
|
IBotCreds creds)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_client = client;
|
_client = client;
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
_ps = ps;
|
|
||||||
_cache = new();
|
_cache = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +52,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
var guild = _client.GetGuild(rero.GuildId);
|
var guild = _client.GetGuild(rero.GuildId);
|
||||||
var role = guild?.GetRole(rero.RoleId);
|
var role = guild?.GetRole(rero.RoleId);
|
||||||
|
|
||||||
if (role is null)
|
if (guild is null || role is null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
var user = guild.GetUser(userId) as IGuildUser
|
var user = guild.GetUser(userId) as IGuildUser
|
||||||
@@ -96,10 +91,11 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
{
|
{
|
||||||
if (user.RoleIds.Contains(role.Id))
|
if (user.RoleIds.Contains(role.Id))
|
||||||
{
|
{
|
||||||
await user.RemoveRoleAsync(role.Id, new RequestOptions()
|
await user.RemoveRoleAsync(role.Id,
|
||||||
{
|
new RequestOptions()
|
||||||
AuditLogReason = $"Reaction role"
|
{
|
||||||
});
|
AuditLogReason = $"Reaction role"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -210,10 +206,11 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.AddRoleAsync(role.Id, new()
|
await user.AddRoleAsync(role.Id,
|
||||||
{
|
new()
|
||||||
AuditLogReason = "Reaction role"
|
{
|
||||||
});
|
AuditLogReason = "Reaction role"
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -244,23 +241,10 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
int levelReq = 0)
|
int levelReq = 0)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(group);
|
ArgumentOutOfRangeException.ThrowIfNegative(group);
|
||||||
|
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(levelReq);
|
ArgumentOutOfRangeException.ThrowIfNegative(levelReq);
|
||||||
|
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
await using var tran = await ctx.Database.BeginTransactionAsync();
|
|
||||||
var activeReactionRoles = await ctx.GetTable<ReactionRoleV2>()
|
|
||||||
.Where(x => x.GuildId == guild.Id)
|
|
||||||
.CountAsync();
|
|
||||||
|
|
||||||
var limit = await _ps.GetUserLimit(LimitedFeatureName.ReactionRole, guild.OwnerId);
|
|
||||||
|
|
||||||
if (!_creds.IsOwner(guild.OwnerId) && (activeReactionRoles >= limit.Quota && limit.Quota >= 0))
|
|
||||||
{
|
|
||||||
return new Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
await ctx.GetTable<ReactionRoleV2>()
|
await ctx.GetTable<ReactionRoleV2>()
|
||||||
.InsertOrUpdateAsync(() => new()
|
.InsertOrUpdateAsync(() => new()
|
||||||
{
|
{
|
||||||
@@ -286,8 +270,6 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
|||||||
Emote = emote,
|
Emote = emote,
|
||||||
});
|
});
|
||||||
|
|
||||||
await tran.CommitAsync();
|
|
||||||
|
|
||||||
var obj = new ReactionRoleV2()
|
var obj = new ReactionRoleV2()
|
||||||
{
|
{
|
||||||
GuildId = guild.Id,
|
GuildId = guild.Id,
|
||||||
|
@@ -174,12 +174,11 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task RoleColor(Color color, [Leftover] IRole role)
|
public async Task RoleColor(Rgba32 color, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var rgba32 = color.ToPixel<Rgba32>();
|
await role.ModifyAsync(r => r.Color = new Discord.Color(color.R, color.G, color.B));
|
||||||
await role.ModifyAsync(r => r.Color = new Discord.Color(rgba32.R, rgba32.G, rgba32.B));
|
|
||||||
await Response().Confirm(strs.rc(Format.Bold(role.Name))).SendAsync();
|
await Response().Confirm(strs.rc(Format.Bold(role.Name))).SendAsync();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
@@ -62,7 +62,7 @@ public partial class Administration
|
|||||||
var (added, updated) = await _service.RefreshUsersAsync(users);
|
var (added, updated) = await _service.RefreshUsersAsync(users);
|
||||||
|
|
||||||
await message.ModifyAsync(x =>
|
await message.ModifyAsync(x =>
|
||||||
x.Embed = _sender.CreateEmbed()
|
x.Embed = CreateEmbed()
|
||||||
.WithDescription(GetText(strs.cache_users_done(added, updated)))
|
.WithDescription(GetText(strs.cache_users_done(added, updated)))
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.Build()
|
.Build()
|
||||||
@@ -115,7 +115,7 @@ public partial class Administration
|
|||||||
_service.AddNewAutoCommand(cmd);
|
_service.AddNewAutoCommand(cmd);
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.scadd))
|
.WithTitle(GetText(strs.scadd))
|
||||||
.AddField(GetText(strs.server),
|
.AddField(GetText(strs.server),
|
||||||
@@ -343,7 +343,7 @@ public partial class Administration
|
|||||||
if (string.IsNullOrWhiteSpace(str))
|
if (string.IsNullOrWhiteSpace(str))
|
||||||
str = GetText(strs.no_shards_on_page);
|
str = GetText(strs.no_shards_on_page);
|
||||||
|
|
||||||
return _sender.CreateEmbed().WithOkColor().WithDescription($"{status}\n\n{str}");
|
return CreateEmbed().WithOkColor().WithDescription($"{status}\n\n{str}");
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
@@ -71,7 +71,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
if (server.OwnerId != _client.CurrentUser.Id)
|
if (server.OwnerId != _client.CurrentUser.Id)
|
||||||
{
|
{
|
||||||
await server.LeaveAsync();
|
await server.LeaveAsync();
|
||||||
Log.Information("Left server {Name} [{Id}]", server.Name, server.Id);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -453,7 +452,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
{
|
{
|
||||||
x.UserId,
|
x.UserId,
|
||||||
x.Username,
|
x.Username,
|
||||||
x.Discriminator
|
|
||||||
})
|
})
|
||||||
.Where(x => users.Select(y => y.Id).Contains(x.UserId))
|
.Where(x => users.Select(y => y.Id).Contains(x.UserId))
|
||||||
.ToArrayAsyncEF();
|
.ToArrayAsyncEF();
|
||||||
@@ -465,12 +463,11 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
UserId = x.Id,
|
UserId = x.Id,
|
||||||
AvatarId = x.AvatarId,
|
AvatarId = x.AvatarId,
|
||||||
Username = x.Username,
|
Username = x.Username,
|
||||||
Discriminator = x.Discriminator
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied;
|
var added = (await ctx.BulkCopyAsync(usersToAdd)).RowsCopied;
|
||||||
var toUpdateUserIds = presentDbUsers
|
var toUpdateUserIds = presentDbUsers
|
||||||
.Where(x => x.Username == "Unknown" && x.Discriminator == "????")
|
.Where(x => x.Username.StartsWith("??"))
|
||||||
.Select(x => x.UserId)
|
.Select(x => x.UserId)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
@@ -481,7 +478,6 @@ public sealed class SelfService : IExecNoCommand, IReadyExecutor, INService
|
|||||||
.UpdateAsync(x => new DiscordUser()
|
.UpdateAsync(x => new DiscordUser()
|
||||||
{
|
{
|
||||||
Username = user.Username,
|
Username = user.Username,
|
||||||
Discriminator = user.Discriminator,
|
|
||||||
|
|
||||||
// .award tends to set AvatarId and DateAdded to NULL, so account for that.
|
// .award tends to set AvatarId and DateAdded to NULL, so account for that.
|
||||||
AvatarId = user.AvatarId,
|
AvatarId = user.AvatarId,
|
||||||
|
@@ -6,16 +6,136 @@ namespace NadekoBot.Modules.Administration;
|
|||||||
|
|
||||||
public partial class Administration
|
public partial class Administration
|
||||||
{
|
{
|
||||||
[Group]
|
public partial class SelfAssignedRolesHelpers : NadekoModule<SelfAssignedRolesService>
|
||||||
|
{
|
||||||
|
private readonly SarAssignerService _sas;
|
||||||
|
|
||||||
|
public SelfAssignedRolesHelpers(SarAssignerService sas)
|
||||||
|
{
|
||||||
|
_sas = sas;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Iam([Leftover] IRole role)
|
||||||
|
{
|
||||||
|
var guildUser = (IGuildUser)ctx.User;
|
||||||
|
|
||||||
|
var group = await _service.GetRoleGroup(ctx.User.Id, role.Id);
|
||||||
|
|
||||||
|
IUserMessage msg = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (group is null)
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<SarAssignResult>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
await _sas.Add(new()
|
||||||
|
{
|
||||||
|
Group = group,
|
||||||
|
RoleId = role.Id,
|
||||||
|
User = guildUser,
|
||||||
|
CompletionTask = tcs
|
||||||
|
});
|
||||||
|
|
||||||
|
var res = await tcs.Task;
|
||||||
|
|
||||||
|
if (res.TryPickT0(out _, out var error))
|
||||||
|
{
|
||||||
|
msg = await Response()
|
||||||
|
.Confirm(strs.self_assign_success(Format.Bold(role.Name)))
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var resStr = error.Match(
|
||||||
|
_ => strs.error_occured,
|
||||||
|
lvlReq => strs.self_assign_not_level(Format.Bold(lvlReq.Level.ToString())),
|
||||||
|
roleRq => strs.self_assign_role_req(Format.Bold(ctx.Guild.GetRole(roleRq.RoleId).ToString()
|
||||||
|
?? "missing role " + roleRq.RoleId),
|
||||||
|
group.Name),
|
||||||
|
_ => strs.self_assign_already(Format.Bold(role.Name)),
|
||||||
|
_ => strs.self_assign_perms);
|
||||||
|
|
||||||
|
msg = await Response().Error(resStr).SendAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var ad = _service.GetAutoDelete(ctx.Guild.Id);
|
||||||
|
|
||||||
|
if (ad)
|
||||||
|
{
|
||||||
|
msg?.DeleteAfter(3);
|
||||||
|
ctx.Message.DeleteAfter(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Iamnot([Leftover] IRole role)
|
||||||
|
{
|
||||||
|
var guildUser = (IGuildUser)ctx.User;
|
||||||
|
|
||||||
|
IUserMessage msg = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (guildUser.RoleIds.Contains(role.Id))
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_not_have(Format.Bold(role.Name))).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var group = await _service.GetRoleGroup(role.Guild.Id, role.Id);
|
||||||
|
|
||||||
|
if (group is null || group.Roles.All(x => x.RoleId != role.Id))
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role.Position >= ((SocketGuild)ctx.Guild).CurrentUser.Roles.Max(x => x.Position))
|
||||||
|
{
|
||||||
|
msg = await Response().Error(strs.self_assign_perms).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await guildUser.RemoveRoleAsync(role);
|
||||||
|
msg = await Response().Confirm(strs.self_assign_remove(Format.Bold(role.Name))).SendAsync();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
var ad = _service.GetAutoDelete(ctx.Guild.Id);
|
||||||
|
if (ad)
|
||||||
|
{
|
||||||
|
msg?.DeleteAfter(3);
|
||||||
|
ctx.Message.DeleteAfter(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Group("sar")]
|
||||||
public partial class SelfAssignedRolesCommands : NadekoModule<SelfAssignedRolesService>
|
public partial class SelfAssignedRolesCommands : NadekoModule<SelfAssignedRolesService>
|
||||||
{
|
{
|
||||||
|
private readonly SarAssignerService _sas;
|
||||||
|
|
||||||
|
public SelfAssignedRolesCommands(SarAssignerService sas)
|
||||||
|
{
|
||||||
|
_sas = sas;
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageMessages)]
|
[UserPerm(GuildPerm.ManageMessages)]
|
||||||
[BotPerm(GuildPerm.ManageMessages)]
|
[BotPerm(GuildPerm.ManageMessages)]
|
||||||
public async Task AdSarm()
|
public async Task SarAutoDelete()
|
||||||
{
|
{
|
||||||
var newVal = _service.ToggleAdSarm(ctx.Guild.Id);
|
var newVal = await _service.ToggleAutoDelete(ctx.Guild.Id);
|
||||||
|
|
||||||
if (newVal)
|
if (newVal)
|
||||||
await Response().Confirm(strs.adsarm_enable(prefix)).SendAsync();
|
await Response().Confirm(strs.adsarm_enable(prefix)).SendAsync();
|
||||||
@@ -28,30 +148,24 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public Task Asar([Leftover] IRole role)
|
public Task SarAdd([Leftover] IRole role)
|
||||||
=> Asar(0, role);
|
=> SarAdd(0, role);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Asar(int group, [Leftover] IRole role)
|
public async Task SarAdd(int group, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var guser = (IGuildUser)ctx.User;
|
if (!await CheckRoleHierarchy(role))
|
||||||
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var succ = _service.AddNew(ctx.Guild.Id, role, group);
|
await _service.AddAsync(ctx.Guild.Id, role.Id, group);
|
||||||
|
|
||||||
if (succ)
|
await Response()
|
||||||
{
|
.Confirm(strs.role_added(Format.Bold(role.Name), Format.Bold(group.ToString())))
|
||||||
await Response()
|
.SendAsync();
|
||||||
.Confirm(strs.role_added(Format.Bold(role.Name), Format.Bold(group.ToString())))
|
|
||||||
.SendAsync();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
await Response().Error(strs.role_in_list(Format.Bold(role.Name))).SendAsync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -59,9 +173,9 @@ public partial class Administration
|
|||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task Sargn(int group, [Leftover] string name = null)
|
public async Task SarGroupName(int group, [Leftover] string name = null)
|
||||||
{
|
{
|
||||||
var set = await _service.SetNameAsync(ctx.Guild.Id, group, name);
|
var set = await _service.SetGroupNameAsync(ctx.Guild.Id, group, name);
|
||||||
|
|
||||||
if (set)
|
if (set)
|
||||||
{
|
{
|
||||||
@@ -70,19 +184,19 @@ public partial class Administration
|
|||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await Response().Confirm(strs.group_name_removed(Format.Bold(group.ToString()))).SendAsync();
|
await Response().Confirm(strs.group_name_removed(Format.Bold(group.ToString()))).SendAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
public async Task Rsar([Leftover] IRole role)
|
public async Task SarRemove([Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var guser = (IGuildUser)ctx.User;
|
var guser = (IGuildUser)ctx.User;
|
||||||
if (ctx.User.Id != guser.Guild.OwnerId && guser.GetRoles().Max(x => x.Position) <= role.Position)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var success = _service.RemoveSar(role.Guild.Id, role.Id);
|
var success = await _service.RemoveAsync(role.Guild.Id, role.Id);
|
||||||
if (!success)
|
if (!success)
|
||||||
await Response().Error(strs.self_assign_not).SendAsync();
|
await Response().Error(strs.self_assign_not).SendAsync();
|
||||||
else
|
else
|
||||||
@@ -91,59 +205,81 @@ public partial class Administration
|
|||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Lsar(int page = 1)
|
public async Task SarList(int page = 1)
|
||||||
{
|
{
|
||||||
if (--page < 0)
|
if (--page < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (exclusive, roles, groups) = _service.GetRoles(ctx.Guild);
|
var groups = await _service.GetSarsAsync(ctx.Guild.Id);
|
||||||
|
|
||||||
|
var gDict = groups.ToDictionary(x => x.Id, x => x);
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Paginated()
|
.Paginated()
|
||||||
.Items(roles.OrderBy(x => x.Model.Group).ToList())
|
.Items(groups.SelectMany(x => x.Roles).ToList())
|
||||||
.PageSize(20)
|
.PageSize(20)
|
||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page(async (items, _) =>
|
||||||
{
|
{
|
||||||
var rolesStr = new StringBuilder();
|
|
||||||
var roleGroups = items
|
var roleGroups = items
|
||||||
.GroupBy(x => x.Model.Group)
|
.GroupBy(x => x.SarGroupId)
|
||||||
.OrderBy(x => x.Key);
|
.OrderBy(x => x.Key);
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.self_assign_list(groups.Sum(x => x.Roles.Count))));
|
||||||
|
|
||||||
foreach (var kvp in roleGroups)
|
foreach (var kvp in roleGroups)
|
||||||
{
|
{
|
||||||
string groupNameText;
|
var group = gDict[kvp.Key];
|
||||||
if (!groups.TryGetValue(kvp.Key, out var name))
|
|
||||||
groupNameText = Format.Bold(GetText(strs.self_assign_group(kvp.Key)));
|
|
||||||
else
|
|
||||||
groupNameText = Format.Bold($"{kvp.Key} - {name.TrimTo(25, true)}");
|
|
||||||
|
|
||||||
rolesStr.AppendLine("\t\t\t\t ⟪" + groupNameText + "⟫");
|
var groupNameText = "";
|
||||||
foreach (var (model, role) in kvp.AsEnumerable())
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(group.Name))
|
||||||
|
groupNameText += $" **{group.Name}**";
|
||||||
|
|
||||||
|
groupNameText = $"`{group.GroupNumber}` {groupNameText}";
|
||||||
|
|
||||||
|
var rolesStr = new StringBuilder();
|
||||||
|
|
||||||
|
if (group.IsExclusive)
|
||||||
{
|
{
|
||||||
if (role is null)
|
rolesStr.AppendLine(Format.Italics(GetText(strs.choose_one)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.RoleReq is ulong rrId)
|
||||||
|
{
|
||||||
|
var rr = ctx.Guild.GetRole(rrId);
|
||||||
|
|
||||||
|
if (rr is null)
|
||||||
{
|
{
|
||||||
|
await _service.SetGroupRoleReq(group.GuildId, group.GroupNumber, null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// first character is invisible space
|
rolesStr.AppendLine(
|
||||||
if (model.LevelRequirement == 0)
|
Format.Italics(GetText(strs.requires_role(Format.Bold(rr.Name)))));
|
||||||
rolesStr.AppendLine(" " + role.Name);
|
|
||||||
else
|
|
||||||
rolesStr.AppendLine(" " + role.Name + $" (lvl {model.LevelRequirement}+)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rolesStr.AppendLine();
|
foreach (var sar in kvp)
|
||||||
|
{
|
||||||
|
var roleName = (ctx.Guild.GetRole(sar.RoleId)?.Name ?? (sar.RoleId + " (deleted)"));
|
||||||
|
rolesStr.Append("- " + Format.Code(roleName));
|
||||||
|
|
||||||
|
if (sar.LevelReq > 0)
|
||||||
|
{
|
||||||
|
rolesStr.Append($" *[lvl {sar.LevelReq}+]*");
|
||||||
|
}
|
||||||
|
|
||||||
|
rolesStr.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
eb.AddField(groupNameText, rolesStr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _sender.CreateEmbed()
|
return eb;
|
||||||
.WithOkColor()
|
|
||||||
.WithTitle(Format.Bold(GetText(strs.self_assign_list(roles.Count()))))
|
|
||||||
.WithDescription(rolesStr.ToString())
|
|
||||||
.WithFooter(exclusive
|
|
||||||
? GetText(strs.self_assign_are_exclusive)
|
|
||||||
: GetText(strs.self_assign_are_not_exclusive));
|
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
@@ -152,25 +288,36 @@ public partial class Administration
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
public async Task Togglexclsar()
|
public async Task SarExclusive(int groupNumber)
|
||||||
{
|
{
|
||||||
var areExclusive = _service.ToggleEsar(ctx.Guild.Id);
|
var areExclusive = await _service.SetGroupExclusivityAsync(ctx.Guild.Id, groupNumber);
|
||||||
if (areExclusive)
|
|
||||||
|
if (areExclusive is null)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.sar_group_not_found).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areExclusive is true)
|
||||||
|
{
|
||||||
await Response().Confirm(strs.self_assign_excl).SendAsync();
|
await Response().Confirm(strs.self_assign_excl).SendAsync();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
await Response().Confirm(strs.self_assign_no_excl).SendAsync();
|
await Response().Confirm(strs.self_assign_no_excl).SendAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.ManageRoles)]
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
[BotPerm(GuildPerm.ManageRoles)]
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
public async Task RoleLevelReq(int level, [Leftover] IRole role)
|
public async Task SarRoleLevelReq(int level, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
if (level < 0)
|
if (level < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var succ = _service.SetLevelReq(ctx.Guild.Id, role, level);
|
var succ = await _service.SetRoleLevelReq(ctx.Guild.Id, role.Id, level);
|
||||||
|
|
||||||
if (!succ)
|
if (!succ)
|
||||||
{
|
{
|
||||||
@@ -186,54 +333,35 @@ public partial class Administration
|
|||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Iam([Leftover] IRole role)
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
|
[BotPerm(GuildPerm.ManageRoles)]
|
||||||
|
public async Task SarGroupRoleReq(int groupNumber, [Leftover] IRole role)
|
||||||
{
|
{
|
||||||
var guildUser = (IGuildUser)ctx.User;
|
var succ = await _service.SetGroupRoleReq(ctx.Guild.Id, groupNumber, role.Id);
|
||||||
|
|
||||||
var (result, autoDelete, extra) = await _service.Assign(guildUser, role);
|
if (!succ)
|
||||||
|
|
||||||
IUserMessage msg;
|
|
||||||
if (result == SelfAssignedRolesService.AssignResult.ErrNotAssignable)
|
|
||||||
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.AssignResult.ErrLvlReq)
|
|
||||||
msg = await Response().Error(strs.self_assign_not_level(Format.Bold(extra.ToString()))).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.AssignResult.ErrAlreadyHave)
|
|
||||||
msg = await Response().Error(strs.self_assign_already(Format.Bold(role.Name))).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.AssignResult.ErrNotPerms)
|
|
||||||
msg = await Response().Error(strs.self_assign_perms).SendAsync();
|
|
||||||
else
|
|
||||||
msg = await Response().Confirm(strs.self_assign_success(Format.Bold(role.Name))).SendAsync();
|
|
||||||
|
|
||||||
if (autoDelete)
|
|
||||||
{
|
{
|
||||||
msg.DeleteAfter(3);
|
await Response().Error(strs.sar_group_not_found).SendAsync();
|
||||||
ctx.Message.DeleteAfter(3);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Confirm(strs.self_assign_group_role_req(
|
||||||
|
Format.Bold(groupNumber.ToString()),
|
||||||
|
Format.Bold(role.Name)))
|
||||||
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Iamnot([Leftover] IRole role)
|
[UserPerm(GuildPerm.ManageRoles)]
|
||||||
|
public async Task SarGroupDelete(int groupNumber)
|
||||||
{
|
{
|
||||||
var guildUser = (IGuildUser)ctx.User;
|
var succ = await _service.DeleteRoleGroup(ctx.Guild.Id, groupNumber);
|
||||||
|
if (succ)
|
||||||
var (result, autoDelete) = await _service.Remove(guildUser, role);
|
await Response().Confirm(strs.sar_group_deleted(Format.Bold(groupNumber.ToString()))).SendAsync();
|
||||||
|
|
||||||
IUserMessage msg;
|
|
||||||
if (result == SelfAssignedRolesService.RemoveResult.ErrNotAssignable)
|
|
||||||
msg = await Response().Error(strs.self_assign_not).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.RemoveResult.ErrNotHave)
|
|
||||||
msg = await Response().Error(strs.self_assign_not_have(Format.Bold(role.Name))).SendAsync();
|
|
||||||
else if (result == SelfAssignedRolesService.RemoveResult.ErrNotPerms)
|
|
||||||
msg = await Response().Error(strs.self_assign_perms).SendAsync();
|
|
||||||
else
|
else
|
||||||
msg = await Response().Confirm(strs.self_assign_remove(Format.Bold(role.Name))).SendAsync();
|
await Response().Error(strs.sar_group_not_found).SendAsync();
|
||||||
|
|
||||||
if (autoDelete)
|
|
||||||
{
|
|
||||||
msg.DeleteAfter(3);
|
|
||||||
ctx.Message.DeleteAfter(3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,233 +1,326 @@
|
|||||||
#nullable disable
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
using OneOf;
|
||||||
|
using OneOf.Types;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration.Services;
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
public class SelfAssignedRolesService : INService
|
public class SelfAssignedRolesService : INService, IReadyExecutor
|
||||||
{
|
{
|
||||||
public enum AssignResult
|
|
||||||
{
|
|
||||||
Assigned, // successfully removed
|
|
||||||
ErrNotAssignable, // not assignable (error)
|
|
||||||
ErrAlreadyHave, // you already have that role (error)
|
|
||||||
ErrNotPerms, // bot doesn't have perms (error)
|
|
||||||
ErrLvlReq // you are not required level (error)
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RemoveResult
|
|
||||||
{
|
|
||||||
Removed, // successfully removed
|
|
||||||
ErrNotAssignable, // not assignable (error)
|
|
||||||
ErrNotHave, // you don't have a role you want to remove (error)
|
|
||||||
ErrNotPerms // bot doesn't have perms (error)
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
|
private readonly DiscordSocketClient _client;
|
||||||
|
private readonly IBotCreds _creds;
|
||||||
|
|
||||||
public SelfAssignedRolesService(DbService db)
|
private ConcurrentHashSet<ulong> _sarAds = new();
|
||||||
=> _db = db;
|
|
||||||
|
|
||||||
public bool AddNew(ulong guildId, IRole role, int group)
|
public SelfAssignedRolesService(DbService db, DiscordSocketClient client, IBotCreds creds)
|
||||||
{
|
{
|
||||||
using var uow = _db.GetDbContext();
|
_db = db;
|
||||||
var roles = uow.Set<SelfAssignedRole>().GetFromGuild(guildId);
|
_client = client;
|
||||||
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
|
_creds = creds;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
uow.Set<SelfAssignedRole>().Add(new()
|
public async Task AddAsync(ulong guildId, ulong roleId, int groupNumber)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
await ctx.GetTable<SarGroup>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
GroupNumber = groupNumber,
|
||||||
|
IsExclusive = false
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId,
|
||||||
|
GroupNumber = groupNumber
|
||||||
|
});
|
||||||
|
|
||||||
|
await ctx.GetTable<Sar>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
RoleId = roleId,
|
||||||
|
LevelReq = 0,
|
||||||
|
GuildId = guildId,
|
||||||
|
SarGroupId = ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.Select(x => x.Id)
|
||||||
|
.First()
|
||||||
|
},
|
||||||
|
_ => new()
|
||||||
|
{
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
RoleId = roleId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> RemoveAsync(ulong guildId, ulong roleId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var deleted = await ctx.GetTable<Sar>()
|
||||||
|
.Where(x => x.RoleId == roleId && x.GuildId == guildId)
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
return deleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetGroupNameAsync(ulong guildId, int groupNumber, string? name)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var changes = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.UpdateAsync(x => new()
|
||||||
|
{
|
||||||
|
Name = name
|
||||||
|
});
|
||||||
|
|
||||||
|
return changes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<SarGroup>> GetSarsAsync(ulong guildId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var sgs = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.LoadWith(x => x.Roles)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
return sgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetRoleLevelReq(ulong guildId, ulong roleId, int levelReq)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var changes = await ctx.GetTable<Sar>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.RoleId == roleId)
|
||||||
|
.UpdateAsync(_ => new()
|
||||||
|
{
|
||||||
|
LevelReq = levelReq,
|
||||||
|
});
|
||||||
|
|
||||||
|
return changes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> SetGroupRoleReq(ulong guildId, int groupNumber, ulong? roleId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var changes = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.UpdateAsync(_ => new()
|
||||||
|
{
|
||||||
|
RoleReq = roleId
|
||||||
|
});
|
||||||
|
|
||||||
|
return changes > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool?> SetGroupExclusivityAsync(ulong guildId, int groupNumber)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var changes = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.UpdateWithOutputAsync(old => new()
|
||||||
|
{
|
||||||
|
IsExclusive = !old.IsExclusive
|
||||||
|
},
|
||||||
|
(o, n) => n.IsExclusive);
|
||||||
|
|
||||||
|
if (changes.Length == 0)
|
||||||
{
|
{
|
||||||
Group = group,
|
return null;
|
||||||
RoleId = role.Id,
|
}
|
||||||
GuildId = role.Guild.Id
|
|
||||||
});
|
return changes[0];
|
||||||
uow.SaveChanges();
|
}
|
||||||
|
|
||||||
|
public async Task<SarGroup?> GetRoleGroup(ulong guildId, ulong roleId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var group = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.Roles.Any(x => x.RoleId == roleId))
|
||||||
|
.LoadWith(x => x.Roles)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> DeleteRoleGroup(ulong guildId, int groupNumber)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var deleted = await ctx.GetTable<SarGroup>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.GroupNumber == groupNumber)
|
||||||
|
.DeleteAsync();
|
||||||
|
|
||||||
|
return deleted > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ToggleAutoDelete(ulong guildId)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var delted = await ctx.GetTable<SarAutoDelete>()
|
||||||
|
.DeleteAsync(x => x.GuildId == guildId);
|
||||||
|
|
||||||
|
if (delted > 0)
|
||||||
|
{
|
||||||
|
_sarAds.TryRemove(guildId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ctx.GetTable<SarAutoDelete>()
|
||||||
|
.InsertOrUpdateAsync(() => new()
|
||||||
|
{
|
||||||
|
IsEnabled = true,
|
||||||
|
GuildId = guildId,
|
||||||
|
},
|
||||||
|
(_) => new()
|
||||||
|
{
|
||||||
|
IsEnabled = true
|
||||||
|
},
|
||||||
|
() => new()
|
||||||
|
{
|
||||||
|
GuildId = guildId
|
||||||
|
});
|
||||||
|
|
||||||
|
_sarAds.Add(guildId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ToggleAdSarm(ulong guildId)
|
public bool GetAutoDelete(ulong guildId)
|
||||||
|
=> _sarAds.Contains(guildId);
|
||||||
|
|
||||||
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
bool newval;
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
var config = uow.GuildConfigsForId(guildId, set => set);
|
|
||||||
newval = config.AutoDeleteSelfAssignedRoleMessages = !config.AutoDeleteSelfAssignedRoleMessages;
|
|
||||||
uow.SaveChanges();
|
|
||||||
return newval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(AssignResult Result, bool AutoDelete, object extra)> Assign(IGuildUser guildUser, IRole role)
|
|
||||||
{
|
|
||||||
LevelStats userLevelData;
|
|
||||||
await using (var uow = _db.GetDbContext())
|
|
||||||
{
|
|
||||||
var stats = uow.GetOrCreateUserXpStats(guildUser.Guild.Id, guildUser.Id);
|
|
||||||
userLevelData = new(stats.Xp + stats.AwardedXp);
|
|
||||||
}
|
|
||||||
|
|
||||||
var (autoDelete, exclusive, roles) = GetAdAndRoles(guildUser.Guild.Id);
|
|
||||||
|
|
||||||
var theRoleYouWant = roles.FirstOrDefault(r => r.RoleId == role.Id);
|
|
||||||
if (theRoleYouWant is null)
|
|
||||||
return (AssignResult.ErrNotAssignable, autoDelete, null);
|
|
||||||
if (theRoleYouWant.LevelRequirement > userLevelData.Level)
|
|
||||||
return (AssignResult.ErrLvlReq, autoDelete, theRoleYouWant.LevelRequirement);
|
|
||||||
if (guildUser.RoleIds.Contains(role.Id))
|
|
||||||
return (AssignResult.ErrAlreadyHave, autoDelete, null);
|
|
||||||
|
|
||||||
var roleIds = roles.Where(x => x.Group == theRoleYouWant.Group).Select(x => x.RoleId).ToArray();
|
|
||||||
if (exclusive)
|
|
||||||
{
|
|
||||||
var sameRoles = guildUser.RoleIds.Where(r => roleIds.Contains(r));
|
|
||||||
|
|
||||||
foreach (var roleId in sameRoles)
|
|
||||||
{
|
|
||||||
var sameRole = guildUser.Guild.GetRole(roleId);
|
|
||||||
if (sameRole is not null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await guildUser.RemoveRoleAsync(sameRole);
|
|
||||||
await Task.Delay(300);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await guildUser.AddRoleAsync(role);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
return (AssignResult.ErrNotPerms, autoDelete, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (AssignResult.Assigned, autoDelete, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> SetNameAsync(ulong guildId, int group, string name)
|
|
||||||
{
|
|
||||||
var set = false;
|
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var gc = uow.GuildConfigsForId(guildId, y => y.Include(x => x.SelfAssignableRoleGroupNames));
|
var guilds = await uow.GetTable<SarAutoDelete>()
|
||||||
var toUpdate = gc.SelfAssignableRoleGroupNames.FirstOrDefault(x => x.Number == group);
|
.Where(x => x.IsEnabled && Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
|
||||||
|
.Select(x => x.GuildId)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
_sarAds = new(guilds);
|
||||||
{
|
|
||||||
if (toUpdate is not null)
|
|
||||||
gc.SelfAssignableRoleGroupNames.Remove(toUpdate);
|
|
||||||
}
|
|
||||||
else if (toUpdate is null)
|
|
||||||
{
|
|
||||||
gc.SelfAssignableRoleGroupNames.Add(new()
|
|
||||||
{
|
|
||||||
Name = name,
|
|
||||||
Number = group
|
|
||||||
});
|
|
||||||
set = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
toUpdate.Name = name;
|
|
||||||
set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await uow.SaveChangesAsync();
|
|
||||||
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(RemoveResult Result, bool AutoDelete)> Remove(IGuildUser guildUser, IRole role)
|
|
||||||
{
|
|
||||||
var (autoDelete, _, roles) = GetAdAndRoles(guildUser.Guild.Id);
|
|
||||||
|
|
||||||
if (roles.FirstOrDefault(r => r.RoleId == role.Id) is null)
|
|
||||||
return (RemoveResult.ErrNotAssignable, autoDelete);
|
|
||||||
if (!guildUser.RoleIds.Contains(role.Id))
|
|
||||||
return (RemoveResult.ErrNotHave, autoDelete);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await guildUser.RemoveRoleAsync(role);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return (RemoveResult.ErrNotPerms, autoDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (RemoveResult.Removed, autoDelete);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemoveSar(ulong guildId, ulong roleId)
|
|
||||||
{
|
|
||||||
bool success;
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
success = uow.Set<SelfAssignedRole>().DeleteByGuildAndRoleId(guildId, roleId);
|
|
||||||
uow.SaveChanges();
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (bool AutoDelete, bool Exclusive, IReadOnlyCollection<SelfAssignedRole>) GetAdAndRoles(ulong guildId)
|
|
||||||
{
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
var gc = uow.GuildConfigsForId(guildId, set => set);
|
|
||||||
var autoDelete = gc.AutoDeleteSelfAssignedRoleMessages;
|
|
||||||
var exclusive = gc.ExclusiveSelfAssignedRoles;
|
|
||||||
var roles = uow.Set<SelfAssignedRole>().GetFromGuild(guildId);
|
|
||||||
|
|
||||||
return (autoDelete, exclusive, roles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SetLevelReq(ulong guildId, IRole role, int level)
|
|
||||||
{
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
var roles = uow.Set<SelfAssignedRole>().GetFromGuild(guildId);
|
|
||||||
var sar = roles.FirstOrDefault(x => x.RoleId == role.Id);
|
|
||||||
if (sar is not null)
|
|
||||||
{
|
|
||||||
sar.LevelRequirement = level;
|
|
||||||
uow.SaveChanges();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ToggleEsar(ulong guildId)
|
|
||||||
{
|
|
||||||
bool areExclusive;
|
|
||||||
using var uow = _db.GetDbContext();
|
|
||||||
var config = uow.GuildConfigsForId(guildId, set => set);
|
|
||||||
|
|
||||||
areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
|
|
||||||
uow.SaveChanges();
|
|
||||||
return areExclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (bool Exclusive, IReadOnlyCollection<(SelfAssignedRole Model, IRole Role)> Roles, IDictionary<int, string>
|
|
||||||
GroupNames
|
|
||||||
) GetRoles(IGuild guild)
|
|
||||||
{
|
|
||||||
var exclusive = false;
|
|
||||||
|
|
||||||
IReadOnlyCollection<(SelfAssignedRole Model, IRole Role)> roles;
|
|
||||||
IDictionary<int, string> groupNames;
|
|
||||||
using (var uow = _db.GetDbContext())
|
|
||||||
{
|
|
||||||
var gc = uow.GuildConfigsForId(guild.Id, set => set.Include(x => x.SelfAssignableRoleGroupNames));
|
|
||||||
exclusive = gc.ExclusiveSelfAssignedRoles;
|
|
||||||
groupNames = gc.SelfAssignableRoleGroupNames.ToDictionary(x => x.Number, x => x.Name);
|
|
||||||
var roleModels = uow.Set<SelfAssignedRole>().GetFromGuild(guild.Id);
|
|
||||||
roles = roleModels.Select(x => (Model: x, Role: guild.GetRole(x.RoleId)))
|
|
||||||
.ToList();
|
|
||||||
uow.Set<SelfAssignedRole>().RemoveRange(roles.Where(x => x.Role is null).Select(x => x.Model).ToArray());
|
|
||||||
uow.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (exclusive, roles.Where(x => x.Role is not null).ToList(), groupNames);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class SarAssignerService : INService, IReadyExecutor
|
||||||
|
{
|
||||||
|
private readonly XpService _xp;
|
||||||
|
private readonly DbService _db;
|
||||||
|
|
||||||
|
private readonly Channel<SarAssignerDataItem> _channel =
|
||||||
|
Channel.CreateBounded<SarAssignerDataItem>(100);
|
||||||
|
|
||||||
|
|
||||||
|
public SarAssignerService(XpService xp, DbService db)
|
||||||
|
{
|
||||||
|
_xp = xp;
|
||||||
|
_db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task OnReadyAsync()
|
||||||
|
{
|
||||||
|
var reader = _channel.Reader;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var item = await reader.ReadAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sar = item.Group.Roles.First(x => x.RoleId == item.RoleId);
|
||||||
|
|
||||||
|
if (item.User.RoleIds.Contains(item.RoleId))
|
||||||
|
{
|
||||||
|
item.CompletionTask.TrySetResult(new SarAlreadyHasRole());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Group.RoleReq is { } rid)
|
||||||
|
{
|
||||||
|
if (!item.User.RoleIds.Contains(rid))
|
||||||
|
{
|
||||||
|
item.CompletionTask.TrySetResult(new SarRoleRequirement(rid));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// passed
|
||||||
|
}
|
||||||
|
|
||||||
|
// check level requirement
|
||||||
|
if (sar.LevelReq > 0)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var xpStats = await ctx.GetTable<UserXpStats>().GetGuildUserXp(sar.GuildId, item.User.Id);
|
||||||
|
var lvlData = new LevelStats(xpStats?.Xp ?? 0);
|
||||||
|
|
||||||
|
if (lvlData.Level < sar.LevelReq)
|
||||||
|
{
|
||||||
|
item.CompletionTask.TrySetResult(new SarLevelRequirement(sar.LevelReq));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// passed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Group.IsExclusive)
|
||||||
|
{
|
||||||
|
var rolesToRemove = item.Group.Roles.Select(x => x.RoleId);
|
||||||
|
await item.User.RemoveRolesAsync(rolesToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
await item.User.AddRoleAsync(item.RoleId);
|
||||||
|
|
||||||
|
item.CompletionTask.TrySetResult(new Success());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Unknown error ocurred in SAR runner: {Error}", ex.Message);
|
||||||
|
item.CompletionTask.TrySetResult(new Error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Add(SarAssignerDataItem item)
|
||||||
|
{
|
||||||
|
await _channel.Writer.WriteAsync(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SarAssignerDataItem
|
||||||
|
{
|
||||||
|
public required SarGroup Group { get; init; }
|
||||||
|
public required IGuildUser User { get; init; }
|
||||||
|
public required ulong RoleId { get; init; }
|
||||||
|
public required TaskCompletionSource<SarAssignResult> CompletionTask { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[GenerateOneOf]
|
||||||
|
public sealed partial class SarAssignResult
|
||||||
|
: OneOfBase<Success, Error, SarLevelRequirement, SarRoleRequirement, SarAlreadyHasRole, SarInsuffPerms>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public record class SarLevelRequirement(int Level);
|
||||||
|
|
||||||
|
public record class SarRoleRequirement(ulong RoleId);
|
||||||
|
|
||||||
|
public record class SarAlreadyHasRole();
|
||||||
|
|
||||||
|
public record class SarInsuffPerms();
|
@@ -35,7 +35,7 @@ public partial class Administration
|
|||||||
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
|
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
|
||||||
?? new List<IgnoredLogItem>();
|
?? new List<IgnoredLogItem>();
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.AddField(GetText(strs.log_ignored_channels),
|
.AddField(GetText(strs.log_ignored_channels),
|
||||||
chs.Count == 0
|
chs.Count == 0
|
||||||
|
@@ -42,7 +42,7 @@ public partial class Administration
|
|||||||
.Items(timezoneStrings)
|
.Items(timezoneStrings)
|
||||||
.PageSize(timezonesPerPage)
|
.PageSize(timezonesPerPage)
|
||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) => _sender.CreateEmbed()
|
.Page((items, _) => CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.timezones_available))
|
.WithTitle(GetText(strs.timezones_available))
|
||||||
.WithDescription(string.Join("\n", items)))
|
.WithDescription(string.Join("\n", items)))
|
||||||
|
@@ -23,27 +23,6 @@ public partial class Administration
|
|||||||
_mute = mute;
|
_mute = mute;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> CheckRoleHierarchy(IGuildUser target)
|
|
||||||
{
|
|
||||||
var curUser = ((SocketGuild)ctx.Guild).CurrentUser;
|
|
||||||
var ownerId = ctx.Guild.OwnerId;
|
|
||||||
var modMaxRole = ((IGuildUser)ctx.User).GetRoles().Max(r => r.Position);
|
|
||||||
var targetMaxRole = target.GetRoles().Max(r => r.Position);
|
|
||||||
var botMaxRole = curUser.GetRoles().Max(r => r.Position);
|
|
||||||
// bot can't punish a user who is higher in the hierarchy. Discord will return 403
|
|
||||||
// moderator can be owner, in which case role hierarchy doesn't matter
|
|
||||||
// otherwise, moderator has to have a higher role
|
|
||||||
if (botMaxRole <= targetMaxRole
|
|
||||||
|| (ctx.User.Id != ownerId && targetMaxRole >= modMaxRole)
|
|
||||||
|| target.Id == ownerId)
|
|
||||||
{
|
|
||||||
await Response().Error(strs.hierarchy).SendAsync();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
[UserPerm(GuildPerm.BanMembers)]
|
[UserPerm(GuildPerm.BanMembers)]
|
||||||
@@ -65,7 +44,7 @@ public partial class Administration
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _sender.Response(user)
|
await _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithErrorColor()
|
.WithErrorColor()
|
||||||
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
|
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
|
||||||
.AddField(GetText(strs.moderator), ctx.User.ToString())
|
.AddField(GetText(strs.moderator), ctx.User.ToString())
|
||||||
@@ -85,7 +64,7 @@ public partial class Administration
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Warning(ex, "Exception occured while warning a user");
|
Log.Warning(ex, "Exception occured while warning a user");
|
||||||
var errorEmbed = _sender.CreateEmbed()
|
var errorEmbed = CreateEmbed()
|
||||||
.WithErrorColor()
|
.WithErrorColor()
|
||||||
.WithDescription(GetText(strs.cant_apply_punishment));
|
.WithDescription(GetText(strs.cant_apply_punishment));
|
||||||
|
|
||||||
@@ -96,7 +75,7 @@ public partial class Administration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed().WithOkColor();
|
var embed = CreateEmbed().WithOkColor();
|
||||||
if (punishment is null)
|
if (punishment is null)
|
||||||
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
|
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
|
||||||
else
|
else
|
||||||
@@ -205,7 +184,7 @@ public partial class Administration
|
|||||||
.Page((warnings, page) =>
|
.Page((warnings, page) =>
|
||||||
{
|
{
|
||||||
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
|
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
|
||||||
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
|
var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
|
||||||
|
|
||||||
if (!warnings.Any())
|
if (!warnings.Any())
|
||||||
embed.WithDescription(GetText(strs.warnings_none));
|
embed.WithDescription(GetText(strs.warnings_none));
|
||||||
@@ -266,7 +245,7 @@ public partial class Administration
|
|||||||
+ $" | {total} ({all} - {forgiven})";
|
+ $" | {total} ({all} - {forgiven})";
|
||||||
});
|
});
|
||||||
|
|
||||||
return _sender.CreateEmbed()
|
return CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.warnings_list))
|
.WithTitle(GetText(strs.warnings_list))
|
||||||
.WithDescription(string.Join("\n", ws));
|
.WithDescription(string.Join("\n", ws));
|
||||||
@@ -478,7 +457,7 @@ public partial class Administration
|
|||||||
var user = await ctx.Client.GetUserAsync(userId);
|
var user = await ctx.Client.GetUserAsync(userId);
|
||||||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
|
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
|
||||||
@@ -507,7 +486,7 @@ public partial class Administration
|
|||||||
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
.AddField("ID", userId.ToString(), true))
|
.AddField("ID", userId.ToString(), true))
|
||||||
@@ -544,7 +523,7 @@ public partial class Administration
|
|||||||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||||
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -739,7 +718,7 @@ public partial class Administration
|
|||||||
try { await ctx.Guild.RemoveBanAsync(user); }
|
try { await ctx.Guild.RemoveBanAsync(user); }
|
||||||
catch { await ctx.Guild.RemoveBanAsync(user); }
|
catch { await ctx.Guild.RemoveBanAsync(user); }
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("☣ " + GetText(strs.sb_user))
|
.WithTitle("☣ " + GetText(strs.sb_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -794,7 +773,7 @@ public partial class Administration
|
|||||||
|
|
||||||
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
|
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.kicked_user))
|
.WithTitle(GetText(strs.kicked_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -827,7 +806,7 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
|
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
|
||||||
await _sender.Response(user)
|
await _sender.Response(user)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithDescription(dmMessage))
|
.WithDescription(dmMessage))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
@@ -839,7 +818,7 @@ public partial class Administration
|
|||||||
|
|
||||||
await user.SetTimeOutAsync(time.Time);
|
await user.SetTimeOutAsync(time.Time);
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("⏳ " + GetText(strs.timedout_user))
|
.WithTitle("⏳ " + GetText(strs.timedout_user))
|
||||||
.AddField(GetText(strs.username), user.ToString(), true)
|
.AddField(GetText(strs.username), user.ToString(), true)
|
||||||
@@ -900,7 +879,7 @@ public partial class Administration
|
|||||||
if (string.IsNullOrWhiteSpace(missStr))
|
if (string.IsNullOrWhiteSpace(missStr))
|
||||||
missStr = "-";
|
missStr = "-";
|
||||||
|
|
||||||
var toSend = _sender.CreateEmbed()
|
var toSend = CreateEmbed()
|
||||||
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
|
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
|
||||||
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
||||||
.WithPendingColor();
|
.WithPendingColor();
|
||||||
@@ -920,7 +899,7 @@ public partial class Administration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
GetText(strs.mass_ban_completed(
|
GetText(strs.mass_ban_completed(
|
||||||
banning.Count())))
|
banning.Count())))
|
||||||
@@ -948,7 +927,7 @@ public partial class Administration
|
|||||||
|
|
||||||
//send a message but don't wait for it
|
//send a message but don't wait for it
|
||||||
var banningMessageTask = Response()
|
var banningMessageTask = Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
GetText(strs.mass_kill_in_progress(bans.Count())))
|
GetText(strs.mass_kill_in_progress(bans.Count())))
|
||||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||||
@@ -969,7 +948,7 @@ public partial class Administration
|
|||||||
//wait for the message and edit it
|
//wait for the message and edit it
|
||||||
var banningMessage = await banningMessageTask;
|
var banningMessage = await banningMessageTask;
|
||||||
|
|
||||||
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
GetText(strs.mass_kill_completed(bans.Count())))
|
GetText(strs.mass_kill_completed(bans.Count())))
|
||||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||||
|
@@ -68,7 +68,7 @@ public partial class Administration
|
|||||||
else
|
else
|
||||||
text = GetText(strs.no_vcroles);
|
text = GetText(strs.no_vcroles);
|
||||||
|
|
||||||
await Response().Embed(_sender.CreateEmbed()
|
await Response().Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.vc_role_list))
|
.WithTitle(GetText(strs.vc_role_list))
|
||||||
.WithDescription(text)).SendAsync();
|
.WithDescription(text)).SendAsync();
|
||||||
|
@@ -35,7 +35,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
|
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.expr_new))
|
.WithTitle(GetText(strs.expr_new))
|
||||||
.WithDescription($"#{new kwum(ex.Id)}")
|
.WithDescription($"#{new kwum(ex.Id)}")
|
||||||
@@ -105,7 +105,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
if (ex is not null)
|
if (ex is not null)
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.expr_edited))
|
.WithTitle(GetText(strs.expr_edited))
|
||||||
.WithDescription($"#{id}")
|
.WithDescription($"#{id}")
|
||||||
@@ -160,7 +160,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
: " // " + string.Join(" ", ex.GetReactions())))
|
: " // " + string.Join(" ", ex.GetReactions())))
|
||||||
.Join('\n');
|
.Join('\n');
|
||||||
|
|
||||||
return _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
|
return CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Interaction(IsValidExprEditor() ? inter : null)
|
.Interaction(IsValidExprEditor() ? inter : null)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription($"#{id}")
|
.WithDescription($"#{id}")
|
||||||
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
|
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
|
||||||
@@ -225,7 +225,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
if (ex is not null)
|
if (ex is not null)
|
||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.expr_deleted))
|
.WithTitle(GetText(strs.expr_deleted))
|
||||||
.WithDescription($"#{id}")
|
.WithDescription($"#{id}")
|
||||||
@@ -376,7 +376,7 @@ public partial class NadekoExpressions : NadekoModule<NadekoExpressionsService>
|
|||||||
[UserPerm(GuildPerm.Administrator)]
|
[UserPerm(GuildPerm.Administrator)]
|
||||||
public async Task ExprClear()
|
public async Task ExprClear()
|
||||||
{
|
{
|
||||||
if (await PromptUserConfirmAsync(_sender.CreateEmbed()
|
if (await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithTitle("Expression clear")
|
.WithTitle("Expression clear")
|
||||||
.WithDescription("This will delete all expressions on this server.")))
|
.WithDescription("This will delete all expressions on this server.")))
|
||||||
{
|
{
|
||||||
|
@@ -6,6 +6,10 @@ namespace NadekoBot.Modules.Gambling.Common.AnimalRacing;
|
|||||||
|
|
||||||
public sealed class AnimalRace : IDisposable
|
public sealed class AnimalRace : IDisposable
|
||||||
{
|
{
|
||||||
|
public const double BASE_MULTIPLIER = 0.87;
|
||||||
|
public const double MAX_MULTIPLIER = 0.94;
|
||||||
|
public const double MULTI_PER_USER = 0.005;
|
||||||
|
|
||||||
public enum Phase
|
public enum Phase
|
||||||
{
|
{
|
||||||
WaitingForPlayers,
|
WaitingForPlayers,
|
||||||
@@ -100,7 +104,7 @@ public sealed class AnimalRace : IDisposable
|
|||||||
foreach (var user in _users)
|
foreach (var user in _users)
|
||||||
{
|
{
|
||||||
if (user.Bet > 0)
|
if (user.Bet > 0)
|
||||||
await _currency.AddAsync(user.UserId, user.Bet, new("animalrace", "refund"));
|
await _currency.AddAsync(user.UserId, (long)(user.Bet * BASE_MULTIPLIER), new("animalrace", "refund"));
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = OnStartingFailed?.Invoke(this);
|
_ = OnStartingFailed?.Invoke(this);
|
||||||
@@ -116,7 +120,7 @@ public sealed class AnimalRace : IDisposable
|
|||||||
{
|
{
|
||||||
foreach (var user in _users)
|
foreach (var user in _users)
|
||||||
{
|
{
|
||||||
user.Progress += rng.Next(1, 11);
|
user.Progress += rng.Next(1, 10);
|
||||||
if (user.Progress >= 60)
|
if (user.Progress >= 60)
|
||||||
user.Progress = 60;
|
user.Progress = 60;
|
||||||
}
|
}
|
||||||
@@ -126,13 +130,15 @@ public sealed class AnimalRace : IDisposable
|
|||||||
FinishedUsers.AddRange(finished);
|
FinishedUsers.AddRange(finished);
|
||||||
|
|
||||||
_ = OnStateUpdate?.Invoke(this);
|
_ = OnStateUpdate?.Invoke(this);
|
||||||
await Task.Delay(2500);
|
await Task.Delay(1750);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FinishedUsers[0].Bet > 0)
|
if (FinishedUsers[0].Bet > 0)
|
||||||
{
|
{
|
||||||
|
Multi = FinishedUsers.Count
|
||||||
|
* Math.Min(MAX_MULTIPLIER, BASE_MULTIPLIER + (MULTI_PER_USER * FinishedUsers.Count));
|
||||||
await _currency.AddAsync(FinishedUsers[0].UserId,
|
await _currency.AddAsync(FinishedUsers[0].UserId,
|
||||||
FinishedUsers[0].Bet * (_users.Count - 1),
|
(long)(FinishedUsers[0].Bet * Multi),
|
||||||
new("animalrace", "win"));
|
new("animalrace", "win"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +146,8 @@ public sealed class AnimalRace : IDisposable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double Multi { get; set; } = BASE_MULTIPLIER;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
CurrentPhase = Phase.Ended;
|
CurrentPhase = Phase.Ended;
|
||||||
|
@@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class AnimalRacingCommands : GamblingSubmodule<AnimalRaceService>
|
public partial class AnimalRacingCommands : GamblingModule<AnimalRaceService>
|
||||||
{
|
{
|
||||||
private readonly ICurrencyService _cs;
|
private readonly ICurrencyService _cs;
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
@@ -74,10 +74,14 @@ public partial class Gambling
|
|||||||
if (race.FinishedUsers[0].Bet > 0)
|
if (race.FinishedUsers[0].Bet > 0)
|
||||||
{
|
{
|
||||||
return Response()
|
return Response()
|
||||||
.Confirm(GetText(strs.animal_race),
|
.Embed(CreateEmbed()
|
||||||
GetText(strs.animal_race_won_money(Format.Bold(winner.Username),
|
.WithOkColor()
|
||||||
winner.Animal.Icon,
|
.WithTitle(GetText(strs.animal_race))
|
||||||
(race.FinishedUsers[0].Bet * (race.Users.Count - 1)) + CurrencySign)))
|
.WithDescription(GetText(strs.animal_race_won_money(
|
||||||
|
Format.Bold(winner.Username),
|
||||||
|
winner.Animal.Icon,
|
||||||
|
N(race.FinishedUsers[0].Bet * race.Multi))))
|
||||||
|
.WithFooter($"x{race.Multi:F2}"))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,14 +117,14 @@ public partial class Gambling
|
|||||||
|
|
||||||
private async Task Ar_OnStateUpdate(AnimalRace race)
|
private async Task Ar_OnStateUpdate(AnimalRace race)
|
||||||
{
|
{
|
||||||
var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|
|
var text = $@"|🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁🔚|
|
||||||
{string.Join("\n", race.Users.Select(p =>
|
{string.Join("\n", race.Users.Select(p =>
|
||||||
{
|
{
|
||||||
var index = race.FinishedUsers.IndexOf(p);
|
var index = race.FinishedUsers.IndexOf(p);
|
||||||
var extra = index == -1 ? "" : $"#{index + 1} {(index == 0 ? "🏆" : "")}";
|
var extra = index == -1 ? "" : $"#{index + 1} {(index == 0 ? "🏆" : "")}";
|
||||||
return $"{(int)(p.Progress / 60f * 100),-2}%|{new string('‣', p.Progress) + p.Animal.Icon + extra}";
|
return $"{(int)(p.Progress / 60f * 100),-2}%|{new string('‣', p.Progress) + p.Animal.Icon + extra}";
|
||||||
}))}
|
}))}
|
||||||
|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|";
|
|🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🏁 🔚|";
|
||||||
|
|
||||||
var msg = raceMessage;
|
var msg = raceMessage;
|
||||||
|
|
||||||
@@ -128,11 +132,11 @@ public partial class Gambling
|
|||||||
raceMessage = await Response().Confirm(text).SendAsync();
|
raceMessage = await Response().Confirm(text).SendAsync();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
await msg.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithTitle(GetText(strs.animal_race))
|
.WithTitle(GetText(strs.animal_race))
|
||||||
.WithDescription(text)
|
.WithDescription(text)
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -59,7 +59,7 @@ public partial class Gambling
|
|||||||
{
|
{
|
||||||
var bal = await _bank.GetBalanceAsync(ctx.User.Id);
|
var bal = await _bank.GetBalanceAsync(ctx.User.Id);
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(strs.bank_balance(N(bal))));
|
.WithDescription(GetText(strs.bank_balance(N(bal))));
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ public partial class Gambling
|
|||||||
{
|
{
|
||||||
var bal = await _bank.GetBalanceAsync(user.Id);
|
var bal = await _bank.GetBalanceAsync(user.Id);
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal))));
|
.WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal))));
|
||||||
|
|
||||||
|
175
src/NadekoBot/Modules/Gambling/BetStatsCommands.cs
Normal file
175
src/NadekoBot/Modules/Gambling/BetStatsCommands.cs
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
#nullable disable
|
||||||
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Gambling;
|
||||||
|
|
||||||
|
public partial class Gambling
|
||||||
|
{
|
||||||
|
[Group]
|
||||||
|
public sealed class BetStatsCommands : GamblingModule<UserBetStatsService>
|
||||||
|
{
|
||||||
|
private readonly GamblingTxTracker _gamblingTxTracker;
|
||||||
|
|
||||||
|
public BetStatsCommands(
|
||||||
|
GamblingTxTracker gamblingTxTracker,
|
||||||
|
GamblingConfigService gcs)
|
||||||
|
: base(gcs)
|
||||||
|
{
|
||||||
|
_gamblingTxTracker = gamblingTxTracker;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task BetStatsReset(GamblingGame? game = null)
|
||||||
|
{
|
||||||
|
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
|
||||||
|
|
||||||
|
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||||
|
.WithDescription(
|
||||||
|
$"""
|
||||||
|
Are you sure you want to reset your bet stats for **{GetGameName(game)}**?
|
||||||
|
|
||||||
|
It will cost you {N(price)}
|
||||||
|
"""));
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var success = await _service.ResetStatsAsync(ctx.User.Id, game);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Error(strs.not_enough(CurrencySign))
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetGameName(GamblingGame? game)
|
||||||
|
{
|
||||||
|
if (game is null)
|
||||||
|
return "all games";
|
||||||
|
|
||||||
|
return game.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(3)]
|
||||||
|
public async Task BetStats()
|
||||||
|
=> await BetStats(ctx.User, null);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(2)]
|
||||||
|
public async Task BetStats(GamblingGame game)
|
||||||
|
=> await BetStats(ctx.User, game);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(1)]
|
||||||
|
public async Task BetStats([Leftover] IUser user)
|
||||||
|
=> await BetStats(user, null);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[Priority(0)]
|
||||||
|
public async Task BetStats(IUser user, GamblingGame? game)
|
||||||
|
{
|
||||||
|
var stats = await _gamblingTxTracker.GetUserStatsAsync(user.Id, game);
|
||||||
|
|
||||||
|
if (stats.Count == 0)
|
||||||
|
stats = new()
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
TotalBet = 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithAuthor(user)
|
||||||
|
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
|
||||||
|
.AddField("Biggest Win", N(stats.Max(x => x.MaxWin)), true)
|
||||||
|
.AddField("Biggest Bet", N(stats.Max(x => x.MaxBet)), true)
|
||||||
|
.AddField("# Bets", stats.Sum(x => x.WinCount + x.LoseCount), true)
|
||||||
|
.AddField("Payout",
|
||||||
|
(stats.Sum(x => x.PaidOut) / stats.Sum(x => x.TotalBet)).ToString("P2", Culture),
|
||||||
|
true);
|
||||||
|
if (game == null)
|
||||||
|
{
|
||||||
|
var favGame = stats.MaxBy(x => x.WinCount + x.LoseCount);
|
||||||
|
eb.AddField("Favorite Game",
|
||||||
|
favGame.Game + "\n" + Format.Italics((favGame.WinCount + favGame.LoseCount) + " plays"),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eb.WithDescription(game.ToString())
|
||||||
|
.AddField("# Wins", stats.Sum(x => x.WinCount), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Embed(eb)
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task GambleStats()
|
||||||
|
{
|
||||||
|
var stats = await _gamblingTxTracker.GetAllAsync();
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
||||||
|
str += "――――――――――――――――――――\n";
|
||||||
|
foreach (var stat in stats)
|
||||||
|
{
|
||||||
|
var perc = (stat.PaidOut / stat.Bet).ToString("P2", Culture);
|
||||||
|
str += $"`{stat.Feature.PadBoth(9)}`"
|
||||||
|
+ $"|`{stat.Bet.ToString("N0").PadLeft(8, ' ')}`"
|
||||||
|
+ $"|`{stat.PaidOut.ToString("N0").PadLeft(8, ' ')}`"
|
||||||
|
+ $"|`{perc.PadLeft(6, ' ')}`\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
var bet = stats.Sum(x => x.Bet);
|
||||||
|
var paidOut = stats.Sum(x => x.PaidOut);
|
||||||
|
|
||||||
|
if (bet == 0)
|
||||||
|
bet = 1;
|
||||||
|
|
||||||
|
var tPerc = (paidOut / bet).ToString("P2", Culture);
|
||||||
|
str += "――――――――――――――――――――\n";
|
||||||
|
str += $"` {("TOTAL").PadBoth(7)}` "
|
||||||
|
+ $"|**{N(bet).PadLeft(8, ' ')}**"
|
||||||
|
+ $"|**{N(paidOut).PadLeft(8, ' ')}**"
|
||||||
|
+ $"|`{tPerc.PadLeft(6, ' ')}`";
|
||||||
|
|
||||||
|
eb.WithDescription(str);
|
||||||
|
|
||||||
|
await Response().Embed(eb).SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task GambleStatsReset()
|
||||||
|
{
|
||||||
|
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||||
|
.WithDescription(
|
||||||
|
"""
|
||||||
|
Are you sure?
|
||||||
|
This will completely reset Gambling Stats.
|
||||||
|
|
||||||
|
This action is irreversible.
|
||||||
|
""")))
|
||||||
|
return;
|
||||||
|
|
||||||
|
await GambleStats();
|
||||||
|
await _service.ResetGamblingStatsAsync();
|
||||||
|
|
||||||
|
await ctx.OkAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,7 +8,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
|
|
||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
public partial class BlackJackCommands : GamblingSubmodule<BlackJackService>
|
public partial class BlackJackCommands : GamblingModule<BlackJackService>
|
||||||
{
|
{
|
||||||
public enum BjAction
|
public enum BjAction
|
||||||
{
|
{
|
||||||
@@ -95,7 +95,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
var cStr = string.Concat(c.Select(x => x[..^1] + " "));
|
var cStr = string.Concat(c.Select(x => x[..^1] + " "));
|
||||||
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
|
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle("BlackJack")
|
.WithTitle("BlackJack")
|
||||||
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);
|
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);
|
||||||
|
@@ -9,7 +9,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class Connect4Commands : GamblingSubmodule<GamblingService>
|
public partial class Connect4Commands : GamblingModule<GamblingService>
|
||||||
{
|
{
|
||||||
private static readonly string[] _numbers =
|
private static readonly string[] _numbers =
|
||||||
[
|
[
|
||||||
@@ -132,7 +132,7 @@ public partial class Gambling
|
|||||||
else
|
else
|
||||||
title = GetText(strs.connect4_draw);
|
title = GetText(strs.connect4_draw);
|
||||||
|
|
||||||
return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
return msg.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||||
.WithTitle(title)
|
.WithTitle(title)
|
||||||
.WithDescription(GetGameStateText(game))
|
.WithDescription(GetGameStateText(game))
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
@@ -142,7 +142,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
private async Task Game_OnGameStateUpdated(Connect4Game game)
|
private async Task Game_OnGameStateUpdated(Connect4Game game)
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
|
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
|
||||||
.WithDescription(GetGameStateText(game))
|
.WithDescription(GetGameStateText(game))
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
@@ -38,7 +38,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
var fileName = $"dice.{format.FileExtensions.First()}";
|
var fileName = $"dice.{format.FileExtensions.First()}";
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.AddField(GetText(strs.roll2), gen)
|
.AddField(GetText(strs.roll2), gen)
|
||||||
@@ -115,7 +115,7 @@ public partial class Gambling
|
|||||||
d.Dispose();
|
d.Dispose();
|
||||||
|
|
||||||
var imageName = $"dice.{format.FileExtensions.First()}";
|
var imageName = $"dice.{format.FileExtensions.First()}";
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
|
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
|
||||||
@@ -141,7 +141,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
for (var i = 0; i < n1; i++)
|
for (var i = 0; i < n1; i++)
|
||||||
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
|
||||||
@@ -170,7 +170,7 @@ public partial class Gambling
|
|||||||
arr[i] = rng.Next(1, n2 + 1);
|
arr[i] = rng.Next(1, n2 + 1);
|
||||||
|
|
||||||
var sum = arr.Sum();
|
var sum = arr.Sum();
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
||||||
|
@@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class DrawCommands : GamblingSubmodule<IGamblingService>
|
public partial class DrawCommands : GamblingModule<IGamblingService>
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<IGuild, Deck> _allDecks = new();
|
private static readonly ConcurrentDictionary<IGuild, Deck> _allDecks = new();
|
||||||
private readonly IImageCache _images;
|
private readonly IImageCache _images;
|
||||||
@@ -56,8 +56,8 @@ public partial class Gambling
|
|||||||
foreach (var i in images)
|
foreach (var i in images)
|
||||||
i.Dispose();
|
i.Dispose();
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var toSend = string.Empty;
|
var toSend = string.Empty;
|
||||||
if (cardObjects.Count == 5)
|
if (cardObjects.Count == 5)
|
||||||
@@ -171,14 +171,15 @@ public partial class Gambling
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(result.Card.GetEmoji())
|
.WithDescription(result.Card.GetEmoji())
|
||||||
.AddField(GetText(strs.guess), GetGuessInfo(val, col), true)
|
.AddField(GetText(strs.guess), GetGuessInfo(val, col), true)
|
||||||
.AddField(GetText(strs.card), GetCardInfo(result.Card), true)
|
.AddField(GetText(strs.card), GetCardInfo(result.Card), false)
|
||||||
.AddField(GetText(strs.won), N((long)result.Won), false)
|
.AddField(GetText(strs.bet), N(amount), true)
|
||||||
.WithImageUrl("attachment://card.png");
|
.AddField(GetText(strs.won), N((long)result.Won), true)
|
||||||
|
.WithImageUrl("attachment://card.png");
|
||||||
|
|
||||||
using var img = await GetCardImageAsync(result.Card);
|
using var img = await GetCardImageAsync(result.Card);
|
||||||
await using var imgStream = await img.ToStreamAsync();
|
await using var imgStream = await img.ToStreamAsync();
|
||||||
|
@@ -9,7 +9,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class CurrencyEventsCommands : GamblingSubmodule<CurrencyEventsService>
|
public partial class CurrencyEventsCommands : GamblingModule<CurrencyEventsService>
|
||||||
{
|
{
|
||||||
public CurrencyEventsCommands(GamblingConfigService gamblingConf)
|
public CurrencyEventsCommands(GamblingConfigService gamblingConf)
|
||||||
: base(gamblingConf)
|
: base(gamblingConf)
|
||||||
@@ -30,12 +30,12 @@ public partial class Gambling
|
|||||||
private EmbedBuilder GetEmbed(CurrencyEvent.Type type, EventOptions opts, long currentPot)
|
private EmbedBuilder GetEmbed(CurrencyEvent.Type type, EventOptions opts, long currentPot)
|
||||||
=> type switch
|
=> type switch
|
||||||
{
|
{
|
||||||
CurrencyEvent.Type.Reaction => _sender.CreateEmbed()
|
CurrencyEvent.Type.Reaction => CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.event_title(type.ToString())))
|
.WithTitle(GetText(strs.event_title(type.ToString())))
|
||||||
.WithDescription(GetReactionDescription(opts.Amount, currentPot))
|
.WithDescription(GetReactionDescription(opts.Amount, currentPot))
|
||||||
.WithFooter(GetText(strs.event_duration_footer(opts.Hours))),
|
.WithFooter(GetText(strs.event_duration_footer(opts.Hours))),
|
||||||
CurrencyEvent.Type.GameStatus => _sender.CreateEmbed()
|
CurrencyEvent.Type.GameStatus => CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.event_title(type.ToString())))
|
.WithTitle(GetText(strs.event_title(type.ToString())))
|
||||||
.WithDescription(GetGameStatusDescription(opts.Amount, currentPot))
|
.WithDescription(GetGameStatusDescription(opts.Amount, currentPot))
|
||||||
|
@@ -11,7 +11,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class FlipCoinCommands : GamblingSubmodule<IGamblingService>
|
public partial class FlipCoinCommands : GamblingModule<IGamblingService>
|
||||||
{
|
{
|
||||||
public enum BetFlipGuess : byte
|
public enum BetFlipGuess : byte
|
||||||
{
|
{
|
||||||
@@ -84,11 +84,11 @@ public partial class Gambling
|
|||||||
? Format.Bold(GetText(strs.heads))
|
? Format.Bold(GetText(strs.heads))
|
||||||
: Format.Bold(GetText(strs.tails))));
|
: Format.Bold(GetText(strs.tails))));
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(msg)
|
.WithDescription(msg)
|
||||||
.WithImageUrl($"attachment://{imgName}");
|
.WithImageUrl($"attachment://{imgName}");
|
||||||
|
|
||||||
await ctx.Channel.SendFileAsync(stream,
|
await ctx.Channel.SendFileAsync(stream,
|
||||||
imgName,
|
imgName,
|
||||||
@@ -123,18 +123,22 @@ public partial class Gambling
|
|||||||
var won = (long)result.Won;
|
var won = (long)result.Won;
|
||||||
if (won > 0)
|
if (won > 0)
|
||||||
{
|
{
|
||||||
str = Format.Bold(GetText(strs.flip_guess(N(won))));
|
str = Format.Bold(GetText(strs.betflip_guess));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
str = Format.Bold(GetText(strs.better_luck));
|
str = Format.Bold(GetText(strs.better_luck));
|
||||||
}
|
}
|
||||||
|
|
||||||
await Response().Embed(_sender.CreateEmbed()
|
await Response()
|
||||||
.WithAuthor(ctx.User)
|
.Embed(CreateEmbed()
|
||||||
.WithDescription(str)
|
.WithAuthor(ctx.User)
|
||||||
.WithOkColor()
|
.WithDescription(str)
|
||||||
.WithImageUrl(imageToSend.ToString())).SendAsync();
|
.AddField(GetText(strs.bet), N(amount), true)
|
||||||
|
.AddField(GetText(strs.won), N((long)result.Won), true)
|
||||||
|
.WithOkColor()
|
||||||
|
.WithImageUrl(imageToSend.ToString()))
|
||||||
|
.SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,6 +14,13 @@ using System.Text;
|
|||||||
using NadekoBot.Modules.Gambling.Rps;
|
using NadekoBot.Modules.Gambling.Rps;
|
||||||
using NadekoBot.Common.TypeReaders;
|
using NadekoBot.Common.TypeReaders;
|
||||||
using NadekoBot.Modules.Patronage;
|
using NadekoBot.Modules.Patronage;
|
||||||
|
using SixLabors.Fonts;
|
||||||
|
using SixLabors.Fonts.Unicode;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Drawing.Processing;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using Color = SixLabors.ImageSharp.Color;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling;
|
namespace NadekoBot.Modules.Gambling;
|
||||||
|
|
||||||
@@ -26,10 +33,13 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
private readonly NumberFormatInfo _enUsCulture;
|
private readonly NumberFormatInfo _enUsCulture;
|
||||||
private readonly DownloadTracker _tracker;
|
private readonly DownloadTracker _tracker;
|
||||||
private readonly GamblingConfigService _configService;
|
private readonly GamblingConfigService _configService;
|
||||||
|
private readonly FontProvider _fonts;
|
||||||
private readonly IBankService _bank;
|
private readonly IBankService _bank;
|
||||||
private readonly IRemindService _remind;
|
private readonly IRemindService _remind;
|
||||||
private readonly GamblingTxTracker _gamblingTxTracker;
|
private readonly GamblingTxTracker _gamblingTxTracker;
|
||||||
private readonly IPatronageService _ps;
|
private readonly IPatronageService _ps;
|
||||||
|
private readonly RakebackService _rb;
|
||||||
|
private readonly IBotCache _cache;
|
||||||
|
|
||||||
public Gambling(
|
public Gambling(
|
||||||
IGamblingService gs,
|
IGamblingService gs,
|
||||||
@@ -38,10 +48,13 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
DownloadTracker tracker,
|
DownloadTracker tracker,
|
||||||
GamblingConfigService configService,
|
GamblingConfigService configService,
|
||||||
|
FontProvider fonts,
|
||||||
IBankService bank,
|
IBankService bank,
|
||||||
IRemindService remind,
|
IRemindService remind,
|
||||||
IPatronageService patronage,
|
IPatronageService patronage,
|
||||||
GamblingTxTracker gamblingTxTracker)
|
GamblingTxTracker gamblingTxTracker,
|
||||||
|
RakebackService rb,
|
||||||
|
IBotCache cache)
|
||||||
: base(configService)
|
: base(configService)
|
||||||
{
|
{
|
||||||
_gs = gs;
|
_gs = gs;
|
||||||
@@ -51,13 +64,17 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
_bank = bank;
|
_bank = bank;
|
||||||
_remind = remind;
|
_remind = remind;
|
||||||
_gamblingTxTracker = gamblingTxTracker;
|
_gamblingTxTracker = gamblingTxTracker;
|
||||||
|
_rb = rb;
|
||||||
|
_cache = cache;
|
||||||
_ps = patronage;
|
_ps = patronage;
|
||||||
|
_rng = new NadekoRandom();
|
||||||
|
|
||||||
_enUsCulture = new CultureInfo("en-US", false).NumberFormat;
|
_enUsCulture = new CultureInfo("en-US", false).NumberFormat;
|
||||||
_enUsCulture.NumberDecimalDigits = 0;
|
_enUsCulture.NumberDecimalDigits = 0;
|
||||||
_enUsCulture.NumberGroupSeparator = " ";
|
_enUsCulture.NumberGroupSeparator = " ";
|
||||||
_tracker = tracker;
|
_tracker = tracker;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
|
_fonts = fonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetBalanceStringAsync(ulong userId)
|
public async Task<string> GetBalanceStringAsync(ulong userId)
|
||||||
@@ -66,42 +83,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
return N(bal);
|
return N(bal);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
|
||||||
public async Task BetStats()
|
|
||||||
{
|
|
||||||
var stats = await _gamblingTxTracker.GetAllAsync();
|
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
|
||||||
.WithOkColor();
|
|
||||||
|
|
||||||
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
|
||||||
str += "――――――――――――――――――――\n";
|
|
||||||
foreach (var stat in stats)
|
|
||||||
{
|
|
||||||
var perc = (stat.PaidOut / stat.Bet).ToString("P2", Culture);
|
|
||||||
str += $"`{stat.Feature.PadBoth(9)}`"
|
|
||||||
+ $"|`{stat.Bet.ToString("N0").PadLeft(8, ' ')}`"
|
|
||||||
+ $"|`{stat.PaidOut.ToString("N0").PadLeft(8, ' ')}`"
|
|
||||||
+ $"|`{perc.PadLeft(6, ' ')}`\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
var bet = stats.Sum(x => x.Bet);
|
|
||||||
var paidOut = stats.Sum(x => x.PaidOut);
|
|
||||||
|
|
||||||
if (bet == 0)
|
|
||||||
bet = 1;
|
|
||||||
|
|
||||||
var tPerc = (paidOut / bet).ToString("P2", Culture);
|
|
||||||
str += "――――――――――――――――――――\n";
|
|
||||||
str += $"` {("TOTAL").PadBoth(7)}` "
|
|
||||||
+ $"|**{N(bet).PadLeft(8, ' ')}**"
|
|
||||||
+ $"|**{N(paidOut).PadLeft(8, ' ')}**"
|
|
||||||
+ $"|`{tPerc.PadLeft(6, ' ')}`";
|
|
||||||
|
|
||||||
eb.WithDescription(str);
|
|
||||||
|
|
||||||
await Response().Embed(eb).SendAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when)
|
private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when)
|
||||||
{
|
{
|
||||||
@@ -140,7 +121,21 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
(smc) => RemindTimelyAction(smc, DateTime.UtcNow.Add(TimeSpan.FromMilliseconds(ms)))
|
(smc) => RemindTimelyAction(smc, DateTime.UtcNow.Add(TimeSpan.FromMilliseconds(ms)))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private NadekoInteractionBase CreateTimelyInteraction()
|
||||||
|
=> _inter
|
||||||
|
.Create(ctx.User.Id,
|
||||||
|
new ButtonBuilder(
|
||||||
|
label: "Timely",
|
||||||
|
emote: Emoji.Parse("💰"),
|
||||||
|
customId: "timely:" + _rng.Next(123456, 999999)),
|
||||||
|
async (smc) =>
|
||||||
|
{
|
||||||
|
await smc.DeferAsync();
|
||||||
|
await ClaimTimely();
|
||||||
|
});
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Timely()
|
public async Task Timely()
|
||||||
{
|
{
|
||||||
var val = Config.Timely.Amount;
|
var val = Config.Timely.Amount;
|
||||||
@@ -151,6 +146,94 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Config.Timely.ProtType == TimelyProt.Button)
|
||||||
|
{
|
||||||
|
var interaction = CreateTimelyInteraction();
|
||||||
|
var msg = await Response().Pending(strs.timely_button).Interaction(interaction).SendAsync();
|
||||||
|
await msg.DeleteAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Config.Timely.ProtType == TimelyProt.Captcha)
|
||||||
|
{
|
||||||
|
var password = await GetUserTimelyPassword(ctx.User.Id);
|
||||||
|
var img = GetPasswordImage(password);
|
||||||
|
using var stream = await img.ToStreamAsync();
|
||||||
|
var captcha = await Response()
|
||||||
|
.File(stream, "timely.png")
|
||||||
|
.SendAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var userInput = await GetUserInputAsync(ctx.User.Id, ctx.Channel.Id);
|
||||||
|
if (userInput?.ToLowerInvariant() != password?.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ClearUserTimelyPassword(ctx.User.Id);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_ = captcha.DeleteAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await ClaimTimely();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypedKey<string> TimelyPasswordKey(ulong userId)
|
||||||
|
=> new($"timely_password:{userId}");
|
||||||
|
|
||||||
|
private async Task<string> GetUserTimelyPassword(ulong userId)
|
||||||
|
{
|
||||||
|
var pw = await _cache.GetOrAddAsync(TimelyPasswordKey(userId),
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
var password = _service.GeneratePassword();
|
||||||
|
return Task.FromResult(password);
|
||||||
|
});
|
||||||
|
|
||||||
|
return pw;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValueTask<bool> ClearUserTimelyPassword(ulong userId)
|
||||||
|
=> _cache.RemoveAsync(TimelyPasswordKey(userId));
|
||||||
|
|
||||||
|
private Image<Rgba32> GetPasswordImage(string password)
|
||||||
|
{
|
||||||
|
var img = new Image<Rgba32>(50, 24);
|
||||||
|
|
||||||
|
var font = _fonts.NotoSans.CreateFont(22);
|
||||||
|
var outlinePen = new SolidPen(Color.Black, 0.5f);
|
||||||
|
var strikeoutRun = new RichTextRun
|
||||||
|
{
|
||||||
|
Start = 0,
|
||||||
|
End = password.GetGraphemeCount(),
|
||||||
|
Font = font,
|
||||||
|
StrikeoutPen = new SolidPen(Color.White, 4),
|
||||||
|
TextDecorations = TextDecorations.Strikeout
|
||||||
|
};
|
||||||
|
// draw password on the image
|
||||||
|
img.Mutate(x =>
|
||||||
|
{
|
||||||
|
x.DrawText(new RichTextOptions(font)
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
FallbackFontFamilies = _fonts.FallBackFonts,
|
||||||
|
Origin = new(25, 12),
|
||||||
|
TextRuns = [strikeoutRun]
|
||||||
|
},
|
||||||
|
password,
|
||||||
|
Brushes.Solid(Color.White),
|
||||||
|
outlinePen);
|
||||||
|
});
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClaimTimely()
|
||||||
|
{
|
||||||
|
var period = Config.Timely.Cooldown;
|
||||||
if (await _service.ClaimTimelyAsync(ctx.User.Id, period) is { } remainder)
|
if (await _service.ClaimTimelyAsync(ctx.User.Id, period) is { } remainder)
|
||||||
{
|
{
|
||||||
// Get correct time form remainder
|
// Get correct time form remainder
|
||||||
@@ -169,6 +252,30 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var val = Config.Timely.Amount;
|
||||||
|
var boostGuilds = Config.BoostBonus.GuildIds ?? new();
|
||||||
|
var guildUsers = await boostGuilds
|
||||||
|
.Select(async gid =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var guild = await _client.Rest.GetGuildAsync(gid, false);
|
||||||
|
var user = await _client.Rest.GetGuildUserAsync(gid, ctx.User.Id);
|
||||||
|
return (guild, user);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.WhenAll();
|
||||||
|
|
||||||
|
var userInfo = guildUsers.FirstOrDefault(x => x.user?.PremiumSince is not null);
|
||||||
|
var booster = userInfo != default;
|
||||||
|
|
||||||
|
if (booster)
|
||||||
|
val += Config.BoostBonus.BaseTimelyBonus;
|
||||||
|
|
||||||
var patron = await _ps.GetPatronAsync(ctx.User.Id);
|
var patron = await _ps.GetPatronAsync(ctx.User.Id);
|
||||||
|
|
||||||
var percentBonus = (_ps.PercentBonus(patron) / 100f);
|
var percentBonus = (_ps.PercentBonus(patron) / 100f);
|
||||||
@@ -179,7 +286,21 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
|
|
||||||
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
|
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
|
||||||
|
|
||||||
await Response().Confirm(strs.timely(N(val), period)).Interaction(inter).SendAsync();
|
var msg = GetText(strs.timely(N(val), period));
|
||||||
|
if (booster || percentBonus > float.Epsilon)
|
||||||
|
{
|
||||||
|
msg += "\n\n";
|
||||||
|
if (booster)
|
||||||
|
msg += $"*+{N(Config.BoostBonus.BaseTimelyBonus)} bonus for boosting {userInfo.guild}!*\n";
|
||||||
|
|
||||||
|
if (percentBonus > float.Epsilon)
|
||||||
|
msg +=
|
||||||
|
$"*+{percentBonus:P0} bonus for the [Patreon](https://patreon.com/nadekobot) pledge! <:hart:746995901758832712>*";
|
||||||
|
|
||||||
|
await Response().Confirm(msg).Interaction(inter).SendAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
await Response().Confirm(strs.timely(N(val), period)).Interaction(inter).SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -270,6 +391,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
public Task CurrencyTransactions([Leftover] IUser usr)
|
public Task CurrencyTransactions([Leftover] IUser usr)
|
||||||
=> InternalCurrencyTransactions(usr.Id, 1);
|
=> InternalCurrencyTransactions(usr.Id, 1);
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
[Priority(-1)]
|
||||||
|
public Task CurrencyTransactions([Leftover] ulong userId)
|
||||||
|
=> InternalCurrencyTransactions(userId, 1);
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
@@ -289,10 +416,11 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
trs = await uow.Set<CurrencyTransaction>().GetPageFor(userId, page);
|
trs = await uow.Set<CurrencyTransaction>().GetPageFor(userId, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithTitle(GetText(strs.transactions(((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
|
.WithTitle(GetText(strs.transactions(
|
||||||
?? $"{userId}")))
|
((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
|
||||||
.WithOkColor();
|
?? $"{userId}")))
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
foreach (var tr in trs)
|
foreach (var tr in trs)
|
||||||
@@ -339,7 +467,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed().WithOkColor();
|
var eb = CreateEmbed().WithOkColor();
|
||||||
|
|
||||||
eb.WithAuthor(ctx.User);
|
eb.WithAuthor(ctx.User);
|
||||||
eb.WithTitle(GetText(strs.transaction));
|
eb.WithTitle(GetText(strs.transaction));
|
||||||
@@ -547,7 +675,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Response().Error(strs.take_fail(N(amount), Format.Bold(user.ToString()), CurrencySign)).SendAsync();
|
await Response()
|
||||||
|
.Error(strs.take_fail(N(amount), Format.Bold(user.ToString()), CurrencySign))
|
||||||
|
.SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,7 +698,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Response().Error(strs.take_fail(N(amount), Format.Code(usrId.ToString()), CurrencySign)).SendAsync();
|
await Response()
|
||||||
|
.Error(strs.take_fail(N(amount), Format.Code(usrId.ToString()), CurrencySign))
|
||||||
|
.SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -592,18 +724,20 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
string str;
|
string str;
|
||||||
if (win > 0)
|
if (win > 0)
|
||||||
{
|
{
|
||||||
str = GetText(strs.br_win(N(win), result.Threshold + (result.Roll == 100 ? " 👑" : "")));
|
str = GetText(strs.betroll_win(result.Threshold + (result.Roll == 100 ? " 👑" : "")));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
str = GetText(strs.better_luck);
|
str = GetText(strs.better_luck);
|
||||||
}
|
}
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(Format.Bold(str))
|
.WithDescription(Format.Bold(str))
|
||||||
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
|
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture), true)
|
||||||
.WithOkColor();
|
.AddField(GetText(strs.bet), N(amount), true)
|
||||||
|
.AddField(GetText(strs.won), N((long)result.Won), true)
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
await Response().Embed(eb).SendAsync();
|
await Response().Embed(eb).SendAsync();
|
||||||
}
|
}
|
||||||
@@ -666,9 +800,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((toSend, curPage) =>
|
.Page((toSend, curPage) =>
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
|
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
|
||||||
|
|
||||||
if (!toSend.Any())
|
if (!toSend.Any())
|
||||||
{
|
{
|
||||||
@@ -729,7 +863,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed();
|
var embed = CreateEmbed();
|
||||||
|
|
||||||
string msg;
|
string msg;
|
||||||
if (result.Result == RpsResultType.Draw)
|
if (result.Result == RpsResultType.Draw)
|
||||||
@@ -762,6 +896,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
private static readonly ImmutableArray<string> _emojis =
|
private static readonly ImmutableArray<string> _emojis =
|
||||||
new[] { "⬆", "↖", "⬅", "↙", "⬇", "↘", "➡", "↗" }.ToImmutableArray();
|
new[] { "⬆", "↖", "⬅", "↙", "⬇", "↘", "➡", "↗" }.ToImmutableArray();
|
||||||
|
|
||||||
|
private readonly NadekoRandom _rng;
|
||||||
|
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task LuckyLadder([OverrideTypeReader(typeof(BalanceTypeReader))] long amount)
|
public async Task LuckyLadder([OverrideTypeReader(typeof(BalanceTypeReader))] long amount)
|
||||||
@@ -791,12 +927,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(sb.ToString())
|
.WithDescription(sb.ToString())
|
||||||
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
|
.AddField(GetText(strs.bet), N(amount), true)
|
||||||
.AddField(GetText(strs.won), $"{(long)result.Won}", true)
|
.AddField(GetText(strs.won), $"{N((long)result.Won)}", true)
|
||||||
.WithAuthor(ctx.User);
|
.WithAuthor(ctx.User);
|
||||||
|
|
||||||
|
|
||||||
await Response().Embed(eb).SendAsync();
|
await Response().Embed(eb).SendAsync();
|
||||||
@@ -900,4 +1036,45 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||||||
footer: $"Total Bet: {tests} | Payout: {payout:F0} | {payout * 1.0M / tests * 100}%")
|
footer: $"Total Bet: {tests} | Payout: {payout:F0} | {payout * 1.0M / tests * 100}%")
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private NadekoInteractionBase CreateRakebackInteraction()
|
||||||
|
=> _inter.Create(ctx.User.Id,
|
||||||
|
new ButtonBuilder(
|
||||||
|
customId: "cash:rakeback",
|
||||||
|
emote: new Emoji("💸")),
|
||||||
|
RakebackAction);
|
||||||
|
|
||||||
|
private async Task RakebackAction(SocketMessageComponent arg)
|
||||||
|
{
|
||||||
|
var rb = await _rb.ClaimRakebackAsync(ctx.User.Id);
|
||||||
|
|
||||||
|
if (rb == 0)
|
||||||
|
{
|
||||||
|
await arg.DeferAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await arg.RespondAsync(_sender, GetText(strs.rakeback_claimed(N(rb))), MsgType.Ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task Rakeback()
|
||||||
|
{
|
||||||
|
var rb = await _rb.GetRakebackAsync(ctx.User.Id);
|
||||||
|
|
||||||
|
if (rb < 1)
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Error(strs.rakeback_none)
|
||||||
|
.SendAsync();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inter = CreateRakebackInteraction();
|
||||||
|
await Response()
|
||||||
|
.Pending(strs.rakeback_available(N(rb)))
|
||||||
|
.Interaction(inter)
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -11,7 +11,7 @@ namespace NadekoBot.Modules.Gambling.Common;
|
|||||||
public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
||||||
{
|
{
|
||||||
[Comment("""DO NOT CHANGE""")]
|
[Comment("""DO NOT CHANGE""")]
|
||||||
public int Version { get; set; } = 8;
|
public int Version { get; set; } = 12;
|
||||||
|
|
||||||
[Comment("""Currency settings""")]
|
[Comment("""Currency settings""")]
|
||||||
public CurrencyConfig Currency { get; set; }
|
public CurrencyConfig Currency { get; set; }
|
||||||
@@ -67,6 +67,11 @@ public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
|||||||
[Comment("""Slot config""")]
|
[Comment("""Slot config""")]
|
||||||
public SlotsConfig Slots { get; set; }
|
public SlotsConfig Slots { get; set; }
|
||||||
|
|
||||||
|
[Comment("""
|
||||||
|
Bonus config for server boosts
|
||||||
|
""")]
|
||||||
|
public BoostBonusConfig BoostBonus { get; set; }
|
||||||
|
|
||||||
public GamblingConfig()
|
public GamblingConfig()
|
||||||
{
|
{
|
||||||
BetRoll = new();
|
BetRoll = new();
|
||||||
@@ -79,6 +84,7 @@ public sealed partial class GamblingConfig : ICloneable<GamblingConfig>
|
|||||||
Slots = new();
|
Slots = new();
|
||||||
LuckyLadder = new();
|
LuckyLadder = new();
|
||||||
BotCuts = new();
|
BotCuts = new();
|
||||||
|
BoostBonus = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,13 +110,26 @@ public partial class TimelyConfig
|
|||||||
How much currency will the users get every time they run .timely command
|
How much currency will the users get every time they run .timely command
|
||||||
setting to 0 or less will disable this feature
|
setting to 0 or less will disable this feature
|
||||||
""")]
|
""")]
|
||||||
public int Amount { get; set; } = 0;
|
public long Amount { get; set; } = 0;
|
||||||
|
|
||||||
[Comment("""
|
[Comment("""
|
||||||
How often (in hours) can users claim currency with .timely command
|
How often (in hours) can users claim currency with .timely command
|
||||||
setting to 0 or less will disable this feature
|
setting to 0 or less will disable this feature
|
||||||
""")]
|
""")]
|
||||||
public int Cooldown { get; set; } = 24;
|
public int Cooldown { get; set; } = 24;
|
||||||
|
|
||||||
|
[Comment("""
|
||||||
|
How will timely be protected?
|
||||||
|
None, Button (users have to click the button) or Captcha (users have to type the captcha from an image)
|
||||||
|
""")]
|
||||||
|
public TimelyProt ProtType { get; set; } = TimelyProt.Button;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TimelyProt
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Button,
|
||||||
|
Captcha
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cloneable]
|
[Cloneable]
|
||||||
@@ -145,7 +164,7 @@ public partial class BetRollConfig
|
|||||||
},
|
},
|
||||||
new()
|
new()
|
||||||
{
|
{
|
||||||
WhenAbove = 66,
|
WhenAbove = 65,
|
||||||
MultiplyBy = 2
|
MultiplyBy = 2
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -207,7 +226,7 @@ public partial class LuckyLadderSettings
|
|||||||
public decimal[] Multipliers { get; set; }
|
public decimal[] Multipliers { get; set; }
|
||||||
|
|
||||||
public LuckyLadderSettings()
|
public LuckyLadderSettings()
|
||||||
=> Multipliers = [2.4M, 1.7M, 1.5M, 1.2M, 0.5M, 0.3M, 0.2M, 0.1M];
|
=> Multipliers = [2.4M, 1.7M, 1.5M, 1.1M, 0.5M, 0.3M, 0.2M, 0.1M];
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cloneable]
|
[Cloneable]
|
||||||
@@ -409,3 +428,14 @@ public sealed partial class BotCutConfig
|
|||||||
""")]
|
""")]
|
||||||
public decimal ShopSaleCut { get; set; } = 0.1m;
|
public decimal ShopSaleCut { get; set; } = 0.1m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cloneable]
|
||||||
|
public sealed partial class BoostBonusConfig
|
||||||
|
{
|
||||||
|
[Comment("Users will receive a bonus if they boost any of these servers")]
|
||||||
|
public List<ulong> GuildIds { get; set; } = new();
|
||||||
|
|
||||||
|
[Comment("This bonus will be added before any other multiplier is applied to the .timely command")]
|
||||||
|
|
||||||
|
public long BaseTimelyBonus { get; set; } = 50;
|
||||||
|
}
|
@@ -144,6 +144,11 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
|||||||
ConfigPrinters.ToString,
|
ConfigPrinters.ToString,
|
||||||
val => val >= 0);
|
val => val >= 0);
|
||||||
|
|
||||||
|
AddParsedProp("timely.prot",
|
||||||
|
gs => gs.Timely.ProtType,
|
||||||
|
ConfigParsers.InsensitiveEnum,
|
||||||
|
ConfigPrinters.ToString);
|
||||||
|
|
||||||
Migrate();
|
Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,22 +172,6 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.Version < 5)
|
|
||||||
{
|
|
||||||
ModifyConfig(c =>
|
|
||||||
{
|
|
||||||
c.Version = 5;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.Version < 6)
|
|
||||||
{
|
|
||||||
ModifyConfig(c =>
|
|
||||||
{
|
|
||||||
c.Version = 6;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.Version < 7)
|
if (data.Version < 7)
|
||||||
{
|
{
|
||||||
ModifyConfig(c =>
|
ModifyConfig(c =>
|
||||||
@@ -199,5 +188,18 @@ public sealed class GamblingConfigService : ConfigServiceBase<GamblingConfig>
|
|||||||
c.Waifu.Decay.UnclaimedDecayPercent = 0;
|
c.Waifu.Decay.UnclaimedDecayPercent = 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.Version < 12)
|
||||||
|
{
|
||||||
|
ModifyConfig(c =>
|
||||||
|
{
|
||||||
|
c.Version = 12;
|
||||||
|
|
||||||
|
if (c.BetRoll.Pairs.Length == 3 && c.BetRoll.Pairs[2].WhenAbove == 66)
|
||||||
|
{
|
||||||
|
c.BetRoll.Pairs[2].WhenAbove = 65;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -16,6 +16,7 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
private readonly IBotCache _cache;
|
private readonly IBotCache _cache;
|
||||||
private readonly GamblingConfigService _gss;
|
private readonly GamblingConfigService _gss;
|
||||||
|
private readonly NadekoRandom _rng;
|
||||||
|
|
||||||
private static readonly TypedKey<long> _curDecayKey = new("currency:last_decay");
|
private static readonly TypedKey<long> _curDecayKey = new("currency:last_decay");
|
||||||
|
|
||||||
@@ -29,11 +30,19 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
_client = client;
|
_client = client;
|
||||||
_cache = cache;
|
_cache = cache;
|
||||||
_gss = gss;
|
_gss = gss;
|
||||||
|
_rng = new NadekoRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnReadyAsync()
|
public Task OnReadyAsync()
|
||||||
=> Task.WhenAll(CurrencyDecayLoopAsync(), TransactionClearLoopAsync());
|
=> Task.WhenAll(CurrencyDecayLoopAsync(), TransactionClearLoopAsync());
|
||||||
|
|
||||||
|
|
||||||
|
public string GeneratePassword()
|
||||||
|
{
|
||||||
|
var num = _rng.Next((int)Math.Pow(31, 2), (int)Math.Pow(32, 3));
|
||||||
|
return new kwum(num).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task TransactionClearLoopAsync()
|
private async Task TransactionClearLoopAsync()
|
||||||
{
|
{
|
||||||
if (_client.ShardId != 0)
|
if (_client.ShardId != 0)
|
||||||
@@ -52,7 +61,7 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
var days = TimeSpan.FromDays(lifetime);
|
var days = TimeSpan.FromDays(lifetime);
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
await uow.Set<CurrencyTransaction>()
|
await uow.Set<CurrencyTransaction>()
|
||||||
.DeleteAsync(ct => ct.DateAdded == null || now - ct.DateAdded < days);
|
.DeleteAsync(ct => ct.DateAdded == null || now - ct.DateAdded < days);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -90,11 +99,11 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
}
|
}
|
||||||
|
|
||||||
Log.Information("""
|
Log.Information("""
|
||||||
--- Decaying users' currency ---
|
--- Decaying users' currency ---
|
||||||
| decay: {ConfigDecayPercent}%
|
| decay: {ConfigDecayPercent}%
|
||||||
| max: {MaxDecay}
|
| max: {MaxDecay}
|
||||||
| threshold: {DecayMinTreshold}
|
| threshold: {DecayMinTreshold}
|
||||||
""",
|
""",
|
||||||
config.Decay.Percent * 100,
|
config.Decay.Percent * 100,
|
||||||
maxDecay,
|
maxDecay,
|
||||||
config.Decay.MinThreshold);
|
config.Decay.MinThreshold);
|
||||||
@@ -104,14 +113,14 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
|
|
||||||
var decay = (double)config.Decay.Percent;
|
var decay = (double)config.Decay.Percent;
|
||||||
await uow.Set<DiscordUser>()
|
await uow.Set<DiscordUser>()
|
||||||
.Where(x => x.CurrencyAmount > config.Decay.MinThreshold && x.UserId != _client.CurrentUser.Id)
|
.Where(x => x.CurrencyAmount > config.Decay.MinThreshold && x.UserId != _client.CurrentUser.Id)
|
||||||
.UpdateAsync(old => new()
|
.UpdateAsync(old => new()
|
||||||
{
|
{
|
||||||
CurrencyAmount =
|
CurrencyAmount =
|
||||||
maxDecay > Sql.Round((old.CurrencyAmount * decay) - 0.5)
|
maxDecay > Sql.Round((old.CurrencyAmount * decay) - 0.5)
|
||||||
? (long)(old.CurrencyAmount - Sql.Round((old.CurrencyAmount * decay) - 0.5))
|
? (long)(old.CurrencyAmount - Sql.Round((old.CurrencyAmount * decay) - 0.5))
|
||||||
: old.CurrencyAmount - maxDecay
|
: old.CurrencyAmount - maxDecay
|
||||||
});
|
});
|
||||||
|
|
||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
|
|
||||||
@@ -133,6 +142,7 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
private static TypedKey<Dictionary<ulong, long>> _timelyKey
|
private static TypedKey<Dictionary<ulong, long>> _timelyKey
|
||||||
= new("timely:claims");
|
= new("timely:claims");
|
||||||
|
|
||||||
|
|
||||||
public async Task<TimeSpan?> ClaimTimelyAsync(ulong userId, int period)
|
public async Task<TimeSpan?> ClaimTimelyAsync(ulong userId, int period)
|
||||||
{
|
{
|
||||||
if (period == 0)
|
if (period == 0)
|
||||||
@@ -178,8 +188,9 @@ public class GamblingService : INService, IReadyExecutor
|
|||||||
public bool UserHasTimelyReminder(ulong userId)
|
public bool UserHasTimelyReminder(ulong userId)
|
||||||
{
|
{
|
||||||
var db = _db.GetDbContext();
|
var db = _db.GetDbContext();
|
||||||
return db.GetTable<Reminder>().Any(x => x.UserId == userId
|
return db.GetTable<Reminder>()
|
||||||
&& x.Type == ReminderType.Timely);
|
.Any(x => x.UserId == userId
|
||||||
|
&& x.Type == ReminderType.Timely);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveAllTimelyClaimsAsync()
|
public async Task RemoveAllTimelyClaimsAsync()
|
||||||
|
@@ -58,11 +58,3 @@ public abstract class GamblingModule<TService> : NadekoModule<TService>
|
|||||||
return InternalCheckBet(amount);
|
return InternalCheckBet(amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class GamblingSubmodule<TService> : GamblingModule<TService>
|
|
||||||
{
|
|
||||||
protected GamblingSubmodule(GamblingConfigService gamblingConfService)
|
|
||||||
: base(gamblingConfService)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
92
src/NadekoBot/Modules/Gambling/Loan/LoanCommands.cs
Normal file
92
src/NadekoBot/Modules/Gambling/Loan/LoanCommands.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// namespace NadekoBot.Modules.Gambling;
|
||||||
|
// public sealed class Loan
|
||||||
|
// {
|
||||||
|
// public int Id { get; set; }
|
||||||
|
// public ulong LenderId { get; set; }
|
||||||
|
// public string LenderName { get; set; }
|
||||||
|
// public ulong BorrowerId { get; set; }
|
||||||
|
// public string BorrowerName { get; set; }
|
||||||
|
// public long Amount { get; set; }
|
||||||
|
// public decimal Interest { get; set; }
|
||||||
|
// public DateTime DueDate { get; set; }
|
||||||
|
// public bool Repaid { get; set; }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public sealed class LoanService : INService
|
||||||
|
// {
|
||||||
|
// public async Task<IReadOnlyList<Loan>> GetLoans(ulong userId)
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public async Task<object> RepayAsync(object loandId)
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public partial class Gambling
|
||||||
|
// {
|
||||||
|
// public partial class LoanCommands : NadekoModule<LoanService>
|
||||||
|
// {
|
||||||
|
// [Cmd]
|
||||||
|
// public async Task Loan(
|
||||||
|
// IUser lender,
|
||||||
|
// long amount,
|
||||||
|
// decimal interest = 0,
|
||||||
|
// TimeSpan dueIn = default)
|
||||||
|
// {
|
||||||
|
// var eb = CreateEmbed()
|
||||||
|
// .WithOkColor()
|
||||||
|
// .WithDescription("User 0 Requests a loan from User {1}")
|
||||||
|
// .AddField("Amount", amount, true)
|
||||||
|
// .AddField("Interest", (interest * 0.01m).ToString("P2"), true);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public Task Loans()
|
||||||
|
// => Loans(ctx.User);
|
||||||
|
//
|
||||||
|
// public async Task Loans([Leftover] IUser user)
|
||||||
|
// {
|
||||||
|
// var loans = await _service.GetLoans(user.Id);
|
||||||
|
//
|
||||||
|
// Response()
|
||||||
|
// .Paginated()
|
||||||
|
// .PageItems(loans)
|
||||||
|
// .Page((items, page) =>
|
||||||
|
// {
|
||||||
|
// var eb = CreateEmbed()
|
||||||
|
// .WithOkColor()
|
||||||
|
// .WithDescription("Current Loans");
|
||||||
|
//
|
||||||
|
// foreach (var item in items)
|
||||||
|
// {
|
||||||
|
// eb.AddField(new kwum(item.id).ToString(),
|
||||||
|
// $"""
|
||||||
|
// To: {item.LenderName}
|
||||||
|
// Amount: {}
|
||||||
|
// """,
|
||||||
|
// true);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return eb;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// [Cmd]
|
||||||
|
// public async Task Repay(kwum loanId)
|
||||||
|
// {
|
||||||
|
// var res = await _service.RepayAsync(loandId);
|
||||||
|
//
|
||||||
|
// if (res.TryPickT0(out var _, out var err))
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// var errStr = err.Match(
|
||||||
|
// _ => "Not enough funds",
|
||||||
|
// _ => "Loan not found");
|
||||||
|
//
|
||||||
|
// await Response().Error(errStr).SendAsync();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
@@ -8,7 +8,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class PlantPickCommands : GamblingSubmodule<PlantPickService>
|
public partial class PlantPickCommands : GamblingModule<PlantPickService>
|
||||||
{
|
{
|
||||||
private readonly ILogCommandService _logService;
|
private readonly ILogCommandService _logService;
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ public partial class Gambling
|
|||||||
}
|
}
|
||||||
|
|
||||||
var success = await _service.PlantAsync(ctx.Guild.Id,
|
var success = await _service.PlantAsync(ctx.Guild.Id,
|
||||||
ctx.Channel,
|
(ITextChannel)ctx.Channel,
|
||||||
ctx.User.Id,
|
ctx.User.Id,
|
||||||
ctx.User.ToString(),
|
ctx.User.ToString(),
|
||||||
amount,
|
amount,
|
||||||
@@ -103,9 +103,9 @@ public partial class Gambling
|
|||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return _sender.CreateEmbed().WithErrorColor().WithDescription("-");
|
return CreateEmbed().WithErrorColor().WithDescription("-");
|
||||||
|
|
||||||
return items.Aggregate(_sender.CreateEmbed().WithOkColor(),
|
return items.Aggregate(CreateEmbed().WithOkColor(),
|
||||||
(eb, i) => eb.AddField(i.GuildId.ToString(), i.ChannelId));
|
(eb, i) => eb.AddField(i.GuildId.ToString(), i.ChannelId));
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
using SixLabors.Fonts;
|
using SixLabors.Fonts;
|
||||||
|
using SixLabors.Fonts.Unicode;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Drawing.Processing;
|
using SixLabors.ImageSharp.Drawing.Processing;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
@@ -25,6 +28,7 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
private readonly NadekoRandom _rng;
|
private readonly NadekoRandom _rng;
|
||||||
private readonly DiscordSocketClient _client;
|
private readonly DiscordSocketClient _client;
|
||||||
private readonly GamblingConfigService _gss;
|
private readonly GamblingConfigService _gss;
|
||||||
|
private readonly GamblingService _gs;
|
||||||
|
|
||||||
private readonly ConcurrentHashSet<ulong> _generationChannels;
|
private readonly ConcurrentHashSet<ulong> _generationChannels;
|
||||||
private readonly SemaphoreSlim _pickLock = new(1, 1);
|
private readonly SemaphoreSlim _pickLock = new(1, 1);
|
||||||
@@ -37,7 +41,8 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
ICurrencyService cs,
|
ICurrencyService cs,
|
||||||
CommandHandler cmdHandler,
|
CommandHandler cmdHandler,
|
||||||
DiscordSocketClient client,
|
DiscordSocketClient client,
|
||||||
GamblingConfigService gss)
|
GamblingConfigService gss,
|
||||||
|
GamblingService gs)
|
||||||
{
|
{
|
||||||
_db = db;
|
_db = db;
|
||||||
_strings = strings;
|
_strings = strings;
|
||||||
@@ -48,6 +53,7 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
_rng = new();
|
_rng = new();
|
||||||
_client = client;
|
_client = client;
|
||||||
_gss = gss;
|
_gss = gss;
|
||||||
|
_gs = gs;
|
||||||
|
|
||||||
using var uow = db.GetDbContext();
|
using var uow = db.GetDbContext();
|
||||||
var guildIds = client.Guilds.Select(x => x.Id).ToList();
|
var guildIds = client.Guilds.Select(x => x.Id).ToList();
|
||||||
@@ -87,6 +93,7 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
var toDelete = guildConfig.GenerateCurrencyChannelIds.FirstOrDefault(x => x.Equals(toAdd));
|
var toDelete = guildConfig.GenerateCurrencyChannelIds.FirstOrDefault(x => x.Equals(toAdd));
|
||||||
if (toDelete is not null)
|
if (toDelete is not null)
|
||||||
uow.Remove(toDelete);
|
uow.Remove(toDelete);
|
||||||
|
|
||||||
_generationChannels.TryRemove(cid);
|
_generationChannels.TryRemove(cid);
|
||||||
enabled = false;
|
enabled = false;
|
||||||
}
|
}
|
||||||
@@ -140,7 +147,7 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
pass = pass.TrimTo(10, true).ToLowerInvariant();
|
pass = pass.TrimTo(10, true).ToLowerInvariant();
|
||||||
using var img = Image.Load<Rgba32>(curImg);
|
using var img = Image.Load<Rgba32>(curImg);
|
||||||
// choose font size based on the image height, so that it's visible
|
// choose font size based on the image height, so that it's visible
|
||||||
var font = _fonts.NotoSans.CreateFont(img.Height / 12.0f, FontStyle.Bold);
|
var font = _fonts.NotoSans.CreateFont(img.Height / 11.0f, FontStyle.Bold);
|
||||||
img.Mutate(x =>
|
img.Mutate(x =>
|
||||||
{
|
{
|
||||||
// measure the size of the text to be drawing
|
// measure the size of the text to be drawing
|
||||||
@@ -152,13 +159,31 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
|
|
||||||
// fill the background with black, add 5 pixels on each side to make it look better
|
// fill the background with black, add 5 pixels on each side to make it look better
|
||||||
x.FillPolygon(Color.ParseHex("00000080"),
|
x.FillPolygon(Color.ParseHex("00000080"),
|
||||||
new PointF(0, 0),
|
new PointF(1, 1),
|
||||||
new PointF(size.Width + 5, 0),
|
new PointF(size.Width + 5, 0),
|
||||||
new PointF(size.Width + 5, size.Height + 10),
|
new PointF(size.Width + 5, size.Height + 10),
|
||||||
new PointF(0, size.Height + 10));
|
new PointF(0, size.Height + 10));
|
||||||
|
|
||||||
|
var strikeoutRun = new RichTextRun
|
||||||
|
{
|
||||||
|
Start = 0,
|
||||||
|
End = pass.GetGraphemeCount(),
|
||||||
|
Font = font,
|
||||||
|
StrikeoutPen = new SolidPen(Color.White, 2),
|
||||||
|
TextDecorations = TextDecorations.Strikeout
|
||||||
|
};
|
||||||
|
|
||||||
// draw the password over the background
|
// draw the password over the background
|
||||||
x.DrawText(pass, font, Color.White, new(0, 0));
|
x.DrawText(new RichTextOptions(font)
|
||||||
|
{
|
||||||
|
Origin = new(0, 0),
|
||||||
|
TextRuns =
|
||||||
|
[
|
||||||
|
strikeoutRun
|
||||||
|
]
|
||||||
|
},
|
||||||
|
pass,
|
||||||
|
new SolidBrush(Color.White));
|
||||||
});
|
});
|
||||||
// return image as a stream for easy sending
|
// return image as a stream for easy sending
|
||||||
var format = img.Metadata.DecodedImageFormat;
|
var format = img.Metadata.DecodedImageFormat;
|
||||||
@@ -208,7 +233,7 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
+ " "
|
+ " "
|
||||||
+ GetText(channel.GuildId, strs.pick_pl(prefix));
|
+ GetText(channel.GuildId, strs.pick_pl(prefix));
|
||||||
|
|
||||||
var pw = config.Generation.HasPassword ? GenerateCurrencyPassword().ToUpperInvariant() : null;
|
var pw = config.Generation.HasPassword ? _gs.GeneratePassword().ToUpperInvariant() : null;
|
||||||
|
|
||||||
IUserMessage sent;
|
IUserMessage sent;
|
||||||
var (stream, ext) = await GetRandomCurrencyImageAsync(pw);
|
var (stream, ext) = await GetRandomCurrencyImageAsync(pw);
|
||||||
@@ -216,12 +241,18 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
await using (stream)
|
await using (stream)
|
||||||
sent = await channel.SendFileAsync(stream, $"currency_image.{ext}", toSend);
|
sent = await channel.SendFileAsync(stream, $"currency_image.{ext}", toSend);
|
||||||
|
|
||||||
await AddPlantToDatabase(channel.GuildId,
|
var res = await AddPlantToDatabase(channel.GuildId,
|
||||||
channel.Id,
|
channel.Id,
|
||||||
_client.CurrentUser.Id,
|
_client.CurrentUser.Id,
|
||||||
sent.Id,
|
sent.Id,
|
||||||
dropAmount,
|
dropAmount,
|
||||||
pw);
|
pw,
|
||||||
|
true);
|
||||||
|
|
||||||
|
if (res.toDelete.Length > 0)
|
||||||
|
{
|
||||||
|
await channel.DeleteMessagesAsync(res.toDelete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,67 +263,44 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate a hexadecimal string from 1000 to ffff.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A hexadecimal string from 1000 to ffff</returns>
|
|
||||||
private string GenerateCurrencyPassword()
|
|
||||||
{
|
|
||||||
// generate a number from 1000 to ffff
|
|
||||||
var num = _rng.Next(4096, 65536);
|
|
||||||
// convert it to hexadecimal
|
|
||||||
return num.ToString("x4");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<long> PickAsync(
|
public async Task<long> PickAsync(
|
||||||
ulong gid,
|
ulong gid,
|
||||||
ITextChannel ch,
|
ITextChannel ch,
|
||||||
ulong uid,
|
ulong uid,
|
||||||
string pass)
|
string pass)
|
||||||
{
|
{
|
||||||
await _pickLock.WaitAsync();
|
long amount;
|
||||||
|
ulong[] ids;
|
||||||
|
await using (var uow = _db.GetDbContext())
|
||||||
|
{
|
||||||
|
// this method will sum all plants with that password,
|
||||||
|
// remove them, and get messageids of the removed plants
|
||||||
|
|
||||||
|
pass = pass?.Trim().TrimTo(10, true)?.ToUpperInvariant();
|
||||||
|
// gets all plants in this channel with the same password
|
||||||
|
var entries = await uow.GetTable<PlantedCurrency>()
|
||||||
|
.Where(x => x.ChannelId == ch.Id && pass == x.Password)
|
||||||
|
.DeleteWithOutputAsync();
|
||||||
|
|
||||||
|
if (!entries.Any())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
amount = entries.Sum(x => x.Amount);
|
||||||
|
ids = entries.Select(x => x.MessageId).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount > 0)
|
||||||
|
await _cs.AddAsync(uid, amount, new("currency", "collect"));
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
long amount;
|
_ = ch.DeleteMessagesAsync(ids);
|
||||||
ulong[] ids;
|
|
||||||
await using (var uow = _db.GetDbContext())
|
|
||||||
{
|
|
||||||
// this method will sum all plants with that password,
|
|
||||||
// remove them, and get messageids of the removed plants
|
|
||||||
|
|
||||||
pass = pass?.Trim().TrimTo(10, true).ToUpperInvariant();
|
|
||||||
// gets all plants in this channel with the same password
|
|
||||||
var entries = uow.Set<PlantedCurrency>()
|
|
||||||
.AsQueryable()
|
|
||||||
.Where(x => x.ChannelId == ch.Id && pass == x.Password)
|
|
||||||
.ToList();
|
|
||||||
// sum how much currency that is, and get all of the message ids (so that i can delete them)
|
|
||||||
amount = entries.Sum(x => x.Amount);
|
|
||||||
ids = entries.Select(x => x.MessageId).ToArray();
|
|
||||||
// remove them from the database
|
|
||||||
uow.RemoveRange(entries);
|
|
||||||
|
|
||||||
|
|
||||||
if (amount > 0)
|
|
||||||
// give the picked currency to the user
|
|
||||||
await _cs.AddAsync(uid, amount, new("currency", "collect"));
|
|
||||||
await uow.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// delete all of the plant messages which have just been picked
|
|
||||||
_ = ch.DeleteMessagesAsync(ids);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
|
|
||||||
// return the amount of currency the user picked
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_pickLock.Release();
|
|
||||||
}
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
// return the amount of currency the user picked
|
||||||
|
return amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ulong?> SendPlantMessageAsync(
|
public async Task<ulong?> SendPlantMessageAsync(
|
||||||
@@ -333,7 +341,7 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
|
|
||||||
public async Task<bool> PlantAsync(
|
public async Task<bool> PlantAsync(
|
||||||
ulong gid,
|
ulong gid,
|
||||||
IMessageChannel ch,
|
ITextChannel ch,
|
||||||
ulong uid,
|
ulong uid,
|
||||||
string user,
|
string user,
|
||||||
long amount,
|
long amount,
|
||||||
@@ -359,6 +367,7 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
|
|
||||||
// if it doesn't fail, put the plant in the database for other people to pick
|
// if it doesn't fail, put the plant in the database for other people to pick
|
||||||
await AddPlantToDatabase(gid, ch.Id, uid, msgId.Value, amount, pass);
|
await AddPlantToDatabase(gid, ch.Id, uid, msgId.Value, amount, pass);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -366,25 +375,41 @@ public class PlantPickService : INService, IExecNoCommand
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddPlantToDatabase(
|
private async Task<(long totalAmount, ulong[] toDelete)> AddPlantToDatabase(
|
||||||
ulong gid,
|
ulong gid,
|
||||||
ulong cid,
|
ulong cid,
|
||||||
ulong uid,
|
ulong uid,
|
||||||
ulong mid,
|
ulong mid,
|
||||||
long amount,
|
long amount,
|
||||||
string pass)
|
string pass,
|
||||||
|
bool auto = false)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
uow.Set<PlantedCurrency>()
|
|
||||||
.Add(new()
|
PlantedCurrency[] deleted = [];
|
||||||
{
|
if (!string.IsNullOrWhiteSpace(pass) && auto)
|
||||||
Amount = amount,
|
{
|
||||||
GuildId = gid,
|
deleted = await uow.GetTable<PlantedCurrency>()
|
||||||
ChannelId = cid,
|
.Where(x => x.GuildId == gid
|
||||||
Password = pass,
|
&& x.ChannelId == cid
|
||||||
UserId = uid,
|
&& x.Password != null
|
||||||
MessageId = mid
|
&& x.Password.Length == pass.Length)
|
||||||
});
|
.DeleteWithOutputAsync();
|
||||||
await uow.SaveChangesAsync();
|
}
|
||||||
|
|
||||||
|
var totalDeletedAmount = deleted.Length == 0 ? 0 : deleted.Sum(x => x.Amount);
|
||||||
|
|
||||||
|
await uow.GetTable<PlantedCurrency>()
|
||||||
|
.InsertAsync(() => new()
|
||||||
|
{
|
||||||
|
Amount = totalDeletedAmount + amount,
|
||||||
|
GuildId = gid,
|
||||||
|
ChannelId = cid,
|
||||||
|
Password = pass,
|
||||||
|
UserId = uid,
|
||||||
|
MessageId = mid,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (totalDeletedAmount + amount, deleted.Select(x => x.MessageId).ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -10,7 +10,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class ShopCommands : GamblingSubmodule<IShopService>
|
public partial class ShopCommands : GamblingModule<IShopService>
|
||||||
{
|
{
|
||||||
public enum List
|
public enum List
|
||||||
{
|
{
|
||||||
@@ -56,8 +56,8 @@ public partial class Gambling
|
|||||||
.Page((items, curPage) =>
|
.Page((items, curPage) =>
|
||||||
{
|
{
|
||||||
if (!items.Any())
|
if (!items.Any())
|
||||||
return _sender.CreateEmbed().WithErrorColor().WithDescription(GetText(strs.shop_none));
|
return CreateEmbed().WithErrorColor().WithDescription(GetText(strs.shop_none));
|
||||||
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.shop));
|
var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.shop));
|
||||||
|
|
||||||
for (var i = 0; i < items.Count; i++)
|
for (var i = 0; i < items.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -188,7 +188,7 @@ public partial class Gambling
|
|||||||
{
|
{
|
||||||
await Response()
|
await Response()
|
||||||
.User(ctx.User)
|
.User(ctx.User)
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name)))
|
.WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name)))
|
||||||
.AddField(GetText(strs.item), item.Text)
|
.AddField(GetText(strs.item), item.Text)
|
||||||
@@ -254,7 +254,7 @@ public partial class Gambling
|
|||||||
.Replace("%you.name%", buyer.GlobalName ?? buyer.Username)
|
.Replace("%you.name%", buyer.GlobalName ?? buyer.Username)
|
||||||
.Replace("%you.nick%", buyer.DisplayName);
|
.Replace("%you.nick%", buyer.DisplayName);
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithTitle("Executing shop command")
|
.WithTitle("Executing shop command")
|
||||||
.WithDescription(cmd);
|
.WithDescription(cmd);
|
||||||
@@ -541,7 +541,7 @@ public partial class Gambling
|
|||||||
|
|
||||||
public EmbedBuilder EntryToEmbed(ShopEntry entry)
|
public EmbedBuilder EntryToEmbed(ShopEntry entry)
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed().WithOkColor();
|
var embed = CreateEmbed().WithOkColor();
|
||||||
|
|
||||||
if (entry.Type == ShopEntryType.Role)
|
if (entry.Type == ShopEntryType.Role)
|
||||||
{
|
{
|
||||||
|
@@ -21,7 +21,7 @@ public enum GamblingError
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class SlotCommands : GamblingSubmodule<IGamblingService>
|
public partial class SlotCommands : GamblingModule<IGamblingService>
|
||||||
{
|
{
|
||||||
private readonly IImageCache _images;
|
private readonly IImageCache _images;
|
||||||
private readonly FontProvider _fonts;
|
private readonly FontProvider _fonts;
|
||||||
@@ -65,11 +65,11 @@ public partial class Gambling
|
|||||||
await using var imgStream = await image.ToStreamAsync();
|
await using var imgStream = await image.ToStreamAsync();
|
||||||
|
|
||||||
|
|
||||||
var eb = _sender.CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithAuthor(ctx.User)
|
.WithAuthor(ctx.User)
|
||||||
.WithDescription(Format.Bold(text))
|
.WithDescription(Format.Bold(text))
|
||||||
.WithImageUrl($"attachment://result.png")
|
.WithImageUrl($"attachment://result.png")
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
|
var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
|
||||||
var inter = _inter.Create(ctx.User.Id,
|
var inter = _inter.Create(ctx.User.Id,
|
||||||
@@ -168,7 +168,8 @@ public partial class Gambling
|
|||||||
await using (var uow = _db.GetDbContext())
|
await using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
ownedAmount = uow.Set<DiscordUser>()
|
ownedAmount = uow.Set<DiscordUser>()
|
||||||
.FirstOrDefault(x => x.UserId == ctx.User.Id)?.CurrencyAmount
|
.FirstOrDefault(x => x.UserId == ctx.User.Id)
|
||||||
|
?.CurrencyAmount
|
||||||
?? 0;
|
?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
src/NadekoBot/Modules/Gambling/UserBetStatsService.cs
Normal file
55
src/NadekoBot/Modules/Gambling/UserBetStatsService.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#nullable disable
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Gambling.Services;
|
||||||
|
|
||||||
|
public sealed class UserBetStatsService : INService
|
||||||
|
{
|
||||||
|
private const long RESET_MIN_PRICE = 1000;
|
||||||
|
private const decimal RESET_TOTAL_MULTIPLIER = 0.002m;
|
||||||
|
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly ICurrencyService _cs;
|
||||||
|
|
||||||
|
public UserBetStatsService(DbService db, ICurrencyService cs)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_cs = cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> GetResetStatsPriceAsync(ulong userId, GamblingGame? game)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var totalBet = await ctx.GetTable<UserBetStats>()
|
||||||
|
.Where(x => x.UserId == userId && (game == null || x.Game == game))
|
||||||
|
.SumAsyncLinqToDB(x => x.TotalBet);
|
||||||
|
|
||||||
|
return Math.Max(RESET_MIN_PRICE, (long)Math.Ceiling(totalBet * RESET_TOTAL_MULTIPLIER));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ResetStatsAsync(ulong userId, GamblingGame? game)
|
||||||
|
{
|
||||||
|
var price = await GetResetStatsPriceAsync(userId, game);
|
||||||
|
|
||||||
|
if (!await _cs.RemoveAsync(userId, price, new("betstats", "reset")))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
await ctx.GetTable<UserBetStats>()
|
||||||
|
.DeleteAsync(x => x.UserId == userId && (game == null || x.Game == game));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ResetGamblingStatsAsync()
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
await ctx.GetTable<GamblingStats>()
|
||||||
|
.DeleteAsync();
|
||||||
|
}
|
||||||
|
}
|
@@ -10,7 +10,7 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
public partial class Gambling
|
public partial class Gambling
|
||||||
{
|
{
|
||||||
[Group]
|
[Group]
|
||||||
public partial class WaifuClaimCommands : GamblingSubmodule<WaifuService>
|
public partial class WaifuClaimCommands : GamblingModule<WaifuService>
|
||||||
{
|
{
|
||||||
public WaifuClaimCommands(GamblingConfigService gamblingConfService)
|
public WaifuClaimCommands(GamblingConfigService gamblingConfService)
|
||||||
: base(gamblingConfService)
|
: base(gamblingConfService)
|
||||||
@@ -21,7 +21,7 @@ public partial class Gambling
|
|||||||
public async Task WaifuReset()
|
public async Task WaifuReset()
|
||||||
{
|
{
|
||||||
var price = _service.GetResetPrice(ctx.User);
|
var price = _service.GetResetPrice(ctx.User);
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithTitle(GetText(strs.waifu_reset_confirm))
|
.WithTitle(GetText(strs.waifu_reset_confirm))
|
||||||
.WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price)))));
|
.WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price)))));
|
||||||
|
|
||||||
@@ -37,6 +37,45 @@ public partial class Gambling
|
|||||||
await Response().Error(strs.waifu_reset_fail).SendAsync();
|
await Response().Error(strs.waifu_reset_fail).SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task WaifuClaims()
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.PageItems(async (page) => await _service.GetClaimsAsync(ctx.User.Id, page))
|
||||||
|
.Page((items, page) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle("Waifus");
|
||||||
|
|
||||||
|
if (items.Count == 0)
|
||||||
|
{
|
||||||
|
eb
|
||||||
|
.WithPendingColor()
|
||||||
|
.WithDescription(GetText(strs.empty_page));
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
eb.AddField($"`#{(page * 9) + 1 + i}` {N(item.Price)}",
|
||||||
|
$"""
|
||||||
|
{item.Username}
|
||||||
|
||{item.UserId}||
|
||||||
|
""",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task WaifuClaim(long amount, [Leftover] IUser target)
|
public async Task WaifuClaim(long amount, [Leftover] IUser target)
|
||||||
@@ -144,7 +183,7 @@ public partial class Gambling
|
|||||||
if (targetId == ctx.User.Id)
|
if (targetId == ctx.User.Id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (w, result, amount, remaining) = await _service.DivorceWaifuAsync(ctx.User, targetId);
|
var (w, result, amount) = await _service.DivorceWaifuAsync(ctx.User, targetId);
|
||||||
|
|
||||||
if (result == DivorceResult.SucessWithPenalty)
|
if (result == DivorceResult.SucessWithPenalty)
|
||||||
{
|
{
|
||||||
@@ -157,14 +196,6 @@ public partial class Gambling
|
|||||||
await Response().Confirm(strs.waifu_divorced_notlike(N(amount))).SendAsync();
|
await Response().Confirm(strs.waifu_divorced_notlike(N(amount))).SendAsync();
|
||||||
else if (result == DivorceResult.NotYourWife)
|
else if (result == DivorceResult.NotYourWife)
|
||||||
await Response().Error(strs.waifu_not_yours).SendAsync();
|
await Response().Error(strs.waifu_not_yours).SendAsync();
|
||||||
else if (remaining is { } rem)
|
|
||||||
{
|
|
||||||
await Response()
|
|
||||||
.Error(strs.waifu_recent_divorce(
|
|
||||||
Format.Bold(((int)rem.TotalHours).ToString()),
|
|
||||||
Format.Bold(rem.Minutes.ToString())))
|
|
||||||
.SendAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -235,7 +266,7 @@ public partial class Gambling
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifus_top_waifus)).WithOkColor();
|
var embed = CreateEmbed().WithTitle(GetText(strs.waifus_top_waifus)).WithOkColor();
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
foreach (var w in waifus)
|
foreach (var w in waifus)
|
||||||
@@ -319,7 +350,7 @@ public partial class Gambling
|
|||||||
if (string.IsNullOrWhiteSpace(fansStr))
|
if (string.IsNullOrWhiteSpace(fansStr))
|
||||||
fansStr = "-";
|
fansStr = "-";
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.waifu)
|
.WithTitle(GetText(strs.waifu)
|
||||||
+ " "
|
+ " "
|
||||||
@@ -362,7 +393,7 @@ public partial class Gambling
|
|||||||
.CurrentPage(page)
|
.CurrentPage(page)
|
||||||
.Page((items, _) =>
|
.Page((items, _) =>
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
|
var embed = CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
|
||||||
|
|
||||||
items
|
items
|
||||||
.ToList()
|
.ToList()
|
||||||
|
@@ -318,25 +318,20 @@ public class WaifuService : INService, IReadyExecutor
|
|||||||
private static TypedKey<long> GetAffinityKey(ulong userId)
|
private static TypedKey<long> GetAffinityKey(ulong userId)
|
||||||
=> new($"waifu:affinity:{userId}");
|
=> new($"waifu:affinity:{userId}");
|
||||||
|
|
||||||
public async Task<(WaifuInfo, DivorceResult, long, TimeSpan?)> DivorceWaifuAsync(IUser user, ulong targetId)
|
public async Task<(WaifuInfo, DivorceResult, long)> DivorceWaifuAsync(IUser user, ulong targetId)
|
||||||
{
|
{
|
||||||
DivorceResult result;
|
DivorceResult result;
|
||||||
TimeSpan? remaining = null;
|
|
||||||
long amount = 0;
|
long amount = 0;
|
||||||
WaifuInfo w;
|
WaifuInfo w;
|
||||||
await using (var uow = _db.GetDbContext())
|
await using (var uow = _db.GetDbContext())
|
||||||
{
|
{
|
||||||
w = uow.Set<WaifuInfo>().ByWaifuUserId(targetId);
|
w = uow.Set<WaifuInfo>().ByWaifuUserId(targetId);
|
||||||
if (w?.Claimer is null || w.Claimer.UserId != user.Id)
|
if (w?.Claimer is null || w.Claimer.UserId != user.Id)
|
||||||
|
{
|
||||||
result = DivorceResult.NotYourWife;
|
result = DivorceResult.NotYourWife;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
remaining = await _cache.GetRatelimitAsync(GetDivorceKey(user.Id), 6.Hours());
|
|
||||||
if (remaining is TimeSpan rem)
|
|
||||||
{
|
|
||||||
result = DivorceResult.Cooldown;
|
|
||||||
return (w, result, amount, rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
amount = w.Price / 2;
|
amount = w.Price / 2;
|
||||||
|
|
||||||
@@ -369,7 +364,7 @@ public class WaifuService : INService, IReadyExecutor
|
|||||||
await uow.SaveChangesAsync();
|
await uow.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (w, result, amount, remaining);
|
return (w, result, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> GiftWaifuAsync(
|
public async Task<bool> GiftWaifuAsync(
|
||||||
@@ -603,7 +598,7 @@ public class WaifuService : INService, IReadyExecutor
|
|||||||
.Where(wi => wi.ClaimerId == waifuId)
|
.Where(wi => wi.ClaimerId == waifuId)
|
||||||
.Select(wi => wi.WaifuId)
|
.Select(wi => wi.WaifuId)
|
||||||
.Contains(x.Id))
|
.Contains(x.Id))
|
||||||
.Select(x => $"{x.Username}#{x.Discriminator}")
|
.Select(x => x.Username)
|
||||||
.ToListAsyncEF();
|
.ToListAsyncEF();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,7 +610,7 @@ public class WaifuService : INService, IReadyExecutor
|
|||||||
.Where(wi => wi.AffinityId == waifuId)
|
.Where(wi => wi.AffinityId == waifuId)
|
||||||
.Select(wi => wi.WaifuId)
|
.Select(wi => wi.WaifuId)
|
||||||
.Contains(x.Id))
|
.Contains(x.Id))
|
||||||
.Select(x => $"{x.Username}#{x.Discriminator}")
|
.Select(x => x.Username)
|
||||||
.ToListAsyncEF();
|
.ToListAsyncEF();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -630,4 +625,38 @@ public class WaifuService : INService, IReadyExecutor
|
|||||||
.FirstOrDefault())
|
.FirstOrDefault())
|
||||||
.ToListAsyncEF();
|
.ToListAsyncEF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<WaifuClaimsResult>> GetClaimsAsync(ulong userId, int page)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
|
var wid = ctx.GetTable<DiscordUser>()
|
||||||
|
.Where(x => x.UserId == userId)
|
||||||
|
.Select(x => x.Id)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (wid == 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return await ctx.GetTable<WaifuInfo>()
|
||||||
|
.Where(x => x.ClaimerId == wid)
|
||||||
|
.LeftJoin(ctx.GetTable<DiscordUser>(),
|
||||||
|
(wi, du) => wi.WaifuId == du.Id,
|
||||||
|
(wi, du) => new WaifuClaimsResult(
|
||||||
|
du.Username,
|
||||||
|
du.UserId,
|
||||||
|
wi.Price
|
||||||
|
))
|
||||||
|
.OrderByDescending(x => x.Price)
|
||||||
|
.Skip(page * 9)
|
||||||
|
.Take(9)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class WaifuClaimsResult(string username, ulong userId, long price)
|
||||||
|
{
|
||||||
|
public string Username { get; } = username;
|
||||||
|
public ulong UserId { get; } = userId;
|
||||||
|
public long Price { get; } = price;
|
||||||
}
|
}
|
@@ -42,15 +42,12 @@ public static class WaifuExtensions
|
|||||||
{
|
{
|
||||||
Affinity = x.Affinity == null
|
Affinity = x.Affinity == null
|
||||||
? null
|
? null
|
||||||
: x.Affinity.Username
|
: x.Affinity.Username,
|
||||||
+ (x.Affinity.Discriminator != "0000" ? "#" + x.Affinity.Discriminator : ""),
|
|
||||||
ClaimerName =
|
ClaimerName =
|
||||||
x.Claimer == null
|
x.Claimer == null
|
||||||
? null
|
? null
|
||||||
: x.Claimer.Username
|
: x.Claimer.Username,
|
||||||
+ (x.Claimer.Discriminator != "0000" ? "#" + x.Claimer.Discriminator : ""),
|
WaifuName = x.Waifu.Username,
|
||||||
WaifuName = x.Waifu.Username
|
|
||||||
+ (x.Waifu.Discriminator != "0000" ? "#" + x.Waifu.Discriminator : ""),
|
|
||||||
Price = x.Price
|
Price = x.Price
|
||||||
})
|
})
|
||||||
.ToListAsyncEF();
|
.ToListAsyncEF();
|
||||||
@@ -62,7 +59,7 @@ public static class WaifuExtensions
|
|||||||
public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name)
|
public static ulong GetWaifuUserId(this DbSet<WaifuInfo> waifus, ulong ownerId, string name)
|
||||||
=> waifus.AsQueryable()
|
=> waifus.AsQueryable()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username + "#" + x.Waifu.Discriminator == name)
|
.Where(x => x.Claimer.UserId == ownerId && x.Waifu.Username == name)
|
||||||
.Select(x => x.Waifu.UserId)
|
.Select(x => x.Waifu.UserId)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
@@ -100,7 +97,7 @@ public static class WaifuExtensions
|
|||||||
ctx.Set<DiscordUser>()
|
ctx.Set<DiscordUser>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(u => u.UserId == userId)
|
.Where(u => u.UserId == userId)
|
||||||
.Select(u => u.Username + "#" + u.Discriminator)
|
.Select(u => u.Username)
|
||||||
.FirstOrDefault(),
|
.FirstOrDefault(),
|
||||||
AffinityCount =
|
AffinityCount =
|
||||||
ctx.Set<WaifuUpdate>()
|
ctx.Set<WaifuUpdate>()
|
||||||
@@ -112,14 +109,14 @@ public static class WaifuExtensions
|
|||||||
ctx.Set<DiscordUser>()
|
ctx.Set<DiscordUser>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(u => u.Id == w.AffinityId)
|
.Where(u => u.Id == w.AffinityId)
|
||||||
.Select(u => u.Username + "#" + u.Discriminator)
|
.Select(u => u.Username)
|
||||||
.FirstOrDefault(),
|
.FirstOrDefault(),
|
||||||
ClaimCount = ctx.Set<WaifuInfo>().AsQueryable().Count(x => x.ClaimerId == w.WaifuId),
|
ClaimCount = ctx.Set<WaifuInfo>().AsQueryable().Count(x => x.ClaimerId == w.WaifuId),
|
||||||
ClaimerName =
|
ClaimerName =
|
||||||
ctx.Set<DiscordUser>()
|
ctx.Set<DiscordUser>()
|
||||||
.AsQueryable()
|
.AsQueryable()
|
||||||
.Where(u => u.Id == w.ClaimerId)
|
.Where(u => u.Id == w.ClaimerId)
|
||||||
.Select(u => u.Username + "#" + u.Discriminator)
|
.Select(u => u.Username)
|
||||||
.FirstOrDefault(),
|
.FirstOrDefault(),
|
||||||
DivorceCount =
|
DivorceCount =
|
||||||
ctx.Set<WaifuUpdate>()
|
ctx.Set<WaifuUpdate>()
|
||||||
|
@@ -13,5 +13,10 @@ public interface IGamblingService
|
|||||||
Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount);
|
Task<OneOf<SlotResult, GamblingError>> SlotAsync(ulong userId, long amount);
|
||||||
Task<FlipResult[]> FlipAsync(int count);
|
Task<FlipResult[]> FlipAsync(int count);
|
||||||
Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick);
|
Task<OneOf<RpsResult, GamblingError>> RpsAsync(ulong userId, long amount, byte pick);
|
||||||
Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? maybeGuessValue, byte? maybeGuessColor);
|
|
||||||
|
Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(
|
||||||
|
ulong userId,
|
||||||
|
long amount,
|
||||||
|
byte? maybeGuessValue,
|
||||||
|
byte? maybeGuessColor);
|
||||||
}
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using NadekoBot.Modules.Gambling.Betdraw;
|
using NadekoBot.Modules.Gambling.Betdraw;
|
||||||
using NadekoBot.Modules.Gambling.Rps;
|
using NadekoBot.Modules.Gambling.Rps;
|
||||||
using NadekoBot.Modules.Gambling.Services;
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
@@ -8,12 +10,12 @@ namespace NadekoBot.Modules.Gambling;
|
|||||||
|
|
||||||
public sealed class NewGamblingService : IGamblingService, INService
|
public sealed class NewGamblingService : IGamblingService, INService
|
||||||
{
|
{
|
||||||
private readonly GamblingConfigService _bcs;
|
private readonly GamblingConfigService _gcs;
|
||||||
private readonly ICurrencyService _cs;
|
private readonly ICurrencyService _cs;
|
||||||
|
|
||||||
public NewGamblingService(GamblingConfigService bcs, ICurrencyService cs)
|
public NewGamblingService(GamblingConfigService gcs, ICurrencyService cs)
|
||||||
{
|
{
|
||||||
_bcs = bcs;
|
_gcs = gcs;
|
||||||
_cs = cs;
|
_cs = cs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,13 +33,13 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var game = new LulaGame(_bcs.Data.LuckyLadder.Multipliers);
|
var game = new LulaGame(_gcs.Data.LuckyLadder.Multipliers);
|
||||||
var result = game.Spin(amount);
|
var result = game.Spin(amount);
|
||||||
|
|
||||||
var won = (long)result.Won;
|
var won = (long)result.Won;
|
||||||
if (won > 0)
|
if (won > 0)
|
||||||
{
|
{
|
||||||
await _cs.AddAsync(userId, won, new("lula", "win"));
|
await _cs.AddAsync(userId, won, new("lula", result.Multiplier >= 1 ? "win" : "lose"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -57,9 +59,9 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var game = new BetrollGame(_bcs.Data.BetRoll.Pairs
|
var game = new BetrollGame(_gcs.Data.BetRoll.Pairs
|
||||||
.Select(x => (x.WhenAbove, (decimal)x.MultiplyBy))
|
.Select(x => (x.WhenAbove, (decimal)x.MultiplyBy))
|
||||||
.ToList());
|
.ToList());
|
||||||
|
|
||||||
var result = game.Roll(amount);
|
var result = game.Roll(amount);
|
||||||
|
|
||||||
@@ -88,7 +90,7 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var game = new BetflipGame(_bcs.Data.BetFlip.Multiplier);
|
var game = new BetflipGame(_gcs.Data.BetFlip.Multiplier);
|
||||||
var result = game.Flip(guess, amount);
|
var result = game.Flip(guess, amount);
|
||||||
|
|
||||||
var won = (long)result.Won;
|
var won = (long)result.Won;
|
||||||
@@ -100,7 +102,11 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(ulong userId, long amount, byte? maybeGuessValue, byte? maybeGuessColor)
|
public async Task<OneOf<BetdrawResult, GamblingError>> BetDrawAsync(
|
||||||
|
ulong userId,
|
||||||
|
long amount,
|
||||||
|
byte? maybeGuessValue,
|
||||||
|
byte? maybeGuessColor)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegative(amount);
|
ArgumentOutOfRangeException.ThrowIfNegative(amount);
|
||||||
|
|
||||||
@@ -155,7 +161,7 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
var won = (long)result.Won;
|
var won = (long)result.Won;
|
||||||
if (won > 0)
|
if (won > 0)
|
||||||
{
|
{
|
||||||
await _cs.AddAsync(userId, won, new("slot", "won"));
|
await _cs.AddAsync(userId, won, new("slot", "win"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -266,3 +272,45 @@ public sealed class NewGamblingService : IGamblingService, INService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class RakebackService: INService
|
||||||
|
{
|
||||||
|
private readonly DbService _db;
|
||||||
|
private readonly ICurrencyService _cs;
|
||||||
|
|
||||||
|
public RakebackService(DbService db, ICurrencyService cs)
|
||||||
|
{
|
||||||
|
_db = db;
|
||||||
|
_cs = cs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> GetRakebackAsync(ulong userId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
|
var rb = uow.GetTable<Rakeback>()
|
||||||
|
.Where(x => x.UserId == userId)
|
||||||
|
.Select(x => x.Amount)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
return (long)rb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<long> ClaimRakebackAsync(ulong userId)
|
||||||
|
{
|
||||||
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
|
var rbs = await uow.GetTable<Rakeback>()
|
||||||
|
.Where(x => x.UserId == userId)
|
||||||
|
.DeleteWithOutputAsync((x) => x.Amount);
|
||||||
|
|
||||||
|
if(rbs.Length == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var rb = (long)rbs[0];
|
||||||
|
|
||||||
|
await _cs.AddAsync(userId, rb, new("rakeback", "claim"));
|
||||||
|
|
||||||
|
return rb;
|
||||||
|
}
|
||||||
|
}
|
@@ -67,7 +67,7 @@ public partial class Games
|
|||||||
|
|
||||||
private Task Game_OnStarted(AcrophobiaGame game)
|
private Task Game_OnStarted(AcrophobiaGame game)
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.acrophobia))
|
.WithTitle(GetText(strs.acrophobia))
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
@@ -92,7 +92,7 @@ public partial class Games
|
|||||||
|
|
||||||
if (submissions.Length == 1)
|
if (submissions.Length == 1)
|
||||||
{
|
{
|
||||||
await Response().Embed(_sender.CreateEmbed()
|
await Response().Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(
|
.WithDescription(GetText(
|
||||||
strs.acro_winner_only(
|
strs.acro_winner_only(
|
||||||
@@ -103,7 +103,7 @@ public partial class Games
|
|||||||
|
|
||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.acrophobia) + " - " + GetText(strs.submissions_closed))
|
.WithTitle(GetText(strs.acrophobia) + " - " + GetText(strs.submissions_closed))
|
||||||
.WithDescription(GetText(strs.acro_nym_was(
|
.WithDescription(GetText(strs.acro_nym_was(
|
||||||
@@ -127,7 +127,7 @@ public partial class Games
|
|||||||
|
|
||||||
var table = votes.OrderByDescending(v => v.Value);
|
var table = votes.OrderByDescending(v => v.Value);
|
||||||
var winner = table.First();
|
var winner = table.First();
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.acrophobia))
|
.WithTitle(GetText(strs.acrophobia))
|
||||||
.WithDescription(GetText(strs.acro_winner(Format.Bold(winner.Key.UserName),
|
.WithDescription(GetText(strs.acro_winner(Format.Bold(winner.Key.UserName),
|
||||||
|
@@ -38,7 +38,7 @@ public partial class Games : NadekoModule<GamesService>
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var res = _service.GetEightballResponse(ctx.User.Id, question);
|
var res = _service.GetEightballResponse(ctx.User.Id, question);
|
||||||
await Response().Embed(_sender.CreateEmbed()
|
await Response().Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(ctx.User.ToString())
|
.WithDescription(ctx.User.ToString())
|
||||||
.AddField("❓ " + GetText(strs.question), question)
|
.AddField("❓ " + GetText(strs.question), question)
|
||||||
|
@@ -53,7 +53,7 @@ public partial class Games
|
|||||||
var hint = GetText(strs.nc_hint(prefix, _service.GetWidth(), _service.GetHeight()));
|
var hint = GetText(strs.nc_hint(prefix, _service.GetWidth(), _service.GetHeight()));
|
||||||
await Response()
|
await Response()
|
||||||
.File(stream, "ncanvas.png")
|
.File(stream, "ncanvas.png")
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
#if GLOBAL_NADEKO
|
#if GLOBAL_NADEKO
|
||||||
.WithDescription("https://dashy.nadeko.bot/ncanvas")
|
.WithDescription("https://dashy.nadeko.bot/ncanvas")
|
||||||
@@ -164,20 +164,36 @@ public partial class Games
|
|||||||
Culture,
|
Culture,
|
||||||
_gcs.Data.Currency.Sign))));
|
_gcs.Data.Currency.Sign))));
|
||||||
|
|
||||||
if (!await PromptUserConfirmAsync(_sender.CreateEmbed()
|
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithPendingColor()
|
.WithPendingColor()
|
||||||
.WithDescription(prompt)))
|
.WithDescription(prompt)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _service.SetPixel(position, clr.PackedValue, text, ctx.User.Id, pixel.Price);
|
var result = await _service.SetPixel(position, clr.PackedValue, text, ctx.User.Id, pixel.Price);
|
||||||
|
|
||||||
|
if (result == SetPixelResult.NotEnoughMoney)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.not_enough(_gcs.Data.Currency.Sign)).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (result == SetPixelResult.InsufficientPayment)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.nc_insuff_payment).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (result == SetPixelResult.InvalidInput)
|
||||||
|
{
|
||||||
|
await Response().Error(strs.invalid_input).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using var img = await GetZoomImage(position);
|
using var img = await GetZoomImage(position);
|
||||||
await using var stream = await img.ToStreamAsync();
|
await using var stream = await img.ToStreamAsync();
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(GetText(strs.nc_pixel_set(Format.Code(position.ToString()))))
|
.WithDescription(GetText(strs.nc_pixel_set(Format.Code(position.ToString()))))
|
||||||
.WithImageUrl($"attachment://zoom_{position}.png"))
|
.WithImageUrl($"attachment://zoom_{position}.png"))
|
||||||
@@ -215,7 +231,7 @@ public partial class Games
|
|||||||
var pos = new kwum(pixel.Position);
|
var pos = new kwum(pixel.Position);
|
||||||
await Response()
|
await Response()
|
||||||
.File(stream, $"{pixel.Position}.png")
|
.File(stream, $"{pixel.Position}.png")
|
||||||
.Embed(_sender.CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithDescription(string.IsNullOrWhiteSpace(pixel.Text) ? string.Empty : pixel.Text)
|
.WithDescription(string.IsNullOrWhiteSpace(pixel.Text) ? string.Empty : pixel.Text)
|
||||||
.WithTitle(GetText(strs.nc_pixel(pos)))
|
.WithTitle(GetText(strs.nc_pixel(pos)))
|
||||||
@@ -247,7 +263,7 @@ public partial class Games
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await PromptUserConfirmAsync(_sender.CreateEmbed()
|
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
"This will reset the canvas to the specified image. All prices, text and colors will be reset.\n\n"
|
"This will reset the canvas to the specified image. All prices, text and colors will be reset.\n\n"
|
||||||
+ "Are you sure you want to continue?")))
|
+ "Are you sure you want to continue?")))
|
||||||
@@ -277,7 +293,7 @@ public partial class Games
|
|||||||
{
|
{
|
||||||
await _service.ResetAsync();
|
await _service.ResetAsync();
|
||||||
|
|
||||||
if (!await PromptUserConfirmAsync(_sender.CreateEmbed()
|
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
"This will delete all pixels and reset the canvas.\n\n"
|
"This will delete all pixels and reset the canvas.\n\n"
|
||||||
+ "Are you sure you want to continue?")))
|
+ "Are you sure you want to continue?")))
|
||||||
|
@@ -20,7 +20,7 @@ public sealed class NCanvasService : INCanvasService, IReadyExecutor, INService
|
|||||||
|
|
||||||
public const int CANVAS_WIDTH = 500;
|
public const int CANVAS_WIDTH = 500;
|
||||||
public const int CANVAS_HEIGHT = 350;
|
public const int CANVAS_HEIGHT = 350;
|
||||||
public const int INITIAL_PRICE = 10;
|
public const int INITIAL_PRICE = 3;
|
||||||
|
|
||||||
public NCanvasService(
|
public NCanvasService(
|
||||||
DbService db,
|
DbService db,
|
||||||
@@ -110,7 +110,7 @@ public sealed class NCanvasService : INCanvasService, IReadyExecutor, INService
|
|||||||
|
|
||||||
var wallet = await _cs.GetWalletAsync(userId);
|
var wallet = await _cs.GetWalletAsync(userId);
|
||||||
|
|
||||||
var paid = await wallet.Take(price, new("canvas", "pixel", $"Bought pixel #{position}"));
|
var paid = await wallet.Take(price, new("canvas", "pixel-buy", $"Bought pixel {new kwum(position)}"));
|
||||||
if (!paid)
|
if (!paid)
|
||||||
{
|
{
|
||||||
return SetPixelResult.NotEnoughMoney;
|
return SetPixelResult.NotEnoughMoney;
|
||||||
@@ -138,7 +138,7 @@ public sealed class NCanvasService : INCanvasService, IReadyExecutor, INService
|
|||||||
|
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
await wallet.Add(price, new("canvas", "pixel-refund", $"Refund pixel #{position} purchase"));
|
await wallet.Add(price, new("canvas", "pixel-refund", $"Refund pixel {new kwum(position)} purchase"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return success ? SetPixelResult.Success : SetPixelResult.InsufficientPayment;
|
return success ? SetPixelResult.Success : SetPixelResult.InsufficientPayment;
|
||||||
|
@@ -29,7 +29,7 @@ public partial class Games
|
|||||||
if (!await nunchi.Join(ctx.User.Id, ctx.User.ToString()))
|
if (!await nunchi.Join(ctx.User.Id, ctx.User.ToString()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Response().Error(strs.nunchi_joined(nunchi.ParticipantCount)).SendAsync();
|
await Response().Confirm(strs.nunchi_joined(nunchi.ParticipantCount)).SendAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -94,7 +94,7 @@ public partial class Games
|
|||||||
if (removed is null)
|
if (removed is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithTitle($"Removed typing article #{index + 1}")
|
.WithTitle($"Removed typing article #{index + 1}")
|
||||||
.WithDescription(removed.Text.TrimTo(50))
|
.WithDescription(removed.Text.TrimTo(50))
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
@@ -160,7 +160,7 @@ public partial class Games
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
questionEmbed = _sender.CreateEmbed()
|
questionEmbed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.trivia_game))
|
.WithTitle(GetText(strs.trivia_game))
|
||||||
.AddField(GetText(strs.category), question.Category)
|
.AddField(GetText(strs.category), question.Category)
|
||||||
@@ -189,7 +189,7 @@ public partial class Games
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithErrorColor()
|
.WithErrorColor()
|
||||||
.WithTitle(GetText(strs.trivia_game))
|
.WithTitle(GetText(strs.trivia_game))
|
||||||
.WithDescription(GetText(strs.trivia_times_up(Format.Bold(question.Answer))));
|
.WithDescription(GetText(strs.trivia_times_up(Format.Bold(question.Answer))));
|
||||||
@@ -221,7 +221,7 @@ public partial class Games
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Response().Embed(_sender.CreateEmbed()
|
await Response().Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(GetText(strs.trivia_ended))
|
.WithAuthor(GetText(strs.trivia_ended))
|
||||||
.WithTitle(GetText(strs.leaderboard))
|
.WithTitle(GetText(strs.leaderboard))
|
||||||
@@ -247,7 +247,7 @@ public partial class Games
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var embed = _sender.CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.trivia_game))
|
.WithTitle(GetText(strs.trivia_game))
|
||||||
.WithDescription(GetText(strs.trivia_win(user.Name,
|
.WithDescription(GetText(strs.trivia_win(user.Name,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user