added addrolereward and removerolereward events for .notify
added .notify with no params showing events with descriptions added .winlb updated discord.net, redid migrations
This commit is contained in:
parent
f8eb585093
commit
29bac7739d
31 changed files with 635 additions and 219 deletions
38
CHANGELOG.md
38
CHANGELOG.md
|
@ -2,6 +2,44 @@
|
||||||
|
|
||||||
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
|
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
|
## [5.3.0] - 08.12.2024
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `.minesweeper` / `.mw` command - spoiler-based minesweeper minigame. Just for fun
|
||||||
|
- Added `.temprole` command - add a role to a user for a certain amount of time, after which the role will be removed
|
||||||
|
- Added `.xplevelset` - you can now set a level for a user in your server
|
||||||
|
- Added `.winlb` command - leaderboard of top gambling wins
|
||||||
|
- Added `.notify` command
|
||||||
|
- Specify an event to be notified about, and the bot will post the specified message in the current channel when the event occurs
|
||||||
|
- A few events supported right now:
|
||||||
|
- `UserLevelUp` when user levels up in the server
|
||||||
|
- `AddRoleReward` when a role is added to a user through .xpreward system
|
||||||
|
- `RemoveRoleReward` when a role is removed from a user through .xpreward system
|
||||||
|
- `Protection` when antialt, antiraid or antispam protection is triggered
|
||||||
|
- Added `.banner` command to see someone's banner
|
||||||
|
- Selfhosters:
|
||||||
|
- Added `.dmmod` and `.dmcmd` - you can now disable or enable whether commands or modules can be executed in bot's DMs
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Giveaway improvements
|
||||||
|
- Now mentions winners in a separate message
|
||||||
|
- Shows the timestamp of when the giveaway ends
|
||||||
|
- Xp Changes
|
||||||
|
- Removed awarded xp (the number in the brackets on the xp card)
|
||||||
|
- Awarded xp, (or the new level set) now directly apply to user's real xp
|
||||||
|
- Server xp notifications are now set by the server admin/manager in a specified channel
|
||||||
|
- `.sclr show` will now show hex code of the current color
|
||||||
|
- Queueing a song will now restart the playback if the queue is on the last track and stopped (there were no more tracks to play)
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- .setstream and .setactivity will now pause .ropl (rotating statuses)
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
|
||||||
## [5.2.4] - 29.11.2024
|
## [5.2.4] - 29.11.2024
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net.Core" Version="3.15.3" />
|
<PackageReference Include="Discord.Net.Core" Version="3.16.0" />
|
||||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||||
<PackageReference Include="YamlDotNet" Version="15.1.4" />
|
<PackageReference Include="YamlDotNet" Version="15.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -164,13 +164,18 @@ public abstract class EllieContext : DbContext
|
||||||
|
|
||||||
#region UserBetStats
|
#region UserBetStats
|
||||||
|
|
||||||
modelBuilder.Entity<UserBetStats>()
|
modelBuilder.Entity<UserBetStats>(ubs =>
|
||||||
.HasIndex(x => new
|
{
|
||||||
{
|
ubs.HasIndex(x => new
|
||||||
x.UserId,
|
{
|
||||||
x.Game
|
x.UserId,
|
||||||
})
|
x.Game
|
||||||
.IsUnique();
|
})
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
ubs.HasIndex(x => x.MaxWin)
|
||||||
|
.IsUnique(false);
|
||||||
|
});
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
@ -19,4 +19,6 @@ public enum NotifyType
|
||||||
{
|
{
|
||||||
LevelUp = 0,
|
LevelUp = 0,
|
||||||
Protection = 1, Prot = 1,
|
Protection = 1, Prot = 1,
|
||||||
|
AddRoleReward = 2,
|
||||||
|
RemoveRoleReward = 3,
|
||||||
}
|
}
|
|
@ -29,7 +29,7 @@
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6" />
|
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6" />
|
||||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageReference Include="Discord.Net" Version="3.15.3" />
|
<PackageReference Include="Discord.Net" Version="3.16.0" />
|
||||||
<PackageReference Include="CoreCLR-NCalc" Version="3.1.246" />
|
<PackageReference Include="CoreCLR-NCalc" Version="3.1.246" />
|
||||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
|
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
|
||||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414" />
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414" />
|
||||||
|
|
|
@ -5,6 +5,16 @@ namespace EllieBot.Migrations;
|
||||||
|
|
||||||
public static class MigrationQueries
|
public static class MigrationQueries
|
||||||
{
|
{
|
||||||
|
public static void MergeAwardedXp(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
UPDATE UserXpStats
|
||||||
|
SET Xp = AwardedXp + Xp,
|
||||||
|
AwardedXp = 0
|
||||||
|
WHERE AwardedXp > 0;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
public static void MigrateSar(MigrationBuilder migrationBuilder)
|
public static void MigrateSar(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.Sql("""
|
migrationBuilder.Sql("""
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace EllieBot.Migrations.PostgreSql
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class awardedxptemprolenotify : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropUniqueConstraint(
|
|
||||||
name: "ak_notify_guildid_event",
|
|
||||||
table: "notify");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "event",
|
|
||||||
table: "notify",
|
|
||||||
newName: "type");
|
|
||||||
|
|
||||||
migrationBuilder.AddUniqueConstraint(
|
|
||||||
name: "ak_notify_guildid_type",
|
|
||||||
table: "notify",
|
|
||||||
columns: new[] { "guildid", "type" });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropUniqueConstraint(
|
|
||||||
name: "ak_notify_guildid_type",
|
|
||||||
table: "notify");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "type",
|
|
||||||
table: "notify",
|
|
||||||
newName: "event");
|
|
||||||
|
|
||||||
migrationBuilder.AddUniqueConstraint(
|
|
||||||
name: "ak_notify_guildid_event",
|
|
||||||
table: "notify",
|
|
||||||
columns: new[] { "guildid", "event" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
namespace EllieBot.Migrations.PostgreSql
|
namespace EllieBot.Migrations.PostgreSql
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PostgreSqlContext))]
|
[DbContext(typeof(PostgreSqlContext))]
|
||||||
[Migration("20241208053644_awardedxp-temprole-notify")]
|
[Migration("20241208063342_awardedxp-temprole-notify")]
|
||||||
partial class awardedxptemprolenotify
|
partial class awardedxptemprolenotify
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -3485,6 +3485,9 @@ namespace EllieBot.Migrations.PostgreSql
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_userbetstats");
|
.HasName("pk_userbetstats");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin")
|
||||||
|
.HasDatabaseName("ix_userbetstats_maxwin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_userbetstats_userid_game");
|
.HasDatabaseName("ix_userbetstats_userid_game");
|
|
@ -0,0 +1,27 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace EllieBot.Migrations.PostgreSql
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class awardedxptemprolenotify : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_userbetstats_maxwin",
|
||||||
|
table: "userbetstats",
|
||||||
|
column: "maxwin");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_userbetstats_maxwin",
|
||||||
|
table: "userbetstats");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3482,6 +3482,9 @@ namespace EllieBot.Migrations.PostgreSql
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_userbetstats");
|
.HasName("pk_userbetstats");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin")
|
||||||
|
.HasDatabaseName("ix_userbetstats_maxwin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_userbetstats_userid_game");
|
.HasDatabaseName("ix_userbetstats_userid_game");
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace EllieBot.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class awardedxptemprolenotify : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropUniqueConstraint(
|
|
||||||
name: "AK_Notify_GuildId_Event",
|
|
||||||
table: "Notify");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "Event",
|
|
||||||
table: "Notify",
|
|
||||||
newName: "Type");
|
|
||||||
|
|
||||||
migrationBuilder.AddUniqueConstraint(
|
|
||||||
name: "AK_Notify_GuildId_Type",
|
|
||||||
table: "Notify",
|
|
||||||
columns: new[] { "GuildId", "Type" });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropUniqueConstraint(
|
|
||||||
name: "AK_Notify_GuildId_Type",
|
|
||||||
table: "Notify");
|
|
||||||
|
|
||||||
migrationBuilder.RenameColumn(
|
|
||||||
name: "Type",
|
|
||||||
table: "Notify",
|
|
||||||
newName: "Event");
|
|
||||||
|
|
||||||
migrationBuilder.AddUniqueConstraint(
|
|
||||||
name: "AK_Notify_GuildId_Event",
|
|
||||||
table: "Notify",
|
|
||||||
columns: new[] { "GuildId", "Event" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
namespace EllieBot.Migrations
|
namespace EllieBot.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteContext))]
|
[DbContext(typeof(SqliteContext))]
|
||||||
[Migration("20241208053549_awardedxp-temprole-notify")]
|
[Migration("20241208063257_awardedxp-temprole-notify")]
|
||||||
partial class awardedxptemprolenotify
|
partial class awardedxptemprolenotify
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -2593,6 +2593,8 @@ namespace EllieBot.Migrations
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace EllieBot.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class awardedxptemprolenotify : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserBetStats_MaxWin",
|
||||||
|
table: "UserBetStats",
|
||||||
|
column: "MaxWin");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_UserBetStats_MaxWin",
|
||||||
|
table: "UserBetStats");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2590,6 +2590,8 @@ namespace EllieBot.Migrations
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
using EllieBot.Db.Models;
|
||||||
|
using EllieBot.Modules.Administration;
|
||||||
|
|
||||||
|
namespace EllieBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
public record struct AddRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong UserId, long Level) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.reward.addrole";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.AddRoleReward;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var model = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.user%", g => g.GetUser(model.UserId)?.ToString() ?? model.UserId.ToString() },
|
||||||
|
{ "%event.role%", g => g.GetRole(model.RoleId)?.ToString() ?? model.RoleId.ToString() },
|
||||||
|
{ "%event.level%", g => model.Level.ToString() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ public record struct LevelUpNotifyModel(
|
||||||
return new Dictionary<string, Func<SocketGuild, string>>()
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
{
|
{
|
||||||
{ "%event.level%", g => data.Level.ToString() },
|
{ "%event.level%", g => data.Level.ToString() },
|
||||||
|
{ "%event.user%", g => g.GetUser(data.UserId)?.ToString() ?? data.UserId.ToString() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,11 +35,4 @@ public record struct LevelUpNotifyModel(
|
||||||
userId = UserId;
|
userId = UserId;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static class INotifyModelExtensions
|
|
||||||
{
|
|
||||||
public static TypedKey<T> GetTypedKey<T>(this T model)
|
|
||||||
where T : struct, INotifyModel
|
|
||||||
=> new(T.KeyName);
|
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#nullable disable
|
||||||
|
using EllieBot.Db.Models;
|
||||||
|
|
||||||
|
namespace EllieBot.Modules.Administration.Services;
|
||||||
|
|
||||||
|
public record struct ProtectionNotifyModel(ulong GuildId, ProtectionType ProtType, ulong UserId) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.protection";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.Protection;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var data = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.type%", g => data.ProtType.ToString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using EllieBot.Db.Models;
|
||||||
|
using EllieBot.Modules.Administration;
|
||||||
|
|
||||||
|
namespace EllieBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
public record struct RemoveRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong UserId, long Level) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.reward.removerole";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.RemoveRoleReward;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var model = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.user%", g => g.GetUser(model.UserId)?.ToString() ?? model.UserId.ToString() },
|
||||||
|
{ "%event.role%", g => g.GetRole(model.RoleId)?.ToString() ?? model.RoleId.ToString() },
|
||||||
|
{ "%event.level%", g => model.Level.ToString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using EllieBot.Db.Models;
|
using EllieBot.Db.Models;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace EllieBot.Modules.Administration;
|
namespace EllieBot.Modules.Administration;
|
||||||
|
|
||||||
|
@ -6,19 +7,108 @@ public partial class Administration
|
||||||
{
|
{
|
||||||
public class NotifyCommands : EllieModule<NotifyService>
|
public class NotifyCommands : EllieModule<NotifyService>
|
||||||
{
|
{
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task Notify()
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.Items(Enum.GetValues<NotifyType>())
|
||||||
|
.PageSize(5)
|
||||||
|
.Page((items, page) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.notify_available));
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
eb.AddField(item.ToString(), GetText(GetDescription(item)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocStr GetDescription(NotifyType item)
|
||||||
|
=> item switch
|
||||||
|
{
|
||||||
|
NotifyType.LevelUp => strs.notify_desc_levelup,
|
||||||
|
NotifyType.Protection => strs.notify_desc_protection,
|
||||||
|
NotifyType.AddRoleReward => strs.notify_desc_addrolerew,
|
||||||
|
NotifyType.RemoveRoleReward => strs.notify_desc_removerolerew,
|
||||||
|
_ => strs.notify_desc_not_found
|
||||||
|
};
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task Notify(NotifyType nType, [Leftover] string? message = null)
|
public async Task Notify(NotifyType nType, [Leftover] string? message = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(message))
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
{
|
{
|
||||||
await _service.DisableAsync(ctx.Guild.Id, nType);
|
// show msg
|
||||||
await Response().Confirm(strs.notify_off(nType)).SendAsync();
|
var conf = await _service.GetNotifyAsync(ctx.Guild.Id, nType);
|
||||||
|
if (conf is null)
|
||||||
|
{
|
||||||
|
await Response().Confirm(strs.notify_msg_not_set).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.notify_msg))
|
||||||
|
.WithDescription(conf.Message.TrimTo(2048))
|
||||||
|
.AddField(GetText(strs.notify_type), conf.Type.ToString(), true)
|
||||||
|
.AddField(GetText(strs.channel),
|
||||||
|
$"""
|
||||||
|
<#{conf.ChannelId}>
|
||||||
|
`{conf.ChannelId}`
|
||||||
|
""",
|
||||||
|
true);
|
||||||
|
|
||||||
|
await Response().Embed(eb).SendAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nType, message);
|
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nType, message);
|
||||||
await Response().Confirm(strs.notify_on(nType.ToString())).SendAsync();
|
await Response().Confirm(strs.notify_on($"<#{ctx.Channel.Id}>", Format.Bold(nType.ToString()))).SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task NotifyList(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var notifs = await _service.GetForGuildAsync(ctx.Guild.Id);
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var notif in notifs)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"""
|
||||||
|
- **{notif.Type}**
|
||||||
|
<#{notif.ChannelId}> `{notif.ChannelId}`
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifs.Count == 0)
|
||||||
|
sb.AppendLine(GetText(strs.notify_none));
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Confirm(GetText(strs.notify_list), text: sb.ToString())
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task NotifyClear(NotifyType nType)
|
||||||
|
{
|
||||||
|
await _service.DisableAsync(ctx.Guild.Id, nType);
|
||||||
|
await Response().Confirm(strs.notify_off(nType)).SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace EllieBot.Modules.Administration;
|
||||||
|
|
||||||
|
public static class NotifyModelExtensions
|
||||||
|
{
|
||||||
|
public static TypedKey<T> GetTypedKey<T>(this T model)
|
||||||
|
where T : struct, INotifyModel
|
||||||
|
=> new(T.KeyName);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using EllieBot.Common.ModuleBehaviors;
|
using EllieBot.Common.ModuleBehaviors;
|
||||||
using EllieBot.Db.Models;
|
using EllieBot.Db.Models;
|
||||||
|
using EllieBot.Generators;
|
||||||
|
|
||||||
namespace EllieBot.Modules.Administration;
|
namespace EllieBot.Modules.Administration;
|
||||||
|
|
||||||
|
@ -199,4 +200,27 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
|
||||||
|
|
||||||
guildsDict.TryRemove(guildId, out _);
|
guildsDict.TryRemove(guildId, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<Notify>> GetForGuildAsync(ulong guildId, int page = 0)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(page);
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var list = await ctx.GetTable<Notify>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.OrderBy(x => x.Type)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Notify?> GetNotifyAsync(ulong guildId, NotifyType nType)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
return await ctx.GetTable<Notify>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.Type == nType)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,36 +5,6 @@ using System.Threading.Channels;
|
||||||
|
|
||||||
namespace EllieBot.Modules.Administration.Services;
|
namespace EllieBot.Modules.Administration.Services;
|
||||||
|
|
||||||
public record struct ProtectionNotifyModel(ulong GuildId, ProtectionType ProtType, ulong UserId) : INotifyModel
|
|
||||||
{
|
|
||||||
public static string KeyName
|
|
||||||
=> "notify.protection";
|
|
||||||
|
|
||||||
public static NotifyType NotifyType
|
|
||||||
=> NotifyType.Protection;
|
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
|
||||||
{
|
|
||||||
var data = this;
|
|
||||||
return new Dictionary<string, Func<SocketGuild, string>>()
|
|
||||||
{
|
|
||||||
{ "%event.type%", g => data.ProtType.ToString() },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetUserId(out ulong userId)
|
|
||||||
{
|
|
||||||
userId = UserId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetGuildId(out ulong guildId)
|
|
||||||
{
|
|
||||||
guildId = GuildId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ProtectionService : IEService
|
public class ProtectionService : IEService
|
||||||
{
|
{
|
||||||
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate
|
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#nullable disable
|
#nullable disable
|
||||||
using EllieBot.Modules.Gambling.Common;
|
using EllieBot.Modules.Gambling.Common;
|
||||||
using EllieBot.Modules.Gambling.Services;
|
using EllieBot.Modules.Gambling.Services;
|
||||||
|
using EllieBot.Modules.Xp.Services;
|
||||||
|
|
||||||
namespace EllieBot.Modules.Gambling;
|
namespace EllieBot.Modules.Gambling;
|
||||||
|
|
||||||
|
@ -10,13 +11,19 @@ public partial class Gambling
|
||||||
public sealed class BetStatsCommands : GamblingModule<UserBetStatsService>
|
public sealed class BetStatsCommands : GamblingModule<UserBetStatsService>
|
||||||
{
|
{
|
||||||
private readonly GamblingTxTracker _gamblingTxTracker;
|
private readonly GamblingTxTracker _gamblingTxTracker;
|
||||||
|
private readonly IBotCache _cache;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public BetStatsCommands(
|
public BetStatsCommands(
|
||||||
GamblingTxTracker gamblingTxTracker,
|
GamblingTxTracker gamblingTxTracker,
|
||||||
GamblingConfigService gcs)
|
GamblingConfigService gcs,
|
||||||
|
IBotCache cache,
|
||||||
|
IUserService userService)
|
||||||
: base(gcs)
|
: base(gcs)
|
||||||
{
|
{
|
||||||
_gamblingTxTracker = gamblingTxTracker;
|
_gamblingTxTracker = gamblingTxTracker;
|
||||||
|
_cache = cache;
|
||||||
|
_userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
@ -25,12 +32,12 @@ public partial class Gambling
|
||||||
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
|
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
|
||||||
|
|
||||||
var result = await PromptUserConfirmAsync(CreateEmbed()
|
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
$"""
|
$"""
|
||||||
Are you sure you want to reset your bet stats for **{GetGameName(game)}**?
|
Are you sure you want to reset your bet stats for **{GetGameName(game)}**?
|
||||||
|
|
||||||
It will cost you {N(price)}
|
It will cost you {N(price)}
|
||||||
"""));
|
"""));
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return;
|
return;
|
||||||
|
@ -88,15 +95,15 @@ public partial class Gambling
|
||||||
};
|
};
|
||||||
|
|
||||||
var eb = CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(user)
|
.WithAuthor(user)
|
||||||
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
|
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
|
||||||
.AddField("Biggest Win", N(stats.Max(x => x.MaxWin)), true)
|
.AddField("Biggest Win", N(stats.Max(x => x.MaxWin)), true)
|
||||||
.AddField("Biggest Bet", N(stats.Max(x => x.MaxBet)), true)
|
.AddField("Biggest Bet", N(stats.Max(x => x.MaxBet)), true)
|
||||||
.AddField("# Bets", stats.Sum(x => x.WinCount + x.LoseCount), true)
|
.AddField("# Bets", stats.Sum(x => x.WinCount + x.LoseCount), true)
|
||||||
.AddField("Payout",
|
.AddField("Payout",
|
||||||
(stats.Sum(x => x.PaidOut) / stats.Sum(x => x.TotalBet)).ToString("P2", Culture),
|
(stats.Sum(x => x.PaidOut) / stats.Sum(x => x.TotalBet)).ToString("P2", Culture),
|
||||||
true);
|
true);
|
||||||
if (game == null)
|
if (game == null)
|
||||||
{
|
{
|
||||||
var favGame = stats.MaxBy(x => x.WinCount + x.LoseCount);
|
var favGame = stats.MaxBy(x => x.WinCount + x.LoseCount);
|
||||||
|
@ -115,23 +122,85 @@ public partial class Gambling
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly record struct WinLbStat(
|
||||||
|
int Rank,
|
||||||
|
string User,
|
||||||
|
GamblingGame Game,
|
||||||
|
long MaxWin);
|
||||||
|
|
||||||
|
private TypedKey<List<WinLbStat>> GetWinLbKey(int page)
|
||||||
|
=> new($"winlb:{page}");
|
||||||
|
|
||||||
|
private async Task<IReadOnlyCollection<WinLbStat>> GetCachedWinLbAsync(int page)
|
||||||
|
{
|
||||||
|
return await _cache.GetOrAddAsync(GetWinLbKey(page),
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
var items = await _service.GetWinLbAsync(page);
|
||||||
|
|
||||||
|
if (items.Count == 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var outputItems = new List<WinLbStat>(items.Count);
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var x = items[i];
|
||||||
|
var user = (await ctx.Client.GetUserAsync(x.UserId, CacheMode.CacheOnly))?.ToString()
|
||||||
|
?? (await _userService.GetUserAsync(x.UserId))?.Username
|
||||||
|
?? x.UserId.ToString();
|
||||||
|
|
||||||
|
outputItems.Add(new WinLbStat(i + 1 + (page * 10), user, x.Game, x.MaxWin));
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputItems;
|
||||||
|
},
|
||||||
|
expiry: TimeSpan.FromMinutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task WinLb(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.PageItems(p => GetCachedWinLbAsync(p))
|
||||||
|
.PageSize(10)
|
||||||
|
.Page((items, curPage) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
eb.AddField($"#{item.Rank} {item.User}",
|
||||||
|
$"[{item.Game}]{N(item.MaxWin)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task GambleStats()
|
public async Task GambleStats()
|
||||||
{
|
{
|
||||||
var stats = await _gamblingTxTracker.GetAllAsync();
|
var stats = await _gamblingTxTracker.GetAllAsync();
|
||||||
|
|
||||||
var eb = CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
||||||
str += "――――――――――――――――――――\n";
|
str += "――――――――――――――――――――\n";
|
||||||
foreach (var stat in stats)
|
foreach (var stat in stats)
|
||||||
{
|
{
|
||||||
var perc = (stat.PaidOut / stat.Bet).ToString("P2", Culture);
|
var perc = (stat.PaidOut / stat.Bet).ToString("P2", Culture);
|
||||||
str += $"`{stat.Feature.PadBoth(9)}`"
|
str += $"`{stat.Feature.PadBoth(9)}`"
|
||||||
+ $"|`{stat.Bet.ToString("N0").PadLeft(8, ' ')}`"
|
+ $"|`{stat.Bet.ToString("N0").PadLeft(8, ' ')}`"
|
||||||
+ $"|`{stat.PaidOut.ToString("N0").PadLeft(8, ' ')}`"
|
+ $"|`{stat.PaidOut.ToString("N0").PadLeft(8, ' ')}`"
|
||||||
+ $"|`{perc.PadLeft(6, ' ')}`\n";
|
+ $"|`{perc.PadLeft(6, ' ')}`\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
var bet = stats.Sum(x => x.Bet);
|
var bet = stats.Sum(x => x.Bet);
|
||||||
|
@ -143,9 +212,9 @@ public partial class Gambling
|
||||||
var tPerc = (paidOut / bet).ToString("P2", Culture);
|
var tPerc = (paidOut / bet).ToString("P2", Culture);
|
||||||
str += "――――――――――――――――――――\n";
|
str += "――――――――――――――――――――\n";
|
||||||
str += $"` {("TOTAL").PadBoth(7)}` "
|
str += $"` {("TOTAL").PadBoth(7)}` "
|
||||||
+ $"|**{N(bet).PadLeft(8, ' ')}**"
|
+ $"|**{N(bet).PadLeft(8, ' ')}**"
|
||||||
+ $"|**{N(paidOut).PadLeft(8, ' ')}**"
|
+ $"|**{N(paidOut).PadLeft(8, ' ')}**"
|
||||||
+ $"|`{tPerc.PadLeft(6, ' ')}`";
|
+ $"|`{tPerc.PadLeft(6, ' ')}`";
|
||||||
|
|
||||||
eb.WithDescription(str);
|
eb.WithDescription(str);
|
||||||
|
|
||||||
|
@ -157,13 +226,13 @@ public partial class Gambling
|
||||||
public async Task GambleStatsReset()
|
public async Task GambleStatsReset()
|
||||||
{
|
{
|
||||||
if (!await PromptUserConfirmAsync(CreateEmbed()
|
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
"""
|
"""
|
||||||
Are you sure?
|
Are you sure?
|
||||||
This will completely reset Gambling Stats.
|
This will completely reset Gambling Stats.
|
||||||
|
|
||||||
This action is irreversible.
|
This action is irreversible.
|
||||||
""")))
|
""")))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await GambleStats();
|
await GambleStats();
|
||||||
|
|
|
@ -52,4 +52,16 @@ public sealed class UserBetStatsService : IEService
|
||||||
await ctx.GetTable<GamblingStats>()
|
await ctx.GetTable<GamblingStats>()
|
||||||
.DeleteAsync();
|
.DeleteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<UserBetStats>> GetWinLbAsync(int page)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(page);
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
return await ctx.GetTable<UserBetStats>()
|
||||||
|
.OrderByDescending(x => x.MaxWin)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -103,11 +103,11 @@ public partial class Searches : EllieModule<SearchesService>
|
||||||
}
|
}
|
||||||
|
|
||||||
var eb = CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.time_new))
|
.WithTitle(GetText(strs.time_new))
|
||||||
.WithDescription(Format.Code(data.Time.ToString(Culture)))
|
.WithDescription(Format.Code(data.Time.ToString(Culture)))
|
||||||
.AddField(GetText(strs.location), string.Join('\n', data.Address.Split(", ")), true)
|
.AddField(GetText(strs.location), string.Join('\n', data.Address.Split(", ")), true)
|
||||||
.AddField(GetText(strs.timezone), data.TimeZoneName, true);
|
.AddField(GetText(strs.timezone), data.TimeZoneName, true);
|
||||||
|
|
||||||
await Response().Embed(eb).SendAsync();
|
await Response().Embed(eb).SendAsync();
|
||||||
}
|
}
|
||||||
|
@ -129,16 +129,16 @@ public partial class Searches : EllieModule<SearchesService>
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(movie.Title)
|
.WithTitle(movie.Title)
|
||||||
.WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/")
|
.WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/")
|
||||||
.WithDescription(movie.Plot.TrimTo(1000))
|
.WithDescription(movie.Plot.TrimTo(1000))
|
||||||
.AddField("Rating", movie.ImdbRating, true)
|
.AddField("Rating", movie.ImdbRating, true)
|
||||||
.AddField("Genre", movie.Genre, true)
|
.AddField("Genre", movie.Genre, true)
|
||||||
.AddField("Year", movie.Year, true)
|
.AddField("Year", movie.Year, true)
|
||||||
.WithImageUrl(Uri.IsWellFormedUriString(movie.Poster, UriKind.Absolute)
|
.WithImageUrl(Uri.IsWellFormedUriString(movie.Poster, UriKind.Absolute)
|
||||||
? movie.Poster
|
? movie.Poster
|
||||||
: null))
|
: null))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,9 +191,9 @@ public partial class Searches : EllieModule<SearchesService>
|
||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.AddField(GetText(strs.original_url), $"<{query}>")
|
.AddField(GetText(strs.original_url), $"<{query}>")
|
||||||
.AddField(GetText(strs.short_url), $"<{shortLink}>"))
|
.AddField(GetText(strs.short_url), $"<{shortLink}>"))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,13 +214,13 @@ public partial class Searches : EllieModule<SearchesService>
|
||||||
}
|
}
|
||||||
|
|
||||||
var embed = CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(card.Name)
|
.WithTitle(card.Name)
|
||||||
.WithDescription(card.Description)
|
.WithDescription(card.Description)
|
||||||
.WithImageUrl(card.ImageUrl)
|
.WithImageUrl(card.ImageUrl)
|
||||||
.AddField(GetText(strs.store_url), card.StoreUrl, true)
|
.AddField(GetText(strs.store_url), card.StoreUrl, true)
|
||||||
.AddField(GetText(strs.cost), card.ManaCost, true)
|
.AddField(GetText(strs.cost), card.ManaCost, true)
|
||||||
.AddField(GetText(strs.types), card.Types, true);
|
.AddField(GetText(strs.types), card.Types, true);
|
||||||
|
|
||||||
await Response().Embed(embed).SendAsync();
|
await Response().Embed(embed).SendAsync();
|
||||||
}
|
}
|
||||||
|
@ -281,10 +281,10 @@ public partial class Searches : EllieModule<SearchesService>
|
||||||
{
|
{
|
||||||
var item = items[0];
|
var item = items[0];
|
||||||
return CreateEmbed()
|
return CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithUrl(item.Permalink)
|
.WithUrl(item.Permalink)
|
||||||
.WithTitle(item.Word)
|
.WithTitle(item.Word)
|
||||||
.WithDescription(item.Definition);
|
.WithDescription(item.Definition);
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
@ -312,11 +312,11 @@ public partial class Searches : EllieModule<SearchesService>
|
||||||
{
|
{
|
||||||
var model = items.First();
|
var model = items.First();
|
||||||
var embed = CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithDescription(ctx.User.Mention)
|
.WithDescription(ctx.User.Mention)
|
||||||
.AddField(GetText(strs.word), model.Word, true)
|
.AddField(GetText(strs.word), model.Word, true)
|
||||||
.AddField(GetText(strs._class), model.WordType, true)
|
.AddField(GetText(strs._class), model.WordType, true)
|
||||||
.AddField(GetText(strs.definition), model.Definition)
|
.AddField(GetText(strs.definition), model.Definition)
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(model.Example))
|
if (!string.IsNullOrWhiteSpace(model.Example))
|
||||||
embed.AddField(GetText(strs.example), model.Example);
|
embed.AddField(GetText(strs.example), model.Example);
|
||||||
|
@ -404,10 +404,28 @@ public partial class Searches : EllieModule<SearchesService>
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(
|
.Embed(
|
||||||
CreateEmbed()
|
CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.AddField("Username", usr.ToString())
|
.AddField("Username", usr.ToString())
|
||||||
.AddField("Avatar Url", avatarUrl)
|
.AddField("Avatar Url", avatarUrl)
|
||||||
.WithThumbnailUrl(avatarUrl.ToString()))
|
.WithThumbnailUrl(avatarUrl.ToString()))
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Banner([Leftover] IGuildUser? usr = null)
|
||||||
|
{
|
||||||
|
usr ??= (IGuildUser)ctx.User;
|
||||||
|
|
||||||
|
var bannerUrl = usr.GetGuildBannerUrl();
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Embed(
|
||||||
|
CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.AddField("Username", usr.ToString())
|
||||||
|
.AddField("Banner Url", bannerUrl)
|
||||||
|
.WithThumbnailUrl(bannerUrl))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -344,9 +344,45 @@ public class XpService : IEService, IReadyExecutor, IExecNoCommand
|
||||||
if (role is not null && user is not null)
|
if (role is not null && user is not null)
|
||||||
{
|
{
|
||||||
if (rrew.Remove)
|
if (rrew.Remove)
|
||||||
_ = user.RemoveRoleAsync(role);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await user.RemoveRoleAsync(role);
|
||||||
|
await _notifySub.NotifyAsync(new RemoveRoleRewardNotifyModel(guild.Id,
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
newLevel),
|
||||||
|
isShardLocal: true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex,
|
||||||
|
"Unable to remove role {RoleId} from user {UserId}: {Message}",
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
_ = user.AddRoleAsync(role);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await user.AddRoleAsync(role);
|
||||||
|
await _notifySub.NotifyAsync(new AddRoleRewardNotifyModel(guild.Id,
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
newLevel),
|
||||||
|
isShardLocal: true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex,
|
||||||
|
"Unable to add role {RoleId} to user {UserId}: {Message}",
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,9 @@ public sealed class DoAsUserMessage : IUserMessage
|
||||||
public MessageCallData? CallData
|
public MessageCallData? CallData
|
||||||
=> _msg.CallData;
|
=> _msg.CallData;
|
||||||
|
|
||||||
|
public IReadOnlyCollection<MessageSnapshot> ForwardedMessages
|
||||||
|
=> _msg.ForwardedMessages;
|
||||||
|
|
||||||
public Task ModifyAsync(Action<MessageProperties> func, RequestOptions? options = null)
|
public Task ModifyAsync(Action<MessageProperties> func, RequestOptions? options = null)
|
||||||
{
|
{
|
||||||
return _msg.ModifyAsync(func, options);
|
return _msg.ModifyAsync(func, options);
|
||||||
|
|
|
@ -12,7 +12,7 @@ public sealed class UserService : IUserService, IEService
|
||||||
_db = db;
|
_db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DiscordUser> GetUserAsync(ulong userId)
|
public async Task<DiscordUser?> GetUserAsync(ulong userId)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var user = await uow
|
var user = await uow
|
||||||
|
|
|
@ -715,6 +715,8 @@ color:
|
||||||
avatar:
|
avatar:
|
||||||
- avatar
|
- avatar
|
||||||
- av
|
- av
|
||||||
|
banner:
|
||||||
|
- banner
|
||||||
translate:
|
translate:
|
||||||
- translate
|
- translate
|
||||||
- trans
|
- trans
|
||||||
|
@ -1549,4 +1551,15 @@ temprole:
|
||||||
- temprole
|
- temprole
|
||||||
notify:
|
notify:
|
||||||
- notify
|
- notify
|
||||||
- nfy
|
- nfy
|
||||||
|
notifylist:
|
||||||
|
- notifylist
|
||||||
|
- notifyl
|
||||||
|
notifyclear:
|
||||||
|
- notifyclear
|
||||||
|
- notifyremove
|
||||||
|
- notifyrm
|
||||||
|
- notifclr
|
||||||
|
winlb:
|
||||||
|
- winlb
|
||||||
|
- wins
|
|
@ -2170,6 +2170,13 @@ avatar:
|
||||||
params:
|
params:
|
||||||
- usr:
|
- usr:
|
||||||
desc: "The user whose avatar is being displayed."
|
desc: "The user whose avatar is being displayed."
|
||||||
|
banner:
|
||||||
|
desc: Shows a mentioned person's banner.
|
||||||
|
ex:
|
||||||
|
- '@Someone'
|
||||||
|
params:
|
||||||
|
- usr:
|
||||||
|
desc: "The user whose banner is being displayed."
|
||||||
translate:
|
translate:
|
||||||
desc: Translates text from the given language to the destination language.
|
desc: Translates text from the given language to the destination language.
|
||||||
ex:
|
ex:
|
||||||
|
@ -4857,10 +4864,38 @@ minesweeper:
|
||||||
notify:
|
notify:
|
||||||
desc: |-
|
desc: |-
|
||||||
Sends a message to the current channel once the specified event occurs.
|
Sends a message to the current channel once the specified event occurs.
|
||||||
|
Provide no parameters to see all available events.
|
||||||
ex:
|
ex:
|
||||||
- 'levelup Congratulations to user %user.name% for reaching level %event.level%'
|
- 'levelup Congratulations to user %user.name% for reaching level %event.level%'
|
||||||
params:
|
params:
|
||||||
|
- { }
|
||||||
- event:
|
- event:
|
||||||
desc: "The event to notify on."
|
desc: "The event to notify on."
|
||||||
- message:
|
- event:
|
||||||
desc: "The message to send."
|
desc: "The event to notify on."
|
||||||
|
message:
|
||||||
|
desc: "The message to send."
|
||||||
|
notifylist:
|
||||||
|
desc: |-
|
||||||
|
Lists all active notifications in this server.
|
||||||
|
ex:
|
||||||
|
- ''
|
||||||
|
params:
|
||||||
|
- { }
|
||||||
|
notifyclear:
|
||||||
|
desc: |-
|
||||||
|
Removes the specified notify event.
|
||||||
|
ex:
|
||||||
|
- 'levelup'
|
||||||
|
params:
|
||||||
|
- event:
|
||||||
|
desc: "The notify event to clear."
|
||||||
|
winlb:
|
||||||
|
desc: |-
|
||||||
|
Shows the biggest wins leaderboard
|
||||||
|
ex:
|
||||||
|
- ''
|
||||||
|
- '5'
|
||||||
|
params:
|
||||||
|
- page:
|
||||||
|
desc: "The optional page to display."
|
|
@ -1128,7 +1128,7 @@
|
||||||
"choose_one": "Choose one",
|
"choose_one": "Choose one",
|
||||||
"requires_role": "Requires role: {0}",
|
"requires_role": "Requires role: {0}",
|
||||||
"invalid_message_id": "Invalid Message Id.",
|
"invalid_message_id": "Invalid Message Id.",
|
||||||
"invalid_message_link": "The message link must be from this server.",
|
"invalid_message_link": "The message link must be this Bot's message. The bot can't add buttons to other users' messages.",
|
||||||
"btnrole_message_max": "Limit reached. You may have up to 25 button roles per message.",
|
"btnrole_message_max": "Limit reached. You may have up to 25 button roles per message.",
|
||||||
"btnrole_not_found": "No button role found on that message.",
|
"btnrole_not_found": "No button role found on that message.",
|
||||||
"btnrole_none": "There are no button roles on this page.",
|
"btnrole_none": "There are no button roles on this page.",
|
||||||
|
@ -1145,6 +1145,17 @@
|
||||||
"level_set": "Level of user {0} set to {1} on this server.",
|
"level_set": "Level of user {0} set to {1} on this server.",
|
||||||
"temp_role_added": "User {0} has been given {1} role temporarily. The role expires {2}",
|
"temp_role_added": "User {0} has been given {1} role temporarily. The role expires {2}",
|
||||||
"user_afk": "User {0} is AFK.",
|
"user_afk": "User {0} is AFK.",
|
||||||
"notify_on": "Notification message will be sent on this channel when {0} event triggers.",
|
"notify_on": "Notification message will be sent in {0} channel when {1} event triggers.",
|
||||||
"notify_off": "Notification message will no longer be sent when {0} event triggers."
|
"notify_off": "Notification message will no longer be sent when {0} event triggers.",
|
||||||
|
"notify_none": "No notifications on this page.",
|
||||||
|
"notify_msg_not_set": "Notification message is not set for this event.",
|
||||||
|
"notify_list": "Notify List",
|
||||||
|
"notify_type": "Type",
|
||||||
|
"notify_msg": "Notify Message",
|
||||||
|
"notify_available": "List of available notify events",
|
||||||
|
"notify_desc_levelup": "Triggers when a user levels up on this server.",
|
||||||
|
"notify_desc_protection": "Triggers when antialt, antispam or antiraid is triggered.",
|
||||||
|
"notify_desc_addrolerew": "Triggers when a user gets a role as a reward for reaching a level (xprew).",
|
||||||
|
"notify_desc_removerolerew": "Triggers when a user loses a role as a reward for reaching a level (xprew).",
|
||||||
|
"notify_desc_not_found": "No description found for this notify event. Please report this."
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue