change: gambling commands now show amount bet. Slightly changed the layout. Updated some gambling strings

add: added .btr excl
This commit is contained in:
Kwoth
2024-11-25 06:08:04 +00:00
parent 96ce7e23bb
commit 75d0eb631d
19 changed files with 184 additions and 80 deletions

View File

@@ -12,6 +12,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except da
- `.btr list` to list all button roles on the server - `.btr list` to list all button roles on the server
- `.btr rm` to remove a button role from the specified message - `.btr rm` to remove a button role from the specified message
- `.btr rma` to remove all button roles on the specified message - `.btr rma` to remove all button roles on the specified message
- `.btr excl` to toggle exclusive button roles (only 1 role per message or any number)
- Use `.h` on any of the above for more info - Use `.h` on any of the above for more info
- Added `.wrongsong` which will delete the last queued song. - Added `.wrongsong` which will delete the last queued song.
- Useful in case you made a mistake, or the bot queued a wrong song - Useful in case you made a mistake, or the bot queued a wrong song

View File

@@ -22,4 +22,6 @@ public sealed class ButtonRole
[MaxLength(50)] [MaxLength(50)]
public string Label { get; set; } = string.Empty; public string Label { get; set; } = string.Empty;
public bool Exclusive { get; set; }
} }

View File

@@ -12,8 +12,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace NadekoBot.Migrations.PostgreSql namespace NadekoBot.Migrations.PostgreSql
{ {
[DbContext(typeof(PostgreSqlContext))] [DbContext(typeof(PostgreSqlContext))]
[Migration("20241119135917_guildcolors")] [Migration("20241126033634_btnroles_guildcolors")]
partial class guildcolors partial class btnroles_guildcolors
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -479,6 +479,10 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("character varying(100)") .HasColumnType("character varying(100)")
.HasColumnName("emote"); .HasColumnName("emote");
b.Property<bool>("Exclusive")
.HasColumnType("boolean")
.HasColumnName("exclusive");
b.Property<decimal>("GuildId") b.Property<decimal>("GuildId")
.HasColumnType("numeric(20,0)") .HasColumnType("numeric(20,0)")
.HasColumnName("guildid"); .HasColumnName("guildid");

View File

@@ -6,7 +6,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace NadekoBot.Migrations.PostgreSql namespace NadekoBot.Migrations.PostgreSql
{ {
/// <inheritdoc /> /// <inheritdoc />
public partial class guildcolors : Migration public partial class btnroles_guildcolors : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
@@ -24,7 +24,8 @@ namespace NadekoBot.Migrations.PostgreSql
position = table.Column<int>(type: "integer", nullable: false), position = table.Column<int>(type: "integer", nullable: false),
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false), roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
emote = table.Column<string>(type: "character varying(100)", maxLength: 100, 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) label = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
exclusive = table.Column<bool>(type: "boolean", nullable: false)
}, },
constraints: table => constraints: table =>
{ {

View File

@@ -476,6 +476,10 @@ namespace NadekoBot.Migrations.PostgreSql
.HasColumnType("character varying(100)") .HasColumnType("character varying(100)")
.HasColumnName("emote"); .HasColumnName("emote");
b.Property<bool>("Exclusive")
.HasColumnType("boolean")
.HasColumnName("exclusive");
b.Property<decimal>("GuildId") b.Property<decimal>("GuildId")
.HasColumnType("numeric(20,0)") .HasColumnType("numeric(20,0)")
.HasColumnName("guildid"); .HasColumnName("guildid");

View File

@@ -11,8 +11,8 @@ using NadekoBot.Db;
namespace NadekoBot.Migrations namespace NadekoBot.Migrations
{ {
[DbContext(typeof(SqliteContext))] [DbContext(typeof(SqliteContext))]
[Migration("20241119135908_guildcolors")] [Migration("20241126033626_btnroles_guildcolors")]
partial class guildcolors partial class btnroles_guildcolors
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -357,6 +357,9 @@ namespace NadekoBot.Migrations
.HasMaxLength(100) .HasMaxLength(100)
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool>("Exclusive")
.HasColumnType("INTEGER");
b.Property<ulong>("GuildId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");

View File

@@ -5,7 +5,7 @@
namespace NadekoBot.Migrations namespace NadekoBot.Migrations
{ {
/// <inheritdoc /> /// <inheritdoc />
public partial class guildcolors : Migration public partial class btnroles_guildcolors : Migration
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
@@ -23,7 +23,8 @@ namespace NadekoBot.Migrations
Position = table.Column<int>(type: "INTEGER", nullable: false), Position = table.Column<int>(type: "INTEGER", nullable: false),
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false), RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
Emote = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false), Emote = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
Label = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false) Label = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false),
Exclusive = table.Column<bool>(type: "INTEGER", nullable: false)
}, },
constraints: table => constraints: table =>
{ {

View File

@@ -354,6 +354,9 @@ namespace NadekoBot.Migrations
.HasMaxLength(100) .HasMaxLength(100)
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<bool>("Exclusive")
.HasColumnType("INTEGER");
b.Property<ulong>("GuildId") b.Property<ulong>("GuildId")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");

View File

@@ -1,4 +1,5 @@
using NadekoBot.Db.Models; using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Db.Models;
using NadekoBot.Modules.Administration.Services; using NadekoBot.Modules.Administration.Services;
using System.Text; using System.Text;
using ContextType = Discord.Commands.ContextType; using ContextType = Discord.Commands.ContextType;
@@ -7,6 +8,7 @@ namespace NadekoBot.Modules.Administration;
public partial class Administration public partial class Administration
{ {
[Group("btr")]
public partial class ButtonRoleCommands : NadekoModule<ButtonRolesService> public partial class ButtonRoleCommands : NadekoModule<ButtonRolesService>
{ {
private List<ActionRowBuilder> GetActionRows(IReadOnlyList<ButtonRole> roles) private List<ActionRowBuilder> GetActionRows(IReadOnlyList<ButtonRole> roles)
@@ -265,5 +267,30 @@ public partial class Administration
}) })
.SendAsync(); .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().Confirm(strs.btnrole_exclusive).SendAsync();
}
else
{
await Response().Confirm(strs.btnrole_multiple).SendAsync();
}
}
} }
} }

View File

@@ -42,32 +42,49 @@ public sealed class ButtonRolesService : INService, IReadyExecutor
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
await using var uow = _db.GetDbContext(); try
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); await using var uow = _db.GetDbContext();
return; var buttonRole = await uow.GetTable<ButtonRole>()
} .Where(x => x.ButtonId == smc.Data.CustomId && x.MessageId == smc.Message.Id)
.FirstOrDefaultAsyncLinqToDB();
await user.AddRoleAsync(role.Id); 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);
}
}); });
} }
@@ -108,7 +125,8 @@ public sealed class ButtonRolesService : INService, IReadyExecutor
: 1, : 1,
Emote = emoteStr, Emote = emoteStr,
Label = string.Empty, Label = string.Empty,
ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}" ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}",
Exclusive = (uow.GetTable<ButtonRole>().Where(x => x.GuildId == guildId && x.MessageId == messageId).All(x => x.Exclusive))
}, },
_ => new() _ => new()
{ {
@@ -151,4 +169,16 @@ public sealed class ButtonRolesService : INService, IReadyExecutor
.OrderBy(x => x.Id) .OrderBy(x => x.Id)
.ToListAsyncLinqToDB(); .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;
}
} }

View File

@@ -57,7 +57,7 @@ public partial class Gambling
i.Dispose(); i.Dispose();
var eb = CreateEmbed() var eb = CreateEmbed()
.WithOkColor(); .WithOkColor();
var toSend = string.Empty; var toSend = string.Empty;
if (cardObjects.Count == 5) if (cardObjects.Count == 5)
@@ -172,13 +172,14 @@ public partial class Gambling
} }
var eb = 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();

View File

@@ -85,10 +85,10 @@ public partial class Gambling
: Format.Bold(GetText(strs.tails)))); : Format.Bold(GetText(strs.tails))));
var eb = 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(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();
} }
} }
} }

View File

@@ -718,7 +718,7 @@ 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
{ {
@@ -728,7 +728,9 @@ public partial class Gambling : GamblingModule<GamblingService>
var eb = 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)
.AddField(GetText(strs.bet), N(amount), true)
.AddField(GetText(strs.won), N((long)result.Won), true)
.WithOkColor(); .WithOkColor();
await Response().Embed(eb).SendAsync(); await Response().Embed(eb).SendAsync();
@@ -922,8 +924,8 @@ public partial class Gambling : GamblingModule<GamblingService>
var eb = 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);

View File

@@ -66,10 +66,10 @@ public partial class Gambling
var eb = 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;
} }

View File

@@ -1499,18 +1499,22 @@ snipe:
- snipe - snipe
- sn - sn
btnroleadd: btnroleadd:
- btnradd - add
- btnra - a
btnroleremove: btnroleremove:
- btnrrem - rem
- btnrr - r
- btnrrm - rm
btnroleremoveall: btnroleremoveall:
- btnrremall - removeall
- btnrra - rma
btnrolelist: btnrolelist:
- btnrlist - list
- btnrl - l
- ls
btnroleexclusive:
- excl
- e
wrongsong: wrongsong:
- wrongsong - wrongsong
- wrongtrack - wrongtrack

View File

@@ -4769,6 +4769,19 @@ btnrolelist:
- '' - ''
params: params:
- { } - { }
btnroleexclusive:
desc: |-
Toggles whether button roles are exclusive or not.
If enabled, users can only pick one role from the buttons per message.
If disabled, users can pick any number of roles.
ex:
- '123123123 enable'
- '123123123 disable'
params:
- message:
desc: "A message link or id of the message"
enable:
desc: "Whether to enable or disable exclusive button roles"
wrongsong: wrongsong:
desc: |- desc: |-
Removes the last queued song. Removes the last queued song.

View File

@@ -238,10 +238,10 @@
"sb_user": "User soft-banned", "sb_user": "User soft-banned",
"awarded": "{2} has awarded {0} to {1}", "awarded": "{2} has awarded {0} to {1}",
"better_luck": "Better luck next time ^_^", "better_luck": "Better luck next time ^_^",
"br_win": "Congratulations! You won {0} for rolling above {1}", "betroll_win": "Congratulations! You rolled above {0}",
"deck_reshuffled": "Deck reshuffled.", "deck_reshuffled": "Deck reshuffled.",
"flipped": "Flipped {0}", "flipped": "Flipped {0}",
"flip_guess": "You guessed it! You won {0}", "betflip_guess": "You guessed it!",
"flip_invalid": "Invalid number specified. You can flip 1 to {0} coins.", "flip_invalid": "Invalid number specified. You can flip 1 to {0} coins.",
"flip_results": "Flipped {0} coins. {1} heads, {2} tails.", "flip_results": "Flipped {0} coins. {1} heads, {2} tails.",
"cards_left": "{0} cards left in the deck.", "cards_left": "{0} cards left in the deck.",
@@ -266,7 +266,8 @@
"available_tests": "Available Tests", "available_tests": "Available Tests",
"test_results_for": "Test results for {0}", "test_results_for": "Test results for {0}",
"won": "Won", "won": "Won",
"multiplier": "Multiplier", "bet": "Bet",
"multi": "Multi",
"tails": "Tail", "tails": "Tail",
"take": "successfully took {0} from {1}", "take": "successfully took {0} from {1}",
"take_fail": "was unable to take {0} from {1} because the user doesn't have that much {2}!", "take_fail": "was unable to take {0} from {1} because the user doesn't have that much {2}!",
@@ -1129,8 +1130,10 @@
"btnrole_none": "There are no button roles on this page.", "btnrole_none": "There are no button roles on this page.",
"btnrole_removeall_not_found": "Button role successfully removed but message wasn't found.", "btnrole_removeall_not_found": "Button role successfully removed but message wasn't found.",
"btnrole_removed": "Button role removed.", "btnrole_removed": "Button role removed.",
"btnrole_exclusive": "Users can now pick only one of the roles from that message.",
"btnrole_multiple": "Users can now pick any number of button roles from that message.",
"no_last_queued_found": "No last queued track found.", "no_last_queued_found": "No last queued track found.",
"wrongsong_success": "Oops! Wrong song removed: {0}", "wrongsong_success": "Oops! Wrong song removed: {0}",
"server_not_found": "Server not found.", "server_not_found": "Server not found.",
"server_color_set": "Successfully set a new server color.", "server_color_set": "Successfully set a new server color."
} }

View File

@@ -300,7 +300,7 @@
"hangman_running": "Hangman game already running on this channel.", "hangman_running": "Hangman game already running on this channel.",
"hangman_start_errored": "Starting hangman errored.", "hangman_start_errored": "Starting hangman errored.",
"hangman_types": "List of \u0022{0}hangman\u0022 term types:", "hangman_types": "List of \u0022{0}hangman\u0022 term types:",
"picked": "picked {0}", "picked": "{0} picked {1}",
"planted": "{0} planted {1}", "planted": "{0} planted {1}",
"trivia_already_running": "Trivia game is already running on this server.", "trivia_already_running": "Trivia game is already running on this server.",
"trivia_game": "Trivia Game", "trivia_game": "Trivia Game",