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

added .btr excl
This commit is contained in:
Toastie 2024-11-28 19:12:37 +13:00
parent 97871f3e47
commit ae338db294
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
18 changed files with 178 additions and 74 deletions

View file

@ -12,6 +12,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.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 EllieBot.Migrations.PostgreSql namespace EllieBot.Migrations.PostgreSql
{ {
[DbContext(typeof(PostgreSqlContext))] [DbContext(typeof(PostgreSqlContext))]
[Migration("20241127120348_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 EllieBot.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 EllieBot.Migrations.PostgreSql namespace EllieBot.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 EllieBot.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 EllieBot.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 Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace EllieBot.Migrations namespace EllieBot.Migrations
{ {
[DbContext(typeof(SqliteContext))] [DbContext(typeof(SqliteContext))]
[Migration("20241127120303_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 EllieBot.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 EllieBot.Migrations namespace EllieBot.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 EllieBot.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 EllieBot.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 EllieBot.Db.Models; using EllieBot.Common.TypeReaders.Models;
using EllieBot.Db.Models;
using EllieBot.Modules.Administration.Services; using EllieBot.Modules.Administration.Services;
using System.Text; using System.Text;
using ContextType = Discord.Commands.ContextType; using ContextType = Discord.Commands.ContextType;
@ -7,6 +8,7 @@ namespace EllieBot.Modules.Administration;
public partial class Administration public partial class Administration
{ {
[Group("btr")]
public partial class ButtonRoleCommands : EllieModule<ButtonRolesService> public partial class ButtonRoleCommands : EllieModule<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 : IEService, 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 : IEService, 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 : IEService, 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 - remall
- 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,6 +1130,8 @@
"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.",