added .clubbanner command

improved userrole slightly
This commit is contained in:
Toastie 2025-03-12 12:13:02 +13:00
parent fb658b8189
commit 2b581473c8
Signed by: toastie_t0ast
GPG key ID: 0861BE54AD481DC7
15 changed files with 209 additions and 107 deletions

View file

@ -9,7 +9,8 @@ public class ClubInfo : DbEntity
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; } = string.Empty;
public string BannerUrl { get; set; } = string.Empty;
public int Xp { get; set; } = 0;
public int? OwnerId { get; set; }
public DiscordUser Owner { get; set; }

View file

@ -0,0 +1,7 @@
START TRANSACTION;
ALTER TABLE clubs ADD bannerurl text;
INSERT INTO "__EFMigrationsHistory" (migrationid, productversion)
VALUES ('20250310143026_club-banners', '9.0.1');
COMMIT;

View file

@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace EllieBot.Migrations.PostgreSql
{
[DbContext(typeof(PostgreSqlContext))]
[Migration("20250310101144_init")]
[Migration("20250310143051_init")]
partial class init
{
/// <inheritdoc />
@ -572,6 +572,10 @@ namespace EllieBot.Migrations.PostgreSql
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("BannerUrl")
.HasColumnType("text")
.HasColumnName("bannerurl");
b.Property<DateTime?>("DateAdded")
.HasColumnType("timestamp without time zone")
.HasColumnName("dateadded");

View file

@ -1601,6 +1601,7 @@ namespace EllieBot.Migrations.PostgreSql
name = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: true),
description = table.Column<string>(type: "text", nullable: true),
imageurl = table.Column<string>(type: "text", nullable: true),
bannerurl = table.Column<string>(type: "text", nullable: true),
xp = table.Column<int>(type: "integer", nullable: false),
ownerid = table.Column<int>(type: "integer", nullable: true),
dateadded = table.Column<DateTime>(type: "timestamp without time zone", nullable: true)

View file

@ -569,6 +569,10 @@ namespace EllieBot.Migrations.PostgreSql
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("BannerUrl")
.HasColumnType("text")
.HasColumnName("bannerurl");
b.Property<DateTime?>("DateAdded")
.HasColumnType("timestamp without time zone")
.HasColumnName("dateadded");

View file

@ -0,0 +1,7 @@
BEGIN TRANSACTION;
ALTER TABLE "Clubs" ADD "BannerUrl" TEXT NULL;
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20250310143023_club-banners', '9.0.1');
COMMIT;

View file

@ -11,7 +11,7 @@ using EllieBot.Db;
namespace EllieBot.Migrations.Sqlite
{
[DbContext(typeof(SqliteContext))]
[Migration("20250310101142_init")]
[Migration("20250310143048_init")]
partial class init
{
/// <inheritdoc />
@ -428,6 +428,9 @@ namespace EllieBot.Migrations.Sqlite
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("BannerUrl")
.HasColumnType("TEXT");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");

View file

@ -1603,6 +1603,7 @@ namespace EllieBot.Migrations.Sqlite
Name = table.Column<string>(type: "TEXT", maxLength: 20, nullable: true),
Description = table.Column<string>(type: "TEXT", nullable: true),
ImageUrl = table.Column<string>(type: "TEXT", nullable: true),
BannerUrl = table.Column<string>(type: "TEXT", nullable: true),
Xp = table.Column<int>(type: "INTEGER", nullable: false),
OwnerId = table.Column<int>(type: "INTEGER", nullable: true),
DateAdded = table.Column<DateTime>(type: "TEXT", nullable: true)

View file

@ -425,6 +425,9 @@ namespace EllieBot.Migrations.Sqlite
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("BannerUrl")
.HasColumnType("TEXT");
b.Property<DateTime?>("DateAdded")
.HasColumnType("TEXT");

View file

@ -1,6 +1,7 @@
using SixLabors.ImageSharp.PixelFormats;
using EllieBot.Modules.Utility.UserRole;
using SixLabors.ImageSharp.PixelFormats;
namespace EllieBot.Modules.Utility.UserRole;
namespace EllieBot.Modules.Utility;
public partial class Utility
{
@ -27,6 +28,14 @@ public partial class Utility
return;
}
var botUser = ((SocketGuild)ctx.Guild).CurrentUser;
if (botUser.GetRoles().Max(x => x.Position) <= role.Position)
{
await Response().Error(strs.hierarchy).SendAsync();
return;
}
var success = await _urs.AddRoleAsync(ctx.Guild.Id, user.Id, role.Id);
if (!success)

View file

@ -29,13 +29,13 @@ public partial class Xp
else
{
await Response()
.Confirm(
strs.club_transfered(
Format.Bold(club.Name),
Format.Bold(newOwner.ToString())
)
)
.SendAsync();
.Confirm(
strs.club_transfered(
Format.Bold(club.Name),
Format.Bold(newOwner.ToString())
)
)
.SendAsync();
}
}
@ -108,57 +108,85 @@ public partial class Xp
await Response().Error(strs.club_icon_invalid_filetype).SendAsync();
}
[Cmd]
public async Task ClubBanner([Leftover] string url = null)
{
if ((!Uri.IsWellFormedUriString(url, UriKind.Absolute) && url is not null))
{
await Response().Error(strs.club_icon_url_format).SendAsync();
return;
}
var result = await _service.SetClubBannerAsync(ctx.User.Id, url);
if (result == SetClubIconResult.Success)
{
if (url is null)
await Response().Confirm(strs.club_banner_reset).SendAsync();
else
await Response().Confirm(strs.club_banner_set).SendAsync();
}
else if (result == SetClubIconResult.NotOwner)
await Response().Error(strs.club_owner_only).SendAsync();
else if (result == SetClubIconResult.TooLarge)
await Response().Error(strs.club_icon_too_large).SendAsync();
else if (result == SetClubIconResult.InvalidFileType)
await Response().Error(strs.club_icon_invalid_filetype).SendAsync();
}
private async Task InternalClubInfoAsync(ClubInfo club)
{
var lvl = new LevelStats(club.Xp);
var allUsers = club.Members.OrderByDescending(x =>
{
var l = new LevelStats(x.TotalXp).Level;
if (club.OwnerId == x.Id)
return int.MaxValue;
if (x.IsClubAdmin)
return (int.MaxValue / 2) + l;
return l;
})
.ToList();
{
var l = new LevelStats(x.TotalXp).Level;
if (club.OwnerId == x.Id)
return int.MaxValue;
if (x.IsClubAdmin)
return (int.MaxValue / 2) + l;
return l;
})
.ToList();
var rank = await _service.GetClubRankAsync(club.Id);
await Response()
.Paginated()
.Items(allUsers)
.PageSize(10)
.Page((users, _) =>
{
var embed = CreateEmbed()
.WithOkColor()
.WithTitle($"{club}")
.WithDescription(GetText(strs.level_x(lvl.Level + $" ({club.Xp} xp)")))
.AddField(GetText(strs.desc),
string.IsNullOrWhiteSpace(club.Description) ? "-" : club.Description)
.AddField(GetText(strs.rank), $"#{rank}", true)
.AddField(GetText(strs.owner), club.Owner.ToString(), true)
// .AddField(GetText(strs.level_req), club.MinimumLevelReq.ToString(), true)
.AddField(GetText(strs.members),
string.Join("\n",
users
.Select(x =>
{
var l = new LevelStats(x.TotalXp);
var lvlStr = Format.Bold($" ⟪{l.Level}⟫");
if (club.OwnerId == x.Id)
return x + "🌟" + lvlStr;
if (x.IsClubAdmin)
return x + "⭐" + lvlStr;
return x + lvlStr;
})));
.Paginated()
.Items(allUsers)
.PageSize(10)
.Page((users, _) =>
{
var embed = CreateEmbed()
.WithOkColor()
.WithTitle($"{club}")
.WithDescription(GetText(strs.level_x(lvl.Level + $" ({club.Xp} xp)")))
.AddField(GetText(strs.desc),
string.IsNullOrWhiteSpace(club.Description) ? "-" : club.Description)
.AddField(GetText(strs.rank), $"#{rank}", true)
.AddField(GetText(strs.owner), club.Owner.ToString(), true)
// .AddField(GetText(strs.level_req), club.MinimumLevelReq.ToString(), true)
.AddField(GetText(strs.members),
string.Join("\n",
users
.Select(x =>
{
var l = new LevelStats(x.TotalXp);
var lvlStr = Format.Bold($" ⟪{l.Level}⟫");
if (club.OwnerId == x.Id)
return x + "🌟" + lvlStr;
if (x.IsClubAdmin)
return x + "⭐" + lvlStr;
return x + lvlStr;
})));
if (Uri.IsWellFormedUriString(club.ImageUrl, UriKind.Absolute))
return embed.WithThumbnailUrl(club.ImageUrl);
if (Uri.IsWellFormedUriString(club.ImageUrl, UriKind.Absolute))
embed.WithThumbnailUrl(club.ImageUrl);
return embed;
})
.SendAsync();
if (Uri.IsWellFormedUriString(club.BannerUrl, UriKind.Absolute))
embed.WithImageUrl(club.BannerUrl);
return embed;
})
.SendAsync();
}
[Cmd]
@ -208,20 +236,20 @@ public partial class Xp
var bans = club.Bans.Select(x => x.User).ToArray();
return Response()
.Paginated()
.Items(bans)
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var toShow = string.Join("\n", items.Select(x => x.ToString()));
.Paginated()
.Items(bans)
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var toShow = string.Join("\n", items.Select(x => x.ToString()));
return CreateEmbed()
.WithTitle(GetText(strs.club_bans_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
})
.SendAsync();
return CreateEmbed()
.WithTitle(GetText(strs.club_bans_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
})
.SendAsync();
}
[Cmd]
@ -237,20 +265,20 @@ public partial class Xp
var apps = club.Applicants.Select(x => x.User).ToArray();
return Response()
.Paginated()
.Items(apps)
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var toShow = string.Join("\n", items.Select(x => x.ToString()));
.Paginated()
.Items(apps)
.PageSize(10)
.CurrentPage(page)
.Page((items, _) =>
{
var toShow = string.Join("\n", items.Select(x => x.ToString()));
return CreateEmbed()
.WithTitle(GetText(strs.club_apps_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
})
.SendAsync();
return CreateEmbed()
.WithTitle(GetText(strs.club_apps_for(club.ToString())))
.WithDescription(toShow)
.WithOkColor();
})
.SendAsync();
}
[Cmd]
@ -338,9 +366,9 @@ public partial class Xp
if (result == ClubKickResult.Success)
{
return Response()
.Confirm(strs.club_user_kick(Format.Bold(userName),
Format.Bold(club.ToString())))
.SendAsync();
.Confirm(strs.club_user_kick(Format.Bold(userName),
Format.Bold(club.ToString())))
.SendAsync();
}
if (result == ClubKickResult.Hierarchy)
@ -365,9 +393,9 @@ public partial class Xp
if (result == ClubBanResult.Success)
{
return Response()
.Confirm(strs.club_user_banned(Format.Bold(userName),
Format.Bold(club.ToString())))
.SendAsync();
.Confirm(strs.club_user_banned(Format.Bold(userName),
Format.Bold(club.ToString())))
.SendAsync();
}
if (result == ClubBanResult.Unbannable)
@ -393,9 +421,9 @@ public partial class Xp
if (result == ClubUnbanResult.Success)
{
return Response()
.Confirm(strs.club_user_unbanned(Format.Bold(userName),
Format.Bold(club.ToString())))
.SendAsync();
.Confirm(strs.club_user_unbanned(Format.Bold(userName),
Format.Bold(club.ToString())))
.SendAsync();
}
if (result == ClubUnbanResult.WrongUser)
@ -415,11 +443,11 @@ public partial class Xp
? "-"
: desc;
var eb = _sender.CreateEmbed()
.WithAuthor(ctx.User)
.WithTitle(GetText(strs.club_desc_update))
.WithOkColor()
.WithDescription(desc);
var eb = CreateEmbed()
.WithAuthor(ctx.User)
.WithTitle(GetText(strs.club_desc_update))
.WithOkColor()
.WithDescription(desc);
await Response().Embed(eb).SendAsync();
}

View file

@ -120,6 +120,26 @@ public class ClubService : IEService, IClubService
return SetClubIconResult.Success;
}
/// <summary>
/// Sets club banner url
/// </summary>
/// <param name="ownerUserId">User ID of the club owner</param>
/// <param name="url">Banner URL to set</param>
/// <returns>Result of the operation</returns>
public async Task<SetClubIconResult> SetClubBannerAsync(ulong ownerUserId, string? url)
{
await using var uow = _db.GetDbContext();
var club = uow.Set<ClubInfo>().GetByOwner(ownerUserId);
if (club is null)
return SetClubIconResult.NotOwner;
club.BannerUrl = url;
await uow.SaveChangesAsync();
return SetClubIconResult.Success;
}
public bool GetClubByName(string clubName, out ClubInfo club)
{
using var uow = _db.GetDbContext();

View file

@ -10,6 +10,7 @@ public interface IClubService
Task<ToggleAdminResult> ToggleAdminAsync(IUser owner, IUser toAdmin);
ClubInfo? GetClubByMember(IUser user);
Task<SetClubIconResult> SetClubIconAsync(ulong ownerUserId, string? url);
Task<SetClubIconResult> SetClubBannerAsync(ulong ownerUserId, string? url);
bool GetClubByName(string clubName, out ClubInfo club);
ClubApplyResult ApplyToClub(IUser user, ClubInfo club);
ClubAcceptResult AcceptApplication(ulong clubOwnerUserId, string userName, out DiscordUser? discordUser);

View file

@ -3694,6 +3694,17 @@ clubicon:
params:
- url:
desc: "The URL of an image file to use as the club icon."
clubbanner:
desc: |-
Sets an image as a club banner.
The banner will be displayed when club information is shown.
ex:
- 'https://i.imgur.com/example.png'
- ''
params:
- { }
- url:
desc: "URL to the image to set as a club banner."
clubapps:
desc: Shows the list of users who have applied to your club. Paginated. You must be club owner to use this command.
ex:
@ -5034,19 +5045,19 @@ userrolecolor:
color:
desc: 'The new color for the role in hex format.'
userroleicon:
desc: |-
Changes the icon of your assigned role.
ex:
- '@Role :thumbsup:'
params:
- role:
desc: 'The assigned role to change the icon of.'
icon:
desc: 'The new icon for the role.'
- role:
desc: 'The assigned role to change the icon of.'
emote:
desc: 'The server emoji for the role.'
desc: |-
Changes the icon of your assigned role.
ex:
- '@Role :thumbsup:'
params:
- role:
desc: 'The assigned role to change the icon of.'
imageUrl:
desc: 'The image url to be used as a new icon for the role.'
- role:
desc: 'The assigned role to change the icon of.'
serverEmoji:
desc: 'The server emoji to be used as a new icon for the role.'
userrolename:
desc: |-
Changes the name of your assigned role.

View file

@ -887,6 +887,8 @@
"club_icon_invalid_filetype": "Specified image has an invalid filetype. Make sure you're specifying a direct image url.",
"club_icon_url_format": "You must specify an absolute image url.",
"club_icon_set": "New club icon set.",
"club_banner_set": "New club banner set.",
"club_banner_reset": "Club banner has been reset.",
"club_bans_for": "Bans for {0} club",
"club_apps_for": "Applicants for {0} club",
"club_leaderboard": "Club leaderboard - page {0}",
@ -1201,6 +1203,6 @@
"userrole_icon_success": "The icon for {0} has been saved.",
"userrole_icon_fail": "Failed to set the role icon.",
"userrole_icon_invalid": "The role icon cannot be empty.",
"userrole_hierarchy_error": "You can't assign or modify roles that are higher than or equal to your highest role.",
"userrole_hierarchy_error": "You can't assign or modify roles that are higher than or equal to your, or bots highest role.",
"userrole_role_not_exists": "That role doesn't exist."
}