.btr and .sclr added, cleanup

This commit is contained in:
Toastie 2024-11-28 01:06:01 +13:00
parent 97094dbe5d
commit b411e8cb25
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
119 changed files with 9181 additions and 664 deletions

View file

@ -2,6 +2,59 @@
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
## [5.2.0] - 28.11.2024
### Added
- Added `.todo undone` command to unmark a todo as done
- Added Button Roles!
- `.btr a` to add a button role to the specified message
- `.btr list` to list all button roles on the server
- `.btr rm` to remove a button role from the specified message
- `.btr rma` to remove all button roles on the specified message
- Use `.h` on any of the above for more info
- Added `.wrongsong` which will delete the last queued song.
- Useful in case you made a mistake, or the bot queued a wrong song
- It will reset after a shuffle or fairplay toggle, or similar events.
- Added Server color Commands!
- Every Server can now set their own colors for ok/error/pending embed (the default green/red/yellow color on the left side of the message the bot sends)
- Use `.h .sclr` to see the list of commands
- `.sclr show` will show the current server colors
- `.sclr ok <color hex>` to set ok color
- `.sclr warn <color hex>` to set warn color
- `.sclr error <color hex>` to set error color
### Changed
- Self Assigned Roles reworked! Use `.h .sar` for the list of commands
- `.sar autodel`
- Toggles the automatic deletion of the user's message and Nadeko's confirmations for .iam and .iamn commands.
- `.sar ad`
- Adds a role to the list of self-assignable roles. You can also specify a group.
- If 'Exclusive self-assignable roles' feature is enabled (.sar exclusive), users will be able to pick one role per group.
- `.sar groupname`
- Sets a self assignable role group name. Provide no name to remove.
- `.sar remove`
- Removes a specified role from the list of self-assignable roles.
- `.sar list`
- Lists self-assignable roles. Shows 20 roles per page.
- `.sar exclusive`
- Toggles whether self-assigned roles are exclusive. While enabled, users can only have one self-assignable role per group.
- `.sar rolelvlreq`
- Set a level requirement on a self-assignable role.
- `.sar grouprolereq`
- Set a role that users have to have in order to assign a self-assignable role from the specified group.
- `.sar groupdelete`
- Deletes a self-assignable role group
- `.iam` and `.iamn` are unchanged
- Removed patron limits from Reaction Roles. Anyone can have as many reros as they like.
- `.timely` captcha made stronger and cached per user.
- `.bsreset` price reduced by 90%
### Fixed
- Fixed `.sinfo` for servers on other shard
## [5.1.20] - 13.11.2024 ## [5.1.20] - 13.11.2024
### Added ### Added

View file

@ -74,6 +74,30 @@ public abstract class EllieContext : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
#region GuildColors
modelBuilder.Entity<GuildColors>()
.HasIndex(x => x.GuildId)
.IsUnique(true);
#endregion
#region Button Roles
modelBuilder.Entity<ButtonRole>(br =>
{
br.HasIndex(x => x.GuildId)
.IsUnique(false);
br.HasAlternateKey(x => new
{
x.RoleId,
x.MessageId,
});
});
#endregion
#region New Sar #region New Sar
modelBuilder.Entity<SarGroup>(sg => modelBuilder.Entity<SarGroup>(sg =>

View file

@ -5,14 +5,15 @@ namespace EllieBot.Db.Models;
public class GuildColors public class GuildColors
{ {
[Key] [Key]
public int Id { get; set; }
public ulong GuildId { get; set; } public ulong GuildId { get; set; }
[Length(0, 9)] [MaxLength(9)]
public string? OkColor { get; set; } public string? OkColor { get; set; }
[Length(0, 9)] [MaxLength(9)]
public string? ErrorColor { get; set; } public string? ErrorColor { get; set; }
[Length(0, 9)] [MaxLength(9)]
public string? PendingColor { get; set; } public string? PendingColor { get; set; }
} }

View file

@ -13,24 +13,6 @@ public class GuildConfig : DbEntity
public string AutoAssignRoleIds { get; set; } public string AutoAssignRoleIds { get; set; }
// //greet stuff
// public int AutoDeleteGreetMessagesTimer { get; set; } = 30;
// public int AutoDeleteByeMessagesTimer { get; set; } = 30;
//
// public ulong GreetMessageChannelId { get; set; }
// public ulong ByeMessageChannelId { get; set; }
//
// public bool SendDmGreetMessage { get; set; }
// public string DmGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
//
// public bool SendChannelGreetMessage { get; set; }
// public string ChannelGreetMessageText { get; set; } = "Welcome to the %server% server, %user%!";
//
// public bool SendChannelByeMessage { get; set; }
// public string ChannelByeMessageText { get; set; } = "%user% has left!";
// public bool SendBoostMessage { get; set; }
// pulic int BoostMessageDeleteAfter { get; set; }
//todo FUTURE: DELETE, UNUSED //todo FUTURE: DELETE, UNUSED
public bool ExclusiveSelfAssignedRoles { get; set; } public bool ExclusiveSelfAssignedRoles { get; set; }
public bool AutoDeleteSelfAssignedRoleMessages { get; set; } public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
@ -97,9 +79,5 @@ public class GuildConfig : DbEntity
public bool DisableGlobalExpressions { get; set; } = false; public bool DisableGlobalExpressions { get; set; } = false;
#region Boost Message
public bool StickyRoles { get; set; } public bool StickyRoles { get; set; }
#endregion
} }

View file

@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
namespace EllieBot.Db.Models;
public sealed class ButtonRole
{
[Key]
public int Id { get; set; }
[MaxLength(200)]
public string ButtonId { get; set; } = string.Empty;
public ulong GuildId { get; set; }
public ulong ChannelId { get; set; }
public ulong MessageId { get; set; }
public int Position { get; set; }
public ulong RoleId { get; set; }
[MaxLength(100)]
public string Emote { get; set; } = string.Empty;
[MaxLength(50)]
public string Label { get; set; } = string.Empty;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,73 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace EllieBot.Migrations.PostgreSql
{
/// <inheritdoc />
public partial class guildcolors : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "buttonrole",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
buttonid = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
channelid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
messageid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
position = table.Column<int>(type: "integer", nullable: false),
roleid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
emote = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
label = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("pk_buttonrole", x => x.id);
table.UniqueConstraint("ak_buttonrole_roleid_messageid", x => new { x.roleid, x.messageid });
});
migrationBuilder.CreateTable(
name: "guildcolors",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
guildid = table.Column<decimal>(type: "numeric(20,0)", nullable: false),
okcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true),
errorcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true),
pendingcolor = table.Column<string>(type: "character varying(9)", maxLength: 9, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_guildcolors", x => x.id);
});
migrationBuilder.CreateIndex(
name: "ix_buttonrole_guildid",
table: "buttonrole",
column: "guildid");
migrationBuilder.CreateIndex(
name: "ix_guildcolors_guildid",
table: "guildcolors",
column: "guildid",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "buttonrole");
migrationBuilder.DropTable(
name: "guildcolors");
}
}
}

View file

@ -451,6 +451,65 @@ namespace EllieBot.Migrations.PostgreSql
b.ToTable("blacklist", (string)null); b.ToTable("blacklist", (string)null);
}); });
modelBuilder.Entity("EllieBot.Db.Models.ButtonRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ButtonId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)")
.HasColumnName("buttonid");
b.Property<decimal>("ChannelId")
.HasColumnType("numeric(20,0)")
.HasColumnName("channelid");
b.Property<string>("Emote")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)")
.HasColumnName("emote");
b.Property<decimal>("GuildId")
.HasColumnType("numeric(20,0)")
.HasColumnName("guildid");
b.Property<string>("Label")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)")
.HasColumnName("label");
b.Property<decimal>("MessageId")
.HasColumnType("numeric(20,0)")
.HasColumnName("messageid");
b.Property<int>("Position")
.HasColumnType("integer")
.HasColumnName("position");
b.Property<decimal>("RoleId")
.HasColumnType("numeric(20,0)")
.HasColumnName("roleid");
b.HasKey("Id")
.HasName("pk_buttonrole");
b.HasAlternateKey("RoleId", "MessageId")
.HasName("ak_buttonrole_roleid_messageid");
b.HasIndex("GuildId")
.HasDatabaseName("ix_buttonrole_guildid");
b.ToTable("buttonrole", (string)null);
});
modelBuilder.Entity("EllieBot.Db.Models.ClubApplicants", b => modelBuilder.Entity("EllieBot.Db.Models.ClubApplicants", b =>
{ {
b.Property<int>("ClubId") b.Property<int>("ClubId")
@ -1253,6 +1312,44 @@ namespace EllieBot.Migrations.PostgreSql
b.ToTable("giveawayuser", (string)null); b.ToTable("giveawayuser", (string)null);
}); });
modelBuilder.Entity("EllieBot.Db.Models.GuildColors", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ErrorColor")
.HasMaxLength(9)
.HasColumnType("character varying(9)")
.HasColumnName("errorcolor");
b.Property<decimal>("GuildId")
.HasColumnType("numeric(20,0)")
.HasColumnName("guildid");
b.Property<string>("OkColor")
.HasMaxLength(9)
.HasColumnType("character varying(9)")
.HasColumnName("okcolor");
b.Property<string>("PendingColor")
.HasMaxLength(9)
.HasColumnType("character varying(9)")
.HasColumnName("pendingcolor");
b.HasKey("Id")
.HasName("pk_guildcolors");
b.HasIndex("GuildId")
.IsUnique()
.HasDatabaseName("ix_guildcolors_guildid");
b.ToTable("guildcolors", (string)null);
});
modelBuilder.Entity("EllieBot.Db.Models.GuildConfig", b => modelBuilder.Entity("EllieBot.Db.Models.GuildConfig", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EllieBot.Migrations
{
/// <inheritdoc />
public partial class guildcolors : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ButtonRole",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
ButtonId = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
ChannelId = table.Column<ulong>(type: "INTEGER", nullable: false),
MessageId = table.Column<ulong>(type: "INTEGER", nullable: false),
Position = table.Column<int>(type: "INTEGER", nullable: false),
RoleId = table.Column<ulong>(type: "INTEGER", nullable: false),
Emote = table.Column<string>(type: "TEXT", maxLength: 100, nullable: false),
Label = table.Column<string>(type: "TEXT", maxLength: 50, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ButtonRole", x => x.Id);
table.UniqueConstraint("AK_ButtonRole_RoleId_MessageId", x => new { x.RoleId, x.MessageId });
});
migrationBuilder.CreateTable(
name: "GuildColors",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
GuildId = table.Column<ulong>(type: "INTEGER", nullable: false),
OkColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true),
ErrorColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true),
PendingColor = table.Column<string>(type: "TEXT", maxLength: 9, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_GuildColors", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_ButtonRole_GuildId",
table: "ButtonRole",
column: "GuildId");
migrationBuilder.CreateIndex(
name: "IX_GuildColors_GuildId",
table: "GuildColors",
column: "GuildId",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ButtonRole");
migrationBuilder.DropTable(
name: "GuildColors");
}
}
}

View file

@ -335,6 +335,51 @@ namespace EllieBot.Migrations
b.ToTable("Blacklist"); b.ToTable("Blacklist");
}); });
modelBuilder.Entity("EllieBot.Db.Models.ButtonRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ButtonId")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.Property<ulong>("ChannelId")
.HasColumnType("INTEGER");
b.Property<string>("Emote")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("TEXT");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<string>("Label")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("TEXT");
b.Property<ulong>("MessageId")
.HasColumnType("INTEGER");
b.Property<int>("Position")
.HasColumnType("INTEGER");
b.Property<ulong>("RoleId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasAlternateKey("RoleId", "MessageId");
b.HasIndex("GuildId");
b.ToTable("ButtonRole");
});
modelBuilder.Entity("EllieBot.Db.Models.ClubApplicants", b => modelBuilder.Entity("EllieBot.Db.Models.ClubApplicants", b =>
{ {
b.Property<int>("ClubId") b.Property<int>("ClubId")
@ -932,6 +977,35 @@ namespace EllieBot.Migrations
b.ToTable("GiveawayUser"); b.ToTable("GiveawayUser");
}); });
modelBuilder.Entity("EllieBot.Db.Models.GuildColors", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ErrorColor")
.HasMaxLength(9)
.HasColumnType("TEXT");
b.Property<ulong>("GuildId")
.HasColumnType("INTEGER");
b.Property<string>("OkColor")
.HasMaxLength(9)
.HasColumnType("TEXT");
b.Property<string>("PendingColor")
.HasMaxLength(9)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("GuildId")
.IsUnique();
b.ToTable("GuildColors");
});
modelBuilder.Entity("EllieBot.Db.Models.GuildConfig", b => modelBuilder.Entity("EllieBot.Db.Models.GuildConfig", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")

View file

@ -96,7 +96,7 @@ public partial class Administration : EllieModule<AdministrationService>
var guild = (SocketGuild)ctx.Guild; var guild = (SocketGuild)ctx.Guild;
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id); var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.server_delmsgoncmd)) .WithTitle(GetText(strs.server_delmsgoncmd))
.WithDescription(enabled ? "✅" : "❌"); .WithDescription(enabled ? "✅" : "❌");

View file

@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using EllieBot.Modules.Administration._common.results; using EllieBot.Modules.Administration._common.results;
namespace EllieBot.Modules.Administration.Services; namespace EllieBot.Modules.Administration;
public class AdministrationService : IEService public class AdministrationService : IEService
{ {

View file

@ -254,6 +254,12 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, IEService
.Contains(x.ServerId)) .Contains(x.ServerId))
.DeleteAsync(); .DeleteAsync();
// delete button roles
await ctx.GetTable<ButtonRole>()
.Where(x => !tempTable.Select(x => x.GuildId)
.Contains(x.GuildId))
.DeleteAsync();
return new() return new()
{ {
GuildCount = guildIds.Keys.Count, GuildCount = guildIds.Keys.Count,

View file

@ -42,9 +42,9 @@ public partial class Administration
.Page((items, _) => .Page((items, _) =>
{ {
if (!items.Any()) if (!items.Any())
return _sender.CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-"); return CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithFooter(sql) .WithFooter(sql)
.WithTitle(string.Join(" ║ ", result.ColumnNames)) .WithTitle(string.Join(" ║ ", result.ColumnNames))
@ -99,7 +99,7 @@ public partial class Administration
{ {
try try
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle(GetText(strs.sql_confirm_exec)) .WithTitle(GetText(strs.sql_confirm_exec))
.WithDescription(Format.Code(sql)); .WithDescription(Format.Code(sql));
@ -119,7 +119,7 @@ public partial class Administration
[OwnerOnly] [OwnerOnly]
public async Task PurgeUser(ulong userId) public async Task PurgeUser(ulong userId)
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString())))); .WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
if (!await PromptUserConfirmAsync(embed)) if (!await PromptUserConfirmAsync(embed))

View file

@ -123,7 +123,7 @@ public partial class Administration
[Cmd] [Cmd]
public async Task LanguagesList() public async Task LanguagesList()
=> await Response().Embed(_sender.CreateEmbed() => await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.lang_list)) .WithTitle(GetText(strs.lang_list))
.WithDescription(string.Join("\n", .WithDescription(string.Join("\n",

View file

@ -122,7 +122,7 @@ public class MuteService : IEService
return; return;
_ = Task.Run(() => _sender.Response(user) _ = Task.Run(() => _sender.Response(user)
.Embed(_sender.CreateEmbed() .Embed(_sender.CreateEmbed(user?.GuildId)
.WithDescription($"You've been muted in {user.Guild} server") .WithDescription($"You've been muted in {user.Guild} server")
.AddField("Mute Type", type.ToString()) .AddField("Mute Type", type.ToString())
.AddField("Moderator", mod.ToString()) .AddField("Moderator", mod.ToString())
@ -140,7 +140,7 @@ public class MuteService : IEService
return; return;
_ = Task.Run(() => _sender.Response(user) _ = Task.Run(() => _sender.Response(user)
.Embed(_sender.CreateEmbed() .Embed(_sender.CreateEmbed(user.GuildId)
.WithDescription($"You've been unmuted in {user.Guild} server") .WithDescription($"You've been unmuted in {user.Guild} server")
.AddField("Unmute Type", type.ToString()) .AddField("Unmute Type", type.ToString())
.AddField("Moderator", mod.ToString()) .AddField("Moderator", mod.ToString())

View file

@ -36,7 +36,7 @@ public partial class Administration
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task DiscordPermOverrideReset() public async Task DiscordPermOverrideReset()
{ {
var result = await PromptUserConfirmAsync(_sender.CreateEmbed() var result = await PromptUserConfirmAsync(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText(strs.perm_override_all_confirm))); .WithDescription(GetText(strs.perm_override_all_confirm)));
@ -65,7 +65,7 @@ public partial class Administration
.CurrentPage(page) .CurrentPage(page)
.Page((items, _) => .Page((items, _) =>
{ {
var eb = _sender.CreateEmbed().WithTitle(GetText(strs.perm_overrides)).WithOkColor(); var eb = CreateEmbed().WithTitle(GetText(strs.perm_overrides)).WithOkColor();
if (items.Count == 0) if (items.Count == 0)
eb.WithDescription(GetText(strs.perm_override_page_none)); eb.WithDescription(GetText(strs.perm_override_page_none));

View file

@ -241,7 +241,7 @@ public partial class Administration
return; return;
} }
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.prot_active)); var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.prot_active));
if (spam is not null) if (spam is not null)
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true); embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);

View file

@ -113,7 +113,7 @@ public partial class Administration
{ {
await progressMsg.ModifyAsync(props => await progressMsg.ModifyAsync(props =>
{ {
props.Embed = _sender.CreateEmbed() props.Embed = CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithDescription(GetText(strs.prune_progress(deleted, total))) .WithDescription(GetText(strs.prune_progress(deleted, total)))
.Build(); .Build();

View file

@ -0,0 +1,269 @@
using EllieBot.Db.Models;
using EllieBot.Modules.Administration.Services;
using System.Text;
using ContextType = Discord.Commands.ContextType;
namespace EllieBot.Modules.Administration;
public partial class Administration
{
public partial class ButtonRoleCommands : EllieModule<ButtonRolesService>
{
private List<ActionRowBuilder> GetActionRows(IReadOnlyList<ButtonRole> roles)
{
var rows = roles.Select((x, i) => (Index: i, ButtonRole: x))
.GroupBy(x => x.Index / 5)
.Select(x => x.Select(y => y.ButtonRole))
.Select(x =>
{
var ab = new ActionRowBuilder()
.WithComponents(x.Select(y =>
{
var curRole = ctx.Guild.GetRole(y.RoleId);
var label = string.IsNullOrWhiteSpace(y.Label)
? curRole?.ToString() ?? "?missing " + y.RoleId
: y.Label;
var btnEmote = EmoteTypeReader.TryParse(y.Emote, out var e)
? e
: null;
return new ButtonBuilder()
.WithCustomId(y.ButtonId)
.WithEmote(btnEmote)
.WithLabel(label)
.WithStyle(ButtonStyle.Secondary)
.Build() as IMessageComponent;
})
.ToList());
return ab;
})
.ToList();
return rows;
}
private async Task<MessageLink?> CreateMessageLinkAsync(ulong messageId)
{
var msg = await ctx.Channel.GetMessageAsync(messageId);
if (msg is null)
return null;
return new MessageLink(ctx.Guild, ctx.Channel, msg);
}
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public async Task BtnRoleAdd(ulong messageId, IEmote emote, [Leftover] IRole role)
{
var link = await CreateMessageLinkAsync(messageId);
if (link is null)
{
await Response().Error(strs.invalid_message_id).SendAsync();
return;
}
await BtnRoleAdd(link, emote, role);
}
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public async Task BtnRoleAdd(MessageLink link, IEmote emote, [Leftover] IRole role)
{
if (link.Message is not IUserMessage msg || !msg.IsAuthor(ctx.Client))
{
await Response().Error(strs.invalid_message_link).SendAsync();
return;
}
if (!await CheckRoleHierarchy(role))
{
await Response().Error(strs.hierarchy).SendAsync();
return;
}
var success = await _service.AddButtonRole(ctx.Guild.Id, link.Channel.Id, role.Id, link.Message.Id, emote);
if (!success)
{
await Response().Error(strs.btnrole_message_max).SendAsync();
return;
}
var roles = await _service.GetButtonRoles(ctx.Guild.Id, link.Message.Id);
var rows = GetActionRows(roles);
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().WithRows(rows).Build()));
await ctx.OkAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public Task BtnRoleRemove(ulong messageId, IRole role)
=> BtnRoleRemove(messageId, role.Id);
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public Task BtnRoleRemove(MessageLink link, IRole role)
=> BtnRoleRemove(link.Message.Id, role.Id);
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public Task BtnRoleRemove(MessageLink link, ulong roleId)
=> BtnRoleRemove(link.Message.Id, roleId);
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public async Task BtnRoleRemove(ulong messageId, ulong roleId)
{
var removed = await _service.RemoveButtonRole(ctx.Guild.Id, messageId, roleId);
if (removed is null)
{
await Response().Error(strs.btnrole_not_found).SendAsync();
return;
}
var roles = await _service.GetButtonRoles(ctx.Guild.Id, messageId);
var ch = await ctx.Guild.GetTextChannelAsync(removed.ChannelId);
if (ch is null)
{
await Response().Error(strs.btnrole_removeall_not_found).SendAsync();
return;
}
var msg = await ch.GetMessageAsync(removed.MessageId) as IUserMessage;
if (msg is null)
{
await Response().Error(strs.btnrole_removeall_not_found).SendAsync();
return;
}
var rows = GetActionRows(roles);
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().WithRows(rows).Build()));
await Response().Confirm(strs.btnrole_removed).SendAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public Task BtnRoleRemoveAll(MessageLink link)
=> BtnRoleRemoveAll(link.Message.Id);
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public async Task BtnRoleRemoveAll(ulong messageId)
{
var succ = await _service.RemoveButtonRoles(ctx.Guild.Id, messageId);
if (succ.Count == 0)
{
await Response().Error(strs.btnrole_not_found).SendAsync();
return;
}
var info = succ[0];
var ch = await ctx.Guild.GetTextChannelAsync(info.ChannelId);
if (ch is null)
{
await Response().Pending(strs.btnrole_removeall_not_found).SendAsync();
return;
}
var msg = await ch.GetMessageAsync(info.MessageId) as IUserMessage;
if (msg is null)
{
await Response().Pending(strs.btnrole_removeall_not_found).SendAsync();
return;
}
await msg.ModifyAsync(x => x.Components = new(new ComponentBuilder().Build()));
await ctx.OkAsync();
}
[Cmd]
[RequireContext(ContextType.Guild)]
[BotPerm(GuildPerm.ManageRoles)]
[RequireUserPermission(GuildPerm.ManageRoles)]
public async Task BtnRoleList()
{
var btnRoles = await _service.GetButtonRoles(ctx.Guild.Id, null);
var groups = btnRoles
.GroupBy(x => (x.ChannelId, x.MessageId))
.ToList();
await Response()
.Paginated()
.Items(groups)
.PageSize(1)
.AddFooter(false)
.Page(async (items, page) =>
{
var eb = CreateEmbed()
.WithOkColor();
var item = items.FirstOrDefault();
if (item == default)
{
eb.WithPendingColor()
.WithDescription(GetText(strs.btnrole_none));
return eb;
}
var (cid, msgId) = item.Key;
var str = new StringBuilder();
var ch = await ctx.Client.GetChannelAsync(cid) as IMessageChannel;
str.AppendLine($"Channel: {ch?.ToString() ?? cid.ToString()}");
str.AppendLine($"Message: {msgId}");
if (ch is not null)
{
var msg = await ch.GetMessageAsync(msgId);
if (msg is not null)
{
str.AppendLine(new MessageLink(ctx.Guild, ch, msg).ToString());
}
}
str.AppendLine("---");
foreach (var x in item.AsEnumerable())
{
var role = ctx.Guild.GetRole(x.RoleId);
str.AppendLine($"{x.Emote} {(role?.ToString() ?? x.RoleId.ToString())}");
}
eb.WithDescription(str.ToString());
return eb;
})
.SendAsync();
}
}
}

View file

@ -0,0 +1,154 @@
using LinqToDB;
using LinqToDB.EntityFrameworkCore;
using LinqToDB.SqlQuery;
using EllieBot.Common.ModuleBehaviors;
using EllieBot.Db.Models;
using NCalc;
namespace EllieBot.Modules.Administration.Services;
public sealed class ButtonRolesService : IEService, IReadyExecutor
{
private const string BTN_PREFIX = "n:btnrole:";
private readonly DbService _db;
private readonly DiscordSocketClient _client;
private readonly IBotCreds _creds;
public ButtonRolesService(IBotCreds creds, DiscordSocketClient client, DbService db)
{
_creds = creds;
_client = client;
_db = db;
}
public Task OnReadyAsync()
{
_client.InteractionCreated += OnInteraction;
return Task.CompletedTask;
}
private async Task OnInteraction(SocketInteraction inter)
{
if (inter is not SocketMessageComponent smc)
return;
if (!smc.Data.CustomId.StartsWith(BTN_PREFIX))
return;
await inter.DeferAsync();
_ = Task.Run(async () =>
{
await using var uow = _db.GetDbContext();
var buttonRole = await uow.GetTable<ButtonRole>()
.Where(x => x.ButtonId == smc.Data.CustomId && x.MessageId == smc.Message.Id)
.FirstOrDefaultAsyncLinqToDB();
if (buttonRole is null)
return;
var guild = _client.GetGuild(buttonRole.GuildId);
if (guild is null)
return;
var role = guild.GetRole(buttonRole.RoleId);
if (role is null)
return;
if (smc.User is not IGuildUser user)
return;
if (user.GetRoles().Any(x => x.Id == role.Id))
{
await user.RemoveRoleAsync(role.Id);
return;
}
await user.AddRoleAsync(role.Id);
});
}
public async Task<bool> AddButtonRole(
ulong guildId,
ulong channelId,
ulong roleId,
ulong messageId,
IEmote emote
)
{
await using var uow = _db.GetDbContext();
// up to 25 per message
if (await uow.GetTable<ButtonRole>()
.Where(x => x.MessageId == messageId)
.CountAsyncLinqToDB()
>= 25)
return false;
var emoteStr = emote.ToString()!;
var guid = Guid.NewGuid();
await uow.GetTable<ButtonRole>()
.InsertOrUpdateAsync(() => new ButtonRole()
{
GuildId = guildId,
ChannelId = channelId,
RoleId = roleId,
MessageId = messageId,
Position =
uow
.GetTable<ButtonRole>()
.Any(x => x.MessageId == messageId)
? uow.GetTable<ButtonRole>()
.Where(x => x.MessageId == messageId)
.Max(x => x.Position)
: 1,
Emote = emoteStr,
Label = string.Empty,
ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}"
},
_ => new()
{
Emote = emoteStr,
Label = string.Empty,
ButtonId = $"{BTN_PREFIX}:{guildId}:{guid}"
},
() => new()
{
RoleId = roleId,
MessageId = messageId,
});
return true;
}
public async Task<IReadOnlyList<ButtonRole>> RemoveButtonRoles(ulong guildId, ulong messageId)
{
await using var uow = _db.GetDbContext();
return await uow.GetTable<ButtonRole>()
.Where(x => x.GuildId == guildId && x.MessageId == messageId)
.DeleteWithOutputAsync();
}
public async Task<ButtonRole?> RemoveButtonRole(ulong guildId, ulong messageId, ulong roleId)
{
await using var uow = _db.GetDbContext();
var deleted = await uow.GetTable<ButtonRole>()
.Where(x => x.GuildId == guildId && x.MessageId == messageId && x.RoleId == roleId)
.DeleteWithOutputAsync();
return deleted.FirstOrDefault();
}
public async Task<IReadOnlyList<ButtonRole>> GetButtonRoles(ulong guildId, ulong? messageId)
{
await using var uow = _db.GetDbContext();
return await uow.GetTable<ButtonRole>()
.Where(x => x.GuildId == guildId && (messageId == null || x.MessageId == messageId))
.OrderBy(x => x.Id)
.ToListAsyncLinqToDB();
}
}

View file

@ -80,7 +80,7 @@ public partial class Administration
.CurrentPage(page) .CurrentPage(page)
.Page((items, _) => .Page((items, _) =>
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor(); .WithOkColor();
var content = string.Empty; var content = string.Empty;

View file

@ -1,8 +1,6 @@
#nullable disable using LinqToDB;
using LinqToDB;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using EllieBot.Common.ModuleBehaviors; using EllieBot.Common.ModuleBehaviors;
using EllieBot.Modules.Patronage;
using EllieBot.Db.Models; using EllieBot.Db.Models;
using OneOf.Types; using OneOf.Types;
using OneOf; using OneOf;
@ -18,18 +16,15 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache; private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache;
private readonly object _cacheLock = new(); private readonly object _cacheLock = new();
private readonly SemaphoreSlim _assignementLock = new(1, 1); private readonly SemaphoreSlim _assignementLock = new(1, 1);
private readonly IPatronageService _ps;
public ReactionRolesService( public ReactionRolesService(
DiscordSocketClient client, DiscordSocketClient client,
IPatronageService ps,
DbService db, DbService db,
IBotCreds creds) IBotCreds creds)
{ {
_db = db; _db = db;
_client = client; _client = client;
_creds = creds; _creds = creds;
_ps = ps;
_cache = new(); _cache = new();
} }
@ -57,7 +52,7 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
var guild = _client.GetGuild(rero.GuildId); var guild = _client.GetGuild(rero.GuildId);
var role = guild?.GetRole(rero.RoleId); var role = guild?.GetRole(rero.RoleId);
if (role is null) if (guild is null || role is null)
return default; return default;
var user = guild.GetUser(userId) as IGuildUser var user = guild.GetUser(userId) as IGuildUser
@ -96,10 +91,11 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
{ {
if (user.RoleIds.Contains(role.Id)) if (user.RoleIds.Contains(role.Id))
{ {
await user.RemoveRoleAsync(role.Id, new RequestOptions() await user.RemoveRoleAsync(role.Id,
{ new RequestOptions()
AuditLogReason = $"Reaction role" {
}); AuditLogReason = $"Reaction role"
});
} }
} }
finally finally
@ -210,10 +206,11 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
} }
} }
await user.AddRoleAsync(role.Id, new() await user.AddRoleAsync(role.Id,
{ new()
AuditLogReason = "Reaction role" {
}); AuditLogReason = "Reaction role"
});
} }
} }
finally finally
@ -244,36 +241,23 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
int levelReq = 0) int levelReq = 0)
{ {
ArgumentOutOfRangeException.ThrowIfNegative(group); ArgumentOutOfRangeException.ThrowIfNegative(group);
ArgumentOutOfRangeException.ThrowIfNegative(levelReq); ArgumentOutOfRangeException.ThrowIfNegative(levelReq);
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
await using var tran = await ctx.Database.BeginTransactionAsync();
var activeReactionRoles = await ctx.GetTable<ReactionRoleV2>()
.Where(x => x.GuildId == guild.Id)
.CountAsync();
var limit = await _ps.GetUserLimit(LimitedFeatureName.ReactionRole, guild.OwnerId);
if (!_creds.IsOwner(guild.OwnerId) && (activeReactionRoles >= limit.Quota && limit.Quota >= 0))
{
return new Error();
}
await ctx.GetTable<ReactionRoleV2>() await ctx.GetTable<ReactionRoleV2>()
.InsertOrUpdateAsync(() => new() .InsertOrUpdateAsync(() => new()
{ {
GuildId = guild.Id, GuildId = guild.Id,
ChannelId = msg.Channel.Id, ChannelId = msg.Channel.Id,
MessageId = msg.Id, MessageId = msg.Id,
Emote = emote, Emote = emote,
RoleId = role.Id, RoleId = role.Id,
Group = group, Group = group,
LevelReq = levelReq LevelReq = levelReq
}, },
(old) => new() (old) => new()
{ {
RoleId = role.Id, RoleId = role.Id,
@ -286,8 +270,6 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
Emote = emote, Emote = emote,
}); });
await tran.CommitAsync();
var obj = new ReactionRoleV2() var obj = new ReactionRoleV2()
{ {
GuildId = guild.Id, GuildId = guild.Id,
@ -380,9 +362,9 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
var updated = ctx.GetTable<ReactionRoleV2>() var updated = ctx.GetTable<ReactionRoleV2>()
.Where(x => x.GuildId == guildId && x.MessageId == fromMessageId) .Where(x => x.GuildId == guildId && x.MessageId == fromMessageId)
.UpdateWithOutput(old => new() .UpdateWithOutput(old => new()
{ {
MessageId = toMessageId MessageId = toMessageId
}, },
(old, neu) => neu); (old, neu) => neu);
lock (_cacheLock) lock (_cacheLock)
{ {

View file

@ -174,12 +174,11 @@ public partial class Administration
[UserPerm(GuildPerm.ManageRoles)] [UserPerm(GuildPerm.ManageRoles)]
[BotPerm(GuildPerm.ManageRoles)] [BotPerm(GuildPerm.ManageRoles)]
[Priority(0)] [Priority(0)]
public async Task RoleColor(Color color, [Leftover] IRole role) public async Task RoleColor(Rgba32 color, [Leftover] IRole role)
{ {
try try
{ {
var rgba32 = color.ToPixel<Rgba32>(); await role.ModifyAsync(r => r.Color = new Discord.Color(color.R, color.G, color.B));
await role.ModifyAsync(r => r.Color = new Discord.Color(rgba32.R, rgba32.G, rgba32.B));
await Response().Confirm(strs.rc(Format.Bold(role.Name))).SendAsync(); await Response().Confirm(strs.rc(Format.Bold(role.Name))).SendAsync();
} }
catch (Exception) catch (Exception)

View file

@ -62,7 +62,7 @@ public partial class Administration
var (added, updated) = await _service.RefreshUsersAsync(users); var (added, updated) = await _service.RefreshUsersAsync(users);
await message.ModifyAsync(x => await message.ModifyAsync(x =>
x.Embed = _sender.CreateEmbed() x.Embed = CreateEmbed()
.WithDescription(GetText(strs.cache_users_done(added, updated))) .WithDescription(GetText(strs.cache_users_done(added, updated)))
.WithOkColor() .WithOkColor()
.Build() .Build()
@ -115,7 +115,7 @@ public partial class Administration
_service.AddNewAutoCommand(cmd); _service.AddNewAutoCommand(cmd);
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.scadd)) .WithTitle(GetText(strs.scadd))
.AddField(GetText(strs.server), .AddField(GetText(strs.server),
@ -343,7 +343,7 @@ public partial class Administration
if (string.IsNullOrWhiteSpace(str)) if (string.IsNullOrWhiteSpace(str))
str = GetText(strs.no_shards_on_page); str = GetText(strs.no_shards_on_page);
return _sender.CreateEmbed().WithOkColor().WithDescription($"{status}\n\n{str}"); return CreateEmbed().WithOkColor().WithDescription($"{status}\n\n{str}");
}) })
.SendAsync(); .SendAsync();
} }

View file

@ -129,25 +129,6 @@ public partial class Administration
_sas = sas; _sas = sas;
} }
protected async Task<bool> CheckRoleHierarchy(IRole role)
{
var botUser = ((SocketGuild)ctx.Guild).CurrentUser;
var ownerId = ctx.Guild.OwnerId;
var modMaxRole = ((IGuildUser)ctx.User).GetRoles().Max(r => r.Position);
var botMaxRole = botUser.GetRoles().Max(r => r.Position);
// role must be lower than the bot role
// and the mod must have a higher role
if (botMaxRole <= role.Position
|| (ctx.User.Id != ownerId && role.Position >= modMaxRole))
{
await Response().Error(strs.hierarchy).SendAsync();
return false;
}
return true;
}
[Cmd] [Cmd]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[UserPerm(GuildPerm.ManageMessages)] [UserPerm(GuildPerm.ManageMessages)]
@ -244,7 +225,7 @@ public partial class Administration
.GroupBy(x => x.SarGroupId) .GroupBy(x => x.SarGroupId)
.OrderBy(x => x.Key); .OrderBy(x => x.Key);
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.self_assign_list(groups.Sum(x => x.Roles.Count)))); .WithTitle(GetText(strs.self_assign_list(groups.Sum(x => x.Roles.Count))));
@ -310,10 +291,21 @@ public partial class Administration
public async Task SarExclusive(int groupNumber) public async Task SarExclusive(int groupNumber)
{ {
var areExclusive = await _service.SetGroupExclusivityAsync(ctx.Guild.Id, groupNumber); var areExclusive = await _service.SetGroupExclusivityAsync(ctx.Guild.Id, groupNumber);
if (areExclusive)
if (areExclusive is null)
{
await Response().Error(strs.sar_group_not_found).SendAsync();
return;
}
if (areExclusive is true)
{
await Response().Confirm(strs.self_assign_excl).SendAsync(); await Response().Confirm(strs.self_assign_excl).SendAsync();
}
else else
{
await Response().Confirm(strs.self_assign_no_excl).SendAsync(); await Response().Confirm(strs.self_assign_no_excl).SendAsync();
}
} }
[Cmd] [Cmd]

View file

@ -129,7 +129,7 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
return changes > 0; return changes > 0;
} }
public async Task<bool> SetGroupExclusivityAsync(ulong guildId, int groupNumber) public async Task<bool?> SetGroupExclusivityAsync(ulong guildId, int groupNumber)
{ {
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var changes = await ctx.GetTable<SarGroup>() var changes = await ctx.GetTable<SarGroup>()
@ -142,8 +142,7 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
if (changes.Length == 0) if (changes.Length == 0)
{ {
// todo group not found return null;
return false;
} }
return changes[0]; return changes[0];
@ -154,7 +153,7 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var group = await ctx.GetTable<SarGroup>() var group = await ctx.GetTable<SarGroup>()
.Where(x => x.Roles.Any(x => x.RoleId == roleId)) .Where(x => x.GuildId == guildId && x.Roles.Any(x => x.RoleId == roleId))
.LoadWith(x => x.Roles) .LoadWith(x => x.Roles)
.FirstOrDefaultAsyncLinqToDB(); .FirstOrDefaultAsyncLinqToDB();

View file

@ -35,7 +35,7 @@ public partial class Administration
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList() var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
?? new List<IgnoredLogItem>(); ?? new List<IgnoredLogItem>();
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField(GetText(strs.log_ignored_channels), .AddField(GetText(strs.log_ignored_channels),
chs.Count == 0 chs.Count == 0

View file

@ -42,7 +42,7 @@ public partial class Administration
.Items(timezoneStrings) .Items(timezoneStrings)
.PageSize(timezonesPerPage) .PageSize(timezonesPerPage)
.CurrentPage(page) .CurrentPage(page)
.Page((items, _) => _sender.CreateEmbed() .Page((items, _) => CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.timezones_available)) .WithTitle(GetText(strs.timezones_available))
.WithDescription(string.Join("\n", items))) .WithDescription(string.Join("\n", items)))

View file

@ -44,7 +44,7 @@ public partial class Administration
try try
{ {
await _sender.Response(user) await _sender.Response(user)
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString()))) .WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
.AddField(GetText(strs.moderator), ctx.User.ToString()) .AddField(GetText(strs.moderator), ctx.User.ToString())
@ -64,7 +64,7 @@ public partial class Administration
catch (Exception ex) catch (Exception ex)
{ {
Log.Warning(ex, "Exception occured while warning a user"); Log.Warning(ex, "Exception occured while warning a user");
var errorEmbed = _sender.CreateEmbed() var errorEmbed = CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.cant_apply_punishment)); .WithDescription(GetText(strs.cant_apply_punishment));
@ -75,7 +75,7 @@ public partial class Administration
return; return;
} }
var embed = _sender.CreateEmbed().WithOkColor(); var embed = CreateEmbed().WithOkColor();
if (punishment is null) if (punishment is null)
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString())))); embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
else else
@ -184,7 +184,7 @@ public partial class Administration
.Page((warnings, page) => .Page((warnings, page) =>
{ {
var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString(); var user = (ctx.Guild as SocketGuild)?.GetUser(userId)?.ToString() ?? userId.ToString();
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user))); var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.warnlog_for(user)));
if (!warnings.Any()) if (!warnings.Any())
embed.WithDescription(GetText(strs.warnings_none)); embed.WithDescription(GetText(strs.warnings_none));
@ -245,7 +245,7 @@ public partial class Administration
+ $" | {total} ({all} - {forgiven})"; + $" | {total} ({all} - {forgiven})";
}); });
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.warnings_list)) .WithTitle(GetText(strs.warnings_list))
.WithDescription(string.Join("\n", ws)); .WithDescription(string.Join("\n", ws));
@ -457,7 +457,7 @@ public partial class Administration
var user = await ctx.Client.GetUserAsync(userId); var user = await ctx.Client.GetUserAsync(userId);
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune); await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
var toSend = _sender.CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true) .AddField(GetText(strs.username), user?.ToString() ?? userId.ToString(), true)
@ -486,7 +486,7 @@ public partial class Administration
await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(userId, banPrune, (ctx.User + " | " + msg).TrimTo(512));
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField("ID", userId.ToString(), true)) .AddField("ID", userId.ToString(), true))
@ -523,7 +523,7 @@ public partial class Administration
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7; var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512)); await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
var toSend = _sender.CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⛔️ " + GetText(strs.banned_user)) .WithTitle("⛔️ " + GetText(strs.banned_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@ -719,7 +719,7 @@ public partial class Administration
{ await ctx.Guild.RemoveBanAsync(user); } { await ctx.Guild.RemoveBanAsync(user); }
catch { await ctx.Guild.RemoveBanAsync(user); } catch { await ctx.Guild.RemoveBanAsync(user); }
var toSend = _sender.CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("☣ " + GetText(strs.sb_user)) .WithTitle("☣ " + GetText(strs.sb_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@ -774,7 +774,7 @@ public partial class Administration
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512)); await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
var toSend = _sender.CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.kicked_user)) .WithTitle(GetText(strs.kicked_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@ -807,7 +807,7 @@ public partial class Administration
{ {
var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg)); var dmMessage = GetText(strs.timeoutdm(Format.Bold(ctx.Guild.Name), msg));
await _sender.Response(user) await _sender.Response(user)
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithDescription(dmMessage)) .WithDescription(dmMessage))
.SendAsync(); .SendAsync();
@ -819,7 +819,7 @@ public partial class Administration
await user.SetTimeOutAsync(time.Time); await user.SetTimeOutAsync(time.Time);
var toSend = _sender.CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("⏳ " + GetText(strs.timedout_user)) .WithTitle("⏳ " + GetText(strs.timedout_user))
.AddField(GetText(strs.username), user.ToString(), true) .AddField(GetText(strs.username), user.ToString(), true)
@ -880,7 +880,7 @@ public partial class Administration
if (string.IsNullOrWhiteSpace(missStr)) if (string.IsNullOrWhiteSpace(missStr))
missStr = "-"; missStr = "-";
var toSend = _sender.CreateEmbed() var toSend = CreateEmbed()
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count))) .WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
.AddField(GetText(strs.invalid(missing.Count)), missStr) .AddField(GetText(strs.invalid(missing.Count)), missStr)
.WithPendingColor(); .WithPendingColor();
@ -900,7 +900,7 @@ public partial class Administration
} }
} }
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed() await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_ban_completed( GetText(strs.mass_ban_completed(
banning.Count()))) banning.Count())))
@ -928,7 +928,7 @@ public partial class Administration
//send a message but don't wait for it //send a message but don't wait for it
var banningMessageTask = Response() var banningMessageTask = Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_kill_in_progress(bans.Count()))) GetText(strs.mass_kill_in_progress(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr) .AddField(GetText(strs.invalid(missing)), missStr)
@ -949,7 +949,7 @@ public partial class Administration
//wait for the message and edit it //wait for the message and edit it
var banningMessage = await banningMessageTask; var banningMessage = await banningMessageTask;
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed() await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
.WithDescription( .WithDescription(
GetText(strs.mass_kill_completed(bans.Count()))) GetText(strs.mass_kill_completed(bans.Count())))
.AddField(GetText(strs.invalid(missing)), missStr) .AddField(GetText(strs.invalid(missing)), missStr)

View file

@ -68,7 +68,7 @@ public partial class Administration
else else
text = GetText(strs.no_vcroles); text = GetText(strs.no_vcroles);
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.vc_role_list)) .WithTitle(GetText(strs.vc_role_list))
.WithDescription(text)).SendAsync(); .WithDescription(text)).SendAsync();

View file

@ -34,7 +34,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message); var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.expr_new)) .WithTitle(GetText(strs.expr_new))
.WithDescription($"#{new kwum(ex.Id)}") .WithDescription($"#{new kwum(ex.Id)}")
@ -104,7 +104,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
if (ex is not null) if (ex is not null)
{ {
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.expr_edited)) .WithTitle(GetText(strs.expr_edited))
.WithDescription($"#{id}") .WithDescription($"#{id}")
@ -159,7 +159,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
: " // " + string.Join(" ", ex.GetReactions()))) : " // " + string.Join(" ", ex.GetReactions())))
.Join('\n'); .Join('\n');
return _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc); return CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
}) })
.SendAsync(); .SendAsync();
} }
@ -179,7 +179,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
await Response() await Response()
.Interaction(IsValidExprEditor() ? inter : null) .Interaction(IsValidExprEditor() ? inter : null)
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription($"#{id}") .WithDescription($"#{id}")
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024)) .AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
@ -224,7 +224,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
if (ex is not null) if (ex is not null)
{ {
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.expr_deleted)) .WithTitle(GetText(strs.expr_deleted))
.WithDescription($"#{id}") .WithDescription($"#{id}")
@ -375,7 +375,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
[UserPerm(GuildPerm.Administrator)] [UserPerm(GuildPerm.Administrator)]
public async Task ExprClear() public async Task ExprClear()
{ {
if (await PromptUserConfirmAsync(_sender.CreateEmbed() if (await PromptUserConfirmAsync(CreateEmbed()
.WithTitle("Expression clear") .WithTitle("Expression clear")
.WithDescription("This will delete all expressions on this server."))) .WithDescription("This will delete all expressions on this server.")))
{ {

View file

@ -74,7 +74,7 @@ public partial class Gambling
if (race.FinishedUsers[0].Bet > 0) if (race.FinishedUsers[0].Bet > 0)
{ {
return Response() return Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.animal_race)) .WithTitle(GetText(strs.animal_race))
.WithDescription(GetText(strs.animal_race_won_money( .WithDescription(GetText(strs.animal_race_won_money(
@ -132,7 +132,7 @@ public partial class Gambling
raceMessage = await Response().Confirm(text).SendAsync(); raceMessage = await Response().Confirm(text).SendAsync();
else else
{ {
await msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed() await msg.ModifyAsync(x => x.Embed = CreateEmbed()
.WithTitle(GetText(strs.animal_race)) .WithTitle(GetText(strs.animal_race))
.WithDescription(text) .WithDescription(text)
.WithOkColor() .WithOkColor()

View file

@ -59,7 +59,7 @@ public partial class Gambling
{ {
var bal = await _bank.GetBalanceAsync(ctx.User.Id); var bal = await _bank.GetBalanceAsync(ctx.User.Id);
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText(strs.bank_balance(N(bal)))); .WithDescription(GetText(strs.bank_balance(N(bal))));
@ -80,7 +80,7 @@ public partial class Gambling
{ {
var bal = await _bank.GetBalanceAsync(user.Id); var bal = await _bank.GetBalanceAsync(user.Id);
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal)))); .WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal))));

View file

@ -24,7 +24,7 @@ public partial class Gambling
{ {
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game); var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
var result = await PromptUserConfirmAsync(_sender.CreateEmbed() var result = await PromptUserConfirmAsync(CreateEmbed()
.WithDescription( .WithDescription(
$""" $"""
Are you sure you want to reset your bet stats for **{GetGameName(game)}**? Are you sure you want to reset your bet stats for **{GetGameName(game)}**?
@ -87,7 +87,7 @@ public partial class Gambling
} }
}; };
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(user) .WithAuthor(user)
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true) .AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
@ -120,7 +120,7 @@ public partial class Gambling
{ {
var stats = await _gamblingTxTracker.GetAllAsync(); var stats = await _gamblingTxTracker.GetAllAsync();
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor(); .WithOkColor();
var str = "` Feature `` Bet ``Paid Out`` RoI `\n"; var str = "` Feature `` Bet ``Paid Out`` RoI `\n";
@ -156,7 +156,7 @@ public partial class Gambling
[OwnerOnly] [OwnerOnly]
public async Task GambleStatsReset() public async Task GambleStatsReset()
{ {
if (!await PromptUserConfirmAsync(_sender.CreateEmbed() if (!await PromptUserConfirmAsync(CreateEmbed()
.WithDescription( .WithDescription(
""" """
Are you sure? Are you sure?

View file

@ -95,7 +95,7 @@ public partial class Gambling
var cStr = string.Concat(c.Select(x => x[..^1] + " ")); var cStr = string.Concat(c.Select(x => x[..^1] + " "));
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " ")); cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("BlackJack") .WithTitle("BlackJack")
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr); .AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);

View file

@ -132,7 +132,7 @@ public partial class Gambling
else else
title = GetText(strs.connect4_draw); title = GetText(strs.connect4_draw);
return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed() return msg.ModifyAsync(x => x.Embed = CreateEmbed()
.WithTitle(title) .WithTitle(title)
.WithDescription(GetGameStateText(game)) .WithDescription(GetGameStateText(game))
.WithOkColor() .WithOkColor()
@ -142,7 +142,7 @@ public partial class Gambling
private async Task Game_OnGameStateUpdated(Connect4Game game) private async Task Game_OnGameStateUpdated(Connect4Game game)
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}") .WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
.WithDescription(GetGameStateText(game)) .WithDescription(GetGameStateText(game))
.WithOkColor(); .WithOkColor();

View file

@ -38,7 +38,7 @@ public partial class Gambling
var fileName = $"dice.{format.FileExtensions.First()}"; var fileName = $"dice.{format.FileExtensions.First()}";
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.AddField(GetText(strs.roll2), gen) .AddField(GetText(strs.roll2), gen)
@ -115,7 +115,7 @@ public partial class Gambling
d.Dispose(); d.Dispose();
var imageName = $"dice.{format.FileExtensions.First()}"; var imageName = $"dice.{format.FileExtensions.First()}";
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true) .AddField(GetText(strs.rolls), values.Select(x => Format.Code(x.ToString())).Join(' '), true)
@ -141,7 +141,7 @@ public partial class Gambling
for (var i = 0; i < n1; i++) for (var i = 0; i < n1; i++)
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]); rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString())))) .WithDescription(GetText(strs.dice_rolled_num(Format.Bold(n1.ToString()))))
@ -170,7 +170,7 @@ public partial class Gambling
arr[i] = rng.Next(1, n2 + 1); arr[i] = rng.Next(1, n2 + 1);
var sum = arr.Sum(); var sum = arr.Sum();
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`"))) .WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))

View file

@ -56,7 +56,7 @@ public partial class Gambling
foreach (var i in images) foreach (var i in images)
i.Dispose(); i.Dispose();
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor(); .WithOkColor();
var toSend = string.Empty; var toSend = string.Empty;
@ -171,7 +171,7 @@ public partial class Gambling
return; return;
} }
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(result.Card.GetEmoji()) .WithDescription(result.Card.GetEmoji())

View file

@ -30,12 +30,12 @@ public partial class Gambling
private EmbedBuilder GetEmbed(CurrencyEvent.Type type, EventOptions opts, long currentPot) private EmbedBuilder GetEmbed(CurrencyEvent.Type type, EventOptions opts, long currentPot)
=> type switch => type switch
{ {
CurrencyEvent.Type.Reaction => _sender.CreateEmbed() CurrencyEvent.Type.Reaction => CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.event_title(type.ToString()))) .WithTitle(GetText(strs.event_title(type.ToString())))
.WithDescription(GetReactionDescription(opts.Amount, currentPot)) .WithDescription(GetReactionDescription(opts.Amount, currentPot))
.WithFooter(GetText(strs.event_duration_footer(opts.Hours))), .WithFooter(GetText(strs.event_duration_footer(opts.Hours))),
CurrencyEvent.Type.GameStatus => _sender.CreateEmbed() CurrencyEvent.Type.GameStatus => CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.event_title(type.ToString()))) .WithTitle(GetText(strs.event_title(type.ToString())))
.WithDescription(GetGameStatusDescription(opts.Amount, currentPot)) .WithDescription(GetGameStatusDescription(opts.Amount, currentPot))

View file

@ -84,7 +84,7 @@ public partial class Gambling
? Format.Bold(GetText(strs.heads)) ? Format.Bold(GetText(strs.heads))
: Format.Bold(GetText(strs.tails)))); : Format.Bold(GetText(strs.tails))));
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(msg) .WithDescription(msg)
@ -130,7 +130,7 @@ public partial class Gambling
str = Format.Bold(GetText(strs.better_luck)); str = Format.Bold(GetText(strs.better_luck));
} }
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(str) .WithDescription(str)
.WithOkColor() .WithOkColor()

View file

@ -39,6 +39,7 @@ public partial class Gambling : GamblingModule<GamblingService>
private readonly GamblingTxTracker _gamblingTxTracker; private readonly GamblingTxTracker _gamblingTxTracker;
private readonly IPatronageService _ps; private readonly IPatronageService _ps;
private readonly RakebackService _rb; private readonly RakebackService _rb;
private readonly IBotCache _cache;
public Gambling( public Gambling(
IGamblingService gs, IGamblingService gs,
@ -52,7 +53,8 @@ public partial class Gambling : GamblingModule<GamblingService>
IRemindService remind, IRemindService remind,
IPatronageService patronage, IPatronageService patronage,
GamblingTxTracker gamblingTxTracker, GamblingTxTracker gamblingTxTracker,
RakebackService rb) RakebackService rb,
IBotCache cache)
: base(configService) : base(configService)
{ {
_gs = gs; _gs = gs;
@ -63,6 +65,7 @@ public partial class Gambling : GamblingModule<GamblingService>
_remind = remind; _remind = remind;
_gamblingTxTracker = gamblingTxTracker; _gamblingTxTracker = gamblingTxTracker;
_rb = rb; _rb = rb;
_cache = cache;
_ps = patronage; _ps = patronage;
_rng = new EllieRandom(); _rng = new EllieRandom();
@ -152,40 +155,10 @@ public partial class Gambling : GamblingModule<GamblingService>
} }
else if (Config.Timely.ProtType == TimelyProt.Captcha) else if (Config.Timely.ProtType == TimelyProt.Captcha)
{ {
var password = _service.GeneratePassword(); var password = await GetUserTimelyPassword(ctx.User.Id);
var img = GetPasswordImage(password);
var img = new Image<Rgba32>(60, 30);
var font = _fonts.NotoSans.CreateFont(25);
var outlinePen = new SolidPen(Color.Black, 1f);
var strikeoutRun = new RichTextRun
{
Start = 0,
End = password.GetGraphemeCount(),
Font = font,
StrikeoutPen = new SolidPen(Color.White, 4),
TextDecorations = TextDecorations.Strikeout
};
// draw password on the image
img.Mutate(x =>
{
x.DrawText(new RichTextOptions(font)
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
FallbackFontFamilies = _fonts.FallBackFonts,
Origin = new(35, 17),
TextRuns = [strikeoutRun]
},
password,
Brushes.Solid(Color.White),
outlinePen);
});
using var stream = await img.ToStreamAsync(); using var stream = await img.ToStreamAsync();
var captcha = await Response() var captcha = await Response()
// .Embed(_sender.CreateEmbed()
// .WithOkColor()
// .WithImageUrl("attachment://timely.png"))
.File(stream, "timely.png") .File(stream, "timely.png")
.SendAsync(); .SendAsync();
try try
@ -195,6 +168,8 @@ public partial class Gambling : GamblingModule<GamblingService>
{ {
return; return;
} }
await ClearUserTimelyPassword(ctx.User.Id);
} }
finally finally
{ {
@ -205,6 +180,57 @@ public partial class Gambling : GamblingModule<GamblingService>
await ClaimTimely(); await ClaimTimely();
} }
private static TypedKey<string> TimelyPasswordKey(ulong userId)
=> new($"timely_password:{userId}");
private async Task<string> GetUserTimelyPassword(ulong userId)
{
var pw = await _cache.GetOrAddAsync(TimelyPasswordKey(userId),
() =>
{
var password = _service.GeneratePassword();
return Task.FromResult(password);
});
return pw;
}
private ValueTask<bool> ClearUserTimelyPassword(ulong userId)
=> _cache.RemoveAsync(TimelyPasswordKey(userId));
private Image<Rgba32> GetPasswordImage(string password)
{
var img = new Image<Rgba32>(50, 24);
var font = _fonts.NotoSans.CreateFont(22);
var outlinePen = new SolidPen(Color.Black, 0.5f);
var strikeoutRun = new RichTextRun
{
Start = 0,
End = password.GetGraphemeCount(),
Font = font,
StrikeoutPen = new SolidPen(Color.White, 4),
TextDecorations = TextDecorations.Strikeout
};
// draw password on the image
img.Mutate(x =>
{
x.DrawText(new RichTextOptions(font)
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
FallbackFontFamilies = _fonts.FallBackFonts,
Origin = new(25, 12),
TextRuns = [strikeoutRun]
},
password,
Brushes.Solid(Color.White),
outlinePen);
});
return img;
}
private async Task ClaimTimely() private async Task ClaimTimely()
{ {
var period = Config.Timely.Cooldown; var period = Config.Timely.Cooldown;
@ -384,11 +410,11 @@ public partial class Gambling : GamblingModule<GamblingService>
trs = await uow.Set<CurrencyTransaction>().GetPageFor(userId, page); trs = await uow.Set<CurrencyTransaction>().GetPageFor(userId, page);
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle(GetText(strs.transactions( .WithTitle(GetText(strs.transactions(
((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString() ((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
?? $"{userId}"))) ?? $"{userId}")))
.WithOkColor(); .WithOkColor();
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var tr in trs) foreach (var tr in trs)
@ -435,7 +461,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return; return;
} }
var eb = _sender.CreateEmbed().WithOkColor(); var eb = CreateEmbed().WithOkColor();
eb.WithAuthor(ctx.User); eb.WithAuthor(ctx.User);
eb.WithTitle(GetText(strs.transaction)); eb.WithTitle(GetText(strs.transaction));
@ -699,11 +725,11 @@ public partial class Gambling : GamblingModule<GamblingService>
str = GetText(strs.better_luck); str = GetText(strs.better_luck);
} }
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(Format.Bold(str)) .WithDescription(Format.Bold(str))
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture)) .AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
.WithOkColor(); .WithOkColor();
await Response().Embed(eb).SendAsync(); await Response().Embed(eb).SendAsync();
} }
@ -766,9 +792,9 @@ public partial class Gambling : GamblingModule<GamblingService>
.CurrentPage(page) .CurrentPage(page)
.Page((toSend, curPage) => .Page((toSend, curPage) =>
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard)); .WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
if (!toSend.Any()) if (!toSend.Any())
{ {
@ -829,7 +855,7 @@ public partial class Gambling : GamblingModule<GamblingService>
return; return;
} }
var embed = _sender.CreateEmbed(); var embed = CreateEmbed();
string msg; string msg;
if (result.Result == RpsResultType.Draw) if (result.Result == RpsResultType.Draw)
@ -893,12 +919,12 @@ public partial class Gambling : GamblingModule<GamblingService>
sb.AppendLine(); sb.AppendLine();
} }
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(sb.ToString()) .WithDescription(sb.ToString())
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true) .AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
.AddField(GetText(strs.won), $"{(long)result.Won}", true) .AddField(GetText(strs.won), $"{(long)result.Won}", true)
.WithAuthor(ctx.User); .WithAuthor(ctx.User);
await Response().Embed(eb).SendAsync(); await Response().Embed(eb).SendAsync();

View file

@ -34,7 +34,7 @@
// decimal interest = 0, // decimal interest = 0,
// TimeSpan dueIn = default) // TimeSpan dueIn = default)
// { // {
// var eb = _sender.CreateEmbed() // var eb = CreateEmbed()
// .WithOkColor() // .WithOkColor()
// .WithDescription("User 0 Requests a loan from User {1}") // .WithDescription("User 0 Requests a loan from User {1}")
// .AddField("Amount", amount, true) // .AddField("Amount", amount, true)
@ -53,7 +53,7 @@
// .PageItems(loans) // .PageItems(loans)
// .Page((items, page) => // .Page((items, page) =>
// { // {
// var eb = _sender.CreateEmbed() // var eb = CreateEmbed()
// .WithOkColor() // .WithOkColor()
// .WithDescription("Current Loans"); // .WithDescription("Current Loans");
// //

View file

@ -103,9 +103,9 @@ public partial class Gambling
.Page((items, _) => .Page((items, _) =>
{ {
if (!items.Any()) if (!items.Any())
return _sender.CreateEmbed().WithErrorColor().WithDescription("-"); return CreateEmbed().WithErrorColor().WithDescription("-");
return items.Aggregate(_sender.CreateEmbed().WithOkColor(), return items.Aggregate(CreateEmbed().WithOkColor(),
(eb, i) => eb.AddField(i.GuildId.ToString(), i.ChannelId)); (eb, i) => eb.AddField(i.GuildId.ToString(), i.ChannelId));
}) })
.SendAsync(); .SendAsync();

View file

@ -56,8 +56,8 @@ public partial class Gambling
.Page((items, curPage) => .Page((items, curPage) =>
{ {
if (!items.Any()) if (!items.Any())
return _sender.CreateEmbed().WithErrorColor().WithDescription(GetText(strs.shop_none)); return CreateEmbed().WithErrorColor().WithDescription(GetText(strs.shop_none));
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.shop)); var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.shop));
for (var i = 0; i < items.Count; i++) for (var i = 0; i < items.Count; i++)
{ {
@ -188,7 +188,7 @@ public partial class Gambling
{ {
await Response() await Response()
.User(ctx.User) .User(ctx.User)
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name))) .WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name)))
.AddField(GetText(strs.item), item.Text) .AddField(GetText(strs.item), item.Text)
@ -254,7 +254,7 @@ public partial class Gambling
.Replace("%you.name%", buyer.GlobalName ?? buyer.Username) .Replace("%you.name%", buyer.GlobalName ?? buyer.Username)
.Replace("%you.nick%", buyer.DisplayName); .Replace("%you.nick%", buyer.DisplayName);
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithTitle("Executing shop command") .WithTitle("Executing shop command")
.WithDescription(cmd); .WithDescription(cmd);
@ -541,7 +541,7 @@ public partial class Gambling
public EmbedBuilder EntryToEmbed(ShopEntry entry) public EmbedBuilder EntryToEmbed(ShopEntry entry)
{ {
var embed = _sender.CreateEmbed().WithOkColor(); var embed = CreateEmbed().WithOkColor();
if (entry.Type == ShopEntryType.Role) if (entry.Type == ShopEntryType.Role)
{ {

View file

@ -65,7 +65,7 @@ public partial class Gambling
await using var imgStream = await image.ToStreamAsync(); await using var imgStream = await image.ToStreamAsync();
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithDescription(Format.Bold(text)) .WithDescription(Format.Bold(text))
.WithImageUrl($"attachment://result.png") .WithImageUrl($"attachment://result.png")

View file

@ -21,7 +21,7 @@ public partial class Gambling
public async Task WaifuReset() public async Task WaifuReset()
{ {
var price = _service.GetResetPrice(ctx.User); var price = _service.GetResetPrice(ctx.User);
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle(GetText(strs.waifu_reset_confirm)) .WithTitle(GetText(strs.waifu_reset_confirm))
.WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price))))); .WithDescription(GetText(strs.waifu_reset_price(Format.Bold(N(price)))));
@ -46,7 +46,7 @@ public partial class Gambling
.PageItems(async (page) => await _service.GetClaimsAsync(ctx.User.Id, page)) .PageItems(async (page) => await _service.GetClaimsAsync(ctx.User.Id, page))
.Page((items, page) => .Page((items, page) =>
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("Waifus"); .WithTitle("Waifus");
@ -266,7 +266,7 @@ public partial class Gambling
return; return;
} }
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifus_top_waifus)).WithOkColor(); var embed = CreateEmbed().WithTitle(GetText(strs.waifus_top_waifus)).WithOkColor();
var i = 0; var i = 0;
foreach (var w in waifus) foreach (var w in waifus)
@ -350,7 +350,7 @@ public partial class Gambling
if (string.IsNullOrWhiteSpace(fansStr)) if (string.IsNullOrWhiteSpace(fansStr))
fansStr = "-"; fansStr = "-";
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.waifu) .WithTitle(GetText(strs.waifu)
+ " " + " "
@ -393,7 +393,7 @@ public partial class Gambling
.CurrentPage(page) .CurrentPage(page)
.Page((items, _) => .Page((items, _) =>
{ {
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor(); var embed = CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
items items
.ToList() .ToList()

View file

@ -67,7 +67,7 @@ public partial class Games
private Task Game_OnStarted(AcrophobiaGame game) private Task Game_OnStarted(AcrophobiaGame game)
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.acrophobia)) .WithTitle(GetText(strs.acrophobia))
.WithDescription( .WithDescription(
@ -92,7 +92,7 @@ public partial class Games
if (submissions.Length == 1) if (submissions.Length == 1)
{ {
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText( .WithDescription(GetText(
strs.acro_winner_only( strs.acro_winner_only(
@ -103,7 +103,7 @@ public partial class Games
var i = 0; var i = 0;
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.acrophobia) + " - " + GetText(strs.submissions_closed)) .WithTitle(GetText(strs.acrophobia) + " - " + GetText(strs.submissions_closed))
.WithDescription(GetText(strs.acro_nym_was( .WithDescription(GetText(strs.acro_nym_was(
@ -127,7 +127,7 @@ public partial class Games
var table = votes.OrderByDescending(v => v.Value); var table = votes.OrderByDescending(v => v.Value);
var winner = table.First(); var winner = table.First();
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.acrophobia)) .WithTitle(GetText(strs.acrophobia))
.WithDescription(GetText(strs.acro_winner(Format.Bold(winner.Key.UserName), .WithDescription(GetText(strs.acro_winner(Format.Bold(winner.Key.UserName),

View file

@ -38,7 +38,7 @@ public partial class Games : EllieModule<GamesService>
return; return;
var res = _service.GetEightballResponse(ctx.User.Id, question); var res = _service.GetEightballResponse(ctx.User.Id, question);
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(ctx.User.ToString()) .WithDescription(ctx.User.ToString())
.AddField("❓ " + GetText(strs.question), question) .AddField("❓ " + GetText(strs.question), question)

View file

@ -53,7 +53,7 @@ public partial class Games
var hint = GetText(strs.nc_hint(prefix, _service.GetWidth(), _service.GetHeight())); var hint = GetText(strs.nc_hint(prefix, _service.GetWidth(), _service.GetHeight()));
await Response() await Response()
.File(stream, "ncanvas.png") .File(stream, "ncanvas.png")
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
#if GLOBAL_ELLIE #if GLOBAL_ELLIE
.WithDescription("This is not available yet.") .WithDescription("This is not available yet.")
@ -164,7 +164,7 @@ public partial class Games
Culture, Culture,
_gcs.Data.Currency.Sign)))); _gcs.Data.Currency.Sign))));
if (!await PromptUserConfirmAsync(_sender.CreateEmbed() if (!await PromptUserConfirmAsync(CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithDescription(prompt))) .WithDescription(prompt)))
{ {
@ -193,7 +193,7 @@ public partial class Games
await using var stream = await img.ToStreamAsync(); await using var stream = await img.ToStreamAsync();
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(GetText(strs.nc_pixel_set(Format.Code(position.ToString())))) .WithDescription(GetText(strs.nc_pixel_set(Format.Code(position.ToString()))))
.WithImageUrl($"attachment://zoom_{position}.png")) .WithImageUrl($"attachment://zoom_{position}.png"))
@ -231,7 +231,7 @@ public partial class Games
var pos = new kwum(pixel.Position); var pos = new kwum(pixel.Position);
await Response() await Response()
.File(stream, $"{pixel.Position}.png") .File(stream, $"{pixel.Position}.png")
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(string.IsNullOrWhiteSpace(pixel.Text) ? string.Empty : pixel.Text) .WithDescription(string.IsNullOrWhiteSpace(pixel.Text) ? string.Empty : pixel.Text)
.WithTitle(GetText(strs.nc_pixel(pos))) .WithTitle(GetText(strs.nc_pixel(pos)))
@ -263,7 +263,7 @@ public partial class Games
return; return;
} }
if (!await PromptUserConfirmAsync(_sender.CreateEmbed() if (!await PromptUserConfirmAsync(CreateEmbed()
.WithDescription( .WithDescription(
"This will reset the canvas to the specified image. All prices, text and colors will be reset.\n\n" "This will reset the canvas to the specified image. All prices, text and colors will be reset.\n\n"
+ "Are you sure you want to continue?"))) + "Are you sure you want to continue?")))
@ -293,7 +293,7 @@ public partial class Games
{ {
await _service.ResetAsync(); await _service.ResetAsync();
if (!await PromptUserConfirmAsync(_sender.CreateEmbed() if (!await PromptUserConfirmAsync(CreateEmbed()
.WithDescription( .WithDescription(
"This will delete all pixels and reset the canvas.\n\n" "This will delete all pixels and reset the canvas.\n\n"
+ "Are you sure you want to continue?"))) + "Are you sure you want to continue?")))

View file

@ -94,7 +94,7 @@ public partial class Games
if (removed is null) if (removed is null)
return; return;
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle($"Removed typing article #{index + 1}") .WithTitle($"Removed typing article #{index + 1}")
.WithDescription(removed.Text.TrimTo(50)) .WithDescription(removed.Text.TrimTo(50))
.WithOkColor(); .WithOkColor();

View file

@ -160,7 +160,7 @@ public partial class Games
{ {
try try
{ {
questionEmbed = _sender.CreateEmbed() questionEmbed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.trivia_game)) .WithTitle(GetText(strs.trivia_game))
.AddField(GetText(strs.category), question.Category) .AddField(GetText(strs.category), question.Category)
@ -189,7 +189,7 @@ public partial class Games
{ {
try try
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithTitle(GetText(strs.trivia_game)) .WithTitle(GetText(strs.trivia_game))
.WithDescription(GetText(strs.trivia_times_up(Format.Bold(question.Answer)))); .WithDescription(GetText(strs.trivia_times_up(Format.Bold(question.Answer))));
@ -221,7 +221,7 @@ public partial class Games
{ {
try try
{ {
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.trivia_ended)) .WithAuthor(GetText(strs.trivia_ended))
.WithTitle(GetText(strs.leaderboard)) .WithTitle(GetText(strs.leaderboard))
@ -247,7 +247,7 @@ public partial class Games
{ {
try try
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.trivia_game)) .WithTitle(GetText(strs.trivia_game))
.WithDescription(GetText(strs.trivia_win(user.Name, .WithDescription(GetText(strs.trivia_win(user.Name,

View file

@ -114,7 +114,7 @@ public sealed partial class Help : EllieModule<HelpService>
.AddFooter(false) .AddFooter(false)
.Page((items, _) => .Page((items, _) =>
{ {
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.list_of_modules)); var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.list_of_modules));
if (!items.Any()) if (!items.Any())
{ {
@ -315,7 +315,7 @@ public sealed partial class Help : EllieModule<HelpService>
.WithPlaceholder("Select a submodule to see detailed commands"); .WithPlaceholder("Select a submodule to see detailed commands");
var groups = cmdsWithGroup.ToArray(); var groups = cmdsWithGroup.ToArray();
var embed = _sender.CreateEmbed().WithOkColor(); var embed = CreateEmbed().WithOkColor();
foreach (var g in groups) foreach (var g in groups)
{ {
sb.AddOption(g.Key, g.Key); sb.AddOption(g.Key, g.Key);
@ -383,7 +383,7 @@ public sealed partial class Help : EllieModule<HelpService>
.Interaction(inter) .Interaction(inter)
.Page((items, _) => .Page((items, _) =>
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithTitle(GetText(strs.cmd_group_commands(group.Name))) .WithTitle(GetText(strs.cmd_group_commands(group.Name)))
.WithOkColor(); .WithOkColor();
@ -520,7 +520,7 @@ public sealed partial class Help : EllieModule<HelpService>
[OnlyPublicBot] [OnlyPublicBot]
public async Task Donate() public async Task Donate()
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("Thank you for considering to donate to the EllieBot project!"); .WithTitle("Thank you for considering to donate to the EllieBot project!");

View file

@ -40,7 +40,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
.PageSize(10) .PageSize(10)
.Page((items, _) => .Page((items, _) =>
{ {
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.list_of_unloaded)) .WithTitle(GetText(strs.list_of_unloaded))
.WithDescription(items.Join('\n')); .WithDescription(items.Join('\n'));
@ -81,7 +81,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
} }
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.loaded_marmalades)) .WithTitle(GetText(strs.loaded_marmalades))
.WithDescription(loaded.Select(x => x.Name) .WithDescription(loaded.Select(x => x.Name)
@ -136,7 +136,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
.Paginated() .Paginated()
.Items(output) .Items(output)
.PageSize(10) .PageSize(10)
.Page((items, _) => _sender.CreateEmbed() .Page((items, _) => CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.list_of_marmalades)) .WithTitle(GetText(strs.list_of_marmalades))
.WithDescription(items.Join('\n'))) .WithDescription(items.Join('\n')))
@ -168,7 +168,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
: $"{x.Prefix} {x.Name}")) : $"{x.Prefix} {x.Name}"))
.Join("\n"); .Join("\n");
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.marmalade_info)) .WithAuthor(GetText(strs.marmalade_info))
.WithTitle(found.Name) .WithTitle(found.Name)
@ -201,7 +201,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
.CurrentPage(0) .CurrentPage(0)
.Page((items, _) => .Page((items, _) =>
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor(); .WithOkColor();
foreach (var marmalade in items) foreach (var marmalade in items)
@ -224,7 +224,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
[OwnerOnly] [OwnerOnly]
public async Task MarmaladeSearch() public async Task MarmaladeSearch()
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithTitle(GetText(strs.list_of_marmalades)) .WithTitle(GetText(strs.list_of_marmalades))
.WithOkColor(); .WithOkColor();

View file

@ -109,7 +109,7 @@ public sealed partial class Music : EllieModule<IMusicService>
try try
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.queued_track) + " #" + (index + 1), MUSIC_ICON_URL) .WithAuthor(GetText(strs.queued_track) + " #" + (index + 1), MUSIC_ICON_URL)
.WithDescription($"{trackInfo.PrettyName()}\n{GetText(strs.queue)} ") .WithDescription($"{trackInfo.PrettyName()}\n{GetText(strs.queue)} ")
@ -314,7 +314,7 @@ public sealed partial class Music : EllieModule<IMusicService>
if (!string.IsNullOrWhiteSpace(add)) if (!string.IsNullOrWhiteSpace(add))
desc = add + "\n" + desc; desc = add + "\n" + desc;
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithAuthor( .WithAuthor(
GetText(strs.player_queue(curPage + 1, (tracks.Count / LQ_ITEMS_PER_PAGE) + 1)), GetText(strs.player_queue(curPage + 1, (tracks.Count / LQ_ITEMS_PER_PAGE) + 1)),
MUSIC_ICON_URL) MUSIC_ICON_URL)
@ -352,7 +352,7 @@ public sealed partial class Music : EllieModule<IMusicService>
} }
var embeds = videos.Select((x, i) => _sender.CreateEmbed() var embeds = videos.Select((x, i) => CreateEmbed()
.WithOkColor() .WithOkColor()
.WithThumbnailUrl(x.Thumbnail) .WithThumbnailUrl(x.Thumbnail)
.WithDescription($"`{i + 1}.` {Format.Bold(x.Title)}\n\t{x.Url}")) .WithDescription($"`{i + 1}.` {Format.Bold(x.Title)}\n\t{x.Url}"))
@ -424,7 +424,7 @@ public sealed partial class Music : EllieModule<IMusicService>
return; return;
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithAuthor(GetText(strs.removed_track) + " #" + index, MUSIC_ICON_URL) .WithAuthor(GetText(strs.removed_track) + " #" + index, MUSIC_ICON_URL)
.WithDescription(track.PrettyName()) .WithDescription(track.PrettyName())
.WithFooter(track.PrettyInfo()) .WithFooter(track.PrettyInfo())
@ -592,7 +592,7 @@ public sealed partial class Music : EllieModule<IMusicService>
return; return;
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle(track.Title.TrimTo(65)) .WithTitle(track.Title.TrimTo(65))
.WithAuthor(GetText(strs.track_moved), MUSIC_ICON_URL) .WithAuthor(GetText(strs.track_moved), MUSIC_ICON_URL)
.AddField(GetText(strs.from_position), $"#{from + 1}", true) .AddField(GetText(strs.from_position), $"#{from + 1}", true)
@ -651,7 +651,7 @@ public sealed partial class Music : EllieModule<IMusicService>
if (currentTrack is null) if (currentTrack is null)
return; return;
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(strs.now_playing), MUSIC_ICON_URL) .WithAuthor(GetText(strs.now_playing), MUSIC_ICON_URL)
.WithDescription(currentTrack.PrettyName()) .WithDescription(currentTrack.PrettyName())
@ -752,4 +752,20 @@ public sealed partial class Music : EllieModule<IMusicService>
else else
await Response().Error(strs.no_player).SendAsync(); await Response().Error(strs.no_player).SendAsync();
} }
[Cmd]
[RequireContext(ContextType.Guild)]
public async Task WrongSong()
{
var removed = await _service.RemoveLastQueuedTrackAsync(ctx.Guild.Id);
if (removed is null)
{
await Response().Error(strs.no_last_queued_found).SendAsync();
}
else
{
await Response().Confirm(strs.wrongsong_success(removed.Title.TrimTo(30))).SendAsync();
}
}
} }

View file

@ -49,7 +49,7 @@ public sealed partial class Music
playlists = uow.Set<MusicPlaylist>().GetPlaylistsOnPage(num); playlists = uow.Set<MusicPlaylist>().GetPlaylistsOnPage(num);
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL) .WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL)
.WithDescription(string.Join("\n", .WithDescription(string.Join("\n",
playlists.Select(r => GetText(strs.playlists(r.Id, r.Name, r.Author, r.Songs.Count))))) playlists.Select(r => GetText(strs.playlists(r.Id, r.Name, r.Author, r.Songs.Count)))))
@ -113,7 +113,7 @@ public sealed partial class Music
var str = string.Join("\n", var str = string.Join("\n",
items items
.Select(x => $"`{++i}.` [{x.Title.TrimTo(45)}]({x.Query}) `{x.Provider}`")); .Select(x => $"`{++i}.` [{x.Title.TrimTo(45)}]({x.Query}) `{x.Provider}`"));
return _sender.CreateEmbed().WithTitle($"\"{mpl.Name}\" by {mpl.Author}") return CreateEmbed().WithTitle($"\"{mpl.Name}\" by {mpl.Author}")
.WithOkColor() .WithOkColor()
.WithDescription(str); .WithDescription(str);
}) })
@ -155,7 +155,7 @@ public sealed partial class Music
} }
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.playlist_saved)) .WithTitle(GetText(strs.playlist_saved))
.AddField(GetText(strs.name), name) .AddField(GetText(strs.name), name)

View file

@ -33,4 +33,5 @@ public interface IMusicService
Task SetMusicQualityAsync(ulong guildId, QualityPreset preset); Task SetMusicQualityAsync(ulong guildId, QualityPreset preset);
Task<bool> ToggleQueueAutoPlayAsync(ulong guildId); Task<bool> ToggleQueueAutoPlayAsync(ulong guildId);
Task<bool> FairplayAsync(ulong guildId); Task<bool> FairplayAsync(ulong guildId);
Task<IQueuedTrackInfo?> RemoveLastQueuedTrackAsync(ulong guildId);
} }

View file

@ -179,11 +179,11 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
return async (mp, trackInfo) => return async (mp, trackInfo) =>
{ {
_ = lastFinishedMessage?.DeleteAsync(); _ = lastFinishedMessage?.DeleteAsync();
var embed = _sender.CreateEmbed() var embed = _sender.CreateEmbed(guildId)
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(guildId, strs.finished_track), Music.MUSIC_ICON_URL) .WithAuthor(GetText(guildId, strs.finished_track), Music.MUSIC_ICON_URL)
.WithDescription(trackInfo.PrettyName()) .WithDescription(trackInfo.PrettyName())
.WithFooter(trackInfo.PrettyTotalTime()); .WithFooter(trackInfo.PrettyTotalTime());
lastFinishedMessage = await SendToOutputAsync(guildId, embed); lastFinishedMessage = await SendToOutputAsync(guildId, embed);
}; };
@ -195,11 +195,11 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
return async (mp, trackInfo, index) => return async (mp, trackInfo, index) =>
{ {
_ = lastPlayingMessage?.DeleteAsync(); _ = lastPlayingMessage?.DeleteAsync();
var embed = _sender.CreateEmbed() var embed = _sender.CreateEmbed(guildId)
.WithOkColor() .WithOkColor()
.WithAuthor(GetText(guildId, strs.playing_track(index + 1)), Music.MUSIC_ICON_URL) .WithAuthor(GetText(guildId, strs.playing_track(index + 1)), Music.MUSIC_ICON_URL)
.WithDescription(trackInfo.PrettyName()) .WithDescription(trackInfo.PrettyName())
.WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}"); .WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}");
lastPlayingMessage = await SendToOutputAsync(guildId, embed); lastPlayingMessage = await SendToOutputAsync(guildId, embed);
}; };
@ -290,7 +290,8 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
return "-"; return "-";
return randomPlayingTrack.Title; return randomPlayingTrack.Title;
}); }
);
// number of servers currently listening to music // number of servers currently listening to music
yield return ("%music.servers%", () => yield return ("%music.servers%", () =>
@ -298,14 +299,16 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
var count = _players.Select(x => x.Value.GetCurrentTrack(out _)).Count(x => x is not null); var count = _players.Select(x => x.Value.GetCurrentTrack(out _)).Count(x => x is not null);
return count.ToString(); return count.ToString();
}); }
);
yield return ("%music.queued%", () => yield return ("%music.queued%", () =>
{ {
var count = _players.Sum(x => x.Value.GetQueuedTracks().Count); var count = _players.Sum(x => x.Value.GetQueuedTracks().Count);
return count.ToString(); return count.ToString();
}); }
);
} }
#region Settings #region Settings
@ -434,5 +437,17 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
return Task.FromResult(false); return Task.FromResult(false);
} }
public async Task<IQueuedTrackInfo?> RemoveLastQueuedTrackAsync(ulong guildId)
{
if (TryGetMusicPlayer(guildId, out var mp))
{
var last = await mp.RemoveLastQueuedTrack();
return last;
}
return null;
}
#endregion #endregion
} }

View file

@ -38,4 +38,5 @@ public interface IMusicPlayer : IDisposable
void SetRepeat(PlayerRepeatType type); void SetRepeat(PlayerRepeatType type);
void ShuffleQueue(); void ShuffleQueue();
void SetFairplay(); void SetFairplay();
Task<IQueuedTrackInfo?> RemoveLastQueuedTrack();
} }

View file

@ -20,4 +20,5 @@ public interface IMusicQueue
void Shuffle(Random rng); void Shuffle(Random rng);
bool IsLast(); bool IsLast();
void ReorderFairly(); void ReorderFairly();
int? GetLastQueuedIndex();
} }

View file

@ -260,7 +260,6 @@ public sealed class MusicPlayer : IMusicPlayer
IsStopped = true; IsStopped = true;
Log.Error("Please install ffmpeg and make sure it's added to your " Log.Error("Please install ffmpeg and make sure it's added to your "
+ "PATH environment variable before trying again"); + "PATH environment variable before trying again");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -420,20 +419,20 @@ public sealed class MusicPlayer : IMusicPlayer
break; break;
await chunk.Select(async data => await chunk.Select(async data =>
{
var (query, platform) = data;
try
{ {
var (query, platform) = data; await TryEnqueueTrackAsync(query, queuer, false, platform);
try errorCount = 0;
{ }
await TryEnqueueTrackAsync(query, queuer, false, platform); catch (Exception ex)
errorCount = 0; {
} Log.Warning(ex, "Error resolving {MusicPlatform} Track {TrackQuery}", platform, query);
catch (Exception ex) ++errorCount;
{ }
Log.Warning(ex, "Error resolving {MusicPlatform} Track {TrackQuery}", platform, query); })
++errorCount; .WhenAll();
}
})
.WhenAll();
await Task.Delay(1000); await Task.Delay(1000);
@ -542,4 +541,15 @@ public sealed class MusicPlayer : IMusicPlayer
{ {
_queue.ReorderFairly(); _queue.ReorderFairly();
} }
public Task<IQueuedTrackInfo?> RemoveLastQueuedTrack()
{
var last = _queue.GetLastQueuedIndex();
if (last is null)
return Task.FromResult<IQueuedTrackInfo?>(null);
return TryRemoveTrackAt(last.Value, out var trackInfo)
? Task.FromResult(trackInfo)
: Task.FromResult<IQueuedTrackInfo?>(null);
}
} }

View file

@ -60,6 +60,7 @@ public sealed partial class MusicQueue : IMusicQueue
private LinkedList<QueuedTrackInfo> tracks; private LinkedList<QueuedTrackInfo> tracks;
private int index; private int index;
private int? _lastQueued = null;
private readonly object _locker = new(); private readonly object _locker = new();
@ -74,7 +75,7 @@ public sealed partial class MusicQueue : IMusicQueue
lock (_locker) lock (_locker)
{ {
var added = new QueuedTrackInfo(trackInfo, queuer); var added = new QueuedTrackInfo(trackInfo, queuer);
enqueuedAt = tracks.Count; _lastQueued = enqueuedAt = tracks.Count;
tracks.AddLast(added); tracks.AddLast(added);
return added; return added;
@ -99,6 +100,8 @@ public sealed partial class MusicQueue : IMusicQueue
tracks.AddAfter(currentNode, added); tracks.AddAfter(currentNode, added);
_lastQueued = i;
return added; return added;
} }
} }
@ -112,6 +115,8 @@ public sealed partial class MusicQueue : IMusicQueue
var added = new QueuedTrackInfo(track, queuer); var added = new QueuedTrackInfo(track, queuer);
tracks.AddLast(added); tracks.AddLast(added);
} }
_lastQueued = tracks.Count;
} }
} }
@ -146,6 +151,7 @@ public sealed partial class MusicQueue : IMusicQueue
lock (_locker) lock (_locker)
{ {
tracks.Clear(); tracks.Clear();
_lastQueued = null;
} }
} }
@ -177,6 +183,18 @@ public sealed partial class MusicQueue : IMusicQueue
if (index < 0) if (index < 0)
index = Count; index = Count;
if (i < _lastQueued)
{
if (_lastQueued is not null)
{
_lastQueued -= 1;
}
}
else if (i == _lastQueued)
{
_lastQueued = null;
}
// if it was the last song in the queue // if it was the last song in the queue
// // wrap back to start // // wrap back to start
// if (_index == Count) // if (_index == Count)
@ -207,6 +225,11 @@ public sealed partial class MusicQueue : IMusicQueue
if (from >= Count || to >= Count) if (from >= Count || to >= Count)
return null; return null;
if (from == _lastQueued)
_lastQueued = to;
else if (to == _lastQueued)
_lastQueued += 1;
// update current track index // update current track index
if (from == index) if (from == index)
{ {
@ -267,6 +290,7 @@ public sealed partial class MusicQueue : IMusicQueue
var list = tracks.ToArray(); var list = tracks.ToArray();
rng.Shuffle(list); rng.Shuffle(list);
tracks = new(list); tracks = new(list);
_lastQueued = null;
} }
} }
@ -318,6 +342,8 @@ public sealed partial class MusicQueue : IMusicQueue
if (queuers.Count == 0) if (queuers.Count == 0)
break; break;
} }
_lastQueued = null;
} }
} }
@ -339,4 +365,6 @@ public sealed partial class MusicQueue : IMusicQueue
return true; return true;
} }
} }
public int? GetLastQueuedIndex() => _lastQueued;
} }

View file

@ -32,7 +32,6 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
_sender = sender; _sender = sender;
_config = config; _config = config;
_client = client; _client = client;
} }
public Task OnReadyAsync() public Task OnReadyAsync()
@ -55,8 +54,8 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
await using (var ctx = _db.GetDbContext()) await using (var ctx = _db.GetDbContext())
{ {
old = await ctx.GetTable<RewardedUser>() old = await ctx.GetTable<RewardedUser>()
.Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId) .Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
if (old is null) if (old is null)
{ {
@ -69,15 +68,15 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
return; return;
var count = await ctx.GetTable<RewardedUser>() var count = await ctx.GetTable<RewardedUser>()
.Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId) .Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId)
.UpdateAsync(_ => new() .UpdateAsync(_ => new()
{ {
PlatformUserId = newPatron.UniquePlatformUserId, PlatformUserId = newPatron.UniquePlatformUserId,
UserId = newPatron.UserId, UserId = newPatron.UserId,
// amount before bonuses // amount before bonuses
AmountRewardedThisMonth = newAmount, AmountRewardedThisMonth = newAmount,
LastReward = newPatron.PaidAt LastReward = newPatron.PaidAt
}); });
// shouldn't ever happen // shouldn't ever happen
if (count == 0) if (count == 0)
@ -103,7 +102,7 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
// if the user pledges 5$ or more, they will get X % more flowers where X is amount in dollars, // if the user pledges 5$ or more, they will get X % more flowers where X is amount in dollars,
// up to 100% // up to 100%
await _cs.AddAsync(newPatron.UserId, diff, new TxData("patron","update")); await _cs.AddAsync(newPatron.UserId, diff, new TxData("patron", "update"));
_ = SendMessageToUser(newPatron.UserId, _ = SendMessageToUser(newPatron.UserId,
$"You've received an additional **{diff}**{_config.Data.Currency.Sign} as a currency reward (+{percentBonus}%)!"); $"You've received an additional **{diff}**{_config.Data.Currency.Sign} as a currency reward (+{percentBonus}%)!");
@ -139,12 +138,12 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
await ctx.GetTable<RewardedUser>() await ctx.GetTable<RewardedUser>()
.InsertOrUpdateAsync(() => new() .InsertOrUpdateAsync(() => new()
{ {
PlatformUserId = patron.UniquePlatformUserId, PlatformUserId = patron.UniquePlatformUserId,
UserId = patron.UserId, UserId = patron.UserId,
AmountRewardedThisMonth = amount, AmountRewardedThisMonth = amount,
LastReward = patron.PaidAt, LastReward = patron.PaidAt,
}, },
old => new() old => new()
{ {
AmountRewardedThisMonth = amount, AmountRewardedThisMonth = amount,
@ -171,8 +170,8 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
return; return;
var eb = _sender.CreateEmbed() var eb = _sender.CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(message); .WithDescription(message);
await _sender.Response(user).Embed(eb).SendAsync(); await _sender.Response(user).Embed(eb).SendAsync();
} }

View file

@ -53,7 +53,7 @@ public partial class Help
// //
// var patron = _service.GiftPatronAsync(user, amount); // var patron = _service.GiftPatronAsync(user, amount);
// //
// var eb = _sender.CreateEmbed(); // var eb = CreateEmbed();
// //
// await Response().Embed(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!") // await Response().Embed(eb.WithDescription($"Added **{days}** days of Patron benefits to {user.Mention}!")
// .AddField("Tier", Format.Bold(patron.Tier.ToString()), true) // .AddField("Tier", Format.Bold(patron.Tier.ToString()), true)
@ -75,7 +75,7 @@ public partial class Help
var quotaStats = await _service.LimitStats(user.Id); var quotaStats = await _service.LimitStats(user.Id);
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithAuthor(user) .WithAuthor(user)
.WithTitle(GetText(strs.patron_info)) .WithTitle(GetText(strs.patron_info))
.WithOkColor(); .WithOkColor();

View file

@ -60,12 +60,12 @@ public partial class Permissions
.Page((pageItems, _) => .Page((pageItems, _) =>
{ {
if (pageItems.Count == 0) if (pageItems.Count == 0)
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(title) .WithTitle(title)
.WithDescription(GetText(strs.empty_page)); .WithDescription(GetText(strs.empty_page));
return _sender.CreateEmbed() return CreateEmbed()
.WithTitle(title) .WithTitle(title)
.WithDescription(pageItems.Join('\n')) .WithDescription(pageItems.Join('\n'))
.WithOkColor(); .WithOkColor();

View file

@ -95,7 +95,7 @@ public partial class Permissions
var output = items.Select(x => var output = items.Select(x =>
$"{Format.Code(x.CommandName)}: {x.Seconds}s"); $"{Format.Code(x.CommandName)}: {x.Seconds}s");
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(output.Join("\n")); .WithDescription(output.Join("\n"));
}) })

View file

@ -28,7 +28,7 @@ public partial class Permissions
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task FilterList() public async Task FilterList()
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle("Server filter settings"); .WithTitle("Server filter settings");
@ -316,7 +316,7 @@ public partial class Permissions
.Items(fws) .Items(fws)
.PageSize(10) .PageSize(10)
.CurrentPage(page) .CurrentPage(page)
.Page((items, _) => _sender.CreateEmbed() .Page((items, _) => CreateEmbed()
.WithTitle(GetText(strs.filter_word_list)) .WithTitle(GetText(strs.filter_word_list))
.WithDescription(string.Join("\n", items)) .WithDescription(string.Join("\n", items))
.WithOkColor()) .WithOkColor())

View file

@ -30,7 +30,7 @@ public partial class Permissions
return; return;
} }
var embed = _sender.CreateEmbed().WithOkColor(); var embed = CreateEmbed().WithOkColor();
if (blockedModule.Any()) if (blockedModule.Any())
embed.AddField(GetText(strs.blocked_modules), string.Join("\n", _service.BlockedModules)); embed.AddField(GetText(strs.blocked_modules), string.Join("\n", _service.BlockedModules));

View file

@ -24,7 +24,7 @@ public partial class Searches
return; return;
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(animeData.Synopsis.Replace("<br>", .WithDescription(animeData.Synopsis.Replace("<br>",
Environment.NewLine, Environment.NewLine,
@ -56,7 +56,7 @@ public partial class Searches
return; return;
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithDescription(mangaData.Synopsis.Replace("<br>", .WithDescription(mangaData.Synopsis.Replace("<br>",
Environment.NewLine, Environment.NewLine,

View file

@ -35,7 +35,7 @@ public partial class Searches
} }
var symbol = symbols.First(); var symbol = symbols.First();
var promptEmbed = _sender.CreateEmbed() var promptEmbed = CreateEmbed()
.WithDescription(symbol.Description) .WithDescription(symbol.Description)
.WithTitle(GetText(strs.did_you_mean(symbol.Symbol))); .WithTitle(GetText(strs.did_you_mean(symbol.Symbol)));
@ -67,7 +67,7 @@ public partial class Searches
var price = stock.Price.ToString("C2", localCulture); var price = stock.Price.ToString("C2", localCulture);
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(stock.Symbol) .WithAuthor(stock.Symbol)
.WithUrl($"https://www.tradingview.com/chart/?symbol={stock.Symbol}") .WithUrl($"https://www.tradingview.com/chart/?symbol={stock.Symbol}")
@ -112,7 +112,7 @@ public partial class Searches
if (nearest is not null) if (nearest is not null)
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle(GetText(strs.crypto_not_found)) .WithTitle(GetText(strs.crypto_not_found))
.WithDescription( .WithDescription(
GetText(strs.did_you_mean(Format.Bold($"{nearest.Name} ({nearest.Symbol})")))); GetText(strs.did_you_mean(Format.Bold($"{nearest.Name} ({nearest.Symbol})"))));
@ -145,7 +145,7 @@ public partial class Searches
await using var sparkline = await _service.GetSparklineAsync(crypto.Id, usd.PercentChange7d >= 0); await using var sparkline = await _service.GetSparklineAsync(crypto.Id, usd.PercentChange7d >= 0);
var fileName = $"{crypto.Slug}_7d.png"; var fileName = $"{crypto.Slug}_7d.png";
var toSend = _sender.CreateEmbed() var toSend = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor($"#{crypto.CmcRank}") .WithAuthor($"#{crypto.CmcRank}")
.WithTitle($"{crypto.Name} ({crypto.Symbol})") .WithTitle($"{crypto.Name} ({crypto.Symbol})")
@ -198,7 +198,7 @@ public partial class Searches
.PageSize(10) .PageSize(10)
.Page((items, _) => .Page((items, _) =>
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor(); .WithOkColor();
if (items.Count > 0) if (items.Count > 0)

View file

@ -123,7 +123,7 @@ public partial class Searches
if (!feeds.Any()) if (!feeds.Any())
{ {
await Response() await Response()
.Embed(_sender.CreateEmbed().WithOkColor().WithDescription(GetText(strs.feed_no_feed))) .Embed(CreateEmbed().WithOkColor().WithDescription(GetText(strs.feed_no_feed)))
.SendAsync(); .SendAsync();
return; return;
} }
@ -135,7 +135,7 @@ public partial class Searches
.CurrentPage(page) .CurrentPage(page)
.Page((items, cur) => .Page((items, cur) =>
{ {
var embed = _sender.CreateEmbed().WithOkColor(); var embed = CreateEmbed().WithOkColor();
var i = 0; var i = 0;
var fs = string.Join("\n", var fs = string.Join("\n",
items.Select(x => $"`{(cur * 10) + ++i}.` <#{x.ChannelId}> {x.Url}")); items.Select(x => $"`{(cur * 10) + ++i}.` <#{x.ChannelId}> {x.Url}"));

View file

@ -44,7 +44,7 @@ public partial class Searches
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"osu! {smode} profile for {user}") .WithTitle($"osu! {smode} profile for {user}")
.WithThumbnailUrl($"https://a.ppy.sh/{userId}") .WithThumbnailUrl($"https://a.ppy.sh/{userId}")
@ -78,7 +78,7 @@ public partial class Searches
return; return;
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"osu!Gatari {modeStr} profile for {user}") .WithTitle($"osu!Gatari {modeStr} profile for {user}")
.WithThumbnailUrl($"https://a.gatari.pw/{userStats.Id}") .WithThumbnailUrl($"https://a.gatari.pw/{userStats.Id}")
@ -113,7 +113,7 @@ public partial class Searches
var plays = await _service.GetOsuPlay(user, mode); var plays = await _service.GetOsuPlay(user, mode);
var eb = _sender.CreateEmbed().WithOkColor().WithTitle($"Top 5 plays for {user}"); var eb = CreateEmbed().WithOkColor().WithTitle($"Top 5 plays for {user}");
foreach(var (title, desc) in plays) foreach(var (title, desc) in plays)
eb.AddField(title, desc); eb.AddField(title, desc);

View file

@ -25,7 +25,7 @@ public partial class Searches
if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant()) if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
{ {
var p = kvp.Value; var p = kvp.Value;
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(kvp.Key.ToTitleCase()) .WithTitle(kvp.Key.ToTitleCase())
.WithDescription(p.BaseStats.ToString()) .WithDescription(p.BaseStats.ToString())
@ -55,7 +55,7 @@ public partial class Searches
{ {
if (kvp.Key.ToUpperInvariant() == ability) if (kvp.Key.ToUpperInvariant() == ability)
{ {
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(kvp.Value.Name) .WithTitle(kvp.Value.Name)
.WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc) .WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)

View file

@ -22,7 +22,7 @@ public partial class Searches
} }
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"{verse.BookName} {verse.Chapter}:{verse.Verse}") .WithTitle($"{verse.BookName} {verse.Chapter}:{verse.Verse}")
.WithDescription(verse.Text)) .WithDescription(verse.Text))
@ -48,7 +48,7 @@ public partial class Searches
await using var audio = await http.GetStreamAsync(arabic.Audio); await using var audio = await http.GetStreamAsync(arabic.Audio);
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Arabic", arabic.Text) .AddField("Arabic", arabic.Text)
.AddField("English", english.Text) .AddField("English", english.Text)

View file

@ -59,14 +59,14 @@ public partial class Searches
descStr = descStr.TrimTo(4096); descStr = descStr.TrimTo(4096);
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithTitle(query.TrimTo(64)!) .WithTitle(query.TrimTo(64)!)
.WithDescription(descStr) .WithDescription(descStr)
.WithFooter( .WithFooter(
GetText(strs.results_in(data.Info.TotalResults, data.Info.SearchTime)), GetText(strs.results_in(data.Info.TotalResults, data.Info.SearchTime)),
"https://i.imgur.com/G46fm8J.png"); "https://i.imgur.com/G46fm8J.png");
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }
@ -93,13 +93,13 @@ public partial class Searches
return; return;
} }
EmbedBuilder CreateEmbed(IImageSearchResultEntry entry) EmbedBuilder CreateImageEmbed(IImageSearchResultEntry entry)
{ {
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithAuthor(ctx.User) .WithAuthor(ctx.User)
.WithTitle(query) .WithTitle(query)
.WithImageUrl(entry.Link); .WithImageUrl(entry.Link);
} }
await Response() await Response()
@ -112,10 +112,11 @@ public partial class Searches
var item = items.FirstOrDefault(); var item = items.FirstOrDefault();
if (item is null) if (item is null)
return _sender.CreateEmbed() return CreateEmbed()
.WithDescription(GetText(strs.no_search_results)); .WithPendingColor()
.WithDescription(GetText(strs.no_search_results));
var embed = CreateEmbed(item); var embed = CreateImageEmbed(item);
return embed; return embed;
}) })
@ -184,7 +185,7 @@ public partial class Searches
// //
// var descStr = string.Join("\n\n", desc); // var descStr = string.Join("\n\n", desc);
// //
// var embed = _sender.CreateEmbed() // var embed = CreateEmbed()
// .WithAuthor(ctx.User.ToString(), // .WithAuthor(ctx.User.ToString(),
// "https://upload.wikimedia.org/wikipedia/en/9/90/The_DuckDuckGo_Duck.png") // "https://upload.wikimedia.org/wikipedia/en/9/90/The_DuckDuckGo_Duck.png")
// .WithDescription($"{GetText(strs.search_for)} **{query}**\n\n" + descStr) // .WithDescription($"{GetText(strs.search_for)} **{query}**\n\n" + descStr)

View file

@ -39,7 +39,7 @@ public partial class Searches : EllieModule<SearchesService>
if (!await ValidateQuery(query)) if (!await ValidateQuery(query))
return; return;
var embed = _sender.CreateEmbed(); var embed = CreateEmbed();
var data = await _service.GetWeatherDataAsync(query); var data = await _service.GetWeatherDataAsync(query);
if (data is null) if (data is null)
@ -102,7 +102,7 @@ public partial class Searches : EllieModule<SearchesService>
return; return;
} }
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.time_new)) .WithTitle(GetText(strs.time_new))
.WithDescription(Format.Code(data.Time.ToString(Culture))) .WithDescription(Format.Code(data.Time.ToString(Culture)))
@ -128,7 +128,7 @@ public partial class Searches : EllieModule<SearchesService>
} }
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(movie.Title) .WithTitle(movie.Title)
.WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/") .WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/")
@ -161,7 +161,7 @@ public partial class Searches : EllieModule<SearchesService>
private Task InternalRandomImage(SearchesService.ImageTag tag) private Task InternalRandomImage(SearchesService.ImageTag tag)
{ {
var url = _service.GetRandomImageUrl(tag); var url = _service.GetRandomImageUrl(tag);
return Response().Embed(_sender.CreateEmbed().WithOkColor().WithImageUrl(url)).SendAsync(); return Response().Embed(CreateEmbed().WithOkColor().WithImageUrl(url)).SendAsync();
} }
[Cmd] [Cmd]
@ -190,7 +190,7 @@ public partial class Searches : EllieModule<SearchesService>
} }
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField(GetText(strs.original_url), $"<{query}>") .AddField(GetText(strs.original_url), $"<{query}>")
.AddField(GetText(strs.short_url), $"<{shortLink}>")) .AddField(GetText(strs.short_url), $"<{shortLink}>"))
@ -213,7 +213,7 @@ public partial class Searches : EllieModule<SearchesService>
return; return;
} }
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(card.Name) .WithTitle(card.Name)
.WithDescription(card.Description) .WithDescription(card.Description)
@ -246,7 +246,7 @@ public partial class Searches : EllieModule<SearchesService>
return; return;
} }
var embed = _sender.CreateEmbed().WithOkColor().WithImageUrl(card.Img); var embed = CreateEmbed().WithOkColor().WithImageUrl(card.Img);
if (!string.IsNullOrWhiteSpace(card.Flavor)) if (!string.IsNullOrWhiteSpace(card.Flavor))
embed.WithDescription(card.Flavor); embed.WithDescription(card.Flavor);
@ -280,7 +280,7 @@ public partial class Searches : EllieModule<SearchesService>
.Page((items, _) => .Page((items, _) =>
{ {
var item = items[0]; var item = items[0];
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithUrl(item.Permalink) .WithUrl(item.Permalink)
.WithTitle(item.Word) .WithTitle(item.Word)
@ -311,7 +311,7 @@ public partial class Searches : EllieModule<SearchesService>
.Page((items, _) => .Page((items, _) =>
{ {
var model = items.First(); var model = items.First();
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithDescription(ctx.User.Mention) .WithDescription(ctx.User.Mention)
.AddField(GetText(strs.word), model.Word, true) .AddField(GetText(strs.word), model.Word, true)
.AddField(GetText(strs._class), model.WordType, true) .AddField(GetText(strs._class), model.WordType, true)
@ -374,7 +374,7 @@ public partial class Searches : EllieModule<SearchesService>
} }
[Cmd] [Cmd]
public async Task Color(params Color[] colors) public async Task Color(params Rgba32[] colors)
{ {
if (!colors.Any()) if (!colors.Any())
return; return;
@ -403,7 +403,7 @@ public partial class Searches : EllieModule<SearchesService>
await Response() await Response()
.Embed( .Embed(
_sender.CreateEmbed() CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Username", usr.ToString()) .AddField("Username", usr.ToString())
.AddField("Avatar Url", avatarUrl) .AddField("Avatar Url", avatarUrl)

View file

@ -79,9 +79,9 @@ public partial class Searches
.Page((elements, cur) => .Page((elements, cur) =>
{ {
if (elements.Count == 0) if (elements.Count == 0)
return _sender.CreateEmbed().WithDescription(GetText(strs.streams_none)).WithErrorColor(); return CreateEmbed().WithDescription(GetText(strs.streams_none)).WithErrorColor();
var eb = _sender.CreateEmbed().WithTitle(GetText(strs.streams_follow_title)).WithOkColor(); var eb = CreateEmbed().WithTitle(GetText(strs.streams_follow_title)).WithOkColor();
for (var index = 0; index < elements.Count; index++) for (var index = 0; index < elements.Count; index++)
{ {
var elem = elements[index]; var elem = elements[index];

View file

@ -491,7 +491,7 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
public EmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true) public EmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
{ {
var embed = _sender.CreateEmbed() var embed = _sender.CreateEmbed(guildId)
.WithTitle(status.Name) .WithTitle(status.Name)
.WithUrl(status.StreamUrl) .WithUrl(status.StreamUrl)
.WithDescription(status.StreamUrl) .WithDescription(status.StreamUrl)

View file

@ -135,7 +135,7 @@ public sealed partial class FlagTranslateService : IReadyExecutor, IEService
var response = await _ts.Translate("", lang, msg.Content).ConfigureAwait(false); var response = await _ts.Translate("", lang, msg.Content).ConfigureAwait(false);
await msg.ReplyAsync(embed: _sender.CreateEmbed() await msg.ReplyAsync(embed: _sender.CreateEmbed(tc.Guild?.Id)
.WithOkColor() .WithOkColor()
.WithFooter(user.ToString() ?? reaction.UserId.ToString(), .WithFooter(user.ToString() ?? reaction.UserId.ToString(),
user.RealAvatarUrl().ToString()) user.RealAvatarUrl().ToString())

View file

@ -67,7 +67,7 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
|| msg.Content.Equals(output, StringComparison.InvariantCultureIgnoreCase)) || msg.Content.Equals(output, StringComparison.InvariantCultureIgnoreCase))
return; return;
var embed = _sender.CreateEmbed().WithOkColor(); var embed = _sender.CreateEmbed(guild?.Id).WithOkColor();
if (autoDelete) if (autoDelete)
{ {

View file

@ -28,7 +28,7 @@ public partial class Searches
await ctx.Channel.TriggerTypingAsync(); await ctx.Channel.TriggerTypingAsync();
var translation = await _service.Translate(fromLang, toLang, text); var translation = await _service.Translate(fromLang, toLang, text);
var embed = _sender.CreateEmbed().WithOkColor().AddField(fromLang, text).AddField(toLang, translation); var embed = CreateEmbed().WithOkColor().AddField(fromLang, text).AddField(toLang, translation);
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
} }
@ -88,7 +88,7 @@ public partial class Searches
{ {
var langs = _service.GetLanguages().ToList(); var langs = _service.GetLanguages().ToList();
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithTitle(GetText(strs.supported_languages)) .WithTitle(GetText(strs.supported_languages))
.WithOkColor(); .WithOkColor();

View file

@ -25,7 +25,7 @@ public partial class Searches
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync($"{XKCD_URL}/info.0.json"); var res = await http.GetStringAsync($"{XKCD_URL}/info.0.json");
var comic = JsonConvert.DeserializeObject<XkcdComic>(res); var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithImageUrl(comic.ImageLink) .WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{comic.Num}") .WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{comic.Num}")
@ -60,7 +60,7 @@ public partial class Searches
var res = await http.GetStringAsync($"{XKCD_URL}/{num}/info.0.json"); var res = await http.GetStringAsync($"{XKCD_URL}/{num}/info.0.json");
var comic = JsonConvert.DeserializeObject<XkcdComic>(res); var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithImageUrl(comic.ImageLink) .WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{num}") .WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{num}")

View file

@ -5,48 +5,46 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
namespace EllieBot.Modules.Searches.Common.StreamNotifications.Providers; namespace EllieBot.Modules.Searches.Common.StreamNotifications.Providers;
public sealed class YoutubeProvide : Provider // public sealed class YoutubeProvide : Provider
{ // {
private readonly IGoogleApiService _api; // private readonly IGoogleApiService _api;
private readonly IHttpClientFactory _httpFactory; // private readonly IHttpClientFactory _httpFactory;
//
public override FollowedStream.FType Platform // public override FollowedStream.FType Platform
=> FollowedStream.FType.Youtube; // => FollowedStream.FType.Youtube;
//
public YoutubeProvide(IGoogleApiService api, IHttpClientFactory httpFactory) // public YoutubeProvide(IGoogleApiService api, IHttpClientFactory httpFactory)
{ // {
_api = api; // _api = api;
_httpFactory = httpFactory; // _httpFactory = httpFactory;
} // }
//
public override async Task<bool> IsValidUrl(string url) // public override async Task<bool> IsValidUrl(string url)
{ // {
await Task.Yield(); // await Task.Yield();
// todo implement // return url.Contains("youtube.com");
return url.Contains("youtube.com"); // }
} //
// public override Task<StreamData?> GetStreamDataByUrlAsync(string url)
public override Task<StreamData?> GetStreamDataByUrlAsync(string url) // {
{ // return default;
return default; // }
} //
// public override Task<StreamData?> GetStreamDataAsync(string login)
public override Task<StreamData?> GetStreamDataAsync(string login) // {
{ // var client = _httpFactory.CreateClient();
var client = _httpFactory.CreateClient(); //
// client.GetAsync()
client.GetAsync(); //
// return default;
// }
return default; //
} // public override Task<IReadOnlyCollection<StreamData>> GetStreamDataAsync(List<string> usernames)
// {
public override Task<IReadOnlyCollection<StreamData>> GetStreamDataAsync(List<string> usernames) // return default;
{ // }
return default; // }
} //
}
public sealed class TwitchHelixProvider : Provider public sealed class TwitchHelixProvider : Provider
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;

View file

@ -164,8 +164,8 @@ public sealed class AiAssistantService
funcs.Add(new() funcs.Add(new()
{ {
Name = cmd, Name = cmd,
Desc = commandStrings?.Desc?.Replace("currency", "flowers") ?? string.Empty, Desc = commandStrings.Desc?.Replace("currency", "flowers") ?? string.Empty,
Params = commandStrings?.Params.FirstOrDefault() Params = commandStrings.Params.FirstOrDefault()
?.Select(x => new AiCommandParamModel() ?.Select(x => new AiCommandParamModel()
{ {
Desc = x.Value.Desc, Desc = x.Value.Desc,
@ -219,6 +219,9 @@ public sealed class AiAssistantService
ITextChannel channel, ITextChannel channel,
string query) string query)
{ {
if (guild is not SocketGuild sg)
return false;
// check permissions // check permissions
var pcResult = await _permChecker.CheckPermsAsync( var pcResult = await _permChecker.CheckPermsAsync(
guild, guild,
@ -239,9 +242,6 @@ public sealed class AiAssistantService
{ {
if (model.Name == ".ai_chat") if (model.Name == ".ai_chat")
{ {
if (guild is not SocketGuild sg)
return false;
var sess = _cbs.GetOrCreateSession(guild.Id); var sess = _cbs.GetOrCreateSession(guild.Id);
if (sess is null) if (sess is null)
return false; return false;
@ -253,7 +253,7 @@ public sealed class AiAssistantService
var commandString = GetCommandString(model); var commandString = GetCommandString(model);
var msgTask = _sender.Response(channel) var msgTask = _sender.Response(channel)
.Embed(_sender.CreateEmbed() .Embed(_sender.CreateEmbed(guild?.Id)
.WithOkColor() .WithOkColor()
.WithAuthor(msg.Author.GlobalName, .WithAuthor(msg.Author.GlobalName,
msg.Author.RealAvatarUrl().ToString()) msg.Author.RealAvatarUrl().ToString())
@ -261,8 +261,7 @@ public sealed class AiAssistantService
.SendAsync(); .SendAsync();
await _cmdHandler.TryRunCommand( await _cmdHandler.TryRunCommand(sg,
(SocketGuild)guild,
(ISocketMessageChannel)channel, (ISocketMessageChannel)channel,
new DoAsUserMessage((SocketUserMessage)msg, msg.Author, commandString)); new DoAsUserMessage((SocketUserMessage)msg, msg.Author, commandString));

View file

@ -8,7 +8,7 @@ public sealed class AiCommandModel
public required string Name { get; set; } public required string Name { get; set; }
[JsonPropertyName("desc")] [JsonPropertyName("desc")]
public required string Desc { get; set; } public required string? Desc { get; set; }
[JsonPropertyName("params")] [JsonPropertyName("params")]
public required IReadOnlyList<AiCommandParamModel> Params { get; set; } public required IReadOnlyList<AiCommandParamModel> Params { get; set; }

View file

@ -127,7 +127,7 @@ public partial class Utility
.CurrentPage(page) .CurrentPage(page)
.Page((items, _) => .Page((items, _) =>
{ {
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.alias_list)) .WithTitle(GetText(strs.alias_list))
.WithDescription(string.Join("\n", items.Select(x => $"`{x.Key}` => `{x.Value}`"))); .WithDescription(string.Join("\n", items.Select(x => $"`{x.Key}` => `{x.Value}`")));

View file

@ -20,7 +20,7 @@ public partial class Utility
if (setting is null) if (setting is null)
{ {
var configNames = _settingServices.Select(x => x.Name); var configNames = _settingServices.Select(x => x.Name);
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.config_not_found(Format.Code(name)))) .WithDescription(GetText(strs.config_not_found(Format.Code(name))))
.AddField(GetText(strs.config_list), string.Join("\n", configNames)); .AddField(GetText(strs.config_list), string.Join("\n", configNames));
@ -43,7 +43,7 @@ public partial class Utility
name = name?.ToLowerInvariant(); name = name?.ToLowerInvariant();
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.config_list)) .WithTitle(GetText(strs.config_list))
.WithDescription(string.Join("\n", configNames)); .WithDescription(string.Join("\n", configNames));
@ -58,7 +58,7 @@ public partial class Utility
// if config name is not found, print error and the list of configs // if config name is not found, print error and the list of configs
if (setting is null) if (setting is null)
{ {
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText(strs.config_not_found(Format.Code(name)))) .WithDescription(GetText(strs.config_not_found(Format.Code(name))))
.AddField(GetText(strs.config_list), string.Join("\n", configNames)); .AddField(GetText(strs.config_list), string.Join("\n", configNames));
@ -75,7 +75,7 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(prop)) if (string.IsNullOrWhiteSpace(prop))
{ {
var propStrings = GetPropsAndValuesString(setting, propNames); var propStrings = GetPropsAndValuesString(setting, propNames);
var embed = _sender.CreateEmbed().WithOkColor().WithTitle($"⚙️ {setting.Name}").WithDescription(propStrings); var embed = CreateEmbed().WithOkColor().WithTitle($"⚙️ {setting.Name}").WithDescription(propStrings);
await Response().Embed(embed).SendAsync(); await Response().Embed(embed).SendAsync();
@ -88,7 +88,7 @@ public partial class Utility
if (!exists) if (!exists)
{ {
var propStrings = GetPropsAndValuesString(setting, propNames); var propStrings = GetPropsAndValuesString(setting, propNames);
var propErrorEmbed = _sender.CreateEmbed() var propErrorEmbed = CreateEmbed()
.WithErrorColor() .WithErrorColor()
.WithDescription(GetText( .WithDescription(GetText(
strs.config_prop_not_found(Format.Code(prop), Format.Code(name)))) strs.config_prop_not_found(Format.Code(prop), Format.Code(name))))
@ -110,7 +110,7 @@ public partial class Utility
if (prop != "currency.sign") if (prop != "currency.sign")
value = Format.Code(Format.Sanitize(value.TrimTo(1000)), "json"); value = Format.Code(Format.Sanitize(value.TrimTo(1000)), "json");
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.AddField("Config", Format.Code(setting.Name), true) .AddField("Config", Format.Code(setting.Name), true)
.AddField("Prop", Format.Code(prop), true) .AddField("Prop", Format.Code(prop), true)

View file

@ -17,7 +17,7 @@ public partial class Utility
return; return;
} }
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithPendingColor() .WithPendingColor()
.WithTitle(GetText(strs.giveaway_starting)) .WithTitle(GetText(strs.giveaway_starting))
.WithDescription(message); .WithDescription(message);
@ -103,7 +103,7 @@ public partial class Utility
return; return;
} }
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithTitle(GetText(strs.giveaway_list)) .WithTitle(GetText(strs.giveaway_list))
.WithOkColor(); .WithOkColor();

View file

@ -20,8 +20,14 @@ public sealed class GiveawayService : IEService, IReadyExecutor
private SortedSet<GiveawayModel> _giveawayCache = new SortedSet<GiveawayModel>(); private SortedSet<GiveawayModel> _giveawayCache = new SortedSet<GiveawayModel>();
private readonly EllieRandom _rng; private readonly EllieRandom _rng;
public GiveawayService(DbService db, IBotCreds creds, DiscordSocketClient client, public GiveawayService(
IMessageSenderService sender, IBotStrings strings, ILocalization localization, IMemoryCache cache) DbService db,
IBotCreds creds,
DiscordSocketClient client,
IMessageSenderService sender,
IBotStrings strings,
ILocalization localization,
IMemoryCache cache)
{ {
_db = db; _db = db;
_creds = creds; _creds = creds;
@ -37,7 +43,8 @@ public sealed class GiveawayService : IEService, IReadyExecutor
_client.ReactionRemoved += OnReactionRemoved; _client.ReactionRemoved += OnReactionRemoved;
} }
private async Task OnReactionRemoved(Cacheable<IUserMessage, ulong> msg, private async Task OnReactionRemoved(
Cacheable<IUserMessage, ulong> msg,
Cacheable<IMessageChannel, ulong> arg2, Cacheable<IMessageChannel, ulong> arg2,
SocketReaction r) SocketReaction r)
{ {
@ -55,7 +62,9 @@ public sealed class GiveawayService : IEService, IReadyExecutor
} }
} }
private async Task OnReactionAdded(Cacheable<IUserMessage, ulong> msg, Cacheable<IMessageChannel, ulong> ch, private async Task OnReactionAdded(
Cacheable<IUserMessage, ulong> msg,
Cacheable<IMessageChannel, ulong> ch,
SocketReaction r) SocketReaction r)
{ {
if (!r.User.IsSpecified) if (!r.User.IsSpecified)
@ -84,9 +93,9 @@ public sealed class GiveawayService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var gas = await ctx var gas = await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId)) .Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
.ToArrayAsync(); .ToArrayAsync();
lock (_giveawayCache) lock (_giveawayCache)
{ {
@ -101,8 +110,8 @@ public sealed class GiveawayService : IEService, IReadyExecutor
lock (_giveawayCache) lock (_giveawayCache)
{ {
toEnd = _giveawayCache.TakeWhile( toEnd = _giveawayCache.TakeWhile(
x => x.EndsAt <= DateTime.UtcNow.AddSeconds(15)) x => x.EndsAt <= DateTime.UtcNow.AddSeconds(15))
.ToArray(); .ToArray();
} }
foreach (var ga in toEnd) foreach (var ga in toEnd)
@ -119,29 +128,33 @@ public sealed class GiveawayService : IEService, IReadyExecutor
} }
} }
public async Task<int?> StartGiveawayAsync(ulong guildId, ulong channelId, ulong messageId, TimeSpan duration, public async Task<int?> StartGiveawayAsync(
ulong guildId,
ulong channelId,
ulong messageId,
TimeSpan duration,
string message) string message)
{ {
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
// first check if there are more than 5 giveaways // first check if there are more than 5 giveaways
var count = await ctx var count = await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.CountAsync(x => x.GuildId == guildId); .CountAsync(x => x.GuildId == guildId);
if (count >= 5) if (count >= 5)
return null; return null;
var endsAt = DateTime.UtcNow + duration; var endsAt = DateTime.UtcNow + duration;
var ga = await ctx.GetTable<GiveawayModel>() var ga = await ctx.GetTable<GiveawayModel>()
.InsertWithOutputAsync(() => new GiveawayModel .InsertWithOutputAsync(() => new GiveawayModel
{ {
GuildId = guildId, GuildId = guildId,
MessageId = messageId, MessageId = messageId,
ChannelId = channelId, ChannelId = channelId,
Message = message, Message = message,
EndsAt = endsAt, EndsAt = endsAt,
}); });
lock (_giveawayCache) lock (_giveawayCache)
{ {
@ -157,18 +170,18 @@ public sealed class GiveawayService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var giveaway = await ctx var giveaway = await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => x.GuildId == guildId && x.Id == id) .Where(x => x.GuildId == guildId && x.Id == id)
.LoadWith(x => x.Participants) .LoadWith(x => x.Participants)
.FirstOrDefaultAsyncLinqToDB(); .FirstOrDefaultAsyncLinqToDB();
if (giveaway is null) if (giveaway is null)
return false; return false;
await ctx await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => x.Id == id) .Where(x => x.Id == id)
.DeleteAsync(); .DeleteAsync();
lock (_giveawayCache) lock (_giveawayCache)
{ {
@ -222,9 +235,9 @@ public sealed class GiveawayService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var ga = await ctx var ga = await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => x.GuildId == guildId && x.Id == id) .Where(x => x.GuildId == guildId && x.Id == id)
.DeleteWithOutputAsync(); .DeleteWithOutputAsync();
if (ga is not { Length: > 0 }) if (ga is not { Length: > 0 })
return false; return false;
@ -242,9 +255,9 @@ public sealed class GiveawayService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
return await ctx return await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => x.GuildId == guildId) .Where(x => x.GuildId == guildId)
.ToListAsync(); .ToListAsync();
} }
public async Task<bool> JoinGivawayAsync(ulong messageId, ulong userId, string userName) public async Task<bool> JoinGivawayAsync(ulong messageId, ulong userId, string userName)
@ -252,23 +265,23 @@ public sealed class GiveawayService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var giveaway = await ctx var giveaway = await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => x.MessageId == messageId) .Where(x => x.MessageId == messageId)
.FirstOrDefaultAsyncLinqToDB(); .FirstOrDefaultAsyncLinqToDB();
if (giveaway is null) if (giveaway is null)
return false; return false;
// add the user to the database // add the user to the database
await ctx.GetTable<GiveawayUser>() await ctx.GetTable<GiveawayUser>()
.InsertAsync( .InsertAsync(
() => new GiveawayUser() () => new GiveawayUser()
{ {
UserId = userId, UserId = userId,
GiveawayId = giveaway.Id, GiveawayId = giveaway.Id,
Name = userName, Name = userName,
} }
); );
return true; return true;
} }
@ -278,17 +291,17 @@ public sealed class GiveawayService : IEService, IReadyExecutor
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var giveaway = await ctx var giveaway = await ctx
.GetTable<GiveawayModel>() .GetTable<GiveawayModel>()
.Where(x => x.MessageId == messageId) .Where(x => x.MessageId == messageId)
.FirstOrDefaultAsyncLinqToDB(); .FirstOrDefaultAsyncLinqToDB();
if (giveaway is null) if (giveaway is null)
return false; return false;
await ctx await ctx
.GetTable<GiveawayUser>() .GetTable<GiveawayUser>()
.Where(x => x.UserId == userId && x.GiveawayId == giveaway.Id) .Where(x => x.UserId == userId && x.GiveawayId == giveaway.Id)
.DeleteAsync(); .DeleteAsync();
return true; return true;
} }
@ -316,14 +329,14 @@ public sealed class GiveawayService : IEService, IReadyExecutor
{Format.Code(winner.UserId.ToString())} {Format.Code(winner.UserId.ToString())}
"""; """;
var eb = _sender.CreateEmbed() var eb = _sender.CreateEmbed(ch.GuildId)
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.giveaway_ended)) .WithTitle(GetText(strs.giveaway_ended))
.WithDescription(ga.Message) .WithDescription(ga.Message)
.WithFooter($"id: {new kwum(ga.Id).ToString()}") .WithFooter($"id: {new kwum(ga.Id).ToString()}")
.AddField(GetText(strs.winner), .AddField(GetText(strs.winner),
winnerStr, winnerStr,
true); true);
try try
{ {

View file

@ -1,39 +1,66 @@
using EllieBot.Db.Models; using SixLabors.ImageSharp.PixelFormats;
namespace EllieBot.Modules.Utility; namespace EllieBot.Modules.Utility;
public interface IGuildColorsService
{
}
public sealed class GuildColorsService : IGuildColorsService, IEService
{
private readonly DbService _db;
public GuildColorsService(DbService db)
{
_db = db;
}
public async Task<GuildColors?> GetGuildColors(ulong guildId)
{
// get from database and cache it with linq2db
await using var ctx = _db.GetDbContext();
return null;
// return await ctx
// .GuildColors
// .FirstOrDefaultAsync(x => x.GuildId == guildId);
}
}
public partial class Utility public partial class Utility
{ {
[Group("sclr")]
public class GuildColorsCommands : EllieModule<IGuildColorsService> public class GuildColorsCommands : EllieModule<IGuildColorsService>
{ {
[Cmd]
[UserPerm(GuildPerm.ManageGuild)]
[RequireContext(ContextType.Guild)]
public async Task ServerColorsShow()
{
EmbedBuilder[] ebs =
[
CreateEmbed()
.WithOkColor()
.WithDescription("\\✅"),
CreateEmbed()
.WithPendingColor()
.WithDescription("\\⏳\\⚠️"),
CreateEmbed()
.WithErrorColor()
.WithDescription("\\❌")
];
await Response()
.Embeds(ebs)
.SendAsync();
}
[Cmd]
[UserPerm(GuildPerm.ManageGuild)]
[RequireContext(ContextType.Guild)]
public async Task ServerColorOk([Leftover] Rgba32? color = null)
{
await _service.SetOkColor(ctx.Guild.Id, color);
await Response().Confirm(strs.server_color_set).SendAsync();
await ServerColorsShow();
}
[Cmd]
[UserPerm(GuildPerm.ManageGuild)]
[RequireContext(ContextType.Guild)]
public async Task ServerColorPending([Leftover] Rgba32? color = null)
{
await _service.SetPendingColor(ctx.Guild.Id, color);
await Response().Confirm(strs.server_color_set).SendAsync();
await ServerColorsShow();
}
[Cmd]
[UserPerm(GuildPerm.ManageGuild)]
[RequireContext(ContextType.Guild)]
public async Task ServerColorError([Leftover] Rgba32? color = null)
{
await _service.SetErrorColor(ctx.Guild.Id, color);
await Response().Confirm(strs.server_color_set).SendAsync();
await ServerColorsShow();
}
} }
} }

View file

@ -48,7 +48,7 @@ public partial class Utility
if (string.IsNullOrWhiteSpace(features)) if (string.IsNullOrWhiteSpace(features))
features = "-"; features = "-";
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithAuthor(GetText(strs.server_info)) .WithAuthor(GetText(strs.server_info))
.WithTitle(guild.Name) .WithTitle(guild.Name)
.AddField(GetText(strs.id), guild.Id.ToString(), true) .AddField(GetText(strs.id), guild.Id.ToString(), true)
@ -81,7 +81,7 @@ public partial class Utility
return; return;
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22); var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(ch.Id >> 22);
var usercount = (await ch.GetUsersAsync().FlattenAsync()).Count(); var usercount = (await ch.GetUsersAsync().FlattenAsync()).Count();
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle(ch.Name) .WithTitle(ch.Name)
.WithDescription(ch.Topic?.SanitizeMentions(true)) .WithDescription(ch.Topic?.SanitizeMentions(true))
.AddField(GetText(strs.id), ch.Id.ToString(), true) .AddField(GetText(strs.id), ch.Id.ToString(), true)
@ -101,7 +101,7 @@ public partial class Utility
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
.AddMilliseconds(role.Id >> 22); .AddMilliseconds(role.Id >> 22);
var usercount = role.Members.LongCount(); var usercount = role.Members.LongCount();
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithTitle(role.Name.TrimTo(128)) .WithTitle(role.Name.TrimTo(128))
.WithDescription(role.Permissions.ToList().Join(" | ")) .WithDescription(role.Permissions.ToList().Join(" | "))
.AddField(GetText(strs.id), role.Id.ToString(), true) .AddField(GetText(strs.id), role.Id.ToString(), true)
@ -129,7 +129,7 @@ public partial class Utility
if (user is null) if (user is null)
return; return;
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.AddField(GetText(strs.name), $"**{user.Username}**#{user.Discriminator}", true); .AddField(GetText(strs.name), $"**{user.Username}**#{user.Discriminator}", true);
if (!string.IsNullOrWhiteSpace(user.Nickname)) if (!string.IsNullOrWhiteSpace(user.Nickname))
embed.AddField(GetText(strs.nickname), user.Nickname, true); embed.AddField(GetText(strs.nickname), user.Nickname, true);

View file

@ -47,9 +47,9 @@ public partial class Utility
var i = 1; var i = 1;
if (!invs.Any()) if (!invs.Any())
return _sender.CreateEmbed().WithErrorColor().WithDescription(GetText(strs.no_invites)); return CreateEmbed().WithErrorColor().WithDescription(GetText(strs.no_invites));
var embed = _sender.CreateEmbed().WithOkColor(); var embed = CreateEmbed().WithOkColor();
foreach (var inv in invs) foreach (var inv in invs)
{ {
var expiryString = inv.MaxAge is null or 0 || inv.CreatedAt is null var expiryString = inv.MaxAge is null or 0 || inv.CreatedAt is null

View file

@ -147,7 +147,7 @@ public partial class Utility
private async Task ShowQuoteData(Quote quote) private async Task ShowQuoteData(Quote quote)
{ {
var inter = CreateEditInteraction(quote.Id, quote); var inter = CreateEditInteraction(quote.Id, quote);
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"{GetText(strs.quote_id($"`{new kwum(quote.Id)}"))}`") .WithTitle($"{GetText(strs.quote_id($"`{new kwum(quote.Id)}"))}`")
.WithDescription(Format.Sanitize(quote.Text).Replace("](", "]\\(").TrimTo(4096)) .WithDescription(Format.Sanitize(quote.Text).Replace("](", "]\\(").TrimTo(4096))
@ -188,7 +188,7 @@ public partial class Utility
var text = quote.Keyword.ToLowerInvariant() + ": " + quote.Text; var text = quote.Keyword.ToLowerInvariant() + ": " + quote.Text;
return _sender.CreateEmbed() return CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle($"{new kwum(quote.Id)} 💬 ") .WithTitle($"{new kwum(quote.Id)} 💬 ")
.WithDescription(text); .WithDescription(text);
@ -266,7 +266,7 @@ public partial class Utility
if (q is not null) if (q is not null)
{ {
await Response() await Response()
.Embed(_sender.CreateEmbed() .Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.quote_edited)) .WithTitle(GetText(strs.quote_edited))
.WithDescription($"#{quoteId}") .WithDescription($"#{quoteId}")

View file

@ -97,7 +97,7 @@ public partial class Utility
if (--page < 0) if (--page < 0)
return; return;
var embed = _sender.CreateEmbed() var embed = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(guildId is not null .WithTitle(GetText(guildId is not null
? strs.reminder_server_list ? strs.reminder_server_list

View file

@ -211,7 +211,7 @@ public class RemindService : IEService, IReadyExecutor, IRemindService
else else
{ {
await res await res
.Embed(_sender.CreateEmbed() .Embed(_sender.CreateEmbed(r.ServerId)
.WithOkColor() .WithOkColor()
.WithTitle("Reminder") .WithTitle("Reminder")
.AddField("Created At", .AddField("Created At",

View file

@ -64,7 +64,7 @@ public partial class Utility
} }
var description = GetRepeaterInfoString(removed); var description = GetRepeaterInfoString(removed);
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.repeater_removed(index + 1))) .WithTitle(GetText(strs.repeater_removed(index + 1)))
.WithDescription(description)).SendAsync(); .WithDescription(description)).SendAsync();
@ -187,7 +187,7 @@ public partial class Utility
} }
var description = GetRepeaterInfoString(runner); var description = GetRepeaterInfoString(runner);
await Response().Embed(_sender.CreateEmbed() await Response().Embed(CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.repeater_created)) .WithTitle(GetText(strs.repeater_created))
.WithDescription(description)).SendAsync(); .WithDescription(description)).SendAsync();
@ -205,7 +205,7 @@ public partial class Utility
return; return;
} }
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.list_of_repeaters)).WithOkColor(); var embed = CreateEmbed().WithTitle(GetText(strs.list_of_repeaters)).WithOkColor();
var i = 0; var i = 0;
foreach (var runner in repeaters.OrderBy(r => r.Repeater.Id)) foreach (var runner in repeaters.OrderBy(r => r.Repeater.Id))

View file

@ -51,7 +51,7 @@ public partial class Utility
.AddFooter(false) .AddFooter(false)
.Page((items, _) => .Page((items, _) =>
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.todo_list)); .WithTitle(GetText(strs.todo_list));
@ -95,6 +95,18 @@ public partial class Utility
await ctx.OkAsync(); await ctx.OkAsync();
} }
[Cmd]
public async Task TodoUncomplete(kwum todoId)
{
if (!await _service.UncompleteTodoAsync(ctx.User.Id, todoId))
{
await Response().Error(strs.todo_not_found).SendAsync();
return;
}
await ctx.OkAsync();
}
[Cmd] [Cmd]
public async Task TodoDelete(kwum todoId) public async Task TodoDelete(kwum todoId)
{ {
@ -175,7 +187,7 @@ public partial class Utility
.CurrentPage(page) .CurrentPage(page)
.Page((items, _) => .Page((items, _) =>
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithTitle(GetText(strs.todo_archive_list)) .WithTitle(GetText(strs.todo_archive_list))
.WithOkColor(); .WithOkColor();
@ -206,7 +218,7 @@ public partial class Utility
.AddFooter(false) .AddFooter(false)
.Page((items, _) => .Page((items, _) =>
{ {
var eb = _sender.CreateEmbed() var eb = CreateEmbed()
.WithOkColor() .WithOkColor()
.WithTitle(GetText(strs.todo_archived_list)); .WithTitle(GetText(strs.todo_archived_list));

View file

@ -21,22 +21,23 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
if (await ctx if (await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.ArchiveId == null) .Where(x => x.UserId == userId && x.ArchiveId == null)
.CountAsync() >= TODO_MAX_COUNT) .CountAsync()
>= TODO_MAX_COUNT)
{ {
return TodoAddResult.MaxLimitReached; return TodoAddResult.MaxLimitReached;
} }
await ctx await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.InsertAsync(() => new TodoModel() .InsertAsync(() => new TodoModel()
{ {
UserId = userId, UserId = userId,
Todo = todo, Todo = todo,
DateAdded = DateTime.UtcNow, DateAdded = DateTime.UtcNow,
IsDone = false, IsDone = false,
}); });
return TodoAddResult.Success; return TodoAddResult.Success;
} }
@ -45,10 +46,11 @@ public sealed class TodoService : IEService
{ {
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
return await ctx return await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.Id == todoId) .Where(x => x.UserId == userId && x.Id == todoId)
.Set(x => x.Todo, newMessage) .Set(x => x.Todo, newMessage)
.UpdateAsync() > 0; .UpdateAsync()
> 0;
} }
public async Task<TodoModel[]> GetAllTodosAsync(ulong userId) public async Task<TodoModel[]> GetAllTodosAsync(ulong userId)
@ -56,9 +58,9 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
return await ctx return await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.ArchiveId == null) .Where(x => x.UserId == userId && x.ArchiveId == null)
.ToArrayAsyncLinqToDB(); .ToArrayAsyncLinqToDB();
} }
public async Task<bool> CompleteTodoAsync(ulong userId, int todoId) public async Task<bool> CompleteTodoAsync(ulong userId, int todoId)
@ -66,10 +68,23 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var count = await ctx var count = await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.Id == todoId) .Where(x => x.UserId == userId && x.Id == todoId)
.Set(x => x.IsDone, true) .Set(x => x.IsDone, true)
.UpdateAsync(); .UpdateAsync();
return count > 0;
}
public async Task<bool> UncompleteTodoAsync(ulong userId, int todoId)
{
await using var ctx = _db.GetDbContext();
var count = await ctx
.GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.Id == todoId)
.Set(x => x.IsDone, false)
.UpdateAsync();
return count > 0; return count > 0;
} }
@ -79,9 +94,9 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var count = await ctx var count = await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.Id == todoId) .Where(x => x.UserId == userId && x.Id == todoId)
.DeleteAsync(); .DeleteAsync();
return count > 0; return count > 0;
} }
@ -91,9 +106,9 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
await ctx await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.ArchiveId == null) .Where(x => x.UserId == userId && x.ArchiveId == null)
.DeleteAsync(); .DeleteAsync();
} }
public async Task<ArchiveTodoResult> ArchiveTodosAsync(ulong userId, string name) public async Task<ArchiveTodoResult> ArchiveTodosAsync(ulong userId, string name)
@ -106,28 +121,28 @@ public sealed class TodoService : IEService
// check if the user reached the limit // check if the user reached the limit
var count = await ctx var count = await ctx
.GetTable<ArchivedTodoListModel>() .GetTable<ArchivedTodoListModel>()
.Where(x => x.UserId == userId) .Where(x => x.UserId == userId)
.CountAsync(); .CountAsync();
if (count >= ARCHIVE_MAX_COUNT) if (count >= ARCHIVE_MAX_COUNT)
return ArchiveTodoResult.MaxLimitReached; return ArchiveTodoResult.MaxLimitReached;
var inserted = await ctx var inserted = await ctx
.GetTable<ArchivedTodoListModel>() .GetTable<ArchivedTodoListModel>()
.InsertWithOutputAsync(() => new ArchivedTodoListModel() .InsertWithOutputAsync(() => new ArchivedTodoListModel()
{ {
UserId = userId, UserId = userId,
Name = name, Name = name,
}); });
// mark all existing todos as archived // mark all existing todos as archived
var updated = await ctx var updated = await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.ArchiveId == null) .Where(x => x.UserId == userId && x.ArchiveId == null)
.Set(x => x.ArchiveId, inserted.Id) .Set(x => x.ArchiveId, inserted.Id)
.UpdateAsync(); .UpdateAsync();
if (updated == 0) if (updated == 0)
{ {
@ -152,9 +167,9 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
return await ctx return await ctx
.GetTable<ArchivedTodoListModel>() .GetTable<ArchivedTodoListModel>()
.Where(x => x.UserId == userId) .Where(x => x.UserId == userId)
.ToArrayAsyncLinqToDB(); .ToArrayAsyncLinqToDB();
} }
public async Task<ArchivedTodoListModel?> GetArchivedTodoListAsync(ulong userId, int archiveId) public async Task<ArchivedTodoListModel?> GetArchivedTodoListAsync(ulong userId, int archiveId)
@ -162,10 +177,10 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
return await ctx return await ctx
.GetTable<ArchivedTodoListModel>() .GetTable<ArchivedTodoListModel>()
.Where(x => x.UserId == userId && x.Id == archiveId) .Where(x => x.UserId == userId && x.Id == archiveId)
.LoadWith(x => x.Items) .LoadWith(x => x.Items)
.FirstOrDefaultAsyncLinqToDB(); .FirstOrDefaultAsyncLinqToDB();
} }
public async Task<bool> ArchiveDeleteAsync(ulong userId, int archiveId) public async Task<bool> ArchiveDeleteAsync(ulong userId, int archiveId)
@ -173,9 +188,9 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
var count = await ctx var count = await ctx
.GetTable<ArchivedTodoListModel>() .GetTable<ArchivedTodoListModel>()
.Where(x => x.UserId == userId && x.Id == archiveId) .Where(x => x.UserId == userId && x.Id == archiveId)
.DeleteAsync(); .DeleteAsync();
return count > 0; return count > 0;
} }
@ -185,8 +200,8 @@ public sealed class TodoService : IEService
await using var ctx = _db.GetDbContext(); await using var ctx = _db.GetDbContext();
return await ctx return await ctx
.GetTable<TodoModel>() .GetTable<TodoModel>()
.Where(x => x.UserId == userId && x.Id == todoId) .Where(x => x.UserId == userId && x.Id == todoId)
.FirstOrDefaultAsyncLinqToDB(); .FirstOrDefaultAsyncLinqToDB();
} }
} }

View file

@ -13,7 +13,7 @@ public partial class Utility
{ {
var units = await _service.GetUnitsAsync(); var units = await _service.GetUnitsAsync();
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.convertlist)).WithOkColor(); var embed = CreateEmbed().WithTitle(GetText(strs.convertlist)).WithOkColor();
foreach (var g in units.GroupBy(x => x.UnitType)) foreach (var g in units.GroupBy(x => x.UnitType))

Some files were not shown because too many files have changed in this diff Show more