Compare commits
4 commits
7246c982df
...
69a02e0e15
Author | SHA1 | Date | |
---|---|---|---|
69a02e0e15 | |||
8d08595a9f | |||
1cbaaed944 | |||
a583c7d763 |
11 changed files with 190 additions and 86 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -2,6 +2,22 @@
|
|||
|
||||
*a,c,f,r,o*
|
||||
|
||||
## [6.0.14]
|
||||
|
||||
### Added
|
||||
- Added `.notify <channel> nicecatch <message>` event
|
||||
- It will show all rare fish/trash and all max star fish caught on any server
|
||||
- You can use `.notifyphs nicecatch` to see the list of placeholders you can use while setting a message
|
||||
- Example: `.notify #fishfeed nicecatch %user% just caught a %event.fish.stars% %event.fish.name% %event.fish.emoji%`
|
||||
|
||||
### Changed
|
||||
- .notify commands now require Manage Messages permission
|
||||
- .notify will now let you know if you can't set a notify message due to a missing channel
|
||||
|
||||
### Fixed
|
||||
- Fixed `.antispamignore` restart persistence
|
||||
- Fixed `.notify` events. Only levelup used to work
|
||||
|
||||
## [6.0.13] - 23.03.2025
|
||||
|
||||
### Added
|
||||
|
|
|
@ -21,5 +21,6 @@ public enum NotifyType
|
|||
Protection = 1, Prot = 1,
|
||||
AddRoleReward = 2,
|
||||
RemoveRoleReward = 3,
|
||||
NiceCatch = 4,
|
||||
// BigWin = 4,
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#nullable disable
|
||||
using EllieBot.Db.Models;
|
||||
using EllieBot.Modules.Games;
|
||||
|
||||
namespace EllieBot.Modules.Administration.Services;
|
||||
|
||||
public record struct NiceCatchNotifyModel(
|
||||
ulong UserId,
|
||||
FishData Fish,
|
||||
string Stars
|
||||
) : INotifyModel<NiceCatchNotifyModel>
|
||||
{
|
||||
public static string KeyName
|
||||
=> "notify.nicecatch";
|
||||
|
||||
public static NotifyType NotifyType
|
||||
=> NotifyType.NiceCatch;
|
||||
|
||||
public const string PH_EMOJI = "fish.emoji";
|
||||
public const string PH_IMAGE = "fish.image";
|
||||
public const string PH_NAME = "fish.name";
|
||||
public const string PH_STARS = "fish.stars";
|
||||
public const string PH_FLUFF = "fish.fluff";
|
||||
|
||||
public bool TryGetUserId(out ulong userId)
|
||||
{
|
||||
userId = UserId;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<NotifyModelPlaceholderData<NiceCatchNotifyModel>> GetReplacements()
|
||||
{
|
||||
return
|
||||
[
|
||||
new(PH_EMOJI, static (data, _) => data.Fish.Emoji),
|
||||
new(PH_IMAGE, static (data, _) => data.Fish.Image),
|
||||
new(PH_NAME, static (data, _) => data.Fish.Name),
|
||||
new(PH_STARS, static (data, _) => data.Stars),
|
||||
new(PH_FLUFF, static (data, _) => data.Fish.Fluff),
|
||||
];
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ public partial class Administration
|
|||
public class NotifyCommands : EllieModule<NotifyService>
|
||||
{
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task Notify()
|
||||
{
|
||||
await Response()
|
||||
|
@ -38,11 +38,12 @@ public partial class Administration
|
|||
NotifyType.Protection => strs.notify_desc_protection,
|
||||
NotifyType.AddRoleReward => strs.notify_desc_addrolerew,
|
||||
NotifyType.RemoveRoleReward => strs.notify_desc_removerolerew,
|
||||
NotifyType.NiceCatch => strs.notify_desc_nicecatch,
|
||||
_ => strs.notify_desc_not_found
|
||||
};
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task Notify(NotifyType nType)
|
||||
{
|
||||
// show msg
|
||||
|
@ -76,12 +77,12 @@ public partial class Administration
|
|||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task Notify(NotifyType nType, [Leftover] string message)
|
||||
=> await NotifyInternalAsync(nType, null, message);
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task Notify(NotifyType nType, IMessageChannel channel, [Leftover] string message)
|
||||
=> await NotifyInternalAsync(nType, channel, message);
|
||||
|
||||
|
@ -89,6 +90,14 @@ public partial class Administration
|
|||
{
|
||||
var result = await _service.EnableAsync(ctx.Guild.Id, channel?.Id, nType, message);
|
||||
|
||||
if(!result)
|
||||
{
|
||||
await Response()
|
||||
.Error(strs.notify_cant_set)
|
||||
.SendAsync();
|
||||
|
||||
return;
|
||||
}
|
||||
var outChannel = channel is null ? "origin" : $"<#{channel.Id}>";
|
||||
await Response()
|
||||
.Confirm(strs.notify_on(outChannel, Format.Bold(nType.ToString())))
|
||||
|
@ -96,7 +105,7 @@ public partial class Administration
|
|||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task NotifyPhs(NotifyType nType)
|
||||
{
|
||||
var data = _service.GetRegisteredModel(nType);
|
||||
|
@ -111,7 +120,7 @@ public partial class Administration
|
|||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task NotifyList(int page = 1)
|
||||
{
|
||||
if (--page < 0)
|
||||
|
@ -139,7 +148,7 @@ public partial class Administration
|
|||
}
|
||||
|
||||
[Cmd]
|
||||
[UserPerm(GuildPerm.Administrator)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
public async Task NotifyClear(NotifyType nType)
|
||||
{
|
||||
await _service.DisableAsync(ctx.Guild.Id, nType);
|
||||
|
|
|
@ -42,12 +42,11 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
|
|||
RegisterModel<ProtectionNotifyModel>();
|
||||
RegisterModel<AddRoleRewardNotifyModel>();
|
||||
RegisterModel<RemoveRoleRewardNotifyModel>();
|
||||
RegisterModel<NiceCatchNotifyModel>();
|
||||
}
|
||||
|
||||
public async Task OnReadyAsync()
|
||||
{
|
||||
RegisterModels();
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
_events = (await uow.GetTable<Notify>()
|
||||
.Where(x => Queries.GuildOnShard(x.GuildId,
|
||||
|
@ -57,9 +56,8 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
|
|||
.GroupBy(x => x.Type)
|
||||
.ToDictionary(x => x.Key, x => x.ToDictionary(x => x.GuildId).ToConcurrent())
|
||||
.ToConcurrent();
|
||||
|
||||
|
||||
await SubscribeToEvent<LevelUpNotifyModel>();
|
||||
|
||||
RegisterModels();
|
||||
}
|
||||
|
||||
private async Task SubscribeToEvent<T>()
|
||||
|
@ -75,7 +73,7 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
|
|||
{
|
||||
if (isShardLocal)
|
||||
{
|
||||
await OnEvent(data);
|
||||
_ = Task.Run(async () => await OnEvent(data));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -283,7 +281,10 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, IEService
|
|||
var data = new NotifyModelData(T.NotifyType,
|
||||
T.SupportsOriginTarget,
|
||||
T.GetReplacements().Map(x => x.Name));
|
||||
|
||||
_models[T.NotifyType] = data;
|
||||
|
||||
_pubSub.Sub<T>(new(T.KeyName), async (data) => await OnEvent(data));
|
||||
}
|
||||
|
||||
public NotifyModelData GetRegisteredModel(NotifyType nType)
|
||||
|
|
|
@ -391,7 +391,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
{
|
||||
var obj = new AntiSpamIgnore
|
||||
{
|
||||
ChannelId = channelId
|
||||
ChannelId = channelId,
|
||||
};
|
||||
|
||||
await using var uow = _db.GetDbContext();
|
||||
|
@ -529,16 +529,18 @@ public class ProtectionService : IReadyExecutor, IEService
|
|||
};
|
||||
}
|
||||
|
||||
var spamConfigs = await uow.GetTable<AntiSpamSetting>()
|
||||
var spamConfigs = await uow.Set<AntiSpamSetting>()
|
||||
.AsNoTracking()
|
||||
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
|
||||
.ToListAsyncLinqToDB();
|
||||
.Where(x => gids.Contains(x.GuildId))
|
||||
.Include(x => x.IgnoredChannels)
|
||||
.ToListAsyncEF();
|
||||
|
||||
foreach (var config in spamConfigs)
|
||||
{
|
||||
_antiSpamGuilds[config.GuildId] = new()
|
||||
{
|
||||
AntiSpamSettings = config,
|
||||
UserStats = new()
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ public partial class Games
|
|||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(desc)
|
||||
.AddField(GetText(strs.fish_quality), GetStarText(res.Stars, res.Fish.Stars), true)
|
||||
.AddField(GetText(strs.fish_quality), fs.GetStarText(res.Stars, res.Fish.Stars), true)
|
||||
.AddField(GetText(strs.desc), res.Fish.Fluff, true)
|
||||
.WithThumbnailUrl(res.Fish.Image))
|
||||
.SendAsync();
|
||||
|
@ -150,7 +150,7 @@ public partial class Games
|
|||
.Items(fishes)
|
||||
.PageSize(9)
|
||||
.CurrentPage(page)
|
||||
.Page((fs, i) =>
|
||||
.Page((fishes, i) =>
|
||||
{
|
||||
var eb = CreateEmbed()
|
||||
.WithDescription($"🧠 **Skill:** {skill} / {maxSkill}")
|
||||
|
@ -158,7 +158,7 @@ public partial class Games
|
|||
.WithTitle(GetText(strs.fish_list_title))
|
||||
.WithOkColor();
|
||||
|
||||
foreach (var f in fs)
|
||||
foreach (var f in fishes)
|
||||
{
|
||||
if (catchDict.TryGetValue(f.Id, out var c))
|
||||
{
|
||||
|
@ -169,14 +169,14 @@ public partial class Games
|
|||
+ GetTodEmoji(f.Time)
|
||||
+ GetWeatherEmoji(f.Weather)
|
||||
+ "\n"
|
||||
+ GetStarText(c.MaxStars, f.Stars)
|
||||
+ fs.GetStarText(c.MaxStars, f.Stars)
|
||||
+ "\n"
|
||||
+ Format.Italics(f.Fluff),
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
eb.AddField("?", GetFishEmoji(null, 0) + "\n" + GetStarText(0, f.Stars), true);
|
||||
eb.AddField("?", GetFishEmoji(null, 0) + "\n" + fs.GetStarText(0, f.Stars), true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,31 +225,8 @@ public partial class Games
|
|||
_ => ""
|
||||
};
|
||||
|
||||
private string GetStarText(int resStars, int fishStars)
|
||||
{
|
||||
if (resStars == fishStars)
|
||||
{
|
||||
return MultiplyStars(fcs.Data.StarEmojis[^1], fishStars);
|
||||
}
|
||||
|
||||
|
||||
var c = fcs.Data;
|
||||
var starsp1 = MultiplyStars(c.StarEmojis[resStars], resStars);
|
||||
var starsp2 = MultiplyStars(c.StarEmojis[0], fishStars - resStars);
|
||||
|
||||
return starsp1 + starsp2;
|
||||
}
|
||||
|
||||
private string MultiplyStars(string starEmoji, int count)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
sb.Append(starEmoji);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,12 @@ public sealed class FishResult
|
|||
public bool IsSkillUp { get; set; }
|
||||
public int Skill { get; set; }
|
||||
public int MaxSkill { get; set; }
|
||||
|
||||
public bool IsMaxStar()
|
||||
=> Stars == Fish.Stars;
|
||||
|
||||
public bool IsRare()
|
||||
=> Fish.Chance <= 15;
|
||||
}
|
||||
|
||||
public readonly record struct AlreadyFishing;
|
|
@ -1,10 +1,19 @@
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using LinqToDB;
|
||||
using LinqToDB.EntityFrameworkCore;
|
||||
using EllieBot.Modules.Administration;
|
||||
using EllieBot.Modules.Administration.Services;
|
||||
|
||||
namespace EllieBot.Modules.Games.Fish;
|
||||
|
||||
public sealed class FishService(FishConfigService fcs, IBotCache cache, DbService db) : IEService
|
||||
public sealed class FishService(
|
||||
FishConfigService fcs,
|
||||
IBotCache cache,
|
||||
DbService db,
|
||||
INotifySubscriber notify
|
||||
)
|
||||
: IEService
|
||||
{
|
||||
private const double MAX_SKILL = 100;
|
||||
|
||||
|
@ -15,7 +24,7 @@ public sealed class FishService(FishConfigService fcs, IBotCache cache, DbServic
|
|||
|
||||
public async Task<OneOf.OneOf<Task<FishResult?>, AlreadyFishing>> FishAsync(ulong userId, ulong channelId)
|
||||
{
|
||||
var duration = _rng.Next(5, 9);
|
||||
var duration = _rng.Next(3, 6);
|
||||
|
||||
if (!await cache.AddAsync(FishingKey(userId), true, TimeSpan.FromSeconds(duration), overwrite: false))
|
||||
{
|
||||
|
@ -69,6 +78,18 @@ public sealed class FishService(FishConfigService fcs, IBotCache cache, DbServic
|
|||
}
|
||||
}
|
||||
|
||||
// notification system
|
||||
if (result is not null)
|
||||
{
|
||||
if (result.IsMaxStar() || result.IsRare())
|
||||
{
|
||||
await notify.NotifyAsync(new NiceCatchNotifyModel(
|
||||
userId,
|
||||
result.Fish,
|
||||
GetStarText(result.Stars, result.Fish.Stars)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -85,21 +106,21 @@ public sealed class FishService(FishConfigService fcs, IBotCache cache, DbServic
|
|||
|
||||
var maxSkill = (int)MAX_SKILL;
|
||||
await ctx.GetTable<UserFishStats>()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Skill = 1,
|
||||
},
|
||||
(old) => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Skill = old.Skill > maxSkill ? maxSkill : old.Skill + 1
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Skill = playerSkill
|
||||
});
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Skill = 1,
|
||||
},
|
||||
(old) => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Skill = old.Skill > maxSkill ? maxSkill : old.Skill + 1
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Skill = playerSkill
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -123,9 +144,9 @@ public sealed class FishService(FishConfigService fcs, IBotCache cache, DbServic
|
|||
await using var ctx = db.GetDbContext();
|
||||
|
||||
var skill = await ctx.GetTable<UserFishStats>()
|
||||
.Where(x => x.UserId == userId)
|
||||
.Select(x => x.Skill)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
.Where(x => x.UserId == userId)
|
||||
.Select(x => x.Skill)
|
||||
.FirstOrDefaultAsyncLinqToDB();
|
||||
|
||||
return (skill, (int)MAX_SKILL);
|
||||
}
|
||||
|
@ -188,23 +209,23 @@ public sealed class FishService(FishConfigService fcs, IBotCache cache, DbServic
|
|||
await using var uow = db.GetDbContext();
|
||||
|
||||
await uow.GetTable<FishCatch>()
|
||||
.InsertOrUpdateAsync(() => new FishCatch()
|
||||
{
|
||||
UserId = userId,
|
||||
FishId = caught.Fish.Id,
|
||||
MaxStars = caught.Stars,
|
||||
Count = 1
|
||||
},
|
||||
(old) => new FishCatch()
|
||||
{
|
||||
Count = old.Count + 1,
|
||||
MaxStars = Math.Max(old.MaxStars, caught.Stars),
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
FishId = caught.Fish.Id,
|
||||
UserId = userId
|
||||
});
|
||||
.InsertOrUpdateAsync(() => new FishCatch()
|
||||
{
|
||||
UserId = userId,
|
||||
FishId = caught.Fish.Id,
|
||||
MaxStars = caught.Stars,
|
||||
Count = 1
|
||||
},
|
||||
(old) => new FishCatch()
|
||||
{
|
||||
Count = old.Count + 1,
|
||||
MaxStars = Math.Max(old.MaxStars, caught.Stars),
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
FishId = caught.Fish.Id,
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
return caught;
|
||||
}
|
||||
|
@ -392,9 +413,35 @@ public sealed class FishService(FishConfigService fcs, IBotCache cache, DbServic
|
|||
await using var ctx = db.GetDbContext();
|
||||
|
||||
var catches = await ctx.GetTable<FishCatch>()
|
||||
.Where(x => x.UserId == userId)
|
||||
.ToListAsyncLinqToDB();
|
||||
.Where(x => x.UserId == userId)
|
||||
.ToListAsyncLinqToDB();
|
||||
|
||||
return catches;
|
||||
}
|
||||
|
||||
public string GetStarText(int resStars, int fishStars)
|
||||
{
|
||||
if (resStars == fishStars)
|
||||
{
|
||||
return MultiplyStars(fcs.Data.StarEmojis[^1], fishStars);
|
||||
}
|
||||
|
||||
var c = fcs.Data;
|
||||
var starsp1 = MultiplyStars(c.StarEmojis[resStars], resStars);
|
||||
var starsp2 = MultiplyStars(c.StarEmojis[0], fishStars - resStars);
|
||||
|
||||
return starsp1 + starsp2;
|
||||
}
|
||||
|
||||
private string MultiplyStars(string starEmoji, int count)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
sb.Append(starEmoji);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
|
@ -88,6 +88,7 @@ public partial class Utility : EllieModule
|
|||
.Text(message)
|
||||
.Channel(channel)
|
||||
.UserBasedMentions()
|
||||
.NoReply()
|
||||
.SendAsync();
|
||||
}
|
||||
|
||||
|
|
|
@ -1154,6 +1154,7 @@
|
|||
"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_nicecatch": "Triggers when a user catches quality fish.",
|
||||
"notify_desc_not_found": "No description found for this notify event. Please report this.",
|
||||
"notify_placeholders": "Placeholders for '{0}' notify event",
|
||||
"winlb": "Biggest Wins Leaderboard",
|
||||
|
@ -1238,5 +1239,6 @@
|
|||
"linkfix_list_none": "No link fixes have been configured for this server.",
|
||||
"linkfix_list_title": "Link Fixes",
|
||||
"linkfix_removed": "Link fix for {0} has been removed.",
|
||||
"linkfix_not_found": "No link fix found for {0}."
|
||||
"linkfix_not_found": "No link fix found for {0}.",
|
||||
"notify_cant_set": "This event doesn't support origin channel, Please specify a channel"
|
||||
}
|
Loading…
Add table
Reference in a new issue