.btr and .sclr added, cleanup
This commit is contained in:
parent
97094dbe5d
commit
b411e8cb25
119 changed files with 9181 additions and 664 deletions
53
CHANGELOG.md
53
CHANGELOG.md
|
@ -2,6 +2,59 @@
|
|||
|
||||
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
|
||||
|
||||
### Added
|
||||
|
|
|
@ -74,6 +74,30 @@ public abstract class EllieContext : DbContext
|
|||
|
||||
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
|
||||
|
||||
modelBuilder.Entity<SarGroup>(sg =>
|
||||
|
|
|
@ -5,14 +5,15 @@ namespace EllieBot.Db.Models;
|
|||
public class GuildColors
|
||||
{
|
||||
[Key]
|
||||
public int Id { get; set; }
|
||||
public ulong GuildId { get; set; }
|
||||
|
||||
[Length(0, 9)]
|
||||
[MaxLength(9)]
|
||||
public string? OkColor { get; set; }
|
||||
|
||||
[Length(0, 9)]
|
||||
[MaxLength(9)]
|
||||
public string? ErrorColor { get; set; }
|
||||
|
||||
[Length(0, 9)]
|
||||
[MaxLength(9)]
|
||||
public string? PendingColor { get; set; }
|
||||
}
|
|
@ -13,24 +13,6 @@ public class GuildConfig : DbEntity
|
|||
|
||||
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
|
||||
public bool ExclusiveSelfAssignedRoles { get; set; }
|
||||
public bool AutoDeleteSelfAssignedRoleMessages { get; set; }
|
||||
|
@ -97,9 +79,5 @@ public class GuildConfig : DbEntity
|
|||
|
||||
public bool DisableGlobalExpressions { get; set; } = false;
|
||||
|
||||
#region Boost Message
|
||||
|
||||
public bool StickyRoles { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
25
src/EllieBot/Db/Models/btnrole/ButtonRole.cs
Normal file
25
src/EllieBot/Db/Models/btnrole/ButtonRole.cs
Normal 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;
|
||||
}
|
4048
src/EllieBot/Migrations/PostgreSql/20241127120348_guildcolors.Designer.cs
generated
Normal file
4048
src/EllieBot/Migrations/PostgreSql/20241127120348_guildcolors.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -451,6 +451,65 @@ namespace EllieBot.Migrations.PostgreSql
|
|||
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 =>
|
||||
{
|
||||
b.Property<int>("ClubId")
|
||||
|
@ -1253,6 +1312,44 @@ namespace EllieBot.Migrations.PostgreSql
|
|||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
3122
src/EllieBot/Migrations/Sqlite/20241127120303_guildcolors.Designer.cs
generated
Normal file
3122
src/EllieBot/Migrations/Sqlite/20241127120303_guildcolors.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
72
src/EllieBot/Migrations/Sqlite/20241127120303_guildcolors.cs
Normal file
72
src/EllieBot/Migrations/Sqlite/20241127120303_guildcolors.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -335,6 +335,51 @@ namespace EllieBot.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.Property<int>("ClubId")
|
||||
|
@ -932,6 +977,35 @@ namespace EllieBot.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
|
|
@ -96,7 +96,7 @@ public partial class Administration : EllieModule<AdministrationService>
|
|||
var guild = (SocketGuild)ctx.Guild;
|
||||
var (enabled, channels) = _service.GetDelMsgOnCmdData(ctx.Guild.Id);
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.server_delmsgoncmd))
|
||||
.WithDescription(enabled ? "✅" : "❌");
|
||||
|
|
|
@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
|
|||
using EllieBot.Db.Models;
|
||||
using EllieBot.Modules.Administration._common.results;
|
||||
|
||||
namespace EllieBot.Modules.Administration.Services;
|
||||
namespace EllieBot.Modules.Administration;
|
||||
|
||||
public class AdministrationService : IEService
|
||||
{
|
||||
|
|
|
@ -254,6 +254,12 @@ public sealed class CleanupService : ICleanupService, IReadyExecutor, IEService
|
|||
.Contains(x.ServerId))
|
||||
.DeleteAsync();
|
||||
|
||||
// delete button roles
|
||||
await ctx.GetTable<ButtonRole>()
|
||||
.Where(x => !tempTable.Select(x => x.GuildId)
|
||||
.Contains(x.GuildId))
|
||||
.DeleteAsync();
|
||||
|
||||
return new()
|
||||
{
|
||||
GuildCount = guildIds.Keys.Count,
|
||||
|
|
|
@ -42,9 +42,9 @@ public partial class Administration
|
|||
.Page((items, _) =>
|
||||
{
|
||||
if (!items.Any())
|
||||
return _sender.CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
|
||||
return CreateEmbed().WithErrorColor().WithFooter(sql).WithDescription("-");
|
||||
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithFooter(sql)
|
||||
.WithTitle(string.Join(" ║ ", result.ColumnNames))
|
||||
|
@ -99,7 +99,7 @@ public partial class Administration
|
|||
{
|
||||
try
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle(GetText(strs.sql_confirm_exec))
|
||||
.WithDescription(Format.Code(sql));
|
||||
|
||||
|
@ -119,7 +119,7 @@ public partial class Administration
|
|||
[OwnerOnly]
|
||||
public async Task PurgeUser(ulong userId)
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithDescription(GetText(strs.purge_user_confirm(Format.Bold(userId.ToString()))));
|
||||
|
||||
if (!await PromptUserConfirmAsync(embed))
|
||||
|
|
|
@ -123,7 +123,7 @@ public partial class Administration
|
|||
|
||||
[Cmd]
|
||||
public async Task LanguagesList()
|
||||
=> await Response().Embed(_sender.CreateEmbed()
|
||||
=> await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.lang_list))
|
||||
.WithDescription(string.Join("\n",
|
||||
|
|
|
@ -122,7 +122,7 @@ public class MuteService : IEService
|
|||
return;
|
||||
|
||||
_ = Task.Run(() => _sender.Response(user)
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(_sender.CreateEmbed(user?.GuildId)
|
||||
.WithDescription($"You've been muted in {user.Guild} server")
|
||||
.AddField("Mute Type", type.ToString())
|
||||
.AddField("Moderator", mod.ToString())
|
||||
|
@ -140,7 +140,7 @@ public class MuteService : IEService
|
|||
return;
|
||||
|
||||
_ = Task.Run(() => _sender.Response(user)
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(_sender.CreateEmbed(user.GuildId)
|
||||
.WithDescription($"You've been unmuted in {user.Guild} server")
|
||||
.AddField("Unmute Type", type.ToString())
|
||||
.AddField("Moderator", mod.ToString())
|
||||
|
|
|
@ -36,7 +36,7 @@ public partial class Administration
|
|||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task DiscordPermOverrideReset()
|
||||
{
|
||||
var result = await PromptUserConfirmAsync(_sender.CreateEmbed()
|
||||
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(GetText(strs.perm_override_all_confirm)));
|
||||
|
||||
|
@ -65,7 +65,7 @@ public partial class Administration
|
|||
.CurrentPage(page)
|
||||
.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)
|
||||
eb.WithDescription(GetText(strs.perm_override_page_none));
|
||||
|
|
|
@ -241,7 +241,7 @@ public partial class Administration
|
|||
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)
|
||||
embed.AddField("Anti-Spam", GetAntiSpamString(spam).TrimTo(1024), true);
|
||||
|
|
|
@ -113,7 +113,7 @@ public partial class Administration
|
|||
{
|
||||
await progressMsg.ModifyAsync(props =>
|
||||
{
|
||||
props.Embed = _sender.CreateEmbed()
|
||||
props.Embed = CreateEmbed()
|
||||
.WithPendingColor()
|
||||
.WithDescription(GetText(strs.prune_progress(deleted, total)))
|
||||
.Build();
|
||||
|
|
269
src/EllieBot/Modules/Administration/Role/ButtonRolesCommands.cs
Normal file
269
src/EllieBot/Modules/Administration/Role/ButtonRolesCommands.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
154
src/EllieBot/Modules/Administration/Role/ButtonRolesService.cs
Normal file
154
src/EllieBot/Modules/Administration/Role/ButtonRolesService.cs
Normal 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();
|
||||
}
|
||||
}
|
|
@ -80,7 +80,7 @@ public partial class Administration
|
|||
.CurrentPage(page)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor();
|
||||
|
||||
var content = string.Empty;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#nullable disable
|
||||
using LinqToDB;
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using EllieBot.Common.ModuleBehaviors;
|
||||
using EllieBot.Modules.Patronage;
|
||||
using EllieBot.Db.Models;
|
||||
using OneOf.Types;
|
||||
using OneOf;
|
||||
|
@ -18,18 +16,15 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
private ConcurrentDictionary<ulong, List<ReactionRoleV2>> _cache;
|
||||
private readonly object _cacheLock = new();
|
||||
private readonly SemaphoreSlim _assignementLock = new(1, 1);
|
||||
private readonly IPatronageService _ps;
|
||||
|
||||
public ReactionRolesService(
|
||||
DiscordSocketClient client,
|
||||
IPatronageService ps,
|
||||
DbService db,
|
||||
IBotCreds creds)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
_ps = ps;
|
||||
_cache = new();
|
||||
}
|
||||
|
||||
|
@ -57,7 +52,7 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
var guild = _client.GetGuild(rero.GuildId);
|
||||
var role = guild?.GetRole(rero.RoleId);
|
||||
|
||||
if (role is null)
|
||||
if (guild is null || role is null)
|
||||
return default;
|
||||
|
||||
var user = guild.GetUser(userId) as IGuildUser
|
||||
|
@ -80,8 +75,8 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
_ = Task.Run(async () =>
|
||||
{
|
||||
var emote = await GetFixedEmoteAsync(cmsg, r.Emote);
|
||||
|
||||
var rero = reros.FirstOrDefault(x => x.Emote == emote.Name
|
||||
|
||||
var rero = reros.FirstOrDefault(x => x.Emote == emote.Name
|
||||
|| x.Emote == emote.ToString());
|
||||
if (rero is null)
|
||||
return;
|
||||
|
@ -96,10 +91,11 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
{
|
||||
if (user.RoleIds.Contains(role.Id))
|
||||
{
|
||||
await user.RemoveRoleAsync(role.Id, new RequestOptions()
|
||||
{
|
||||
AuditLogReason = $"Reaction role"
|
||||
});
|
||||
await user.RemoveRoleAsync(role.Id,
|
||||
new RequestOptions()
|
||||
{
|
||||
AuditLogReason = $"Reaction role"
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -111,7 +107,7 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// had to add this because for some reason, reactionremoved event's reaction doesn't have IsAnimated set,
|
||||
// causing the .ToString() to be wrong on animated custom emotes
|
||||
private async Task<IEmote> GetFixedEmoteAsync(
|
||||
|
@ -210,10 +206,11 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
}
|
||||
}
|
||||
|
||||
await user.AddRoleAsync(role.Id, new()
|
||||
{
|
||||
AuditLogReason = "Reaction role"
|
||||
});
|
||||
await user.AddRoleAsync(role.Id,
|
||||
new()
|
||||
{
|
||||
AuditLogReason = "Reaction role"
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -244,36 +241,23 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
int levelReq = 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(group);
|
||||
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(levelReq);
|
||||
|
||||
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>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
GuildId = guild.Id,
|
||||
ChannelId = msg.Channel.Id,
|
||||
{
|
||||
GuildId = guild.Id,
|
||||
ChannelId = msg.Channel.Id,
|
||||
|
||||
MessageId = msg.Id,
|
||||
Emote = emote,
|
||||
MessageId = msg.Id,
|
||||
Emote = emote,
|
||||
|
||||
RoleId = role.Id,
|
||||
Group = group,
|
||||
LevelReq = levelReq
|
||||
},
|
||||
RoleId = role.Id,
|
||||
Group = group,
|
||||
LevelReq = levelReq
|
||||
},
|
||||
(old) => new()
|
||||
{
|
||||
RoleId = role.Id,
|
||||
|
@ -286,8 +270,6 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
Emote = emote,
|
||||
});
|
||||
|
||||
await tran.CommitAsync();
|
||||
|
||||
var obj = new ReactionRoleV2()
|
||||
{
|
||||
GuildId = guild.Id,
|
||||
|
@ -380,9 +362,9 @@ public sealed class ReactionRolesService : IReadyExecutor, IEService, IReactionR
|
|||
var updated = ctx.GetTable<ReactionRoleV2>()
|
||||
.Where(x => x.GuildId == guildId && x.MessageId == fromMessageId)
|
||||
.UpdateWithOutput(old => new()
|
||||
{
|
||||
MessageId = toMessageId
|
||||
},
|
||||
{
|
||||
MessageId = toMessageId
|
||||
},
|
||||
(old, neu) => neu);
|
||||
lock (_cacheLock)
|
||||
{
|
||||
|
|
|
@ -174,12 +174,11 @@ public partial class Administration
|
|||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[Priority(0)]
|
||||
public async Task RoleColor(Color color, [Leftover] IRole role)
|
||||
public async Task RoleColor(Rgba32 color, [Leftover] IRole role)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rgba32 = color.ToPixel<Rgba32>();
|
||||
await role.ModifyAsync(r => r.Color = new Discord.Color(rgba32.R, rgba32.G, rgba32.B));
|
||||
await role.ModifyAsync(r => r.Color = new Discord.Color(color.R, color.G, color.B));
|
||||
await Response().Confirm(strs.rc(Format.Bold(role.Name))).SendAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
|
@ -62,7 +62,7 @@ public partial class Administration
|
|||
var (added, updated) = await _service.RefreshUsersAsync(users);
|
||||
|
||||
await message.ModifyAsync(x =>
|
||||
x.Embed = _sender.CreateEmbed()
|
||||
x.Embed = CreateEmbed()
|
||||
.WithDescription(GetText(strs.cache_users_done(added, updated)))
|
||||
.WithOkColor()
|
||||
.Build()
|
||||
|
@ -115,7 +115,7 @@ public partial class Administration
|
|||
_service.AddNewAutoCommand(cmd);
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.scadd))
|
||||
.AddField(GetText(strs.server),
|
||||
|
@ -343,7 +343,7 @@ public partial class Administration
|
|||
if (string.IsNullOrWhiteSpace(str))
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -129,25 +129,6 @@ public partial class Administration
|
|||
_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]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[UserPerm(GuildPerm.ManageMessages)]
|
||||
|
@ -244,7 +225,7 @@ public partial class Administration
|
|||
.GroupBy(x => x.SarGroupId)
|
||||
.OrderBy(x => x.Key);
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
else
|
||||
{
|
||||
await Response().Confirm(strs.self_assign_no_excl).SendAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
|
|
|
@ -129,7 +129,7 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
|
|||
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();
|
||||
var changes = await ctx.GetTable<SarGroup>()
|
||||
|
@ -142,8 +142,7 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
|
|||
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
// todo group not found
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
return changes[0];
|
||||
|
@ -154,7 +153,7 @@ public class SelfAssignedRolesService : IEService, IReadyExecutor
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
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)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ public partial class Administration
|
|||
var usrs = settings?.LogIgnores.Where(x => x.ItemType == IgnoredItemType.User).ToList()
|
||||
?? new List<IgnoredLogItem>();
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.AddField(GetText(strs.log_ignored_channels),
|
||||
chs.Count == 0
|
||||
|
|
|
@ -42,7 +42,7 @@ public partial class Administration
|
|||
.Items(timezoneStrings)
|
||||
.PageSize(timezonesPerPage)
|
||||
.CurrentPage(page)
|
||||
.Page((items, _) => _sender.CreateEmbed()
|
||||
.Page((items, _) => CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.timezones_available))
|
||||
.WithDescription(string.Join("\n", items)))
|
||||
|
|
|
@ -44,7 +44,7 @@ public partial class Administration
|
|||
try
|
||||
{
|
||||
await _sender.Response(user)
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithErrorColor()
|
||||
.WithDescription(GetText(strs.warned_on(ctx.Guild.ToString())))
|
||||
.AddField(GetText(strs.moderator), ctx.User.ToString())
|
||||
|
@ -64,7 +64,7 @@ public partial class Administration
|
|||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Exception occured while warning a user");
|
||||
var errorEmbed = _sender.CreateEmbed()
|
||||
var errorEmbed = CreateEmbed()
|
||||
.WithErrorColor()
|
||||
.WithDescription(GetText(strs.cant_apply_punishment));
|
||||
|
||||
|
@ -75,7 +75,7 @@ public partial class Administration
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed().WithOkColor();
|
||||
var embed = CreateEmbed().WithOkColor();
|
||||
if (punishment is null)
|
||||
embed.WithDescription(GetText(strs.user_warned(Format.Bold(user.ToString()))));
|
||||
else
|
||||
|
@ -184,7 +184,7 @@ public partial class Administration
|
|||
.Page((warnings, page) =>
|
||||
{
|
||||
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())
|
||||
embed.WithDescription(GetText(strs.warnings_none));
|
||||
|
@ -245,7 +245,7 @@ public partial class Administration
|
|||
+ $" | {total} ({all} - {forgiven})";
|
||||
});
|
||||
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.warnings_list))
|
||||
.WithDescription(string.Join("\n", ws));
|
||||
|
@ -457,7 +457,7 @@ public partial class Administration
|
|||
var user = await ctx.Client.GetUserAsync(userId);
|
||||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||
await _mute.TimedBan(ctx.Guild, userId, time.Time, (ctx.User + " | " + msg).TrimTo(512), banPrune);
|
||||
var toSend = _sender.CreateEmbed()
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||
.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 Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||
.AddField("ID", userId.ToString(), true))
|
||||
|
@ -523,7 +523,7 @@ public partial class Administration
|
|||
var banPrune = await _service.GetBanPruneAsync(ctx.Guild.Id) ?? 7;
|
||||
await ctx.Guild.AddBanAsync(user, banPrune, (ctx.User + " | " + msg).TrimTo(512));
|
||||
|
||||
var toSend = _sender.CreateEmbed()
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("⛔️ " + GetText(strs.banned_user))
|
||||
.AddField(GetText(strs.username), user.ToString(), true)
|
||||
|
@ -719,7 +719,7 @@ public partial class Administration
|
|||
{ await ctx.Guild.RemoveBanAsync(user); }
|
||||
catch { await ctx.Guild.RemoveBanAsync(user); }
|
||||
|
||||
var toSend = _sender.CreateEmbed()
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("☣ " + GetText(strs.sb_user))
|
||||
.AddField(GetText(strs.username), user.ToString(), true)
|
||||
|
@ -774,7 +774,7 @@ public partial class Administration
|
|||
|
||||
await user.KickAsync((ctx.User + " | " + msg).TrimTo(512));
|
||||
|
||||
var toSend = _sender.CreateEmbed()
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.kicked_user))
|
||||
.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));
|
||||
await _sender.Response(user)
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithPendingColor()
|
||||
.WithDescription(dmMessage))
|
||||
.SendAsync();
|
||||
|
@ -819,7 +819,7 @@ public partial class Administration
|
|||
|
||||
await user.SetTimeOutAsync(time.Time);
|
||||
|
||||
var toSend = _sender.CreateEmbed()
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("⏳ " + GetText(strs.timedout_user))
|
||||
.AddField(GetText(strs.username), user.ToString(), true)
|
||||
|
@ -880,7 +880,7 @@ public partial class Administration
|
|||
if (string.IsNullOrWhiteSpace(missStr))
|
||||
missStr = "-";
|
||||
|
||||
var toSend = _sender.CreateEmbed()
|
||||
var toSend = CreateEmbed()
|
||||
.WithDescription(GetText(strs.mass_ban_in_progress(banning.Count)))
|
||||
.AddField(GetText(strs.invalid(missing.Count)), missStr)
|
||||
.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(
|
||||
GetText(strs.mass_ban_completed(
|
||||
banning.Count())))
|
||||
|
@ -928,7 +928,7 @@ public partial class Administration
|
|||
|
||||
//send a message but don't wait for it
|
||||
var banningMessageTask = Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithDescription(
|
||||
GetText(strs.mass_kill_in_progress(bans.Count())))
|
||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||
|
@ -949,7 +949,7 @@ public partial class Administration
|
|||
//wait for the message and edit it
|
||||
var banningMessage = await banningMessageTask;
|
||||
|
||||
await banningMessage.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
||||
await banningMessage.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||
.WithDescription(
|
||||
GetText(strs.mass_kill_completed(bans.Count())))
|
||||
.AddField(GetText(strs.invalid(missing)), missStr)
|
||||
|
|
|
@ -68,7 +68,7 @@ public partial class Administration
|
|||
else
|
||||
text = GetText(strs.no_vcroles);
|
||||
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.vc_role_list))
|
||||
.WithDescription(text)).SendAsync();
|
||||
|
|
|
@ -34,7 +34,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
|
|||
var ex = await _service.AddAsync(ctx.Guild?.Id, key, message);
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.expr_new))
|
||||
.WithDescription($"#{new kwum(ex.Id)}")
|
||||
|
@ -104,7 +104,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
|
|||
if (ex is not null)
|
||||
{
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.expr_edited))
|
||||
.WithDescription($"#{id}")
|
||||
|
@ -159,7 +159,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
|
|||
: " // " + string.Join(" ", ex.GetReactions())))
|
||||
.Join('\n');
|
||||
|
||||
return _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
|
||||
return CreateEmbed().WithOkColor().WithTitle(GetText(strs.expressions)).WithDescription(desc);
|
||||
})
|
||||
.SendAsync();
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
|
|||
|
||||
await Response()
|
||||
.Interaction(IsValidExprEditor() ? inter : null)
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription($"#{id}")
|
||||
.AddField(GetText(strs.trigger), found.Trigger.TrimTo(1024))
|
||||
|
@ -224,7 +224,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
|
|||
if (ex is not null)
|
||||
{
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.expr_deleted))
|
||||
.WithDescription($"#{id}")
|
||||
|
@ -375,7 +375,7 @@ public partial class EllieExpressions : EllieModule<EllieExpressionsService>
|
|||
[UserPerm(GuildPerm.Administrator)]
|
||||
public async Task ExprClear()
|
||||
{
|
||||
if (await PromptUserConfirmAsync(_sender.CreateEmbed()
|
||||
if (await PromptUserConfirmAsync(CreateEmbed()
|
||||
.WithTitle("Expression clear")
|
||||
.WithDescription("This will delete all expressions on this server.")))
|
||||
{
|
||||
|
|
|
@ -74,7 +74,7 @@ public partial class Gambling
|
|||
if (race.FinishedUsers[0].Bet > 0)
|
||||
{
|
||||
return Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.animal_race))
|
||||
.WithDescription(GetText(strs.animal_race_won_money(
|
||||
|
@ -132,7 +132,7 @@ public partial class Gambling
|
|||
raceMessage = await Response().Confirm(text).SendAsync();
|
||||
else
|
||||
{
|
||||
await msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
||||
await msg.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||
.WithTitle(GetText(strs.animal_race))
|
||||
.WithDescription(text)
|
||||
.WithOkColor()
|
||||
|
|
|
@ -59,7 +59,7 @@ public partial class Gambling
|
|||
{
|
||||
var bal = await _bank.GetBalanceAsync(ctx.User.Id);
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(GetText(strs.bank_balance(N(bal))));
|
||||
|
||||
|
@ -80,7 +80,7 @@ public partial class Gambling
|
|||
{
|
||||
var bal = await _bank.GetBalanceAsync(user.Id);
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(GetText(strs.bank_balance_other(user.ToString(), N(bal))));
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ public partial class Gambling
|
|||
{
|
||||
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
|
||||
|
||||
var result = await PromptUserConfirmAsync(_sender.CreateEmbed()
|
||||
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||
.WithDescription(
|
||||
$"""
|
||||
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()
|
||||
.WithAuthor(user)
|
||||
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
|
||||
|
@ -120,7 +120,7 @@ public partial class Gambling
|
|||
{
|
||||
var stats = await _gamblingTxTracker.GetAllAsync();
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor();
|
||||
|
||||
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
||||
|
@ -156,7 +156,7 @@ public partial class Gambling
|
|||
[OwnerOnly]
|
||||
public async Task GambleStatsReset()
|
||||
{
|
||||
if (!await PromptUserConfirmAsync(_sender.CreateEmbed()
|
||||
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||
.WithDescription(
|
||||
"""
|
||||
Are you sure?
|
||||
|
|
|
@ -95,7 +95,7 @@ public partial class Gambling
|
|||
|
||||
var cStr = string.Concat(c.Select(x => x[..^1] + " "));
|
||||
cStr += "\n" + string.Concat(c.Select(x => x.Last() + " "));
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("BlackJack")
|
||||
.AddField($"{dealerIcon} Dealer's Hand | Value: {bj.Dealer.GetHandValue()}", cStr);
|
||||
|
|
|
@ -132,7 +132,7 @@ public partial class Gambling
|
|||
else
|
||||
title = GetText(strs.connect4_draw);
|
||||
|
||||
return msg.ModifyAsync(x => x.Embed = _sender.CreateEmbed()
|
||||
return msg.ModifyAsync(x => x.Embed = CreateEmbed()
|
||||
.WithTitle(title)
|
||||
.WithDescription(GetGameStateText(game))
|
||||
.WithOkColor()
|
||||
|
@ -142,7 +142,7 @@ public partial class Gambling
|
|||
|
||||
private async Task Game_OnGameStateUpdated(Connect4Game game)
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle($"{game.CurrentPlayer.Username} vs {game.OtherPlayer.Username}")
|
||||
.WithDescription(GetGameStateText(game))
|
||||
.WithOkColor();
|
||||
|
|
|
@ -38,7 +38,7 @@ public partial class Gambling
|
|||
|
||||
var fileName = $"dice.{format.FileExtensions.First()}";
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.AddField(GetText(strs.roll2), gen)
|
||||
|
@ -115,7 +115,7 @@ public partial class Gambling
|
|||
d.Dispose();
|
||||
|
||||
var imageName = $"dice.{format.FileExtensions.First()}";
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.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++)
|
||||
rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]);
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.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);
|
||||
|
||||
var sum = arr.Sum();
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(GetText(strs.dice_rolled_num(n1 + $"`1 - {n2}`")))
|
||||
|
|
|
@ -56,7 +56,7 @@ public partial class Gambling
|
|||
foreach (var i in images)
|
||||
i.Dispose();
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor();
|
||||
|
||||
var toSend = string.Empty;
|
||||
|
@ -171,7 +171,7 @@ public partial class Gambling
|
|||
return;
|
||||
}
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(result.Card.GetEmoji())
|
||||
|
|
|
@ -30,12 +30,12 @@ public partial class Gambling
|
|||
private EmbedBuilder GetEmbed(CurrencyEvent.Type type, EventOptions opts, long currentPot)
|
||||
=> type switch
|
||||
{
|
||||
CurrencyEvent.Type.Reaction => _sender.CreateEmbed()
|
||||
CurrencyEvent.Type.Reaction => CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.event_title(type.ToString())))
|
||||
.WithDescription(GetReactionDescription(opts.Amount, currentPot))
|
||||
.WithFooter(GetText(strs.event_duration_footer(opts.Hours))),
|
||||
CurrencyEvent.Type.GameStatus => _sender.CreateEmbed()
|
||||
CurrencyEvent.Type.GameStatus => CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.event_title(type.ToString())))
|
||||
.WithDescription(GetGameStatusDescription(opts.Amount, currentPot))
|
||||
|
|
|
@ -84,7 +84,7 @@ public partial class Gambling
|
|||
? Format.Bold(GetText(strs.heads))
|
||||
: Format.Bold(GetText(strs.tails))));
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(msg)
|
||||
|
@ -130,7 +130,7 @@ public partial class Gambling
|
|||
str = Format.Bold(GetText(strs.better_luck));
|
||||
}
|
||||
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(str)
|
||||
.WithOkColor()
|
||||
|
|
|
@ -39,6 +39,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
private readonly GamblingTxTracker _gamblingTxTracker;
|
||||
private readonly IPatronageService _ps;
|
||||
private readonly RakebackService _rb;
|
||||
private readonly IBotCache _cache;
|
||||
|
||||
public Gambling(
|
||||
IGamblingService gs,
|
||||
|
@ -52,7 +53,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
IRemindService remind,
|
||||
IPatronageService patronage,
|
||||
GamblingTxTracker gamblingTxTracker,
|
||||
RakebackService rb)
|
||||
RakebackService rb,
|
||||
IBotCache cache)
|
||||
: base(configService)
|
||||
{
|
||||
_gs = gs;
|
||||
|
@ -63,6 +65,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
_remind = remind;
|
||||
_gamblingTxTracker = gamblingTxTracker;
|
||||
_rb = rb;
|
||||
_cache = cache;
|
||||
_ps = patronage;
|
||||
_rng = new EllieRandom();
|
||||
|
||||
|
@ -152,40 +155,10 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
}
|
||||
else if (Config.Timely.ProtType == TimelyProt.Captcha)
|
||||
{
|
||||
var password = _service.GeneratePassword();
|
||||
|
||||
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);
|
||||
});
|
||||
var password = await GetUserTimelyPassword(ctx.User.Id);
|
||||
var img = GetPasswordImage(password);
|
||||
using var stream = await img.ToStreamAsync();
|
||||
var captcha = await Response()
|
||||
// .Embed(_sender.CreateEmbed()
|
||||
// .WithOkColor()
|
||||
// .WithImageUrl("attachment://timely.png"))
|
||||
.File(stream, "timely.png")
|
||||
.SendAsync();
|
||||
try
|
||||
|
@ -195,6 +168,8 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await ClearUserTimelyPassword(ctx.User.Id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -205,6 +180,57 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
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()
|
||||
{
|
||||
var period = Config.Timely.Cooldown;
|
||||
|
@ -384,11 +410,11 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
trs = await uow.Set<CurrencyTransaction>().GetPageFor(userId, page);
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
.WithTitle(GetText(strs.transactions(
|
||||
((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
|
||||
?? $"{userId}")))
|
||||
.WithOkColor();
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle(GetText(strs.transactions(
|
||||
((SocketGuild)ctx.Guild)?.GetUser(userId)?.ToString()
|
||||
?? $"{userId}")))
|
||||
.WithOkColor();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var tr in trs)
|
||||
|
@ -435,7 +461,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
return;
|
||||
}
|
||||
|
||||
var eb = _sender.CreateEmbed().WithOkColor();
|
||||
var eb = CreateEmbed().WithOkColor();
|
||||
|
||||
eb.WithAuthor(ctx.User);
|
||||
eb.WithTitle(GetText(strs.transaction));
|
||||
|
@ -699,11 +725,11 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
str = GetText(strs.better_luck);
|
||||
}
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(Format.Bold(str))
|
||||
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
|
||||
.WithOkColor();
|
||||
var eb = CreateEmbed()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(Format.Bold(str))
|
||||
.AddField(GetText(strs.roll2), result.Roll.ToString(CultureInfo.InvariantCulture))
|
||||
.WithOkColor();
|
||||
|
||||
await Response().Embed(eb).SendAsync();
|
||||
}
|
||||
|
@ -766,9 +792,9 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
.CurrentPage(page)
|
||||
.Page((toSend, curPage) =>
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(CurrencySign + " " + GetText(strs.leaderboard));
|
||||
|
||||
if (!toSend.Any())
|
||||
{
|
||||
|
@ -829,7 +855,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed();
|
||||
var embed = CreateEmbed();
|
||||
|
||||
string msg;
|
||||
if (result.Result == RpsResultType.Draw)
|
||||
|
@ -893,12 +919,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
|||
sb.AppendLine();
|
||||
}
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(sb.ToString())
|
||||
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
|
||||
.AddField(GetText(strs.won), $"{(long)result.Won}", true)
|
||||
.WithAuthor(ctx.User);
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(sb.ToString())
|
||||
.AddField(GetText(strs.multiplier), $"{result.Multiplier:0.##}x", true)
|
||||
.AddField(GetText(strs.won), $"{(long)result.Won}", true)
|
||||
.WithAuthor(ctx.User);
|
||||
|
||||
|
||||
await Response().Embed(eb).SendAsync();
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
// decimal interest = 0,
|
||||
// TimeSpan dueIn = default)
|
||||
// {
|
||||
// var eb = _sender.CreateEmbed()
|
||||
// var eb = CreateEmbed()
|
||||
// .WithOkColor()
|
||||
// .WithDescription("User 0 Requests a loan from User {1}")
|
||||
// .AddField("Amount", amount, true)
|
||||
|
@ -53,7 +53,7 @@
|
|||
// .PageItems(loans)
|
||||
// .Page((items, page) =>
|
||||
// {
|
||||
// var eb = _sender.CreateEmbed()
|
||||
// var eb = CreateEmbed()
|
||||
// .WithOkColor()
|
||||
// .WithDescription("Current Loans");
|
||||
//
|
||||
|
|
|
@ -103,9 +103,9 @@ public partial class Gambling
|
|||
.Page((items, _) =>
|
||||
{
|
||||
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));
|
||||
})
|
||||
.SendAsync();
|
||||
|
|
|
@ -56,8 +56,8 @@ public partial class Gambling
|
|||
.Page((items, curPage) =>
|
||||
{
|
||||
if (!items.Any())
|
||||
return _sender.CreateEmbed().WithErrorColor().WithDescription(GetText(strs.shop_none));
|
||||
var embed = _sender.CreateEmbed().WithOkColor().WithTitle(GetText(strs.shop));
|
||||
return CreateEmbed().WithErrorColor().WithDescription(GetText(strs.shop_none));
|
||||
var embed = CreateEmbed().WithOkColor().WithTitle(GetText(strs.shop));
|
||||
|
||||
for (var i = 0; i < items.Count; i++)
|
||||
{
|
||||
|
@ -188,7 +188,7 @@ public partial class Gambling
|
|||
{
|
||||
await Response()
|
||||
.User(ctx.User)
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.shop_purchase(ctx.Guild.Name)))
|
||||
.AddField(GetText(strs.item), item.Text)
|
||||
|
@ -254,7 +254,7 @@ public partial class Gambling
|
|||
.Replace("%you.name%", buyer.GlobalName ?? buyer.Username)
|
||||
.Replace("%you.nick%", buyer.DisplayName);
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithPendingColor()
|
||||
.WithTitle("Executing shop command")
|
||||
.WithDescription(cmd);
|
||||
|
@ -541,7 +541,7 @@ public partial class Gambling
|
|||
|
||||
public EmbedBuilder EntryToEmbed(ShopEntry entry)
|
||||
{
|
||||
var embed = _sender.CreateEmbed().WithOkColor();
|
||||
var embed = CreateEmbed().WithOkColor();
|
||||
|
||||
if (entry.Type == ShopEntryType.Role)
|
||||
{
|
||||
|
|
|
@ -65,7 +65,7 @@ public partial class Gambling
|
|||
await using var imgStream = await image.ToStreamAsync();
|
||||
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(Format.Bold(text))
|
||||
.WithImageUrl($"attachment://result.png")
|
||||
|
|
|
@ -21,7 +21,7 @@ public partial class Gambling
|
|||
public async Task WaifuReset()
|
||||
{
|
||||
var price = _service.GetResetPrice(ctx.User);
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle(GetText(strs.waifu_reset_confirm))
|
||||
.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))
|
||||
.Page((items, page) =>
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("Waifus");
|
||||
|
||||
|
@ -266,7 +266,7 @@ public partial class Gambling
|
|||
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;
|
||||
foreach (var w in waifus)
|
||||
|
@ -350,7 +350,7 @@ public partial class Gambling
|
|||
if (string.IsNullOrWhiteSpace(fansStr))
|
||||
fansStr = "-";
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.waifu)
|
||||
+ " "
|
||||
|
@ -393,7 +393,7 @@ public partial class Gambling
|
|||
.CurrentPage(page)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var embed = _sender.CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
|
||||
var embed = CreateEmbed().WithTitle(GetText(strs.waifu_gift_shop)).WithOkColor();
|
||||
|
||||
items
|
||||
.ToList()
|
||||
|
|
|
@ -67,7 +67,7 @@ public partial class Games
|
|||
|
||||
private Task Game_OnStarted(AcrophobiaGame game)
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.acrophobia))
|
||||
.WithDescription(
|
||||
|
@ -92,7 +92,7 @@ public partial class Games
|
|||
|
||||
if (submissions.Length == 1)
|
||||
{
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(GetText(
|
||||
strs.acro_winner_only(
|
||||
|
@ -103,7 +103,7 @@ public partial class Games
|
|||
|
||||
|
||||
var i = 0;
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.acrophobia) + " - " + GetText(strs.submissions_closed))
|
||||
.WithDescription(GetText(strs.acro_nym_was(
|
||||
|
@ -127,7 +127,7 @@ public partial class Games
|
|||
|
||||
var table = votes.OrderByDescending(v => v.Value);
|
||||
var winner = table.First();
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.acrophobia))
|
||||
.WithDescription(GetText(strs.acro_winner(Format.Bold(winner.Key.UserName),
|
||||
|
|
|
@ -38,7 +38,7 @@ public partial class Games : EllieModule<GamesService>
|
|||
return;
|
||||
|
||||
var res = _service.GetEightballResponse(ctx.User.Id, question);
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(ctx.User.ToString())
|
||||
.AddField("❓ " + GetText(strs.question), question)
|
||||
|
|
|
@ -53,7 +53,7 @@ public partial class Games
|
|||
var hint = GetText(strs.nc_hint(prefix, _service.GetWidth(), _service.GetHeight()));
|
||||
await Response()
|
||||
.File(stream, "ncanvas.png")
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
#if GLOBAL_ELLIE
|
||||
.WithDescription("This is not available yet.")
|
||||
|
@ -164,7 +164,7 @@ public partial class Games
|
|||
Culture,
|
||||
_gcs.Data.Currency.Sign))));
|
||||
|
||||
if (!await PromptUserConfirmAsync(_sender.CreateEmbed()
|
||||
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||
.WithPendingColor()
|
||||
.WithDescription(prompt)))
|
||||
{
|
||||
|
@ -193,7 +193,7 @@ public partial class Games
|
|||
await using var stream = await img.ToStreamAsync();
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(GetText(strs.nc_pixel_set(Format.Code(position.ToString()))))
|
||||
.WithImageUrl($"attachment://zoom_{position}.png"))
|
||||
|
@ -231,7 +231,7 @@ public partial class Games
|
|||
var pos = new kwum(pixel.Position);
|
||||
await Response()
|
||||
.File(stream, $"{pixel.Position}.png")
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(string.IsNullOrWhiteSpace(pixel.Text) ? string.Empty : pixel.Text)
|
||||
.WithTitle(GetText(strs.nc_pixel(pos)))
|
||||
|
@ -263,7 +263,7 @@ public partial class Games
|
|||
return;
|
||||
}
|
||||
|
||||
if (!await PromptUserConfirmAsync(_sender.CreateEmbed()
|
||||
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||
.WithDescription(
|
||||
"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?")))
|
||||
|
@ -293,7 +293,7 @@ public partial class Games
|
|||
{
|
||||
await _service.ResetAsync();
|
||||
|
||||
if (!await PromptUserConfirmAsync(_sender.CreateEmbed()
|
||||
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||
.WithDescription(
|
||||
"This will delete all pixels and reset the canvas.\n\n"
|
||||
+ "Are you sure you want to continue?")))
|
||||
|
|
|
@ -94,7 +94,7 @@ public partial class Games
|
|||
if (removed is null)
|
||||
return;
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle($"Removed typing article #{index + 1}")
|
||||
.WithDescription(removed.Text.TrimTo(50))
|
||||
.WithOkColor();
|
||||
|
|
|
@ -160,7 +160,7 @@ public partial class Games
|
|||
{
|
||||
try
|
||||
{
|
||||
questionEmbed = _sender.CreateEmbed()
|
||||
questionEmbed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.trivia_game))
|
||||
.AddField(GetText(strs.category), question.Category)
|
||||
|
@ -189,7 +189,7 @@ public partial class Games
|
|||
{
|
||||
try
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithErrorColor()
|
||||
.WithTitle(GetText(strs.trivia_game))
|
||||
.WithDescription(GetText(strs.trivia_times_up(Format.Bold(question.Answer))));
|
||||
|
@ -221,7 +221,7 @@ public partial class Games
|
|||
{
|
||||
try
|
||||
{
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(strs.trivia_ended))
|
||||
.WithTitle(GetText(strs.leaderboard))
|
||||
|
@ -247,7 +247,7 @@ public partial class Games
|
|||
{
|
||||
try
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.trivia_game))
|
||||
.WithDescription(GetText(strs.trivia_win(user.Name,
|
||||
|
|
|
@ -114,7 +114,7 @@ public sealed partial class Help : EllieModule<HelpService>
|
|||
.AddFooter(false)
|
||||
.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())
|
||||
{
|
||||
|
@ -315,7 +315,7 @@ public sealed partial class Help : EllieModule<HelpService>
|
|||
.WithPlaceholder("Select a submodule to see detailed commands");
|
||||
|
||||
var groups = cmdsWithGroup.ToArray();
|
||||
var embed = _sender.CreateEmbed().WithOkColor();
|
||||
var embed = CreateEmbed().WithOkColor();
|
||||
foreach (var g in groups)
|
||||
{
|
||||
sb.AddOption(g.Key, g.Key);
|
||||
|
@ -383,7 +383,7 @@ public sealed partial class Help : EllieModule<HelpService>
|
|||
.Interaction(inter)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithTitle(GetText(strs.cmd_group_commands(group.Name)))
|
||||
.WithOkColor();
|
||||
|
||||
|
@ -520,7 +520,7 @@ public sealed partial class Help : EllieModule<HelpService>
|
|||
[OnlyPublicBot]
|
||||
public async Task Donate()
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("Thank you for considering to donate to the EllieBot project!");
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
|
|||
.PageSize(10)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.list_of_unloaded))
|
||||
.WithDescription(items.Join('\n'));
|
||||
|
@ -81,7 +81,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
|
|||
}
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.loaded_marmalades))
|
||||
.WithDescription(loaded.Select(x => x.Name)
|
||||
|
@ -136,7 +136,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
|
|||
.Paginated()
|
||||
.Items(output)
|
||||
.PageSize(10)
|
||||
.Page((items, _) => _sender.CreateEmbed()
|
||||
.Page((items, _) => CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.list_of_marmalades))
|
||||
.WithDescription(items.Join('\n')))
|
||||
|
@ -168,7 +168,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
|
|||
: $"{x.Prefix} {x.Name}"))
|
||||
.Join("\n");
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(strs.marmalade_info))
|
||||
.WithTitle(found.Name)
|
||||
|
@ -201,7 +201,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
|
|||
.CurrentPage(0)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor();
|
||||
|
||||
foreach (var marmalade in items)
|
||||
|
@ -224,7 +224,7 @@ public partial class Marmalade : EllieModule<IMarmaladeLoaderService>
|
|||
[OwnerOnly]
|
||||
public async Task MarmaladeSearch()
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithTitle(GetText(strs.list_of_marmalades))
|
||||
.WithOkColor();
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ public sealed partial class Music : EllieModule<IMusicService>
|
|||
|
||||
try
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(strs.queued_track) + " #" + (index + 1), MUSIC_ICON_URL)
|
||||
.WithDescription($"{trackInfo.PrettyName()}\n{GetText(strs.queue)} ")
|
||||
|
@ -314,7 +314,7 @@ public sealed partial class Music : EllieModule<IMusicService>
|
|||
if (!string.IsNullOrWhiteSpace(add))
|
||||
desc = add + "\n" + desc;
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithAuthor(
|
||||
GetText(strs.player_queue(curPage + 1, (tracks.Count / LQ_ITEMS_PER_PAGE) + 1)),
|
||||
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()
|
||||
.WithThumbnailUrl(x.Thumbnail)
|
||||
.WithDescription($"`{i + 1}.` {Format.Bold(x.Title)}\n\t{x.Url}"))
|
||||
|
@ -424,7 +424,7 @@ public sealed partial class Music : EllieModule<IMusicService>
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithAuthor(GetText(strs.removed_track) + " #" + index, MUSIC_ICON_URL)
|
||||
.WithDescription(track.PrettyName())
|
||||
.WithFooter(track.PrettyInfo())
|
||||
|
@ -592,7 +592,7 @@ public sealed partial class Music : EllieModule<IMusicService>
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle(track.Title.TrimTo(65))
|
||||
.WithAuthor(GetText(strs.track_moved), MUSIC_ICON_URL)
|
||||
.AddField(GetText(strs.from_position), $"#{from + 1}", true)
|
||||
|
@ -651,7 +651,7 @@ public sealed partial class Music : EllieModule<IMusicService>
|
|||
if (currentTrack is null)
|
||||
return;
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(strs.now_playing), MUSIC_ICON_URL)
|
||||
.WithDescription(currentTrack.PrettyName())
|
||||
|
@ -752,4 +752,20 @@ public sealed partial class Music : EllieModule<IMusicService>
|
|||
else
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,7 +49,7 @@ public sealed partial class Music
|
|||
playlists = uow.Set<MusicPlaylist>().GetPlaylistsOnPage(num);
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL)
|
||||
.WithDescription(string.Join("\n",
|
||||
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",
|
||||
items
|
||||
.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()
|
||||
.WithDescription(str);
|
||||
})
|
||||
|
@ -155,7 +155,7 @@ public sealed partial class Music
|
|||
}
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.playlist_saved))
|
||||
.AddField(GetText(strs.name), name)
|
||||
|
|
|
@ -33,4 +33,5 @@ public interface IMusicService
|
|||
Task SetMusicQualityAsync(ulong guildId, QualityPreset preset);
|
||||
Task<bool> ToggleQueueAutoPlayAsync(ulong guildId);
|
||||
Task<bool> FairplayAsync(ulong guildId);
|
||||
Task<IQueuedTrackInfo?> RemoveLastQueuedTrackAsync(ulong guildId);
|
||||
}
|
|
@ -179,11 +179,11 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
|||
return async (mp, trackInfo) =>
|
||||
{
|
||||
_ = lastFinishedMessage?.DeleteAsync();
|
||||
var embed = _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.finished_track), Music.MUSIC_ICON_URL)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter(trackInfo.PrettyTotalTime());
|
||||
var embed = _sender.CreateEmbed(guildId)
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.finished_track), Music.MUSIC_ICON_URL)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter(trackInfo.PrettyTotalTime());
|
||||
|
||||
lastFinishedMessage = await SendToOutputAsync(guildId, embed);
|
||||
};
|
||||
|
@ -195,11 +195,11 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
|||
return async (mp, trackInfo, index) =>
|
||||
{
|
||||
_ = lastPlayingMessage?.DeleteAsync();
|
||||
var embed = _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.playing_track(index + 1)), Music.MUSIC_ICON_URL)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}");
|
||||
var embed = _sender.CreateEmbed(guildId)
|
||||
.WithOkColor()
|
||||
.WithAuthor(GetText(guildId, strs.playing_track(index + 1)), Music.MUSIC_ICON_URL)
|
||||
.WithDescription(trackInfo.PrettyName())
|
||||
.WithFooter($"{mp.PrettyVolume()} | {trackInfo.PrettyInfo()}");
|
||||
|
||||
lastPlayingMessage = await SendToOutputAsync(guildId, embed);
|
||||
};
|
||||
|
@ -213,7 +213,7 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
|||
if (settings.AutoDisconnect)
|
||||
return LeaveVoiceChannelAsync(guildId);
|
||||
}
|
||||
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
|
@ -290,7 +290,8 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
|||
return "-";
|
||||
|
||||
return randomPlayingTrack.Title;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// number of servers currently listening to music
|
||||
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);
|
||||
|
||||
return count.ToString();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
yield return ("%music.queued%", () =>
|
||||
{
|
||||
var count = _players.Sum(x => x.Value.GetQueuedTracks().Count);
|
||||
|
||||
return count.ToString();
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#region Settings
|
||||
|
@ -434,5 +437,17 @@ public sealed class MusicService : IMusicService, IPlaceholderProvider
|
|||
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
|
||||
}
|
||||
}
|
|
@ -38,4 +38,5 @@ public interface IMusicPlayer : IDisposable
|
|||
void SetRepeat(PlayerRepeatType type);
|
||||
void ShuffleQueue();
|
||||
void SetFairplay();
|
||||
Task<IQueuedTrackInfo?> RemoveLastQueuedTrack();
|
||||
}
|
|
@ -20,4 +20,5 @@ public interface IMusicQueue
|
|||
void Shuffle(Random rng);
|
||||
bool IsLast();
|
||||
void ReorderFairly();
|
||||
int? GetLastQueuedIndex();
|
||||
}
|
|
@ -260,7 +260,6 @@ public sealed class MusicPlayer : IMusicPlayer
|
|||
IsStopped = true;
|
||||
Log.Error("Please install ffmpeg and make sure it's added to your "
|
||||
+ "PATH environment variable before trying again");
|
||||
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
@ -313,7 +312,7 @@ public sealed class MusicPlayer : IMusicPlayer
|
|||
{
|
||||
if (track.TrackInfo is SimpleTrackInfo sti)
|
||||
return sti.StreamUrl;
|
||||
|
||||
|
||||
return await _ytResolverFactory.GetYoutubeResolver().GetStreamUrl(track.TrackInfo.Id);
|
||||
}
|
||||
|
||||
|
@ -420,20 +419,20 @@ public sealed class MusicPlayer : IMusicPlayer
|
|||
break;
|
||||
|
||||
await chunk.Select(async data =>
|
||||
{
|
||||
var (query, platform) = data;
|
||||
try
|
||||
{
|
||||
var (query, platform) = data;
|
||||
try
|
||||
{
|
||||
await TryEnqueueTrackAsync(query, queuer, false, platform);
|
||||
errorCount = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error resolving {MusicPlatform} Track {TrackQuery}", platform, query);
|
||||
++errorCount;
|
||||
}
|
||||
})
|
||||
.WhenAll();
|
||||
await TryEnqueueTrackAsync(query, queuer, false, platform);
|
||||
errorCount = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Error resolving {MusicPlatform} Track {TrackQuery}", platform, query);
|
||||
++errorCount;
|
||||
}
|
||||
})
|
||||
.WhenAll();
|
||||
|
||||
await Task.Delay(1000);
|
||||
|
||||
|
@ -542,4 +541,15 @@ public sealed class MusicPlayer : IMusicPlayer
|
|||
{
|
||||
_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);
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
private LinkedList<QueuedTrackInfo> tracks;
|
||||
|
||||
private int index;
|
||||
private int? _lastQueued = null;
|
||||
|
||||
private readonly object _locker = new();
|
||||
|
||||
|
@ -74,9 +75,9 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
lock (_locker)
|
||||
{
|
||||
var added = new QueuedTrackInfo(trackInfo, queuer);
|
||||
enqueuedAt = tracks.Count;
|
||||
_lastQueued = enqueuedAt = tracks.Count;
|
||||
tracks.AddLast(added);
|
||||
|
||||
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +100,8 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
|
||||
tracks.AddAfter(currentNode, added);
|
||||
|
||||
_lastQueued = i;
|
||||
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +115,8 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
var added = new QueuedTrackInfo(track, queuer);
|
||||
tracks.AddLast(added);
|
||||
}
|
||||
|
||||
_lastQueued = tracks.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +151,7 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
lock (_locker)
|
||||
{
|
||||
tracks.Clear();
|
||||
_lastQueued = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +183,18 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
if (index < 0)
|
||||
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
|
||||
// // wrap back to start
|
||||
// if (_index == Count)
|
||||
|
@ -207,6 +225,11 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
if (from >= Count || to >= Count)
|
||||
return null;
|
||||
|
||||
if (from == _lastQueued)
|
||||
_lastQueued = to;
|
||||
else if (to == _lastQueued)
|
||||
_lastQueued += 1;
|
||||
|
||||
// update current track index
|
||||
if (from == index)
|
||||
{
|
||||
|
@ -267,6 +290,7 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
var list = tracks.ToArray();
|
||||
rng.Shuffle(list);
|
||||
tracks = new(list);
|
||||
_lastQueued = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,6 +342,8 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
if (queuers.Count == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
_lastQueued = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,4 +365,6 @@ public sealed partial class MusicQueue : IMusicQueue
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int? GetLastQueuedIndex() => _lastQueued;
|
||||
}
|
|
@ -32,9 +32,8 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
|
|||
_sender = sender;
|
||||
_config = config;
|
||||
_client = client;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Task OnReadyAsync()
|
||||
{
|
||||
_ps.OnNewPatronPayment += OnNewPayment;
|
||||
|
@ -55,29 +54,29 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
|
|||
await using (var ctx = _db.GetDbContext())
|
||||
{
|
||||
old = await ctx.GetTable<RewardedUser>()
|
||||
.Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
.Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (old is null)
|
||||
{
|
||||
await OnNewPayment(newPatron);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// no action as the amount is the same or lower
|
||||
if (old.AmountRewardedThisMonth >= newAmount)
|
||||
return;
|
||||
|
||||
|
||||
var count = await ctx.GetTable<RewardedUser>()
|
||||
.Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId)
|
||||
.UpdateAsync(_ => new()
|
||||
{
|
||||
PlatformUserId = newPatron.UniquePlatformUserId,
|
||||
UserId = newPatron.UserId,
|
||||
// amount before bonuses
|
||||
AmountRewardedThisMonth = newAmount,
|
||||
LastReward = newPatron.PaidAt
|
||||
});
|
||||
.Where(x => x.PlatformUserId == newPatron.UniquePlatformUserId)
|
||||
.UpdateAsync(_ => new()
|
||||
{
|
||||
PlatformUserId = newPatron.UniquePlatformUserId,
|
||||
UserId = newPatron.UserId,
|
||||
// amount before bonuses
|
||||
AmountRewardedThisMonth = newAmount,
|
||||
LastReward = newPatron.PaidAt
|
||||
});
|
||||
|
||||
// shouldn't ever happen
|
||||
if (count == 0)
|
||||
|
@ -90,21 +89,21 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
|
|||
(int)(newAmount / conf.PatreonCurrencyPerCent),
|
||||
newAmount,
|
||||
out var percentBonus);
|
||||
|
||||
|
||||
var realOldAmount = GetRealCurrencyReward(
|
||||
(int)(oldAmount / conf.PatreonCurrencyPerCent),
|
||||
oldAmount,
|
||||
out _);
|
||||
|
||||
|
||||
var diff = realNewAmount - realOldAmount;
|
||||
if (diff <= 0)
|
||||
return; // no action if new is lower
|
||||
|
||||
// if the user pledges 5$ or more, they will get X % more flowers where X is amount in dollars,
|
||||
// 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,
|
||||
$"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 ctx.GetTable<RewardedUser>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
PlatformUserId = patron.UniquePlatformUserId,
|
||||
UserId = patron.UserId,
|
||||
AmountRewardedThisMonth = amount,
|
||||
LastReward = patron.PaidAt,
|
||||
},
|
||||
{
|
||||
PlatformUserId = patron.UniquePlatformUserId,
|
||||
UserId = patron.UserId,
|
||||
AmountRewardedThisMonth = amount,
|
||||
LastReward = patron.PaidAt,
|
||||
},
|
||||
old => new()
|
||||
{
|
||||
AmountRewardedThisMonth = amount,
|
||||
|
@ -155,7 +154,7 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
|
|||
{
|
||||
PlatformUserId = patron.UniquePlatformUserId
|
||||
});
|
||||
|
||||
|
||||
var realAmount = GetRealCurrencyReward(patron.Amount, amount, out var percentBonus);
|
||||
await _cs.AddAsync(patron.UserId, realAmount, new("patron", "new"));
|
||||
_ = SendMessageToUser(patron.UserId,
|
||||
|
@ -171,9 +170,9 @@ public sealed class CurrencyRewardService : IEService, IReadyExecutor
|
|||
return;
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(message);
|
||||
|
||||
.WithOkColor()
|
||||
.WithDescription(message);
|
||||
|
||||
await _sender.Response(user).Embed(eb).SendAsync();
|
||||
}
|
||||
catch
|
||||
|
|
|
@ -53,7 +53,7 @@ public partial class Help
|
|||
//
|
||||
// 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}!")
|
||||
// .AddField("Tier", Format.Bold(patron.Tier.ToString()), true)
|
||||
|
@ -75,7 +75,7 @@ public partial class Help
|
|||
|
||||
var quotaStats = await _service.LimitStats(user.Id);
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithAuthor(user)
|
||||
.WithTitle(GetText(strs.patron_info))
|
||||
.WithOkColor();
|
||||
|
|
|
@ -60,12 +60,12 @@ public partial class Permissions
|
|||
.Page((pageItems, _) =>
|
||||
{
|
||||
if (pageItems.Count == 0)
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(title)
|
||||
.WithDescription(GetText(strs.empty_page));
|
||||
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithTitle(title)
|
||||
.WithDescription(pageItems.Join('\n'))
|
||||
.WithOkColor();
|
||||
|
|
|
@ -95,7 +95,7 @@ public partial class Permissions
|
|||
var output = items.Select(x =>
|
||||
$"{Format.Code(x.CommandName)}: {x.Seconds}s");
|
||||
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(output.Join("\n"));
|
||||
})
|
||||
|
|
|
@ -28,7 +28,7 @@ public partial class Permissions
|
|||
[RequireContext(ContextType.Guild)]
|
||||
public async Task FilterList()
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle("Server filter settings");
|
||||
|
||||
|
@ -316,7 +316,7 @@ public partial class Permissions
|
|||
.Items(fws)
|
||||
.PageSize(10)
|
||||
.CurrentPage(page)
|
||||
.Page((items, _) => _sender.CreateEmbed()
|
||||
.Page((items, _) => CreateEmbed()
|
||||
.WithTitle(GetText(strs.filter_word_list))
|
||||
.WithDescription(string.Join("\n", items))
|
||||
.WithOkColor())
|
||||
|
|
|
@ -30,7 +30,7 @@ public partial class Permissions
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed().WithOkColor();
|
||||
var embed = CreateEmbed().WithOkColor();
|
||||
|
||||
if (blockedModule.Any())
|
||||
embed.AddField(GetText(strs.blocked_modules), string.Join("\n", _service.BlockedModules));
|
||||
|
|
|
@ -24,7 +24,7 @@ public partial class Searches
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(animeData.Synopsis.Replace("<br>",
|
||||
Environment.NewLine,
|
||||
|
@ -56,7 +56,7 @@ public partial class Searches
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithDescription(mangaData.Synopsis.Replace("<br>",
|
||||
Environment.NewLine,
|
||||
|
|
|
@ -35,7 +35,7 @@ public partial class Searches
|
|||
}
|
||||
|
||||
var symbol = symbols.First();
|
||||
var promptEmbed = _sender.CreateEmbed()
|
||||
var promptEmbed = CreateEmbed()
|
||||
.WithDescription(symbol.Description)
|
||||
.WithTitle(GetText(strs.did_you_mean(symbol.Symbol)));
|
||||
|
||||
|
@ -67,7 +67,7 @@ public partial class Searches
|
|||
|
||||
var price = stock.Price.ToString("C2", localCulture);
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(stock.Symbol)
|
||||
.WithUrl($"https://www.tradingview.com/chart/?symbol={stock.Symbol}")
|
||||
|
@ -112,7 +112,7 @@ public partial class Searches
|
|||
|
||||
if (nearest is not null)
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle(GetText(strs.crypto_not_found))
|
||||
.WithDescription(
|
||||
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);
|
||||
var fileName = $"{crypto.Slug}_7d.png";
|
||||
|
||||
var toSend = _sender.CreateEmbed()
|
||||
var toSend = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor($"#{crypto.CmcRank}")
|
||||
.WithTitle($"{crypto.Name} ({crypto.Symbol})")
|
||||
|
@ -198,7 +198,7 @@ public partial class Searches
|
|||
.PageSize(10)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor();
|
||||
|
||||
if (items.Count > 0)
|
||||
|
|
|
@ -123,7 +123,7 @@ public partial class Searches
|
|||
if (!feeds.Any())
|
||||
{
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed().WithOkColor().WithDescription(GetText(strs.feed_no_feed)))
|
||||
.Embed(CreateEmbed().WithOkColor().WithDescription(GetText(strs.feed_no_feed)))
|
||||
.SendAsync();
|
||||
return;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ public partial class Searches
|
|||
.CurrentPage(page)
|
||||
.Page((items, cur) =>
|
||||
{
|
||||
var embed = _sender.CreateEmbed().WithOkColor();
|
||||
var embed = CreateEmbed().WithOkColor();
|
||||
var i = 0;
|
||||
var fs = string.Join("\n",
|
||||
items.Select(x => $"`{(cur * 10) + ++i}.` <#{x.ChannelId}> {x.Url}"));
|
||||
|
|
|
@ -44,7 +44,7 @@ public partial class Searches
|
|||
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle($"osu! {smode} profile for {user}")
|
||||
.WithThumbnailUrl($"https://a.ppy.sh/{userId}")
|
||||
|
@ -78,7 +78,7 @@ public partial class Searches
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle($"osu!Gatari {modeStr} profile for {user}")
|
||||
.WithThumbnailUrl($"https://a.gatari.pw/{userStats.Id}")
|
||||
|
@ -113,7 +113,7 @@ public partial class Searches
|
|||
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)
|
||||
eb.AddField(title, desc);
|
||||
|
|
|
@ -25,7 +25,7 @@ public partial class Searches
|
|||
if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant())
|
||||
{
|
||||
var p = kvp.Value;
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(kvp.Key.ToTitleCase())
|
||||
.WithDescription(p.BaseStats.ToString())
|
||||
|
@ -55,7 +55,7 @@ public partial class Searches
|
|||
{
|
||||
if (kvp.Key.ToUpperInvariant() == ability)
|
||||
{
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(kvp.Value.Name)
|
||||
.WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)
|
||||
|
|
|
@ -22,7 +22,7 @@ public partial class Searches
|
|||
}
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle($"{verse.BookName} {verse.Chapter}:{verse.Verse}")
|
||||
.WithDescription(verse.Text))
|
||||
|
@ -48,7 +48,7 @@ public partial class Searches
|
|||
await using var audio = await http.GetStreamAsync(arabic.Audio);
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.AddField("Arabic", arabic.Text)
|
||||
.AddField("English", english.Text)
|
||||
|
|
|
@ -59,14 +59,14 @@ public partial class Searches
|
|||
|
||||
descStr = descStr.TrimTo(4096);
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithTitle(query.TrimTo(64)!)
|
||||
.WithDescription(descStr)
|
||||
.WithFooter(
|
||||
GetText(strs.results_in(data.Info.TotalResults, data.Info.SearchTime)),
|
||||
"https://i.imgur.com/G46fm8J.png");
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithTitle(query.TrimTo(64)!)
|
||||
.WithDescription(descStr)
|
||||
.WithFooter(
|
||||
GetText(strs.results_in(data.Info.TotalResults, data.Info.SearchTime)),
|
||||
"https://i.imgur.com/G46fm8J.png");
|
||||
|
||||
await Response().Embed(embed).SendAsync();
|
||||
}
|
||||
|
@ -93,13 +93,13 @@ public partial class Searches
|
|||
return;
|
||||
}
|
||||
|
||||
EmbedBuilder CreateEmbed(IImageSearchResultEntry entry)
|
||||
EmbedBuilder CreateImageEmbed(IImageSearchResultEntry entry)
|
||||
{
|
||||
return _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithTitle(query)
|
||||
.WithImageUrl(entry.Link);
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithTitle(query)
|
||||
.WithImageUrl(entry.Link);
|
||||
}
|
||||
|
||||
await Response()
|
||||
|
@ -112,10 +112,11 @@ public partial class Searches
|
|||
var item = items.FirstOrDefault();
|
||||
|
||||
if (item is null)
|
||||
return _sender.CreateEmbed()
|
||||
.WithDescription(GetText(strs.no_search_results));
|
||||
return CreateEmbed()
|
||||
.WithPendingColor()
|
||||
.WithDescription(GetText(strs.no_search_results));
|
||||
|
||||
var embed = CreateEmbed(item);
|
||||
var embed = CreateImageEmbed(item);
|
||||
|
||||
return embed;
|
||||
})
|
||||
|
@ -184,7 +185,7 @@ public partial class Searches
|
|||
//
|
||||
// var descStr = string.Join("\n\n", desc);
|
||||
//
|
||||
// var embed = _sender.CreateEmbed()
|
||||
// var embed = CreateEmbed()
|
||||
// .WithAuthor(ctx.User.ToString(),
|
||||
// "https://upload.wikimedia.org/wikipedia/en/9/90/The_DuckDuckGo_Duck.png")
|
||||
// .WithDescription($"{GetText(strs.search_for)} **{query}**\n\n" + descStr)
|
||||
|
|
|
@ -39,7 +39,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
if (!await ValidateQuery(query))
|
||||
return;
|
||||
|
||||
var embed = _sender.CreateEmbed();
|
||||
var embed = CreateEmbed();
|
||||
var data = await _service.GetWeatherDataAsync(query);
|
||||
|
||||
if (data is null)
|
||||
|
@ -102,7 +102,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
return;
|
||||
}
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.time_new))
|
||||
.WithDescription(Format.Code(data.Time.ToString(Culture)))
|
||||
|
@ -128,7 +128,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
}
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(movie.Title)
|
||||
.WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/")
|
||||
|
@ -161,7 +161,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
private Task InternalRandomImage(SearchesService.ImageTag tag)
|
||||
{
|
||||
var url = _service.GetRandomImageUrl(tag);
|
||||
return Response().Embed(_sender.CreateEmbed().WithOkColor().WithImageUrl(url)).SendAsync();
|
||||
return Response().Embed(CreateEmbed().WithOkColor().WithImageUrl(url)).SendAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
|
@ -190,7 +190,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
}
|
||||
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.AddField(GetText(strs.original_url), $"<{query}>")
|
||||
.AddField(GetText(strs.short_url), $"<{shortLink}>"))
|
||||
|
@ -213,7 +213,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(card.Name)
|
||||
.WithDescription(card.Description)
|
||||
|
@ -246,7 +246,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
return;
|
||||
}
|
||||
|
||||
var embed = _sender.CreateEmbed().WithOkColor().WithImageUrl(card.Img);
|
||||
var embed = CreateEmbed().WithOkColor().WithImageUrl(card.Img);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(card.Flavor))
|
||||
embed.WithDescription(card.Flavor);
|
||||
|
@ -280,7 +280,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
.Page((items, _) =>
|
||||
{
|
||||
var item = items[0];
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithUrl(item.Permalink)
|
||||
.WithTitle(item.Word)
|
||||
|
@ -311,7 +311,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
.Page((items, _) =>
|
||||
{
|
||||
var model = items.First();
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithDescription(ctx.User.Mention)
|
||||
.AddField(GetText(strs.word), model.Word, true)
|
||||
.AddField(GetText(strs._class), model.WordType, true)
|
||||
|
@ -374,7 +374,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task Color(params Color[] colors)
|
||||
public async Task Color(params Rgba32[] colors)
|
||||
{
|
||||
if (!colors.Any())
|
||||
return;
|
||||
|
@ -403,7 +403,7 @@ public partial class Searches : EllieModule<SearchesService>
|
|||
|
||||
await Response()
|
||||
.Embed(
|
||||
_sender.CreateEmbed()
|
||||
CreateEmbed()
|
||||
.WithOkColor()
|
||||
.AddField("Username", usr.ToString())
|
||||
.AddField("Avatar Url", avatarUrl)
|
||||
|
|
|
@ -79,9 +79,9 @@ public partial class Searches
|
|||
.Page((elements, cur) =>
|
||||
{
|
||||
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++)
|
||||
{
|
||||
var elem = elements[index];
|
||||
|
|
|
@ -491,7 +491,7 @@ public sealed class StreamNotificationService : IEService, IReadyExecutor
|
|||
|
||||
public EmbedBuilder GetEmbed(ulong guildId, StreamData status, bool showViewers = true)
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = _sender.CreateEmbed(guildId)
|
||||
.WithTitle(status.Name)
|
||||
.WithUrl(status.StreamUrl)
|
||||
.WithDescription(status.StreamUrl)
|
||||
|
|
|
@ -135,7 +135,7 @@ public sealed partial class FlagTranslateService : IReadyExecutor, IEService
|
|||
|
||||
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()
|
||||
.WithFooter(user.ToString() ?? reaction.UserId.ToString(),
|
||||
user.RealAvatarUrl().ToString())
|
||||
|
|
|
@ -67,7 +67,7 @@ public sealed class TranslateService : ITranslateService, IExecNoCommand, IReady
|
|||
|| msg.Content.Equals(output, StringComparison.InvariantCultureIgnoreCase))
|
||||
return;
|
||||
|
||||
var embed = _sender.CreateEmbed().WithOkColor();
|
||||
var embed = _sender.CreateEmbed(guild?.Id).WithOkColor();
|
||||
|
||||
if (autoDelete)
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ public partial class Searches
|
|||
await ctx.Channel.TriggerTypingAsync();
|
||||
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();
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ public partial class Searches
|
|||
{
|
||||
var langs = _service.GetLanguages().ToList();
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithTitle(GetText(strs.supported_languages))
|
||||
.WithOkColor();
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ public partial class Searches
|
|||
using var http = _httpFactory.CreateClient();
|
||||
var res = await http.GetStringAsync($"{XKCD_URL}/info.0.json");
|
||||
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithImageUrl(comic.ImageLink)
|
||||
.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 comic = JsonConvert.DeserializeObject<XkcdComic>(res);
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithImageUrl(comic.ImageLink)
|
||||
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{XKCD_URL}/{num}")
|
||||
|
|
|
@ -5,48 +5,46 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
|
|||
|
||||
namespace EllieBot.Modules.Searches.Common.StreamNotifications.Providers;
|
||||
|
||||
public sealed class YoutubeProvide : Provider
|
||||
{
|
||||
private readonly IGoogleApiService _api;
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
|
||||
public override FollowedStream.FType Platform
|
||||
=> FollowedStream.FType.Youtube;
|
||||
|
||||
public YoutubeProvide(IGoogleApiService api, IHttpClientFactory httpFactory)
|
||||
{
|
||||
_api = api;
|
||||
_httpFactory = httpFactory;
|
||||
}
|
||||
|
||||
public override async Task<bool> IsValidUrl(string url)
|
||||
{
|
||||
await Task.Yield();
|
||||
// todo implement
|
||||
return url.Contains("youtube.com");
|
||||
}
|
||||
|
||||
public override Task<StreamData?> GetStreamDataByUrlAsync(string url)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public override Task<StreamData?> GetStreamDataAsync(string login)
|
||||
{
|
||||
var client = _httpFactory.CreateClient();
|
||||
|
||||
client.GetAsync();
|
||||
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public override Task<IReadOnlyCollection<StreamData>> GetStreamDataAsync(List<string> usernames)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
// public sealed class YoutubeProvide : Provider
|
||||
// {
|
||||
// private readonly IGoogleApiService _api;
|
||||
// private readonly IHttpClientFactory _httpFactory;
|
||||
//
|
||||
// public override FollowedStream.FType Platform
|
||||
// => FollowedStream.FType.Youtube;
|
||||
//
|
||||
// public YoutubeProvide(IGoogleApiService api, IHttpClientFactory httpFactory)
|
||||
// {
|
||||
// _api = api;
|
||||
// _httpFactory = httpFactory;
|
||||
// }
|
||||
//
|
||||
// public override async Task<bool> IsValidUrl(string url)
|
||||
// {
|
||||
// await Task.Yield();
|
||||
// return url.Contains("youtube.com");
|
||||
// }
|
||||
//
|
||||
// public override Task<StreamData?> GetStreamDataByUrlAsync(string url)
|
||||
// {
|
||||
// return default;
|
||||
// }
|
||||
//
|
||||
// public override Task<StreamData?> GetStreamDataAsync(string login)
|
||||
// {
|
||||
// var client = _httpFactory.CreateClient();
|
||||
//
|
||||
// client.GetAsync()
|
||||
//
|
||||
// return default;
|
||||
// }
|
||||
//
|
||||
// public override Task<IReadOnlyCollection<StreamData>> GetStreamDataAsync(List<string> usernames)
|
||||
// {
|
||||
// return default;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
public sealed class TwitchHelixProvider : Provider
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
|
|
|
@ -164,8 +164,8 @@ public sealed class AiAssistantService
|
|||
funcs.Add(new()
|
||||
{
|
||||
Name = cmd,
|
||||
Desc = commandStrings?.Desc?.Replace("currency", "flowers") ?? string.Empty,
|
||||
Params = commandStrings?.Params.FirstOrDefault()
|
||||
Desc = commandStrings.Desc?.Replace("currency", "flowers") ?? string.Empty,
|
||||
Params = commandStrings.Params.FirstOrDefault()
|
||||
?.Select(x => new AiCommandParamModel()
|
||||
{
|
||||
Desc = x.Value.Desc,
|
||||
|
@ -219,6 +219,9 @@ public sealed class AiAssistantService
|
|||
ITextChannel channel,
|
||||
string query)
|
||||
{
|
||||
if (guild is not SocketGuild sg)
|
||||
return false;
|
||||
|
||||
// check permissions
|
||||
var pcResult = await _permChecker.CheckPermsAsync(
|
||||
guild,
|
||||
|
@ -239,9 +242,6 @@ public sealed class AiAssistantService
|
|||
{
|
||||
if (model.Name == ".ai_chat")
|
||||
{
|
||||
if (guild is not SocketGuild sg)
|
||||
return false;
|
||||
|
||||
var sess = _cbs.GetOrCreateSession(guild.Id);
|
||||
if (sess is null)
|
||||
return false;
|
||||
|
@ -253,7 +253,7 @@ public sealed class AiAssistantService
|
|||
var commandString = GetCommandString(model);
|
||||
|
||||
var msgTask = _sender.Response(channel)
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(_sender.CreateEmbed(guild?.Id)
|
||||
.WithOkColor()
|
||||
.WithAuthor(msg.Author.GlobalName,
|
||||
msg.Author.RealAvatarUrl().ToString())
|
||||
|
@ -261,8 +261,7 @@ public sealed class AiAssistantService
|
|||
.SendAsync();
|
||||
|
||||
|
||||
await _cmdHandler.TryRunCommand(
|
||||
(SocketGuild)guild,
|
||||
await _cmdHandler.TryRunCommand(sg,
|
||||
(ISocketMessageChannel)channel,
|
||||
new DoAsUserMessage((SocketUserMessage)msg, msg.Author, commandString));
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ public sealed class AiCommandModel
|
|||
public required string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("desc")]
|
||||
public required string Desc { get; set; }
|
||||
public required string? Desc { get; set; }
|
||||
|
||||
[JsonPropertyName("params")]
|
||||
public required IReadOnlyList<AiCommandParamModel> Params { get; set; }
|
||||
|
|
|
@ -127,7 +127,7 @@ public partial class Utility
|
|||
.CurrentPage(page)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.alias_list))
|
||||
.WithDescription(string.Join("\n", items.Select(x => $"`{x.Key}` => `{x.Value}`")));
|
||||
|
|
|
@ -20,7 +20,7 @@ public partial class Utility
|
|||
if (setting is null)
|
||||
{
|
||||
var configNames = _settingServices.Select(x => x.Name);
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithErrorColor()
|
||||
.WithDescription(GetText(strs.config_not_found(Format.Code(name))))
|
||||
.AddField(GetText(strs.config_list), string.Join("\n", configNames));
|
||||
|
@ -43,7 +43,7 @@ public partial class Utility
|
|||
name = name?.ToLowerInvariant();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.config_list))
|
||||
.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 (setting is null)
|
||||
{
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithErrorColor()
|
||||
.WithDescription(GetText(strs.config_not_found(Format.Code(name))))
|
||||
.AddField(GetText(strs.config_list), string.Join("\n", configNames));
|
||||
|
@ -75,7 +75,7 @@ public partial class Utility
|
|||
if (string.IsNullOrWhiteSpace(prop))
|
||||
{
|
||||
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();
|
||||
|
@ -88,7 +88,7 @@ public partial class Utility
|
|||
if (!exists)
|
||||
{
|
||||
var propStrings = GetPropsAndValuesString(setting, propNames);
|
||||
var propErrorEmbed = _sender.CreateEmbed()
|
||||
var propErrorEmbed = CreateEmbed()
|
||||
.WithErrorColor()
|
||||
.WithDescription(GetText(
|
||||
strs.config_prop_not_found(Format.Code(prop), Format.Code(name))))
|
||||
|
@ -110,7 +110,7 @@ public partial class Utility
|
|||
if (prop != "currency.sign")
|
||||
value = Format.Code(Format.Sanitize(value.TrimTo(1000)), "json");
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.AddField("Config", Format.Code(setting.Name), true)
|
||||
.AddField("Prop", Format.Code(prop), true)
|
||||
|
|
|
@ -17,7 +17,7 @@ public partial class Utility
|
|||
return;
|
||||
}
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithPendingColor()
|
||||
.WithTitle(GetText(strs.giveaway_starting))
|
||||
.WithDescription(message);
|
||||
|
@ -103,7 +103,7 @@ public partial class Utility
|
|||
return;
|
||||
}
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithTitle(GetText(strs.giveaway_list))
|
||||
.WithOkColor();
|
||||
|
||||
|
|
|
@ -20,8 +20,14 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
private SortedSet<GiveawayModel> _giveawayCache = new SortedSet<GiveawayModel>();
|
||||
private readonly EllieRandom _rng;
|
||||
|
||||
public GiveawayService(DbService db, IBotCreds creds, DiscordSocketClient client,
|
||||
IMessageSenderService sender, IBotStrings strings, ILocalization localization, IMemoryCache cache)
|
||||
public GiveawayService(
|
||||
DbService db,
|
||||
IBotCreds creds,
|
||||
DiscordSocketClient client,
|
||||
IMessageSenderService sender,
|
||||
IBotStrings strings,
|
||||
ILocalization localization,
|
||||
IMemoryCache cache)
|
||||
{
|
||||
_db = db;
|
||||
_creds = creds;
|
||||
|
@ -37,7 +43,8 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
_client.ReactionRemoved += OnReactionRemoved;
|
||||
}
|
||||
|
||||
private async Task OnReactionRemoved(Cacheable<IUserMessage, ulong> msg,
|
||||
private async Task OnReactionRemoved(
|
||||
Cacheable<IUserMessage, ulong> msg,
|
||||
Cacheable<IMessageChannel, ulong> arg2,
|
||||
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)
|
||||
{
|
||||
if (!r.User.IsSpecified)
|
||||
|
@ -84,9 +93,9 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var gas = await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
|
||||
.ToArrayAsync();
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => Linq2DbExpressions.GuildOnShard(x.GuildId, _creds.TotalShards, _client.ShardId))
|
||||
.ToArrayAsync();
|
||||
|
||||
lock (_giveawayCache)
|
||||
{
|
||||
|
@ -101,8 +110,8 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
lock (_giveawayCache)
|
||||
{
|
||||
toEnd = _giveawayCache.TakeWhile(
|
||||
x => x.EndsAt <= DateTime.UtcNow.AddSeconds(15))
|
||||
.ToArray();
|
||||
x => x.EndsAt <= DateTime.UtcNow.AddSeconds(15))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
// first check if there are more than 5 giveaways
|
||||
var count = await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.CountAsync(x => x.GuildId == guildId);
|
||||
.GetTable<GiveawayModel>()
|
||||
.CountAsync(x => x.GuildId == guildId);
|
||||
|
||||
if (count >= 5)
|
||||
return null;
|
||||
|
||||
var endsAt = DateTime.UtcNow + duration;
|
||||
var ga = await ctx.GetTable<GiveawayModel>()
|
||||
.InsertWithOutputAsync(() => new GiveawayModel
|
||||
{
|
||||
GuildId = guildId,
|
||||
MessageId = messageId,
|
||||
ChannelId = channelId,
|
||||
Message = message,
|
||||
EndsAt = endsAt,
|
||||
});
|
||||
.InsertWithOutputAsync(() => new GiveawayModel
|
||||
{
|
||||
GuildId = guildId,
|
||||
MessageId = messageId,
|
||||
ChannelId = channelId,
|
||||
Message = message,
|
||||
EndsAt = endsAt,
|
||||
});
|
||||
|
||||
lock (_giveawayCache)
|
||||
{
|
||||
|
@ -157,18 +170,18 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var giveaway = await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.GuildId == guildId && x.Id == id)
|
||||
.LoadWith(x => x.Participants)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.GuildId == guildId && x.Id == id)
|
||||
.LoadWith(x => x.Participants)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
if (giveaway is null)
|
||||
return false;
|
||||
|
||||
await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.Id == id)
|
||||
.DeleteAsync();
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.Id == id)
|
||||
.DeleteAsync();
|
||||
|
||||
lock (_giveawayCache)
|
||||
{
|
||||
|
@ -222,9 +235,9 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var ga = await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.GuildId == guildId && x.Id == id)
|
||||
.DeleteWithOutputAsync();
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.GuildId == guildId && x.Id == id)
|
||||
.DeleteWithOutputAsync();
|
||||
|
||||
if (ga is not { Length: > 0 })
|
||||
return false;
|
||||
|
@ -242,9 +255,9 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
return await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.ToListAsync();
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.GuildId == guildId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
var giveaway = await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.MessageId == messageId)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.MessageId == messageId)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
if (giveaway is null)
|
||||
return false;
|
||||
|
||||
// add the user to the database
|
||||
await ctx.GetTable<GiveawayUser>()
|
||||
.InsertAsync(
|
||||
() => new GiveawayUser()
|
||||
{
|
||||
UserId = userId,
|
||||
GiveawayId = giveaway.Id,
|
||||
Name = userName,
|
||||
}
|
||||
);
|
||||
.InsertAsync(
|
||||
() => new GiveawayUser()
|
||||
{
|
||||
UserId = userId,
|
||||
GiveawayId = giveaway.Id,
|
||||
Name = userName,
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -278,17 +291,17 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var giveaway = await ctx
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.MessageId == messageId)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
.GetTable<GiveawayModel>()
|
||||
.Where(x => x.MessageId == messageId)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
if (giveaway is null)
|
||||
return false;
|
||||
|
||||
await ctx
|
||||
.GetTable<GiveawayUser>()
|
||||
.Where(x => x.UserId == userId && x.GiveawayId == giveaway.Id)
|
||||
.DeleteAsync();
|
||||
.GetTable<GiveawayUser>()
|
||||
.Where(x => x.UserId == userId && x.GiveawayId == giveaway.Id)
|
||||
.DeleteAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -316,14 +329,14 @@ public sealed class GiveawayService : IEService, IReadyExecutor
|
|||
{Format.Code(winner.UserId.ToString())}
|
||||
""";
|
||||
|
||||
var eb = _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.giveaway_ended))
|
||||
.WithDescription(ga.Message)
|
||||
.WithFooter($"id: {new kwum(ga.Id).ToString()}")
|
||||
.AddField(GetText(strs.winner),
|
||||
winnerStr,
|
||||
true);
|
||||
var eb = _sender.CreateEmbed(ch.GuildId)
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.giveaway_ended))
|
||||
.WithDescription(ga.Message)
|
||||
.WithFooter($"id: {new kwum(ga.Id).ToString()}")
|
||||
.AddField(GetText(strs.winner),
|
||||
winnerStr,
|
||||
true);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
@ -1,39 +1,66 @@
|
|||
using EllieBot.Db.Models;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
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
|
||||
{
|
||||
[Group("sclr")]
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ public partial class Utility
|
|||
if (string.IsNullOrWhiteSpace(features))
|
||||
features = "-";
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithAuthor(GetText(strs.server_info))
|
||||
.WithTitle(guild.Name)
|
||||
.AddField(GetText(strs.id), guild.Id.ToString(), true)
|
||||
|
@ -81,7 +81,7 @@ public partial class Utility
|
|||
return;
|
||||
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 embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle(ch.Name)
|
||||
.WithDescription(ch.Topic?.SanitizeMentions(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)
|
||||
.AddMilliseconds(role.Id >> 22);
|
||||
var usercount = role.Members.LongCount();
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithTitle(role.Name.TrimTo(128))
|
||||
.WithDescription(role.Permissions.ToList().Join(" | "))
|
||||
.AddField(GetText(strs.id), role.Id.ToString(), true)
|
||||
|
@ -129,7 +129,7 @@ public partial class Utility
|
|||
if (user is null)
|
||||
return;
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.AddField(GetText(strs.name), $"**{user.Username}**#{user.Discriminator}", true);
|
||||
if (!string.IsNullOrWhiteSpace(user.Nickname))
|
||||
embed.AddField(GetText(strs.nickname), user.Nickname, true);
|
||||
|
|
|
@ -47,9 +47,9 @@ public partial class Utility
|
|||
var i = 1;
|
||||
|
||||
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)
|
||||
{
|
||||
var expiryString = inv.MaxAge is null or 0 || inv.CreatedAt is null
|
||||
|
|
|
@ -147,7 +147,7 @@ public partial class Utility
|
|||
private async Task ShowQuoteData(Quote quote)
|
||||
{
|
||||
var inter = CreateEditInteraction(quote.Id, quote);
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle($"{GetText(strs.quote_id($"`{new kwum(quote.Id)}"))}`")
|
||||
.WithDescription(Format.Sanitize(quote.Text).Replace("](", "]\\(").TrimTo(4096))
|
||||
|
@ -188,7 +188,7 @@ public partial class Utility
|
|||
|
||||
var text = quote.Keyword.ToLowerInvariant() + ": " + quote.Text;
|
||||
|
||||
return _sender.CreateEmbed()
|
||||
return CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle($"{new kwum(quote.Id)} 💬 ")
|
||||
.WithDescription(text);
|
||||
|
@ -266,7 +266,7 @@ public partial class Utility
|
|||
if (q is not null)
|
||||
{
|
||||
await Response()
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.quote_edited))
|
||||
.WithDescription($"#{quoteId}")
|
||||
|
|
|
@ -97,7 +97,7 @@ public partial class Utility
|
|||
if (--page < 0)
|
||||
return;
|
||||
|
||||
var embed = _sender.CreateEmbed()
|
||||
var embed = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(guildId is not null
|
||||
? strs.reminder_server_list
|
||||
|
|
|
@ -211,7 +211,7 @@ public class RemindService : IEService, IReadyExecutor, IRemindService
|
|||
else
|
||||
{
|
||||
await res
|
||||
.Embed(_sender.CreateEmbed()
|
||||
.Embed(_sender.CreateEmbed(r.ServerId)
|
||||
.WithOkColor()
|
||||
.WithTitle("Reminder")
|
||||
.AddField("Created At",
|
||||
|
|
|
@ -64,7 +64,7 @@ public partial class Utility
|
|||
}
|
||||
|
||||
var description = GetRepeaterInfoString(removed);
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.repeater_removed(index + 1)))
|
||||
.WithDescription(description)).SendAsync();
|
||||
|
@ -187,7 +187,7 @@ public partial class Utility
|
|||
}
|
||||
|
||||
var description = GetRepeaterInfoString(runner);
|
||||
await Response().Embed(_sender.CreateEmbed()
|
||||
await Response().Embed(CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.repeater_created))
|
||||
.WithDescription(description)).SendAsync();
|
||||
|
@ -205,7 +205,7 @@ public partial class Utility
|
|||
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;
|
||||
foreach (var runner in repeaters.OrderBy(r => r.Repeater.Id))
|
||||
|
|
|
@ -51,7 +51,7 @@ public partial class Utility
|
|||
.AddFooter(false)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.todo_list));
|
||||
|
||||
|
@ -95,6 +95,18 @@ public partial class Utility
|
|||
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]
|
||||
public async Task TodoDelete(kwum todoId)
|
||||
{
|
||||
|
@ -175,7 +187,7 @@ public partial class Utility
|
|||
.CurrentPage(page)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithTitle(GetText(strs.todo_archive_list))
|
||||
.WithOkColor();
|
||||
|
||||
|
@ -206,7 +218,7 @@ public partial class Utility
|
|||
.AddFooter(false)
|
||||
.Page((items, _) =>
|
||||
{
|
||||
var eb = _sender.CreateEmbed()
|
||||
var eb = CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithTitle(GetText(strs.todo_archived_list));
|
||||
|
||||
|
|
|
@ -21,22 +21,23 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
if (await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.CountAsync() >= TODO_MAX_COUNT)
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.CountAsync()
|
||||
>= TODO_MAX_COUNT)
|
||||
{
|
||||
return TodoAddResult.MaxLimitReached;
|
||||
}
|
||||
|
||||
await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.InsertAsync(() => new TodoModel()
|
||||
{
|
||||
UserId = userId,
|
||||
Todo = todo,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IsDone = false,
|
||||
});
|
||||
.GetTable<TodoModel>()
|
||||
.InsertAsync(() => new TodoModel()
|
||||
{
|
||||
UserId = userId,
|
||||
Todo = todo,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IsDone = false,
|
||||
});
|
||||
|
||||
return TodoAddResult.Success;
|
||||
}
|
||||
|
@ -45,10 +46,11 @@ public sealed class TodoService : IEService
|
|||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
return await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.Set(x => x.Todo, newMessage)
|
||||
.UpdateAsync() > 0;
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.Set(x => x.Todo, newMessage)
|
||||
.UpdateAsync()
|
||||
> 0;
|
||||
}
|
||||
|
||||
public async Task<TodoModel[]> GetAllTodosAsync(ulong userId)
|
||||
|
@ -56,9 +58,9 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
return await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.ToArrayAsyncLinqToDB();
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.ToArrayAsyncLinqToDB();
|
||||
}
|
||||
|
||||
public async Task<bool> CompleteTodoAsync(ulong userId, int todoId)
|
||||
|
@ -66,10 +68,23 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var count = await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.Set(x => x.IsDone, true)
|
||||
.UpdateAsync();
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.Set(x => x.IsDone, true)
|
||||
.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;
|
||||
}
|
||||
|
@ -79,9 +94,9 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var count = await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.DeleteAsync();
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.DeleteAsync();
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
@ -91,9 +106,9 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.DeleteAsync();
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.DeleteAsync();
|
||||
}
|
||||
|
||||
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
|
||||
var count = await ctx
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId)
|
||||
.CountAsync();
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId)
|
||||
.CountAsync();
|
||||
|
||||
if (count >= ARCHIVE_MAX_COUNT)
|
||||
return ArchiveTodoResult.MaxLimitReached;
|
||||
|
||||
var inserted = await ctx
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.InsertWithOutputAsync(() => new ArchivedTodoListModel()
|
||||
{
|
||||
UserId = userId,
|
||||
Name = name,
|
||||
});
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.InsertWithOutputAsync(() => new ArchivedTodoListModel()
|
||||
{
|
||||
UserId = userId,
|
||||
Name = name,
|
||||
});
|
||||
|
||||
// mark all existing todos as archived
|
||||
|
||||
var updated = await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.Set(x => x.ArchiveId, inserted.Id)
|
||||
.UpdateAsync();
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||
.Set(x => x.ArchiveId, inserted.Id)
|
||||
.UpdateAsync();
|
||||
|
||||
if (updated == 0)
|
||||
{
|
||||
|
@ -140,7 +155,7 @@ public sealed class TodoService : IEService
|
|||
|
||||
return ArchiveTodoResult.NoTodos;
|
||||
}
|
||||
|
||||
|
||||
await tr.CommitAsync();
|
||||
|
||||
return ArchiveTodoResult.Success;
|
||||
|
@ -152,9 +167,9 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
return await ctx
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId)
|
||||
.ToArrayAsyncLinqToDB();
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId)
|
||||
.ToArrayAsyncLinqToDB();
|
||||
}
|
||||
|
||||
public async Task<ArchivedTodoListModel?> GetArchivedTodoListAsync(ulong userId, int archiveId)
|
||||
|
@ -162,10 +177,10 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
return await ctx
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == archiveId)
|
||||
.LoadWith(x => x.Items)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == archiveId)
|
||||
.LoadWith(x => x.Items)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
}
|
||||
|
||||
public async Task<bool> ArchiveDeleteAsync(ulong userId, int archiveId)
|
||||
|
@ -173,9 +188,9 @@ public sealed class TodoService : IEService
|
|||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
var count = await ctx
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == archiveId)
|
||||
.DeleteAsync();
|
||||
.GetTable<ArchivedTodoListModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == archiveId)
|
||||
.DeleteAsync();
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
@ -183,10 +198,10 @@ public sealed class TodoService : IEService
|
|||
public async Task<TodoModel?> GetTodoAsync(ulong userId, int todoId)
|
||||
{
|
||||
await using var ctx = _db.GetDbContext();
|
||||
|
||||
|
||||
return await ctx
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
.GetTable<TodoModel>()
|
||||
.Where(x => x.UserId == userId && x.Id == todoId)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ public partial class Utility
|
|||
{
|
||||
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))
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue