mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 01:38:27 -04:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7d5c4666b8 | ||
|
8d3f2f186a | ||
|
9572b9dc43 | ||
|
ca32086089 | ||
|
57f839dbcd | ||
|
71d6eeb9dd | ||
|
8c51cf8537 | ||
|
b683026cf3 | ||
|
bca2bc5af1 | ||
|
b385a83bdd | ||
|
3bf0286c81 | ||
|
98272f66e7 | ||
|
cf3788c6ea | ||
|
4b3fc53cb6 | ||
|
4e17dca856 | ||
|
82d89148f3 | ||
|
cc4c09b4d7 | ||
|
616f01f8b2 | ||
|
56f89a02bc | ||
|
48ce988d20 | ||
|
119b1cdec2 |
33
CHANGELOG.md
33
CHANGELOG.md
@@ -2,11 +2,29 @@
|
||||
|
||||
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||
|
||||
## [4.3.1] - 27.07.2022
|
||||
## Unreleased
|
||||
|
||||
## [4.3.3] - 06.08.2022
|
||||
|
||||
### Added
|
||||
|
||||
- Added `betroll` option to `.bettest` command
|
||||
- Added `.xpshopbuy` and `.xpshopuse` convenience commands
|
||||
- Added an optional preview url to teh xp shop item config model which will be shown instead of the real Url
|
||||
|
||||
### Changed
|
||||
|
||||
- Check for updates will run once per hour as it was supposed to
|
||||
- Updated position of Username and Club name on the .xp card
|
||||
- Improved text visibility on the .xp card
|
||||
|
||||
### Fixed
|
||||
|
||||
- Possibly fixed .trivia not stopping bug
|
||||
- Fixed very low payout rate on `.betroll`
|
||||
- Fixed an issue with youtube song resolver which caused invalid data to be cached
|
||||
- Added client id to the cache key as a potential fix for VoiceXp 'bug'. The solution may be to use different redis instances for each bot, or to switch from botCache: from 'redis' to 'memory' in creds.yml
|
||||
- Bot owner should now be able to buy items from the xpshop when patron requirement is set
|
||||
- Fixed youtube-dl caching invalid data. Please use yt-dlp instead
|
||||
|
||||
## [4.3.2] - 28.07.2022
|
||||
|
||||
@@ -19,6 +37,12 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
||||
- Fixed `.bank withdraw <expression>` will now correctly use bank amount for calculation
|
||||
- [dev] Fixed medusa Reply*LocalizedAsync not working with placeholders
|
||||
|
||||
## [4.3.1] - 27.07.2022
|
||||
|
||||
### Changed
|
||||
|
||||
- Check for updates will run once per hour as it was supposed to
|
||||
|
||||
## [4.3.0] - 27.07.2022
|
||||
|
||||
### Added
|
||||
@@ -32,9 +56,10 @@ Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.
|
||||
- They payouts are very good, but seven always loses
|
||||
- Added `.lula` command. Plays the same as `.wof` but looks much nicer, and is easily customizable from gambling.yml without any changes to the sourcecode needed.
|
||||
- Added `.repeatskip` command which makes the next repeat trigger not post anything
|
||||
- Added `.imageonly` which will make the bot only allow link posts in the channel. Exclusive with `.imageonly`
|
||||
- Added `.linkonly` which will make the bot only allow link posts in the channel. Exclusive with `.imageonly`
|
||||
- Added release notifications. Bot owners will now receive new release notifications in dms if they have `checkForUpdates` set to `true` in data/bot.yml
|
||||
- You can also configure it via `.conf bot checkforupdates <true/false>`
|
||||
- You can also configure it via `.conf bot checkfor
|
||||
- updates <true/false>`
|
||||
- Added `.xpshop` which lets bot owners add xp backgrounds and xp frames for sale by configuring `data/xp.yml`
|
||||
- You can also toggle xpshop feature via `.conf xp shop.is_enabled`
|
||||
|
||||
|
@@ -69,7 +69,7 @@ Open Terminal (if you're on an installation with a window manager) and navigate
|
||||
|
||||
###### Prerequisites
|
||||
|
||||
1. Nadeko requires redis to function
|
||||
1. (Optional) Installing Redis
|
||||
- ubuntu installation command: `sudo apt-get install redis-server`
|
||||
2. Playing music requires `ffmpeg`, `libopus`, `libsodium` and `youtube-dl` (which in turn requires python3)
|
||||
- ubuntu installation command: `sudo apt-get install ffmpeg libopus0 opus-tools libopus-dev libsodium-dev -y`
|
||||
|
@@ -26,20 +26,6 @@ public class NadekoRandom : Random
|
||||
return Math.Abs(BitConverter.ToInt32(bytes, 0)) % maxValue;
|
||||
}
|
||||
|
||||
public byte Next(byte minValue, byte maxValue)
|
||||
{
|
||||
if (minValue > maxValue)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxValue));
|
||||
|
||||
if (minValue == maxValue)
|
||||
return minValue;
|
||||
|
||||
var bytes = new byte[1];
|
||||
_rng.GetBytes(bytes);
|
||||
|
||||
return (byte)((bytes[0] % (maxValue - minValue)) + minValue);
|
||||
}
|
||||
|
||||
public override int Next(int minValue, int maxValue)
|
||||
{
|
||||
if (minValue > maxValue)
|
||||
|
@@ -12,7 +12,7 @@ public sealed class BetflipGame
|
||||
|
||||
public BetflipResult Flip(byte guess, decimal amount)
|
||||
{
|
||||
var side = _rng.Next(0, 2);
|
||||
var side = (byte)_rng.Next(0, 2);
|
||||
if (side == guess)
|
||||
{
|
||||
return new BetflipResult()
|
||||
|
@@ -13,7 +13,7 @@ public sealed class BetrollGame
|
||||
|
||||
public BetrollResult Roll(decimal amount = 0)
|
||||
{
|
||||
var roll = _rng.Next(0, 101);
|
||||
var roll = _rng.Next(1, 101);
|
||||
|
||||
for (var i = 0; i < _thresholdPairs.Length; i++)
|
||||
{
|
||||
|
@@ -8,9 +8,9 @@ public class SlotGame
|
||||
{
|
||||
var rolls = new[]
|
||||
{
|
||||
_rng.Next(0, 6),
|
||||
_rng.Next(0, 6),
|
||||
_rng.Next(0, 6)
|
||||
(byte)_rng.Next(0, 6),
|
||||
(byte)_rng.Next(0, 6),
|
||||
(byte)_rng.Next(0, 6)
|
||||
};
|
||||
|
||||
ref var a = ref rolls[0];
|
||||
|
@@ -875,11 +875,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public enum GambleTestTarget
|
||||
{
|
||||
Slot,
|
||||
Betroll,
|
||||
Betflip,
|
||||
BetflipT,
|
||||
BetDraw,
|
||||
BetDrawHL,
|
||||
BetDrawRB,
|
||||
Betflip,
|
||||
BetflipT,
|
||||
Lula,
|
||||
Rps,
|
||||
}
|
||||
@@ -920,6 +921,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
GambleTestTarget.BetflipT => (await _gs.BetFlipAsync(ctx.User.Id, 0, 1)).AsT0.Multiplier,
|
||||
GambleTestTarget.Lula => (await _gs.LulaAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||
GambleTestTarget.Rps => (await _gs.RpsAsync(ctx.User.Id, 0, (byte)(i % 3))).AsT0.Multiplier,
|
||||
GambleTestTarget.Betroll => (await _gs.BetRollAsync(ctx.User.Id, 0)).AsT0.Multiplier,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(target))
|
||||
};
|
||||
|
||||
|
@@ -57,11 +57,14 @@ public sealed class TriviaGame
|
||||
// loop until game is stopped
|
||||
// each iteration is one round
|
||||
var firstRun = true;
|
||||
try
|
||||
{
|
||||
while (!_isStopped)
|
||||
{
|
||||
if (errorCount >= 5)
|
||||
{
|
||||
Log.Warning("Trivia errored 5 times and will quit");
|
||||
await OnEnded(this);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -77,7 +80,7 @@ public sealed class TriviaGame
|
||||
|
||||
var maybeQuestion = await _questionPool.GetQuestionAsync();
|
||||
|
||||
if(!(maybeQuestion is TriviaQuestion question))
|
||||
if (!(maybeQuestion is TriviaQuestion question))
|
||||
{
|
||||
// if question is null (ran out of question, or other bugg ) - stop
|
||||
break;
|
||||
@@ -87,7 +90,8 @@ public sealed class TriviaGame
|
||||
try
|
||||
{
|
||||
// clear out all of the past guesses
|
||||
while (_inputs.Reader.TryRead(out _)) ;
|
||||
while (_inputs.Reader.TryRead(out _))
|
||||
;
|
||||
|
||||
await OnQuestion(this, question);
|
||||
}
|
||||
@@ -169,11 +173,17 @@ public sealed class TriviaGame
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
// make sure game is set as ended
|
||||
_isStopped = true;
|
||||
|
||||
await OnEnded(this);
|
||||
_ = OnEnded(this);
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyList<(ulong User, int points)> GetLeaderboard()
|
||||
|
@@ -274,7 +274,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
|
||||
Log.Information("Resolving youtube song by search term: {YoutubeQuery}", query);
|
||||
|
||||
var cachedData = await _trackCacher.GetCachedDataByQueryAsync(query, MusicPlatform.Youtube);
|
||||
if (cachedData is null)
|
||||
if (cachedData is null || string.IsNullOrWhiteSpace(cachedData.Title))
|
||||
{
|
||||
var stringData = await _ytdlSearchOperation.GetDataAsync(query);
|
||||
var trackData = ResolveYtdlData(stringData);
|
||||
|
@@ -716,7 +716,7 @@ public sealed class PatronageService
|
||||
{
|
||||
Name = key.PrettyName,
|
||||
Quota = 0,
|
||||
IsPatronLimit = false,
|
||||
IsPatronLimit = true,
|
||||
};
|
||||
|
||||
return new()
|
||||
|
@@ -334,14 +334,15 @@ public partial class Xp : NadekoModule<XpService>
|
||||
|
||||
public enum XpShopInputType
|
||||
{
|
||||
F = 0,
|
||||
Frs = 0,
|
||||
Fs = 0,
|
||||
Frames = 0,
|
||||
Backgrounds = 1,
|
||||
B = 1,
|
||||
Bg = 1,
|
||||
Bgs = 1,
|
||||
Backgrounds = 1
|
||||
Frames = 0,
|
||||
F = 0,
|
||||
Fr = 0,
|
||||
Frs = 0,
|
||||
Fs = 0,
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -353,8 +354,11 @@ public partial class Xp : NadekoModule<XpService>
|
||||
return;
|
||||
}
|
||||
|
||||
await SendConfirmAsync(GetText(strs.available_commands), $@"`{prefix}xpshop bgs`
|
||||
`{prefix}xpshop frames`");
|
||||
await SendConfirmAsync(GetText(strs.available_commands),
|
||||
$@"`{prefix}xpshop bgs`
|
||||
`{prefix}xpshop frames`
|
||||
|
||||
*{GetText(strs.xpshop_website)}*");
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -394,13 +398,18 @@ public partial class Xp : NadekoModule<XpService>
|
||||
.WithOkColor()
|
||||
.WithTitle(item.Name)
|
||||
.AddField(GetText(strs.price), Gambling.Gambling.N(item.Price, culture), true)
|
||||
// .AddField(GetText(strs.buy), $"{prefix}xpbuy {key}", true)
|
||||
.WithImageUrl(item.Url.ToString());
|
||||
.WithImageUrl(string.IsNullOrWhiteSpace(item.Preview)
|
||||
? item.Url
|
||||
: item.Preview);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(item.Desc))
|
||||
eb.WithDescription(item.Desc);
|
||||
eb.AddField(GetText(strs.desc), item.Desc);
|
||||
|
||||
var tier = _service.GetXpShopTierRequirement();
|
||||
if (key == "default")
|
||||
eb.WithDescription(GetText(strs.xpshop_website));
|
||||
|
||||
|
||||
var tier = _service.GetXpShopTierRequirement(type);
|
||||
if (tier != PatronTier.None)
|
||||
{
|
||||
eb.WithFooter(GetText(strs.xp_shop_buy_required_tier(tier.ToString())));
|
||||
@@ -455,6 +464,42 @@ public partial class Xp : NadekoModule<XpService>
|
||||
addPaginatedFooter: false);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task XpShopBuy(XpShopInputType type, string key)
|
||||
{
|
||||
var result = await _service.BuyShopItemAsync(ctx.User.Id, (XpShopItemType)type, key);
|
||||
|
||||
if (result != BuyResult.Success)
|
||||
{
|
||||
var _ = result switch
|
||||
{
|
||||
BuyResult.InsufficientFunds => await ReplyErrorLocalizedAsync(strs.not_enough(_gss.Data.Currency.Sign)),
|
||||
BuyResult.AlreadyOwned => await ReplyErrorLocalizedAsync(strs.xpshop_already_owned),
|
||||
BuyResult.UnknownItem => await ReplyErrorLocalizedAsync(strs.xpshop_item_not_found),
|
||||
BuyResult.InsufficientPatronTier => await ReplyErrorLocalizedAsync(strs.patron_insuff_tier),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.xpshop_buy_success(type.ToString().ToLowerInvariant(),
|
||||
key.ToLowerInvariant()));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task XpShopUse(XpShopInputType type, string key)
|
||||
{
|
||||
var result = await _service.UseShopItemAsync(ctx.User.Id, (XpShopItemType)type, key);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
await ReplyConfirmLocalizedAsync(strs.xp_shop_item_cant_use);
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
private async Task OnShopUse(SocketMessageComponent smc, (string? key, XpShopItemType type)? maybeState)
|
||||
{
|
||||
if (maybeState is not { } state)
|
||||
|
@@ -10,7 +10,7 @@ namespace NadekoBot.Modules.Xp;
|
||||
public sealed partial class XpConfig : ICloneable<XpConfig>
|
||||
{
|
||||
[Comment(@"DO NOT CHANGE")]
|
||||
public int Version { get; set; } = 3;
|
||||
public int Version { get; set; } = 5;
|
||||
|
||||
[Comment(@"How much XP will the users receive per message")]
|
||||
public int XpPerMessage { get; set; } = 3;
|
||||
@@ -37,9 +37,13 @@ True -> Users can access the xp shop using .xpshop command
|
||||
False -> Users can't access the xp shop")]
|
||||
public bool IsEnabled { get; set; } = false;
|
||||
|
||||
[Comment(@"Which patron tier do users need in order to use the .xpshop command
|
||||
[Comment(@"Which patron tier do users need in order to use the .xpshop bgs command
|
||||
Leave at 'None' if patron system is disabled or you don't want any restrictions")]
|
||||
public PatronTier TierRequirement { get; set; } = PatronTier.None;
|
||||
public PatronTier BgsTierRequirement { get; set; } = PatronTier.None;
|
||||
|
||||
[Comment(@"Which patron tier do users need in order to use the .xpshop frames command
|
||||
Leave at 'None' if patron system is disabled or you don't want any restrictions")]
|
||||
public PatronTier FramesTierRequirement { get; set; } = PatronTier.None;
|
||||
|
||||
[Comment(@"Frames available for sale. Keys are unique IDs.
|
||||
Do not change keys as they are not publicly visible. Only change properties (name, price, id)
|
||||
@@ -71,6 +75,9 @@ To remove an item from the shop, but keep previous purchases, set the price to -
|
||||
[Comment(@"Direct url to the .png image which will be applied to the user's XP card")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[Comment(@"Optional preview url which will show instead of the real URL in the shop ")]
|
||||
public string Preview { get; set; }
|
||||
|
||||
[Comment(@"Optional description of the item")]
|
||||
public string Desc { get; set; }
|
||||
}
|
||||
|
@@ -52,11 +52,11 @@ public sealed class XpConfigService : ConfigServiceBase<XpConfig>
|
||||
});
|
||||
}
|
||||
|
||||
if (data.Version < 3)
|
||||
if (data.Version < 5)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 3;
|
||||
c.Version = 5;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -593,12 +593,12 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
{
|
||||
if (ShouldTrackVoiceChannel(channel))
|
||||
{
|
||||
foreach (var user in channel.Users)
|
||||
foreach (var user in channel.ConnectedUsers)
|
||||
await ScanUserForVoiceXp(user, channel);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var user in channel.Users)
|
||||
foreach (var user in channel.ConnectedUsers)
|
||||
await UserLeftVoiceChannel(user, channel);
|
||||
}
|
||||
}
|
||||
@@ -617,13 +617,13 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
}
|
||||
|
||||
private bool ShouldTrackVoiceChannel(SocketVoiceChannel channel)
|
||||
=> channel.Users.Where(UserParticipatingInVoiceChannel).Take(2).Count() >= 2;
|
||||
=> channel.ConnectedUsers.Where(UserParticipatingInVoiceChannel).Take(2).Count() >= 2;
|
||||
|
||||
private bool UserParticipatingInVoiceChannel(SocketGuildUser user)
|
||||
=> !user.IsDeafened && !user.IsMuted && !user.IsSelfDeafened && !user.IsSelfMuted;
|
||||
|
||||
private TypedKey<long> GetVoiceXpKey(ulong userId)
|
||||
=> new($"xp:vc_join:{userId}");
|
||||
=> new($"xp:{_client.CurrentUser.Id}:vc_join:{userId}");
|
||||
|
||||
private async Task UserJoinedVoiceChannel(SocketGuildUser user)
|
||||
{
|
||||
@@ -635,6 +635,18 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
overwrite: false);
|
||||
}
|
||||
|
||||
// private void UserJoinedVoiceChannel(SocketGuildUser user)
|
||||
// {
|
||||
// var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
|
||||
// var value = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
//
|
||||
// _cache.Redis.GetDatabase()
|
||||
// .StringSet(key,
|
||||
// value,
|
||||
// TimeSpan.FromMinutes(_xpConfig.Data.VoiceMaxMinutes),
|
||||
// when: When.NotExists);
|
||||
// }
|
||||
|
||||
private async Task UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
|
||||
{
|
||||
var key = GetVoiceXpKey(user.Id);
|
||||
@@ -654,6 +666,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
|
||||
if (actualXp > 0)
|
||||
{
|
||||
Log.Information("Adding {Amount} voice xp to {User}", actualXp, user.ToString());
|
||||
await _xpGainQueue.Writer.WriteAsync(new()
|
||||
{
|
||||
Guild = channel.Guild,
|
||||
@@ -664,6 +677,38 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* private void UserLeftVoiceChannel(SocketGuildUser user, SocketVoiceChannel channel)
|
||||
{
|
||||
var key = $"{_creds.RedisKey()}_user_xp_vc_join_{user.Id}";
|
||||
var value = _cache.Redis.GetDatabase().StringGet(key);
|
||||
_cache.Redis.GetDatabase().KeyDelete(key);
|
||||
|
||||
// Allow for if this function gets called multiple times when a user leaves a channel.
|
||||
if (value.IsNull)
|
||||
return;
|
||||
|
||||
if (!value.TryParse(out long startUnixTime))
|
||||
return;
|
||||
|
||||
var dateStart = DateTimeOffset.FromUnixTimeSeconds(startUnixTime);
|
||||
var dateEnd = DateTimeOffset.UtcNow;
|
||||
var minutes = (dateEnd - dateStart).TotalMinutes;
|
||||
var xp = _xpConfig.Data.VoiceXpPerMinute * minutes;
|
||||
var actualXp = (int)Math.Floor(xp);
|
||||
|
||||
if (actualXp > 0)
|
||||
{
|
||||
_addMessageXp.Enqueue(new()
|
||||
{
|
||||
Guild = channel.Guild,
|
||||
User = user,
|
||||
XpAmount = actualXp
|
||||
});
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private bool ShouldTrackXp(SocketGuildUser user, ulong channelId)
|
||||
{
|
||||
if (_excludedChannels.TryGetValue(user.Guild.Id, out var chans) && chans.Contains(channelId))
|
||||
@@ -767,8 +812,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
return Enumerable.Empty<ulong>();
|
||||
}
|
||||
|
||||
private static TypedKey<bool> GetUserRewKey(ulong userId)
|
||||
=> new($"xp:user_gain:{userId}");
|
||||
private TypedKey<bool> GetUserRewKey(ulong userId)
|
||||
=> new($"xp:{_client.CurrentUser.Id}:user_gain:{userId}");
|
||||
|
||||
private async Task<bool> SetUserRewardedAsync(ulong userId)
|
||||
=> await _c.AddAsync(GetUserRewKey(userId),
|
||||
@@ -887,6 +932,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
throw new ArgumentNullException(nameof(bgBytes));
|
||||
}
|
||||
|
||||
var outlinePen = new Pen(Color.Black, 1f);
|
||||
|
||||
using var img = Image.Load<Rgba32>(bgBytes, out var imageFormat);
|
||||
if (template.User.Name.Show)
|
||||
{
|
||||
@@ -909,7 +956,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
Origin = new(template.User.Name.Pos.X, template.User.Name.Pos.Y + 8)
|
||||
},
|
||||
"@" + username,
|
||||
template.User.Name.Color);
|
||||
Brushes.Solid(template.User.Name.Color),
|
||||
outlinePen);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -929,7 +977,8 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
Origin = new(template.Club.Name.Pos.X + 50, template.Club.Name.Pos.Y - 8)
|
||||
},
|
||||
clubName,
|
||||
template.Club.Name.Color));
|
||||
Brushes.Solid(template.Club.Name.Color),
|
||||
outlinePen));
|
||||
}
|
||||
|
||||
Font GetTruncatedFont(
|
||||
@@ -987,7 +1036,6 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
});
|
||||
}
|
||||
|
||||
var pen = new Pen(Color.Black, 1.25f);
|
||||
|
||||
var global = stats.Global;
|
||||
var guild = stats.Guild;
|
||||
@@ -1012,7 +1060,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
},
|
||||
$"{global.LevelXp}/{global.RequiredXp}",
|
||||
Brushes.Solid(template.User.Xp.Global.Color),
|
||||
pen));
|
||||
outlinePen));
|
||||
}
|
||||
|
||||
if (template.User.Xp.Guild.Show)
|
||||
@@ -1026,7 +1074,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
},
|
||||
$"{guild.LevelXp}/{guild.RequiredXp}",
|
||||
Brushes.Solid(template.User.Xp.Guild.Color),
|
||||
pen));
|
||||
outlinePen));
|
||||
}
|
||||
|
||||
if (stats.FullGuildStats.AwardedXp != 0 && template.User.Xp.Awarded.Show)
|
||||
@@ -1038,10 +1086,11 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
img.Mutate(x => x.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})",
|
||||
_fonts.NotoSans.CreateFont(template.User.Xp.Awarded.FontSize, FontStyle.Bold),
|
||||
Brushes.Solid(template.User.Xp.Awarded.Color),
|
||||
pen,
|
||||
outlinePen,
|
||||
new(awX, awY)));
|
||||
}
|
||||
|
||||
var rankPen = new Pen(Color.White, 1);
|
||||
//ranking
|
||||
if (template.User.GlobalRank.Show)
|
||||
{
|
||||
@@ -1054,10 +1103,15 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
globalRankStr,
|
||||
68);
|
||||
|
||||
img.Mutate(x => x.DrawText(globalRankStr,
|
||||
globalRankFont,
|
||||
template.User.GlobalRank.Color,
|
||||
new(template.User.GlobalRank.Pos.X, template.User.GlobalRank.Pos.Y)));
|
||||
img.Mutate(x => x.DrawText(
|
||||
new TextOptions(globalRankFont)
|
||||
{
|
||||
Origin = new(template.User.GlobalRank.Pos.X, template.User.GlobalRank.Pos.Y)
|
||||
},
|
||||
globalRankStr,
|
||||
Brushes.Solid(template.User.GlobalRank.Color),
|
||||
rankPen
|
||||
));
|
||||
}
|
||||
|
||||
if (template.User.GuildRank.Show)
|
||||
@@ -1071,10 +1125,15 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
guildRankStr,
|
||||
43);
|
||||
|
||||
img.Mutate(x => x.DrawText(guildRankStr,
|
||||
guildRankFont,
|
||||
template.User.GuildRank.Color,
|
||||
new(template.User.GuildRank.Pos.X, template.User.GuildRank.Pos.Y)));
|
||||
img.Mutate(x => x.DrawText(
|
||||
new TextOptions(guildRankFont)
|
||||
{
|
||||
Origin = new(template.User.GuildRank.Pos.X, template.User.GuildRank.Pos.Y)
|
||||
},
|
||||
guildRankStr,
|
||||
Brushes.Solid(template.User.GuildRank.Color),
|
||||
rankPen
|
||||
));
|
||||
}
|
||||
|
||||
//avatar
|
||||
@@ -1337,11 +1396,15 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
if (!conf.Shop.IsEnabled)
|
||||
return BuyResult.UnknownItem;
|
||||
|
||||
if (conf.Shop.TierRequirement != PatronTier.None)
|
||||
var req = type == XpShopItemType.Background
|
||||
? conf.Shop.BgsTierRequirement
|
||||
: conf.Shop.FramesTierRequirement;
|
||||
|
||||
if (req != PatronTier.None && !_creds.IsOwner(userId))
|
||||
{
|
||||
var patron = await _ps.GetPatronAsync(userId);
|
||||
|
||||
if ((int)patron.Tier < (int)conf.Shop.TierRequirement)
|
||||
if ((int)patron.Tier < (int)req)
|
||||
return BuyResult.InsufficientPatronTier;
|
||||
}
|
||||
|
||||
@@ -1468,8 +1531,12 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
public PatronTier GetXpShopTierRequirement()
|
||||
=> _xpConfig.Data.Shop.TierRequirement;
|
||||
public PatronTier GetXpShopTierRequirement(Xp.XpShopInputType type)
|
||||
=> type switch
|
||||
{
|
||||
Xp.XpShopInputType.F => _xpConfig.Data.Shop.FramesTierRequirement,
|
||||
_ => _xpConfig.Data.Shop.BgsTierRequirement,
|
||||
};
|
||||
|
||||
public bool IsShopEnabled()
|
||||
=> _xpConfig.Data.Shop.IsEnabled;
|
||||
|
@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
|
||||
|
||||
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
||||
{
|
||||
public const string BOT_VERSION = "4.3.2";
|
||||
public const string BOT_VERSION = "4.3.4";
|
||||
|
||||
public string Author
|
||||
=> "Kwoth#2452";
|
||||
|
@@ -1064,6 +1064,10 @@ xpadd:
|
||||
- xpadd
|
||||
xpshop:
|
||||
- xpshop
|
||||
xpshopbuy:
|
||||
- xpshopbuy
|
||||
xpshopuse:
|
||||
- xpshopuse
|
||||
clubcreate:
|
||||
- clubcreate
|
||||
clubtransfer:
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 13 KiB |
@@ -2128,6 +2128,16 @@ xpshop:
|
||||
- "bgs"
|
||||
- "frames"
|
||||
- "bgs 3"
|
||||
xpshopbuy:
|
||||
desc: "Buy an item from the xp shop by specifying the type and the key of the item."
|
||||
args:
|
||||
- "bg open_sea"
|
||||
- "fr gold"
|
||||
xpshopuse:
|
||||
desc: "Use a previously purchased item from the xp shop by specifying the type and the key of the item."
|
||||
args:
|
||||
- "bg synth"
|
||||
- "fr default"
|
||||
bible:
|
||||
desc: "Shows bible verse. You need to supply book name and chapter:verse"
|
||||
args:
|
||||
|
@@ -1003,7 +1003,7 @@
|
||||
"feature_limit_reached_you": "You've reached the limit of {0} for the {1} feature. You may be able to increase this limit by upgrading your patron tier.",
|
||||
"feature_limit_reached_owner": "Server owner has reached the limit of {0} for the {1} feature. Server owner may be able to upgrade this limit by upgrading patron tier.",
|
||||
"feature_limit_reached_either": "The limit of {0} for the {1} feature has been reached. Either you or the server owner may able to upgrade this limit by upgrading the patron tier.",
|
||||
"xp_shop_buy_required_tier": "Buying items from the shop requires Patron Tier {0} or higher.",
|
||||
"xp_shop_buy_required_tier": "Buying items from this shop requires Patron Tier {0} or higher.",
|
||||
"available_commands": "Available Commands",
|
||||
"tier": "Tier",
|
||||
"pledge": "Pledge",
|
||||
@@ -1017,5 +1017,10 @@
|
||||
"patron_not_enabled": "Patron system is disabled.",
|
||||
"results_in": "{0} results in {1}s",
|
||||
"patron_msg_sent": "Done sending messages to patrons at and above tier {1}. {1} successfully sent and {2} failed.",
|
||||
"xpadd_users": "Added {0} server XP to {1} users."
|
||||
"xpadd_users": "Added {0} server XP to {1} users.",
|
||||
"xpshop_buy_success": "Successfully purchased `{0}/{1}`",
|
||||
"patron_insuff_tier": "Your Patron Tier insufficient to perform this action.",
|
||||
"xpshop_already_owned": "You already own this item.",
|
||||
"xpshop_item_not_found": "An item with that key doesn't exist.",
|
||||
"xpshop_website": "You can see the list of all Xp Shop items here: <https://xpshop.nadeko.bot>"
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# DO NOT CHANGE
|
||||
version: 3
|
||||
version: 5
|
||||
# How much XP will the users receive per message
|
||||
xpPerMessage: 3
|
||||
# How often can the users receive XP in minutes
|
||||
@@ -16,9 +16,12 @@ shop:
|
||||
# True -> Users can access the xp shop using .xpshop command
|
||||
# False -> Users can't access the xp shop
|
||||
isEnabled: false
|
||||
# Which patron tier do users need in order to use the .xpshop command
|
||||
# Which patron tier do users need in order to use the .xpshop bgs command
|
||||
# Leave at 'None' if patron system is disabled or you don't want any restrictions
|
||||
tierRequirement: None
|
||||
bgsTierRequirement: None
|
||||
# Which patron tier do users need in order to use the .xpshop frames command
|
||||
# Leave at 'None' if patron system is disabled or you don't want any restrictions
|
||||
framesTierRequirement: None
|
||||
# Frames available for sale. Keys are unique IDs.
|
||||
# Do not change keys as they are not publicly visible. Only change properties (name, price, id)
|
||||
# Removing a key which previously existed means that all previous purchases will also be unusable.
|
||||
@@ -31,6 +34,8 @@ shop:
|
||||
price: 0
|
||||
# Direct url to the .png image which will be applied to the user's XP card
|
||||
url: ''
|
||||
# Optional preview url which will show instead of the real URL in the shop
|
||||
preview:
|
||||
# Optional description of the item
|
||||
desc:
|
||||
# Backgrounds available for sale. Keys are unique IDs.
|
||||
@@ -45,5 +50,7 @@ shop:
|
||||
price: 0
|
||||
# Direct url to the .png image which will be applied to the user's XP card
|
||||
url: ''
|
||||
# Optional preview url which will show instead of the real URL in the shop
|
||||
preview:
|
||||
# Optional description of the item
|
||||
desc:
|
||||
|
@@ -10,8 +10,8 @@
|
||||
"Show": true,
|
||||
"FontSize": 50,
|
||||
"Pos": {
|
||||
"X": 130,
|
||||
"Y": 17
|
||||
"X": 105,
|
||||
"Y": 25
|
||||
}
|
||||
},
|
||||
"Icon": {
|
||||
@@ -65,7 +65,7 @@
|
||||
"Bar": {
|
||||
"Show": true,
|
||||
"Global": {
|
||||
"Color": "00000066",
|
||||
"Color": "00000095",
|
||||
"PointA": {
|
||||
"X": 321,
|
||||
"Y": 104
|
||||
@@ -78,7 +78,7 @@
|
||||
"Direction": 3
|
||||
},
|
||||
"Guild": {
|
||||
"Color": "00000066",
|
||||
"Color": "00000095",
|
||||
"PointA": {
|
||||
"X": 282,
|
||||
"Y": 248
|
||||
@@ -114,7 +114,7 @@
|
||||
"Show": true,
|
||||
"FontSize": 25,
|
||||
"Pos": {
|
||||
"X": 490,
|
||||
"X": 450,
|
||||
"Y": 345
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@
|
||||
"FontSize": 35,
|
||||
"Pos": {
|
||||
"X": 650,
|
||||
"Y": 49
|
||||
"Y": 55
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user