Added .shopadd cmd

This commit is contained in:
Kwoth
2024-04-23 14:18:30 +00:00
parent 1aebca14e1
commit 74690c73d6
22 changed files with 18474 additions and 6275 deletions

View File

@@ -2,6 +2,14 @@
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
## [5.0.0]
### Added
- `.shopadd command` You can now sell commands in the shop. The command will execute as if you were the one running it when someone buys it
- type `.h .shopadd` for more info
-
## [4.3.22] - 23.04.2023 ## [4.3.22] - 23.04.2023
### Added ### Added

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.203.0" /> <PackageReference Include="Discord.Net" Version="3.204.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -5,8 +5,8 @@ public enum ShopEntryType
{ {
Role, Role,
List List,
//Infinite_List, Command
} }
public class ShopEntry : DbEntity, IIndexed public class ShopEntry : DbEntity, IIndexed
@@ -25,6 +25,9 @@ public class ShopEntry : DbEntity, IIndexed
//list //list
public HashSet<ShopEntryItem> Items { get; set; } = new(); public HashSet<ShopEntryItem> Items { get; set; } = new();
public ulong? RoleRequirement { get; set; } public ulong? RoleRequirement { get; set; }
// command
public string Command { get; set; }
} }
public class ShopEntryItem : DbEntity public class ShopEntryItem : DbEntity

View File

@@ -7,7 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.203.0" /> <PackageReference Include="Discord.Net" Version="3.204.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@@ -4,6 +4,7 @@ using Nadeko.Bot.Db.Models;
using System.Collections.Immutable; using System.Collections.Immutable;
using LinqToDB; using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Db.Models; using NadekoBot.Db.Models;
namespace NadekoBot.Modules.Administration.Services; namespace NadekoBot.Modules.Administration.Services;

View File

@@ -14,7 +14,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net" Version="3.203.0" /> <PackageReference Include="Discord.Net" Version="3.204.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
</ItemGroup> </ItemGroup>

View File

@@ -1,4 +1,6 @@
#nullable disable #nullable disable
using Nadeko.Bot.Db.Models;
namespace NadekoBot.Modules.Gambling.Services; namespace NadekoBot.Modules.Gambling.Services;
public interface IShopService public interface IShopService
@@ -40,4 +42,5 @@ public interface IShopService
Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex); Task<bool> MoveEntryAsync(ulong guildId, int fromIndex, int toIndex);
Task<bool> SetItemRoleRequirementAsync(ulong guildId, int index, ulong? roleId); Task<bool> SetItemRoleRequirementAsync(ulong guildId, int index, ulong? roleId);
Task<ShopEntry> AddShopCommandAsync(ulong guildId, ulong userId, int price, string command);
} }

View File

@@ -4,6 +4,7 @@ using NadekoBot.Db;
using NadekoBot.Modules.Gambling.Common; using NadekoBot.Modules.Gambling.Common;
using NadekoBot.Modules.Gambling.Services; using NadekoBot.Modules.Gambling.Services;
using Nadeko.Bot.Db.Models; using Nadeko.Bot.Db.Models;
using NadekoBot.Modules.Administration;
namespace NadekoBot.Modules.Gambling; namespace NadekoBot.Modules.Gambling;
@@ -22,6 +23,12 @@ public partial class Gambling
Role Role
} }
public enum Command
{
Command,
Cmd
}
private readonly DbService _db; private readonly DbService _db;
private readonly ICurrencyService _cs; private readonly ICurrencyService _cs;
@@ -39,8 +46,8 @@ public partial class Gambling
using var uow = _db.GetDbContext(); using var uow = _db.GetDbContext();
var entries = uow.GuildConfigsForId(ctx.Guild.Id, var entries = uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)) set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items))
.ShopEntries.ToIndexed(); .ShopEntries.ToIndexed();
return ctx.SendPaginatedConfirmAsync(page, return ctx.SendPaginatedConfirmAsync(page,
curPage => curPage =>
{ {
@@ -156,7 +163,7 @@ public partial class Gambling
return; return;
} }
if (entry.Type == ShopEntryType.List) else if (entry.Type == ShopEntryType.List)
{ {
if (entry.Items.Count == 0) if (entry.Items.Count == 0)
{ {
@@ -177,11 +184,11 @@ public partial class Gambling
try try
{ {
await ctx.User.EmbedAsync(_eb.Create() await ctx.User.EmbedAsync(_eb.Create()
.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)
.AddField(GetText(strs.price), entry.Price.ToString(), true) .AddField(GetText(strs.price), entry.Price.ToString(), true)
.AddField(GetText(strs.name), entry.Name, true)); .AddField(GetText(strs.name), entry.Name, true));
await _cs.AddAsync(entry.AuthorId, await _cs.AddAsync(entry.AuthorId,
GetProfitAmount(entry.Price), GetProfitAmount(entry.Price),
@@ -193,9 +200,9 @@ public partial class Gambling
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries) set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items)) .ThenInclude(x => x.Items))
.ShopEntries); .ShopEntries);
entry = entries.ElementAtOrDefault(index); entry = entries.ElementAtOrDefault(index);
if (entry is not null) if (entry is not null)
{ {
@@ -213,11 +220,79 @@ public partial class Gambling
else else
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign)); await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
} }
else if (entry.Type == ShopEntryType.Command)
{
var guild = ctx.Guild as SocketGuild;
var channel = ctx.Channel as ISocketMessageChannel;
var msg = ctx.Message as SocketUserMessage;
var user = await ctx.Guild.GetUserAsync(entry.AuthorId);
if (guild is null || channel is null || msg is null || user is null)
{
await ReplyErrorLocalizedAsync(strs.shop_command_invalid_context);
return;
}
if (!await _cs.RemoveAsync(ctx.User.Id, entry.Price, new("shop", "buy", entry.Type.ToString())))
{
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
return;
}
else
{
var cmd = entry.Command.Replace("%you%", ctx.User.Id.ToString());
var eb = _eb.Create()
.WithPendingColor()
.WithTitle("Executing shop command")
.WithDescription(cmd);
var msgTask = ctx.Channel.EmbedAsync(eb);
await _cs.AddAsync(entry.AuthorId,
GetProfitAmount(entry.Price),
new("shop", "sell", entry.Name));
await _cmdHandler.TryRunCommand(guild,
channel,
new DoAsUserMessage(
msg,
user,
cmd
));
try
{
var pendingMsg = await msgTask;
await pendingMsg.EditAsync(SmartEmbedText.FromEmbed(eb
.WithOkColor()
.WithTitle("Shop command executed")
.Build()));
}
catch
{
}
}
}
} }
private static long GetProfitAmount(int price) private static long GetProfitAmount(int price)
=> (int)Math.Ceiling(0.90 * price); => (int)Math.Ceiling(0.90 * price);
[Cmd]
[RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)]
[BotPerm(GuildPerm.ManageRoles)]
public async Task ShopAdd(Command _, int price, [Leftover] string command)
{
if (price < 1)
return;
var entry = await _service.AddShopCommandAsync(ctx.Guild.Id, ctx.User.Id, price, command);
await ctx.Channel.EmbedAsync(EntryToEmbed(entry).WithTitle(GetText(strs.shop_item_add)));
}
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
@@ -239,9 +314,9 @@ public partial class Gambling
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries) set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items)) .ThenInclude(x => x.Items))
.ShopEntries) .ShopEntries)
{ {
entry entry
}; };
@@ -271,9 +346,9 @@ public partial class Gambling
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries) set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items)) .ThenInclude(x => x.Items))
.ShopEntries) .ShopEntries)
{ {
entry entry
}; };
@@ -302,9 +377,9 @@ public partial class Gambling
await using (var uow = _db.GetDbContext()) await using (var uow = _db.GetDbContext())
{ {
var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id, var entries = new IndexedCollection<ShopEntry>(uow.GuildConfigsForId(ctx.Guild.Id,
set => set.Include(x => x.ShopEntries) set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items)) .ThenInclude(x => x.Items))
.ShopEntries); .ShopEntries);
entry = entries.ElementAtOrDefault(index); entry = entries.ElementAtOrDefault(index);
if (entry is not null && (rightType = entry.Type == ShopEntryType.List)) if (entry is not null && (rightType = entry.Type == ShopEntryType.List))
{ {
@@ -456,19 +531,27 @@ public partial class Gambling
if (entry.Type == ShopEntryType.Role) if (entry.Type == ShopEntryType.Role)
{ {
return embed return embed
.AddField(GetText(strs.name), .AddField(GetText(strs.name),
GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name
?? "MISSING_ROLE"))), ?? "MISSING_ROLE"))),
true) true)
.AddField(GetText(strs.price), N(entry.Price), true) .AddField(GetText(strs.price), N(entry.Price), true)
.AddField(GetText(strs.type), entry.Type.ToString(), true); .AddField(GetText(strs.type), entry.Type.ToString(), true);
} }
if (entry.Type == ShopEntryType.List) if (entry.Type == ShopEntryType.List)
{ {
return embed.AddField(GetText(strs.name), entry.Name, true) return embed.AddField(GetText(strs.name), entry.Name, true)
.AddField(GetText(strs.price), N(entry.Price), true) .AddField(GetText(strs.price), N(entry.Price), true)
.AddField(GetText(strs.type), GetText(strs.random_unique_item), true); .AddField(GetText(strs.type), GetText(strs.random_unique_item), true);
}
else if (entry.Type == ShopEntryType.Command)
{
return embed
.AddField(GetText(strs.name), Format.Code(entry.Command), true)
.AddField(GetText(strs.price), N(entry.Price), true)
.AddField(GetText(strs.type), entry.Type.ToString(), true);
} }
//else if (entry.Type == ShopEntryType.Infinite_List) //else if (entry.Type == ShopEntryType.Infinite_List)
@@ -490,6 +573,9 @@ public partial class Gambling
+ GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE"))); + GetText(strs.shop_role(Format.Bold(ctx.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE")));
if (entry.Type == ShopEntryType.List) if (entry.Type == ShopEntryType.List)
return prepend + GetText(strs.unique_items_left(entry.Items.Count)) + "\n" + entry.Name; return prepend + GetText(strs.unique_items_left(entry.Items.Count)) + "\n" + entry.Name;
if (entry.Type == ShopEntryType.Command)
return prepend + Format.Code(entry.Command);
return prepend; return prepend;
} }
} }

View File

@@ -1,7 +1,6 @@
#nullable disable #nullable disable
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NadekoBot.Db; using NadekoBot.Db;
using Nadeko.Bot.Db.Models; using Nadeko.Bot.Db.Models;
namespace NadekoBot.Modules.Gambling.Services; namespace NadekoBot.Modules.Gambling.Services;
@@ -14,8 +13,10 @@ public class ShopService : IShopService, INService
=> _db = db; => _db = db;
private IndexedCollection<ShopEntry> GetEntriesInternal(DbContext uow, ulong guildId) private IndexedCollection<ShopEntry> GetEntriesInternal(DbContext uow, ulong guildId)
=> uow.GuildConfigsForId(guildId, set => set.Include(x => x.ShopEntries).ThenInclude(x => x.Items)) => uow.GuildConfigsForId(guildId,
.ShopEntries.ToIndexed(); set => set.Include(x => x.ShopEntries)
.ThenInclude(x => x.Items))
.ShopEntries.ToIndexed();
public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice) public async Task<bool> ChangeEntryPriceAsync(ulong guildId, int index, int newPrice)
{ {
@@ -109,4 +110,24 @@ public class ShopService : IShopService, INService
await uow.SaveChangesAsync(); await uow.SaveChangesAsync();
return true; return true;
} }
public async Task<ShopEntry> AddShopCommandAsync(ulong guildId, ulong userId, int price, string command)
{
await using var uow = _db.GetDbContext();
var entries = GetEntriesInternal(uow, guildId);
var entry = new ShopEntry()
{
AuthorId = userId,
Command = command,
Type = ShopEntryType.Command,
Price = price,
};
entries.Add(entry);
uow.GuildConfigsForId(guildId, set => set).ShopEntries = entries;
await uow.SaveChangesAsync();
return entry;
}
} }

View File

@@ -14,7 +14,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Discord.Net.Core" Version="3.203.0" /> <PackageReference Include="Discord.Net.Core" Version="3.204.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,4 +1,5 @@
#nullable disable #nullable disable
using Nadeko.Bot.Db.Models;
using NadekoBot.Db.Models; using NadekoBot.Db.Models;
namespace NadekoBot.Services.Database.Models; namespace NadekoBot.Services.Database.Models;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Db.Migrations
{
/// <inheritdoc />
public partial class shopcommands : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "command",
table: "shopentry",
type: "text",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "command",
table: "shopentry");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NadekoBot.Db.Migrations.Sqlite
{
/// <inheritdoc />
public partial class shopcommands : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Command",
table: "ShopEntry",
type: "TEXT",
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Command",
table: "ShopEntry");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1751,9 +1751,14 @@ shop:
- "" - ""
- "2" - "2"
shopadd: shopadd:
desc: "Adds an item to the shop by specifying type price and name. Available types are role and list. 90% of currency from each purchase will be received by the user who added the item to the shop." desc: |-
- Available types are role, list and command.
- If the item is a role, specify a role id or a role name.
- If the item is a command, specify the full command, replacing the user with %user% (for a mention) or %user.id% for user id.
90% of currency from each purchase will be received by the user who added the item to the shop.
args: args:
- "role 1000 Rich" - "role 1000 Rich"
- "cmd 1000 .setrole %user% Rich"
shopremove: shopremove:
desc: "Removes an item from the shop by its ID." desc: "Removes an item from the shop by its ID."
args: args:

View File

@@ -710,6 +710,7 @@
"shop_item_add": "Shop item added", "shop_item_add": "Shop item added",
"shop_none": "No shop items found on this page.", "shop_none": "No shop items found on this page.",
"shop_role": "You will get {0} role.", "shop_role": "You will get {0} role.",
"shop_command_invalid_context": "Unable to retrieve user, channel or server in order to execute the command.",
"type": "Type", "type": "Type",
"gvc_disabled": "Game Voice Channel feature has been disabled on this server.", "gvc_disabled": "Game Voice Channel feature has been disabled on this server.",
"gvc_enabled": "{0} is a Game Voice Channel now.", "gvc_enabled": "{0} is a Game Voice Channel now.",