mirror of
				https://gitlab.com/Kwoth/nadekobot.git
				synced 2025-11-04 00:34:26 -05:00 
			
		
		
		
	- Added a simple bank system. Users can deposit, withdraw and check the balance of their currency in the bank.
- Users can't check other user's bank balances. - Added a button on a .$ command which, when clicked, sends you a message with your bank balance that only you can see. - Updated pagination, it now uses buttons instead of reactions - using .h <command group> (atm only .bank is a proper group) will list commands with their descriptions in that group
This commit is contained in:
		
							
								
								
									
										80
									
								
								src/NadekoBot/Common/NadekoInteractionBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/NadekoBot/Common/NadekoInteractionBase.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
namespace NadekoBot;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
public abstract class NadekoInteraction
 | 
			
		||||
{
 | 
			
		||||
    // improvements:
 | 
			
		||||
    //  - state in OnAction
 | 
			
		||||
    //  - configurable delay
 | 
			
		||||
    //  - 
 | 
			
		||||
    public abstract string Name { get; }
 | 
			
		||||
    public abstract IEmote Emote { get; }
 | 
			
		||||
    public Func<SocketMessageComponent, Task> OnAction { get; }
 | 
			
		||||
 | 
			
		||||
    protected readonly DiscordSocketClient _client;
 | 
			
		||||
 | 
			
		||||
    protected readonly TaskCompletionSource<bool> _interactionCompletedSource;
 | 
			
		||||
 | 
			
		||||
    protected ulong _authorId;
 | 
			
		||||
    protected IUserMessage message;
 | 
			
		||||
 | 
			
		||||
    protected NadekoInteraction(DiscordSocketClient client, ulong authorId, Func<SocketMessageComponent, Task> onAction)
 | 
			
		||||
    {
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _authorId = authorId;
 | 
			
		||||
        OnAction = onAction;
 | 
			
		||||
        _interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task RunAsync(IUserMessage msg)
 | 
			
		||||
    {
 | 
			
		||||
        message = msg;
 | 
			
		||||
 | 
			
		||||
        _client.InteractionCreated += OnInteraction;
 | 
			
		||||
        await Task.WhenAny(Task.Delay(10_000), _interactionCompletedSource.Task);
 | 
			
		||||
        _client.InteractionCreated -= OnInteraction;
 | 
			
		||||
 | 
			
		||||
        await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task OnInteraction(SocketInteraction arg)
 | 
			
		||||
    {
 | 
			
		||||
        if (arg is not SocketMessageComponent smc)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (smc.Message.Id != message.Id)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (smc.Data.CustomId != Name)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (smc.User.Id != _authorId)
 | 
			
		||||
        {
 | 
			
		||||
            await arg.DeferAsync();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        _ = Task.Run(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            await OnAction(smc);
 | 
			
		||||
            
 | 
			
		||||
            // this should only be a thing on single-response buttons
 | 
			
		||||
            _interactionCompletedSource.TrySetResult(true);
 | 
			
		||||
 | 
			
		||||
            if (!smc.HasResponded)
 | 
			
		||||
            {
 | 
			
		||||
                await smc.DeferAsync();
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public MessageComponent CreateComponent()
 | 
			
		||||
    {
 | 
			
		||||
        var comp = new ComponentBuilder()
 | 
			
		||||
            .WithButton(new ButtonBuilder(style: ButtonStyle.Secondary, emote: Emote, customId: Name));
 | 
			
		||||
 | 
			
		||||
        return comp.Build();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
    
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#nullable disable
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using MessageType = NadekoBot.Extensions.MessageType;
 | 
			
		||||
 | 
			
		||||
// ReSharper disable InconsistentNaming
 | 
			
		||||
 | 
			
		||||
@@ -29,20 +30,15 @@ public abstract class NadekoModule : ModuleBase
 | 
			
		||||
 | 
			
		||||
    protected string GetText(in LocStr data)
 | 
			
		||||
        => Strings.GetText(data, Culture);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> SendErrorAsync(string error)
 | 
			
		||||
        => ctx.Channel.SendErrorAsync(_eb, error);
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    public Task<IUserMessage> SendErrorAsync(
 | 
			
		||||
        string title,
 | 
			
		||||
        string error,
 | 
			
		||||
        string url = null,
 | 
			
		||||
        string footer = null)
 | 
			
		||||
        string footer = null, 
 | 
			
		||||
        NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(string text)
 | 
			
		||||
        => ctx.Channel.SendConfirmAsync(_eb, text);
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(
 | 
			
		||||
        string title,
 | 
			
		||||
        string text,
 | 
			
		||||
@@ -50,25 +46,33 @@ public abstract class NadekoModule : ModuleBase
 | 
			
		||||
        string footer = null)
 | 
			
		||||
        => ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> SendPendingAsync(string text)
 | 
			
		||||
        => ctx.Channel.SendPendingAsync(_eb, text);
 | 
			
		||||
    // 
 | 
			
		||||
    public Task<IUserMessage> SendErrorAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Error, inter);
 | 
			
		||||
    public Task<IUserMessage> SendConfirmAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Ok, inter);
 | 
			
		||||
    public Task<IUserMessage> SendPendingAsync(string text, NadekoInteraction inter = null)
 | 
			
		||||
        => ctx.Channel.SendAsync(_eb, text, MessageType.Pending, inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ErrorLocalizedAsync(LocStr str)
 | 
			
		||||
        => SendErrorAsync(GetText(str));
 | 
			
		||||
    
 | 
			
		||||
    // localized normal
 | 
			
		||||
    public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendErrorAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> PendingLocalizedAsync(LocStr str)
 | 
			
		||||
        => SendPendingAsync(GetText(str));
 | 
			
		||||
    public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendPendingAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str)
 | 
			
		||||
        => SendConfirmAsync(GetText(str));
 | 
			
		||||
    public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendConfirmAsync(GetText(str), inter);
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str)
 | 
			
		||||
    // localized replies
 | 
			
		||||
    public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str)
 | 
			
		||||
    public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
 | 
			
		||||
 | 
			
		||||
    public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str)
 | 
			
		||||
    public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
 | 
			
		||||
        => SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}");
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								src/NadekoBot/Db/Models/BankUser.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/NadekoBot/Db/Models/BankUser.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
using NadekoBot.Services.Database.Models;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Db.Models;
 | 
			
		||||
 | 
			
		||||
public class BankUser : DbEntity
 | 
			
		||||
{
 | 
			
		||||
    public ulong UserId { get; set; }
 | 
			
		||||
    public long Balance { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -51,6 +51,8 @@ public abstract class NadekoContext : DbContext
 | 
			
		||||
    public DbSet<AutoTranslateUser> AutoTranslateUsers { get; set; }
 | 
			
		||||
 | 
			
		||||
    public DbSet<Permissionv2> Permissions { get; set; }
 | 
			
		||||
    
 | 
			
		||||
    public DbSet<BankUser> BankUsers { get; set; }
 | 
			
		||||
 | 
			
		||||
    #region Mandatory Provider-Specific Values
 | 
			
		||||
 | 
			
		||||
@@ -402,6 +404,12 @@ public abstract class NadekoContext : DbContext
 | 
			
		||||
            x.ChannelId,
 | 
			
		||||
            x.UserId
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        #region BANK
 | 
			
		||||
 | 
			
		||||
        modelBuilder.Entity<BankUser>(bu => bu.HasIndex(x => x.UserId).IsUnique());
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if DEBUG
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3458
									
								
								src/NadekoBot/Migrations/MySql/20220429044757_bank.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										3458
									
								
								src/NadekoBot/Migrations/MySql/20220429044757_bank.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										42
									
								
								src/NadekoBot/Migrations/MySql/20220429044757_bank.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/NadekoBot/Migrations/MySql/20220429044757_bank.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Metadata;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Migrations.Mysql
 | 
			
		||||
{
 | 
			
		||||
    public partial class bank : Migration
 | 
			
		||||
    {
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "bankusers",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
                {
 | 
			
		||||
                    id = table.Column<int>(type: "int", nullable: false)
 | 
			
		||||
                        .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
 | 
			
		||||
                    userid = table.Column<ulong>(type: "bigint unsigned", nullable: false),
 | 
			
		||||
                    balance = table.Column<long>(type: "bigint", nullable: false),
 | 
			
		||||
                    dateadded = table.Column<DateTime>(type: "datetime(6)", nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("pk_bankusers", x => x.id);
 | 
			
		||||
                })
 | 
			
		||||
                .Annotation("MySql:CharSet", "utf8mb4");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "ix_bankusers_userid",
 | 
			
		||||
                table: "bankusers",
 | 
			
		||||
                column: "userid",
 | 
			
		||||
                unique: true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "bankusers");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -19,6 +19,35 @@ namespace NadekoBot.Migrations.Mysql
 | 
			
		||||
                .HasAnnotation("ProductVersion", "6.0.3")
 | 
			
		||||
                .HasAnnotation("Relational:MaxIdentifierLength", 64);
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Db.Models.BankUser", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("int")
 | 
			
		||||
                        .HasColumnName("id");
 | 
			
		||||
 | 
			
		||||
                    b.Property<long>("Balance")
 | 
			
		||||
                        .HasColumnType("bigint")
 | 
			
		||||
                        .HasColumnName("balance");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("DateAdded")
 | 
			
		||||
                        .HasColumnType("datetime(6)")
 | 
			
		||||
                        .HasColumnName("dateadded");
 | 
			
		||||
 | 
			
		||||
                    b.Property<ulong>("UserId")
 | 
			
		||||
                        .HasColumnType("bigint unsigned")
 | 
			
		||||
                        .HasColumnName("userid");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id")
 | 
			
		||||
                        .HasName("pk_bankusers");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("UserId")
 | 
			
		||||
                        .IsUnique()
 | 
			
		||||
                        .HasDatabaseName("ix_bankusers_userid");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("bankusers", (string)null);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("ClubId")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3600
									
								
								src/NadekoBot/Migrations/Postgresql/20220429044808_bank.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										3600
									
								
								src/NadekoBot/Migrations/Postgresql/20220429044808_bank.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										41
									
								
								src/NadekoBot/Migrations/Postgresql/20220429044808_bank.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/NadekoBot/Migrations/Postgresql/20220429044808_bank.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Migrations.PostgreSql
 | 
			
		||||
{
 | 
			
		||||
    public partial class bank : Migration
 | 
			
		||||
    {
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "bankusers",
 | 
			
		||||
                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),
 | 
			
		||||
                    balance = table.Column<long>(type: "bigint", nullable: false),
 | 
			
		||||
                    dateadded = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("pk_bankusers", x => x.id);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "ix_bankusers_userid",
 | 
			
		||||
                table: "bankusers",
 | 
			
		||||
                column: "userid",
 | 
			
		||||
                unique: true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "bankusers");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,6 +22,37 @@ namespace NadekoBot.Migrations.PostgreSql
 | 
			
		||||
 | 
			
		||||
            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Db.Models.BankUser", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("integer")
 | 
			
		||||
                        .HasColumnName("id");
 | 
			
		||||
 | 
			
		||||
                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
 | 
			
		||||
 | 
			
		||||
                    b.Property<long>("Balance")
 | 
			
		||||
                        .HasColumnType("bigint")
 | 
			
		||||
                        .HasColumnName("balance");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("DateAdded")
 | 
			
		||||
                        .HasColumnType("timestamp with time zone")
 | 
			
		||||
                        .HasColumnName("dateadded");
 | 
			
		||||
 | 
			
		||||
                    b.Property<decimal>("UserId")
 | 
			
		||||
                        .HasColumnType("numeric(20,0)")
 | 
			
		||||
                        .HasColumnName("userid");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id")
 | 
			
		||||
                        .HasName("pk_bankusers");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("UserId")
 | 
			
		||||
                        .IsUnique()
 | 
			
		||||
                        .HasDatabaseName("ix_bankusers_userid");
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("bankusers", (string)null);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("ClubId")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2782
									
								
								src/NadekoBot/Migrations/Sqlite/20220428051304_bank.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2782
									
								
								src/NadekoBot/Migrations/Sqlite/20220428051304_bank.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										40
									
								
								src/NadekoBot/Migrations/Sqlite/20220428051304_bank.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/NadekoBot/Migrations/Sqlite/20220428051304_bank.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Migrations
 | 
			
		||||
{
 | 
			
		||||
    public partial class bank : Migration
 | 
			
		||||
    {
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.CreateTable(
 | 
			
		||||
                name: "BankUsers",
 | 
			
		||||
                columns: table => new
 | 
			
		||||
                {
 | 
			
		||||
                    Id = table.Column<int>(type: "INTEGER", nullable: false)
 | 
			
		||||
                        .Annotation("Sqlite:Autoincrement", true),
 | 
			
		||||
                    UserId = table.Column<ulong>(type: "INTEGER", nullable: false),
 | 
			
		||||
                    Balance = table.Column<long>(type: "INTEGER", nullable: false),
 | 
			
		||||
                    DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("PK_BankUsers", x => x.Id);
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.CreateIndex(
 | 
			
		||||
                name: "IX_BankUsers_UserId",
 | 
			
		||||
                table: "BankUsers",
 | 
			
		||||
                column: "UserId",
 | 
			
		||||
                unique: true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DropTable(
 | 
			
		||||
                name: "BankUsers");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -17,6 +17,29 @@ namespace NadekoBot.Migrations
 | 
			
		||||
#pragma warning disable 612, 618
 | 
			
		||||
            modelBuilder.HasAnnotation("ProductVersion", "6.0.3");
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Db.Models.BankUser", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("Id")
 | 
			
		||||
                        .ValueGeneratedOnAdd()
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<long>("Balance")
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime?>("DateAdded")
 | 
			
		||||
                        .HasColumnType("TEXT");
 | 
			
		||||
 | 
			
		||||
                    b.Property<ulong>("UserId")
 | 
			
		||||
                        .HasColumnType("INTEGER");
 | 
			
		||||
 | 
			
		||||
                    b.HasKey("Id");
 | 
			
		||||
 | 
			
		||||
                    b.HasIndex("UserId")
 | 
			
		||||
                        .IsUnique();
 | 
			
		||||
 | 
			
		||||
                    b.ToTable("BankUsers");
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            modelBuilder.Entity("NadekoBot.Db.Models.ClubApplicants", b =>
 | 
			
		||||
                {
 | 
			
		||||
                    b.Property<int>("ClubId")
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/NadekoBot/Modules/Gambling/Bank/BankCommands.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
using NadekoBot.Modules.Gambling.Bank;
 | 
			
		||||
using NadekoBot.Modules.Gambling.Common;
 | 
			
		||||
using NadekoBot.Modules.Gambling.Services;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Gambling;
 | 
			
		||||
 | 
			
		||||
// todo .h [group] should show commands in that group 
 | 
			
		||||
public partial class Gambling
 | 
			
		||||
{
 | 
			
		||||
    [Name("Bank")]
 | 
			
		||||
    [Group("bank")]
 | 
			
		||||
    public partial class BankCommands : GamblingModule<IBankService>
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IBankService _bank;
 | 
			
		||||
 | 
			
		||||
        public BankCommands(GamblingConfigService gcs, IBankService bank) : base(gcs)
 | 
			
		||||
        {
 | 
			
		||||
            _bank = bank;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        public async partial Task BankDeposit(ShmartNumber amount)
 | 
			
		||||
        {
 | 
			
		||||
            if (amount <= 0)
 | 
			
		||||
                return;
 | 
			
		||||
            
 | 
			
		||||
            if (await _bank.DepositAsync(ctx.User.Id, amount))
 | 
			
		||||
            {
 | 
			
		||||
                await ReplyConfirmLocalizedAsync(strs.bank_deposited(N(amount)));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        public async partial Task BankWithdraw(ShmartNumber amount)
 | 
			
		||||
        {
 | 
			
		||||
            if (amount <= 0)
 | 
			
		||||
                return;
 | 
			
		||||
            
 | 
			
		||||
            if (await _bank.WithdrawAsync(ctx.User.Id, amount))
 | 
			
		||||
            {
 | 
			
		||||
                await ReplyConfirmLocalizedAsync(strs.bank_withdrew(N(amount)));
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                await ReplyErrorLocalizedAsync(strs.bank_withdraw_insuff(CurrencySign));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        [Cmd]
 | 
			
		||||
        public async partial Task BankBalance()
 | 
			
		||||
        {
 | 
			
		||||
            var bal = await _bank.GetBalanceAsync(ctx.User.Id);
 | 
			
		||||
 | 
			
		||||
            await ReplyConfirmLocalizedAsync(strs.bank_balance(N(bal)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								src/NadekoBot/Modules/Gambling/Bank/BankService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/NadekoBot/Modules/Gambling/Bank/BankService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,77 @@
 | 
			
		||||
using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
 | 
			
		||||
namespace NadekoBot.Modules.Gambling.Bank;
 | 
			
		||||
 | 
			
		||||
public sealed class BankService : IBankService, INService
 | 
			
		||||
{
 | 
			
		||||
    private readonly ICurrencyService _cur;
 | 
			
		||||
    private readonly DbService _db;
 | 
			
		||||
 | 
			
		||||
    public BankService(ICurrencyService cur, DbService db)
 | 
			
		||||
    {
 | 
			
		||||
        _cur = cur;
 | 
			
		||||
        _db = db;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> DepositAsync(ulong userId, long amount)
 | 
			
		||||
    {
 | 
			
		||||
        if (amount <= 0)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(amount));
 | 
			
		||||
        
 | 
			
		||||
        if (!await _cur.RemoveAsync(userId, amount, new("bank", "deposit")))
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        await using var ctx = _db.GetDbContext();
 | 
			
		||||
        await ctx.BankUsers
 | 
			
		||||
                 .ToLinqToDBTable()
 | 
			
		||||
                 .InsertOrUpdateAsync(() => new()
 | 
			
		||||
                     {
 | 
			
		||||
                         UserId = userId,
 | 
			
		||||
                         Balance = amount
 | 
			
		||||
                     },
 | 
			
		||||
                     (old) => new()
 | 
			
		||||
                     {
 | 
			
		||||
                         Balance = old.Balance + amount
 | 
			
		||||
                     },
 | 
			
		||||
                     () => new()
 | 
			
		||||
                     {
 | 
			
		||||
                         UserId = userId
 | 
			
		||||
                     });
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<bool> WithdrawAsync(ulong userId, long amount)
 | 
			
		||||
    {
 | 
			
		||||
        if (amount <= 0)
 | 
			
		||||
            throw new ArgumentOutOfRangeException(nameof(amount));
 | 
			
		||||
        
 | 
			
		||||
        await using var ctx = _db.GetDbContext();
 | 
			
		||||
        var rows = await ctx.BankUsers
 | 
			
		||||
                 .ToLinqToDBTable()
 | 
			
		||||
                 .Where(x => x.UserId == userId && x.Balance >= amount)
 | 
			
		||||
                 .UpdateAsync((old) => new()
 | 
			
		||||
                 {
 | 
			
		||||
                     Balance = old.Balance - amount
 | 
			
		||||
                 });
 | 
			
		||||
 | 
			
		||||
        if (rows > 0)
 | 
			
		||||
        {
 | 
			
		||||
            await _cur.AddAsync(userId, amount, new("bank", "withdraw"));
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public async Task<long> GetBalanceAsync(ulong userId)
 | 
			
		||||
    {
 | 
			
		||||
        await using var ctx = _db.GetDbContext();
 | 
			
		||||
        return (await ctx.BankUsers
 | 
			
		||||
                         .ToLinqToDBTable()
 | 
			
		||||
                         .FirstOrDefaultAsync(x => x.UserId == userId))
 | 
			
		||||
               ?.Balance
 | 
			
		||||
               ?? 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								src/NadekoBot/Modules/Gambling/Bank/IBankService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Modules/Gambling/Bank/IBankService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
namespace NadekoBot.Modules.Gambling.Bank;
 | 
			
		||||
 | 
			
		||||
public interface IBankService
 | 
			
		||||
{
 | 
			
		||||
    Task<bool> DepositAsync(ulong userId, long amount);
 | 
			
		||||
    Task<bool> WithdrawAsync(ulong userId, long amount);
 | 
			
		||||
    Task<long> GetBalanceAsync(ulong userId);
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ using LinqToDB;
 | 
			
		||||
using LinqToDB.EntityFrameworkCore;
 | 
			
		||||
using NadekoBot.Db;
 | 
			
		||||
using NadekoBot.Db.Models;
 | 
			
		||||
using NadekoBot.Modules.Gambling.Bank;
 | 
			
		||||
using NadekoBot.Modules.Gambling.Common;
 | 
			
		||||
using NadekoBot.Modules.Gambling.Services;
 | 
			
		||||
using NadekoBot.Services.Currency;
 | 
			
		||||
@@ -40,6 +41,7 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
    private readonly NumberFormatInfo _enUsCulture;
 | 
			
		||||
    private readonly DownloadTracker _tracker;
 | 
			
		||||
    private readonly GamblingConfigService _configService;
 | 
			
		||||
    private readonly IBankService _bank;
 | 
			
		||||
 | 
			
		||||
    private IUserMessage rdMsg;
 | 
			
		||||
 | 
			
		||||
@@ -49,13 +51,16 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        IDataCache cache,
 | 
			
		||||
        DiscordSocketClient client,
 | 
			
		||||
        DownloadTracker tracker,
 | 
			
		||||
        GamblingConfigService configService)
 | 
			
		||||
        GamblingConfigService configService,
 | 
			
		||||
        IBankService bank)
 | 
			
		||||
        : base(configService)
 | 
			
		||||
    {
 | 
			
		||||
        _db = db;
 | 
			
		||||
        _cs = currency;
 | 
			
		||||
        _cache = cache;
 | 
			
		||||
        _client = client;
 | 
			
		||||
        _bank = bank;
 | 
			
		||||
        
 | 
			
		||||
        _enUsCulture = new CultureInfo("en-US", false).NumberFormat;
 | 
			
		||||
        _enUsCulture.NumberDecimalDigits = 0;
 | 
			
		||||
        _enUsCulture.NumberGroupSeparator = " ";
 | 
			
		||||
@@ -312,6 +317,31 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
            _ => $"{type.Titleize()} - {subType.Titleize()}"
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public sealed class CashInteraction : NadekoInteraction
 | 
			
		||||
    {
 | 
			
		||||
        public override string Name
 | 
			
		||||
            => "CASH_OPEN_BANK";
 | 
			
		||||
 | 
			
		||||
        public override IEmote Emote
 | 
			
		||||
            => new Emoji("🏦");
 | 
			
		||||
 | 
			
		||||
        public CashInteraction(
 | 
			
		||||
            [NotNull] DiscordSocketClient client,
 | 
			
		||||
            ulong authorId,
 | 
			
		||||
            Func<SocketMessageComponent, Task> onAction)
 | 
			
		||||
            : base(client, authorId, onAction)
 | 
			
		||||
        {
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static CashInteraction Create(
 | 
			
		||||
            DiscordSocketClient client,
 | 
			
		||||
            ulong userId,
 | 
			
		||||
            Func<SocketMessageComponent, Task> onAction)
 | 
			
		||||
            => new(client, userId, onAction);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [Priority(0)]
 | 
			
		||||
    public async partial Task Cash(ulong userId)
 | 
			
		||||
@@ -319,14 +349,30 @@ public partial class Gambling : GamblingModule<GamblingService>
 | 
			
		||||
        var cur = await GetBalanceStringAsync(userId);
 | 
			
		||||
        await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), cur));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    private async Task BankAction(SocketMessageComponent smc)
 | 
			
		||||
    {
 | 
			
		||||
        var balance = await _bank.GetBalanceAsync(ctx.User.Id);
 | 
			
		||||
        await smc.RespondAsync(GetText(strs.bank_balance(N(balance))), ephemeral: true);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [Priority(1)]
 | 
			
		||||
    public async partial Task Cash([Leftover] IUser user = null)
 | 
			
		||||
    {
 | 
			
		||||
        user ??= ctx.User;
 | 
			
		||||
        var cur = await GetBalanceStringAsync(user.Id);
 | 
			
		||||
        await ConfirmLocalizedAsync(strs.has(Format.Bold(user.ToString()), cur));
 | 
			
		||||
 | 
			
		||||
        if (user == ctx.User)
 | 
			
		||||
        {
 | 
			
		||||
            var inter = CashInteraction.Create(_client, ctx.User.Id, BankAction);
 | 
			
		||||
            await ConfirmLocalizedAsync(strs.has(Format.Bold(user.ToString()), cur), inter);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            await ConfirmLocalizedAsync(strs.has(Format.Bold(user.ToString()), cur));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
 
 | 
			
		||||
@@ -266,6 +266,20 @@ public partial class Help : NadekoModule<HelpService>
 | 
			
		||||
        await ctx.Channel.EmbedAsync(embed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task Group(ModuleInfo group)
 | 
			
		||||
    {
 | 
			
		||||
        var eb = _eb.Create(ctx)
 | 
			
		||||
                    .WithTitle(GetText(strs.cmd_group_commands(group.Name)))
 | 
			
		||||
                    .WithOkColor();
 | 
			
		||||
 | 
			
		||||
        foreach (var cmd in group.Commands)
 | 
			
		||||
        {
 | 
			
		||||
            eb.AddField(prefix + cmd.Aliases.First(), cmd.RealSummary(_strings, _medusae, Culture, prefix));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ctx.Channel.EmbedAsync(eb);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Cmd]
 | 
			
		||||
    [Priority(0)]
 | 
			
		||||
    public async partial Task H([Leftover] string fail)
 | 
			
		||||
@@ -278,6 +292,20 @@ public partial class Help : NadekoModule<HelpService>
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fail.StartsWith(prefix))
 | 
			
		||||
            fail = fail.Substring(prefix.Length);
 | 
			
		||||
        
 | 
			
		||||
        var group = _cmds.Modules
 | 
			
		||||
                         .SelectMany(x => x.Submodules)
 | 
			
		||||
                         .Where(x => !string.IsNullOrWhiteSpace(x.Group))
 | 
			
		||||
                         .FirstOrDefault(x => x.Group.Equals(fail, StringComparison.InvariantCultureIgnoreCase));
 | 
			
		||||
 | 
			
		||||
        if (group is not null)
 | 
			
		||||
        {
 | 
			
		||||
            await Group(group);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await ReplyErrorLocalizedAsync(strs.command_not_found);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -121,7 +121,7 @@ public static class Extensions
 | 
			
		||||
            args = strings.GetCommandStrings(cmd.Summary, culture).Args;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return args.Map(arg => GetFullUsage(cmd.Name, arg, prefix));
 | 
			
		||||
        return args.Map(arg => GetFullUsage(cmd.Aliases.First(), arg, prefix));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static string GetFullUsage(string commandName, string args, string prefix)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,53 +2,116 @@ namespace NadekoBot.Extensions;
 | 
			
		||||
 | 
			
		||||
public static class MessageChannelExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static Task<IUserMessage> EmbedAsync(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        IEmbedBuilder embed,
 | 
			
		||||
        string msg = "",
 | 
			
		||||
    // main overload that all other send methods reduce to
 | 
			
		||||
    public static Task<IUserMessage> SendAsync(
 | 
			
		||||
        this IMessageChannel channel,
 | 
			
		||||
        string? plainText,
 | 
			
		||||
        Embed? embed = null,
 | 
			
		||||
        IReadOnlyCollection<Embed>? embeds = null,
 | 
			
		||||
        bool sanitizeAll = false,
 | 
			
		||||
        MessageComponent? components = null)
 | 
			
		||||
        => ch.SendMessageAsync(msg,
 | 
			
		||||
            embed: embed.Build(),
 | 
			
		||||
    {
 | 
			
		||||
        plainText = sanitizeAll
 | 
			
		||||
            ? plainText?.SanitizeAllMentions() ?? ""
 | 
			
		||||
            : plainText?.SanitizeMentions() ?? "";
 | 
			
		||||
 | 
			
		||||
        return channel.SendMessageAsync(plainText,
 | 
			
		||||
            embed: embed,
 | 
			
		||||
            embeds: embeds is null
 | 
			
		||||
                ? null
 | 
			
		||||
                : embeds as Embed[] ?? embeds.ToArray(),
 | 
			
		||||
            components: components,
 | 
			
		||||
            options: new()
 | 
			
		||||
            {
 | 
			
		||||
                RetryMode = RetryMode.AlwaysRetry
 | 
			
		||||
            });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static async Task<IUserMessage> SendAsync(
 | 
			
		||||
        this IMessageChannel channel,
 | 
			
		||||
        string? plainText,
 | 
			
		||||
        NadekoInteraction? inter,
 | 
			
		||||
        Embed? embed = null,
 | 
			
		||||
        IReadOnlyCollection<Embed>? embeds = null,
 | 
			
		||||
        bool sanitizeAll = false)
 | 
			
		||||
    {
 | 
			
		||||
        var msg = await channel.SendAsync(plainText,
 | 
			
		||||
            embed,
 | 
			
		||||
            embeds,
 | 
			
		||||
            sanitizeAll,
 | 
			
		||||
            inter?.CreateComponent());
 | 
			
		||||
        
 | 
			
		||||
        if (inter is not null)
 | 
			
		||||
            await inter.RunAsync(msg);
 | 
			
		||||
 | 
			
		||||
        return msg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Task<IUserMessage> SendAsync(
 | 
			
		||||
        this IMessageChannel channel,
 | 
			
		||||
        string? plainText,
 | 
			
		||||
        Embed? embed = null,
 | 
			
		||||
        Embed[]? embeds = null,
 | 
			
		||||
        SmartText text,
 | 
			
		||||
        bool sanitizeAll = false)
 | 
			
		||||
    {
 | 
			
		||||
        plainText = sanitizeAll ? plainText?.SanitizeAllMentions() ?? "" : plainText?.SanitizeMentions() ?? "";
 | 
			
		||||
 | 
			
		||||
        return channel.SendMessageAsync(plainText, embed: embed, embeds: embeds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Task<IUserMessage> SendAsync(this IMessageChannel channel, SmartText text, bool sanitizeAll = false)
 | 
			
		||||
        => text switch
 | 
			
		||||
        {
 | 
			
		||||
            SmartEmbedText set => channel.SendAsync(set.PlainText, set.GetEmbed().Build(), sanitizeAll: sanitizeAll),
 | 
			
		||||
            SmartPlainText st => channel.SendAsync(st.Text, null, sanitizeAll: sanitizeAll),
 | 
			
		||||
            SmartEmbedText set => channel.SendAsync(set.PlainText,
 | 
			
		||||
                set.GetEmbed().Build(),
 | 
			
		||||
                sanitizeAll: sanitizeAll),
 | 
			
		||||
            SmartPlainText st => channel.SendAsync(st.Text,
 | 
			
		||||
                default(Embed),
 | 
			
		||||
                sanitizeAll: sanitizeAll),
 | 
			
		||||
            SmartEmbedTextArray arr => channel.SendAsync(arr.Content,
 | 
			
		||||
                embeds: arr.GetEmbedBuilders().Map(e => e.Build())),
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(text))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    // this is a huge problem, because now i don't have
 | 
			
		||||
    // access to embed builder service
 | 
			
		||||
    // as this is an extension of the message channel
 | 
			
		||||
    public static Task<IUserMessage> SendErrorAsync(
 | 
			
		||||
    public static Task<IUserMessage> EmbedAsync(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        IEmbedBuilder? embed,
 | 
			
		||||
        string plainText = "",
 | 
			
		||||
        IReadOnlyCollection<IEmbedBuilder>? embeds = null,
 | 
			
		||||
        NadekoInteraction? inter = null)
 | 
			
		||||
        => ch.SendAsync(plainText,
 | 
			
		||||
            inter,
 | 
			
		||||
            embed: embed?.Build(),
 | 
			
		||||
            embeds: embeds?.Map(x => x.Build()));
 | 
			
		||||
    
 | 
			
		||||
    public static Task<IUserMessage> SendAsync(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        string text,
 | 
			
		||||
        MessageType type,
 | 
			
		||||
        NadekoInteraction? inter = null)
 | 
			
		||||
    {
 | 
			
		||||
        var builder = eb.Create().WithDescription(text);
 | 
			
		||||
 | 
			
		||||
        builder = (type switch
 | 
			
		||||
        {
 | 
			
		||||
            MessageType.Error => builder.WithErrorColor(),
 | 
			
		||||
            MessageType.Ok => builder.WithOkColor(),
 | 
			
		||||
            MessageType.Pending => builder.WithPendingColor(),
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(type))
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return ch.EmbedAsync(builder, inter: inter);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // regular send overloads
 | 
			
		||||
    public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, IEmbedBuilderService eb, string text)
 | 
			
		||||
        => ch.SendAsync(eb, text, MessageType.Error);
 | 
			
		||||
    
 | 
			
		||||
    public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, IEmbedBuilderService eb, string text)
 | 
			
		||||
        => ch.SendAsync(eb, text, MessageType.Ok);
 | 
			
		||||
    
 | 
			
		||||
    public static Task<IUserMessage> SendAsync(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        MessageType type,
 | 
			
		||||
        string title,
 | 
			
		||||
        string error,
 | 
			
		||||
        string text,
 | 
			
		||||
        string? url = null,
 | 
			
		||||
        string? footer = null)
 | 
			
		||||
    {
 | 
			
		||||
        var embed = eb.Create().WithErrorColor().WithDescription(error).WithTitle(title);
 | 
			
		||||
        var embed = eb.Create().WithDescription(text).WithTitle(title);
 | 
			
		||||
 | 
			
		||||
        if (url is not null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
 | 
			
		||||
            embed.WithUrl(url);
 | 
			
		||||
@@ -56,15 +119,19 @@ public static class MessageChannelExtensions
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(footer))
 | 
			
		||||
            embed.WithFooter(footer);
 | 
			
		||||
 | 
			
		||||
        return ch.SendMessageAsync("", embed: embed.Build());
 | 
			
		||||
        embed = type switch
 | 
			
		||||
        {
 | 
			
		||||
            MessageType.Error => embed.WithErrorColor(),
 | 
			
		||||
            MessageType.Ok => embed.WithOkColor(),
 | 
			
		||||
            MessageType.Pending => embed.WithPendingColor(),
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(type))
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        return ch.EmbedAsync(embed);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Task<IUserMessage> SendErrorAsync(this IMessageChannel ch, IEmbedBuilderService eb, string error)
 | 
			
		||||
        => ch.SendMessageAsync("", embed: eb.Create().WithErrorColor().WithDescription(error).Build());
 | 
			
		||||
 | 
			
		||||
    public static Task<IUserMessage> SendPendingAsync(this IMessageChannel ch, IEmbedBuilderService eb, string message)
 | 
			
		||||
        => ch.SendMessageAsync("", embed: eb.Create().WithPendingColor().WithDescription(message).Build());
 | 
			
		||||
 | 
			
		||||
    // embed title and optional footer overloads
 | 
			
		||||
    
 | 
			
		||||
    public static Task<IUserMessage> SendConfirmAsync(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
@@ -72,21 +139,19 @@ public static class MessageChannelExtensions
 | 
			
		||||
        string text,
 | 
			
		||||
        string? url = null,
 | 
			
		||||
        string? footer = null)
 | 
			
		||||
    {
 | 
			
		||||
        var embed = eb.Create().WithOkColor().WithDescription(text).WithTitle(title);
 | 
			
		||||
 | 
			
		||||
        if (url is not null && Uri.IsWellFormedUriString(url, UriKind.Absolute))
 | 
			
		||||
            embed.WithUrl(url);
 | 
			
		||||
 | 
			
		||||
        if (!string.IsNullOrWhiteSpace(footer))
 | 
			
		||||
            embed.WithFooter(footer);
 | 
			
		||||
 | 
			
		||||
        return ch.SendMessageAsync("", embed: embed.Build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Task<IUserMessage> SendConfirmAsync(this IMessageChannel ch, IEmbedBuilderService eb, string text)
 | 
			
		||||
        => ch.SendMessageAsync("", embed: eb.Create().WithOkColor().WithDescription(text).Build());
 | 
			
		||||
        => ch.SendAsync(eb, MessageType.Ok, title, text, url, footer);
 | 
			
		||||
    
 | 
			
		||||
    public static Task<IUserMessage> SendErrorAsync(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        IEmbedBuilderService eb,
 | 
			
		||||
        string title,
 | 
			
		||||
        string text,
 | 
			
		||||
        string? url = null,
 | 
			
		||||
        string? footer = null)
 | 
			
		||||
        => ch.SendAsync(eb, MessageType.Error, title, text, url, footer);
 | 
			
		||||
 | 
			
		||||
    // weird stuff
 | 
			
		||||
    
 | 
			
		||||
    public static Task<IUserMessage> SendTableAsync<T>(
 | 
			
		||||
        this IMessageChannel ch,
 | 
			
		||||
        string seed,
 | 
			
		||||
@@ -142,7 +207,7 @@ public static class MessageChannelExtensions
 | 
			
		||||
 | 
			
		||||
        var component = new ComponentBuilder()
 | 
			
		||||
                        .WithButton(new ButtonBuilder()
 | 
			
		||||
                                    .WithStyle(ButtonStyle.Secondary)
 | 
			
		||||
                                    .WithStyle(ButtonStyle.Primary)
 | 
			
		||||
                                    .WithCustomId(BUTTON_LEFT)
 | 
			
		||||
                                    .WithDisabled(lastPage == 0)
 | 
			
		||||
                                    .WithEmote(_arrowLeft))
 | 
			
		||||
@@ -164,6 +229,9 @@ public static class MessageChannelExtensions
 | 
			
		||||
 | 
			
		||||
                if (smc.Message.Id != msg.Id)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                if (smc.Data.CustomId != BUTTON_LEFT && smc.Data.CustomId != BUTTON_RIGHT)
 | 
			
		||||
                    return;
 | 
			
		||||
                
 | 
			
		||||
                await si.DeferAsync();
 | 
			
		||||
                if (smc.User.Id != ctx.User.Id)
 | 
			
		||||
@@ -210,12 +278,36 @@ public static class MessageChannelExtensions
 | 
			
		||||
        await msg.ModifyAsync(mp => mp.Components = new ComponentBuilder().Build());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static readonly Emoji _okEmoji = new Emoji("✅");
 | 
			
		||||
    private static readonly Emoji _warnEmoji = new Emoji("⚠️");
 | 
			
		||||
    private static readonly Emoji _errorEmoji = new Emoji("❌");
 | 
			
		||||
    
 | 
			
		||||
    public static Task ReactAsync(this ICommandContext ctx, MessageType type)
 | 
			
		||||
    {
 | 
			
		||||
        var emoji = type switch
 | 
			
		||||
        {
 | 
			
		||||
            MessageType.Error => _errorEmoji,
 | 
			
		||||
            MessageType.Pending => _warnEmoji,
 | 
			
		||||
            MessageType.Ok => _okEmoji,
 | 
			
		||||
            _ => throw new ArgumentOutOfRangeException(nameof(type)),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return ctx.Message.AddReactionAsync(emoji);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Task OkAsync(this ICommandContext ctx)
 | 
			
		||||
        => ctx.Message.AddReactionAsync(new Emoji("✅"));
 | 
			
		||||
        => ctx.ReactAsync(MessageType.Ok);
 | 
			
		||||
 | 
			
		||||
    public static Task ErrorAsync(this ICommandContext ctx)
 | 
			
		||||
        => ctx.Message.AddReactionAsync(new Emoji("❌"));
 | 
			
		||||
        => ctx.ReactAsync(MessageType.Error);
 | 
			
		||||
 | 
			
		||||
    public static Task WarningAsync(this ICommandContext ctx)
 | 
			
		||||
        => ctx.Message.AddReactionAsync(new Emoji("⚠️"));
 | 
			
		||||
        => ctx.ReactAsync(MessageType.Pending);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum MessageType
 | 
			
		||||
{
 | 
			
		||||
    Ok,
 | 
			
		||||
    Pending,
 | 
			
		||||
    Error
 | 
			
		||||
}
 | 
			
		||||
@@ -519,7 +519,7 @@ streamoffline:
 | 
			
		||||
- sto
 | 
			
		||||
- stoff
 | 
			
		||||
streamonlinedelete:
 | 
			
		||||
- streamonlinedelte
 | 
			
		||||
- streamonlinedelete
 | 
			
		||||
- stondel
 | 
			
		||||
streammessage:
 | 
			
		||||
- streammsg
 | 
			
		||||
@@ -1285,4 +1285,16 @@ medusalist:
 | 
			
		||||
  - melist
 | 
			
		||||
medusainfo:
 | 
			
		||||
  - medusainfo
 | 
			
		||||
  - meinfo
 | 
			
		||||
  - meinfo
 | 
			
		||||
bankdeposit:
 | 
			
		||||
  - deposit
 | 
			
		||||
  - d
 | 
			
		||||
  - dep
 | 
			
		||||
bankwithdraw:
 | 
			
		||||
  - withdraw
 | 
			
		||||
  - w
 | 
			
		||||
  - with
 | 
			
		||||
bankbalance:
 | 
			
		||||
  - balance
 | 
			
		||||
  - b
 | 
			
		||||
  - bal
 | 
			
		||||
 
 | 
			
		||||
@@ -2185,4 +2185,15 @@ medusalist:
 | 
			
		||||
    Read about the medusa system [here](https://nadekobot.readthedocs.io/en/latest/medusa/creating-a-medusa/)
 | 
			
		||||
  args:
 | 
			
		||||
    - ""
 | 
			
		||||
  
 | 
			
		||||
bankdeposit:
 | 
			
		||||
  desc: "Deposits the specified amount of currency into the bank for later use."
 | 
			
		||||
  args:
 | 
			
		||||
    - "50"
 | 
			
		||||
bankwithdraw:
 | 
			
		||||
  desc: "Withdraws the specified amount of currency from the bank if available."
 | 
			
		||||
  args:
 | 
			
		||||
    - "49"
 | 
			
		||||
bankbalance:
 | 
			
		||||
  desc: "Shows your current bank balance available for withdrawal."
 | 
			
		||||
  args:
 | 
			
		||||
    - ""
 | 
			
		||||
@@ -993,5 +993,10 @@
 | 
			
		||||
  "medusa_unloaded": "Medusa {0} has been unloaded.",
 | 
			
		||||
  "medusa_empty": "Medusa wasn't loaded as it didn't contain any Sneks.",
 | 
			
		||||
  "medusa_already_loaded": "Medusa {0} is already loaded",
 | 
			
		||||
  "medusa_invalid_not_found": "Medusa with that name wasn't found or the file was invalid"
 | 
			
		||||
  "medusa_invalid_not_found": "Medusa with that name wasn't found or the file was invalid",
 | 
			
		||||
  "bank_balance": "You have {0} in your bank account.",
 | 
			
		||||
  "bank_deposited": "You deposited {0} to your bank account.",
 | 
			
		||||
  "bank_withdrew": "You withdrew {0} from your bank account.",
 | 
			
		||||
  "bank_withdraw_insuff": "You don't have sufficient {0} in your bank account.",
 | 
			
		||||
  "cmd_group_commands": "'{0}' command group" 
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user