mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7c8756096d | ||
|
7d9e456fe5 | ||
|
df1a775fec | ||
|
6c169e057b | ||
|
b164da38e3 | ||
|
dc229ea2b3 | ||
|
b853495d65 | ||
|
0ff02cede9 | ||
|
f7e0e635e6 | ||
|
a065189023 | ||
|
1affeb0683 | ||
|
6b3c9f01ca | ||
|
e9eb6ff2ad | ||
|
0170536d1c | ||
|
c65e769128 | ||
|
998779203a | ||
|
e0e4d697c3 | ||
|
e9c7293014 | ||
|
cf876a4148 | ||
|
6b9a858f28 | ||
|
38e3badb87 | ||
|
13d2fbd560 | ||
|
8d3f2f186a | ||
|
9572b9dc43 | ||
|
ca32086089 | ||
|
57f839dbcd | ||
|
71d6eeb9dd | ||
|
8c51cf8537 | ||
|
b683026cf3 | ||
|
bca2bc5af1 | ||
|
b385a83bdd | ||
|
3bf0286c81 | ||
|
98272f66e7 | ||
|
cf3788c6ea | ||
|
4b3fc53cb6 | ||
|
4e17dca856 | ||
|
82d89148f3 | ||
|
cc4c09b4d7 | ||
|
616f01f8b2 | ||
|
56f89a02bc | ||
|
48ce988d20 | ||
|
119b1cdec2 | ||
|
43fa5a22f5 | ||
|
3c715a29ca | ||
|
31e1cbb19f | ||
|
8e464e9f09 | ||
|
a190a3d933 | ||
|
bedba98130 | ||
|
d69f8435f6 | ||
|
8440b34338 | ||
|
a2715740c1 | ||
|
36b7fd2352 | ||
|
6563cb507a | ||
|
cc6128997e | ||
|
e942da4470 | ||
|
ab2bcdf00d |
73
CHANGELOG.md
73
CHANGELOG.md
@@ -2,6 +2,74 @@
|
||||
|
||||
Experimental changelog. Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [4.3.5] - 17.08.2022
|
||||
|
||||
### Added
|
||||
|
||||
- Added a 'Use' button when a user already owns an item
|
||||
- Added a 'Pull Again' button to slots
|
||||
- Added `.roleinfo` command
|
||||
- Added `.emojiremove` command
|
||||
- Added `.threadcreate` and `.threaddelete` commands
|
||||
- Added `.bank seize` / `.bank award` owner only commands
|
||||
|
||||
### Changed
|
||||
|
||||
- Running a .timely command early now shows a pending color
|
||||
- .xp system is once again no longer opt in for servers
|
||||
- It's still opt-in for global and requires users to run .xp at least once in order to start gaining global xp
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed users not getting club xp
|
||||
|
||||
## [4.3.4] - 07.08.2022
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed users getting XP out of nowhere while voice xp is enabled
|
||||
|
||||
## [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
|
||||
|
||||
- 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
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Reaction Roles not working properly with animated emojis
|
||||
- Fixed `.slot` alignment
|
||||
- Fixed `mysql` and `postgresql` reactionrole migration
|
||||
- Fixed repeat loop with `postgresql` db provider
|
||||
- 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
|
||||
@@ -15,9 +83,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`
|
||||
|
||||
|
@@ -42,6 +42,7 @@ COPY docker-entrypoint.sh /usr/local/sbin
|
||||
|
||||
ENV shard_id=0
|
||||
ENV total_shards=1
|
||||
ENV NadekoBot__creds=/app/data/creds.yml
|
||||
|
||||
VOLUME [ "/app/data" ]
|
||||
ENTRYPOINT [ "/usr/local/sbin/docker-entrypoint.sh" ]
|
||||
|
@@ -1,8 +1,3 @@
|
||||
[](https://discord.gg/nadekobot)
|
||||
[](http://nadekobot.readthedocs.io/en/v4/?badge=v4)
|
||||
[](https://top.gg/bot/116275390695079945)
|
||||
|
||||
|
||||
[](https://nadeko.bot/)
|
||||
|
||||
[](https://invite.nadeko.bot/)
|
||||
|
@@ -13,7 +13,15 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
# fix folder permissions
|
||||
# creds.yml migration
|
||||
if [ -f /app/creds.yml ]; then
|
||||
echo "Default location for creds.yml is now /app/data/creds.yml."
|
||||
echo "Please move your creds.yml and update your docker-compose.yml accordingly."
|
||||
|
||||
export Nadeko_creds=/app/creds.yml
|
||||
fi
|
||||
|
||||
# ensure nadeko can write on /app/data
|
||||
chown -R nadeko:nadeko "$data"
|
||||
|
||||
# drop to regular user and launch command
|
||||
|
@@ -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`
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
**Optional**
|
||||
|
||||
- [Notepad++] (makes it easier to edit your credentials)
|
||||
- [Visual Studio Code](https://code.visualstudio.com/Download) (Highly suggested if you plan on editing files)
|
||||
- [Visual C++ 2010 (x86)] and [Visual C++ 2017 (x64)] (both are required if you want Nadeko to play music - restart Windows after installation)
|
||||
|
||||
#### Setup
|
||||
|
@@ -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)
|
||||
|
19
src/Nadeko.Common/ShmartBankAmount.cs
Normal file
19
src/Nadeko.Common/ShmartBankAmount.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Nadeko.Common;
|
||||
|
||||
public readonly struct ShmartBankAmount
|
||||
{
|
||||
public long Amount { get; }
|
||||
public ShmartBankAmount(long amount)
|
||||
{
|
||||
Amount = amount;
|
||||
}
|
||||
|
||||
public static implicit operator ShmartBankAmount(long num)
|
||||
=> new(num);
|
||||
|
||||
public static implicit operator long(ShmartBankAmount num)
|
||||
=> num.Amount;
|
||||
|
||||
public static implicit operator ShmartBankAmount(int num)
|
||||
=> new(num);
|
||||
}
|
@@ -3,12 +3,10 @@
|
||||
public readonly struct ShmartNumber : IEquatable<ShmartNumber>
|
||||
{
|
||||
public long Value { get; }
|
||||
public string? Input { get; }
|
||||
|
||||
public ShmartNumber(long val, string? input = null)
|
||||
public ShmartNumber(long val)
|
||||
{
|
||||
Value = val;
|
||||
Input = input;
|
||||
}
|
||||
|
||||
public static implicit operator ShmartNumber(long num)
|
||||
|
@@ -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++)
|
||||
{
|
||||
|
@@ -1,5 +1,8 @@
|
||||
namespace Nadeko.Econ.Gambling;
|
||||
|
||||
//here is a payout chart
|
||||
//https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
|
||||
//thanks to judge for helping me with this
|
||||
public class SlotGame
|
||||
{
|
||||
private static readonly NadekoRandom _rng = new NadekoRandom();
|
||||
@@ -8,9 +11,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];
|
||||
|
@@ -47,7 +47,7 @@ public static class MedusaExtensions
|
||||
=> ctx.Message.AddReactionAsync(new Emoji("🤔"));
|
||||
|
||||
public static Task<IUserMessage> ErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||
=> ctx.SendErrorAsync(ctx.GetText(key));
|
||||
=> ctx.SendErrorAsync(ctx.GetText(key, args));
|
||||
|
||||
public static Task<IUserMessage> PendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||
=> ctx.SendPendingAsync(ctx.GetText(key, args));
|
||||
@@ -56,11 +56,11 @@ public static class MedusaExtensions
|
||||
=> ctx.SendConfirmAsync(ctx.GetText(key, args));
|
||||
|
||||
public static Task<IUserMessage> ReplyErrorLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||
=> ctx.SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key)}");
|
||||
=> ctx.SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
|
||||
|
||||
public static Task<IUserMessage> ReplyPendingLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||
=> ctx.SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key)}");
|
||||
=> ctx.SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
|
||||
|
||||
public static Task<IUserMessage> ReplyConfirmLocalizedAsync(this AnyContext ctx, string key, params object[]? args)
|
||||
=> ctx.SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key)}");
|
||||
=> ctx.SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {ctx.GetText(key, args)}");
|
||||
}
|
@@ -10,6 +10,7 @@ public interface IEmbedBuilder
|
||||
IEmbedBuilder WithFooter(string text, string? iconUrl = null);
|
||||
IEmbedBuilder WithAuthor(string name, string? iconUrl = null, string? url = null);
|
||||
IEmbedBuilder WithColor(EmbedColor color);
|
||||
IEmbedBuilder WithDiscordColor(Color color);
|
||||
Embed Build();
|
||||
IEmbedBuilder WithUrl(string url);
|
||||
IEmbedBuilder WithImageUrl(string url);
|
||||
|
@@ -35,13 +35,13 @@ public sealed class Bot
|
||||
private readonly IBotCredsProvider _credsProvider;
|
||||
// private readonly InteractionService _interactionService;
|
||||
|
||||
public Bot(int shardId, int? totalShards)
|
||||
public Bot(int shardId, int? totalShards, string credPath = null)
|
||||
{
|
||||
if (shardId < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(shardId));
|
||||
|
||||
ShardId = shardId;
|
||||
_credsProvider = new BotCredsProvider(totalShards);
|
||||
_credsProvider = new BotCredsProvider(totalShards, credPath);
|
||||
_creds = _credsProvider.GetCreds();
|
||||
|
||||
_db = new(_credsProvider);
|
||||
|
@@ -0,0 +1,8 @@
|
||||
namespace NadekoBot;
|
||||
|
||||
public interface INadekoInteractionService
|
||||
{
|
||||
public NadekoInteraction Create<T>(
|
||||
ulong userId,
|
||||
SimpleInteraction<T> inter);
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
namespace NadekoBot;
|
||||
|
||||
public sealed class NadekoButtonActionInteraction : NadekoButtonOwnInteraction
|
||||
{
|
||||
private readonly NadekoInteractionData _data;
|
||||
private readonly Func<SocketMessageComponent, Task> _action;
|
||||
|
||||
public NadekoButtonActionInteraction(
|
||||
DiscordSocketClient client,
|
||||
ulong authorId,
|
||||
NadekoInteractionData data,
|
||||
Func<SocketMessageComponent, Task> action
|
||||
)
|
||||
: base(client, authorId)
|
||||
{
|
||||
_data = data;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
protected override string Name
|
||||
=> _data.CustomId;
|
||||
protected override IEmote Emote
|
||||
=> _data.Emote;
|
||||
protected override string? Text
|
||||
=> _data.Text;
|
||||
|
||||
public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
|
||||
=> _action(smc);
|
||||
}
|
@@ -1,25 +1,30 @@
|
||||
namespace NadekoBot;
|
||||
|
||||
public abstract class NadekoButtonInteraction
|
||||
public sealed class NadekoInteraction
|
||||
{
|
||||
// improvements:
|
||||
// - state in OnAction
|
||||
// - configurable delay
|
||||
// -
|
||||
protected abstract string Name { get; }
|
||||
protected abstract IEmote Emote { get; }
|
||||
protected virtual string? Text { get; } = null;
|
||||
|
||||
private readonly ulong _authorId;
|
||||
private readonly ButtonBuilder _button;
|
||||
private readonly Func<SocketMessageComponent, Task> _onClick;
|
||||
private readonly bool _onlyAuthor;
|
||||
public DiscordSocketClient Client { get; }
|
||||
|
||||
protected readonly TaskCompletionSource<bool> _interactionCompletedSource;
|
||||
private readonly TaskCompletionSource<bool> _interactionCompletedSource;
|
||||
|
||||
protected IUserMessage message = null!;
|
||||
private IUserMessage message = null!;
|
||||
|
||||
protected NadekoButtonInteraction(DiscordSocketClient client)
|
||||
public NadekoInteraction(DiscordSocketClient client,
|
||||
ulong authorId,
|
||||
ButtonBuilder button,
|
||||
Func<SocketMessageComponent, Task> onClick,
|
||||
bool onlyAuthor)
|
||||
{
|
||||
Client = client;
|
||||
_authorId = authorId;
|
||||
_button = button;
|
||||
_onClick = onClick;
|
||||
_onlyAuthor = onlyAuthor;
|
||||
_interactionCompletedSource = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
Client = client;
|
||||
}
|
||||
|
||||
public async Task RunAsync(IUserMessage msg)
|
||||
@@ -27,13 +32,12 @@ public abstract class NadekoButtonInteraction
|
||||
message = msg;
|
||||
|
||||
Client.InteractionCreated += OnInteraction;
|
||||
await Task.WhenAny(Task.Delay(10_000), _interactionCompletedSource.Task);
|
||||
await Task.WhenAny(Task.Delay(15_000), _interactionCompletedSource.Task);
|
||||
Client.InteractionCreated -= OnInteraction;
|
||||
|
||||
await msg.ModifyAsync(m => m.Components = new ComponentBuilder().Build());
|
||||
}
|
||||
|
||||
protected abstract ValueTask<bool> Validate(SocketMessageComponent smc);
|
||||
private async Task OnInteraction(SocketInteraction arg)
|
||||
{
|
||||
if (arg is not SocketMessageComponent smc)
|
||||
@@ -42,14 +46,11 @@ public abstract class NadekoButtonInteraction
|
||||
if (smc.Message.Id != message.Id)
|
||||
return;
|
||||
|
||||
if (smc.Data.CustomId != Name)
|
||||
if (_onlyAuthor && smc.User.Id != _authorId)
|
||||
return;
|
||||
|
||||
if (!await Validate(smc))
|
||||
{
|
||||
await smc.DeferAsync();
|
||||
if (smc.Data.CustomId != _button.CustomId)
|
||||
return;
|
||||
}
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
@@ -66,18 +67,14 @@ public abstract class NadekoButtonInteraction
|
||||
}
|
||||
|
||||
|
||||
public virtual MessageComponent CreateComponent()
|
||||
public MessageComponent CreateComponent()
|
||||
{
|
||||
var comp = new ComponentBuilder()
|
||||
.WithButton(GetButtonBuilder());
|
||||
.WithButton(_button);
|
||||
|
||||
return comp.Build();
|
||||
}
|
||||
|
||||
public ButtonBuilder GetButtonBuilder()
|
||||
=> new ButtonBuilder(style: ButtonStyle.Secondary, emote: Emote, customId: Name, label: Text);
|
||||
|
||||
public abstract Task ExecuteOnActionAsync(SocketMessageComponent smc);
|
||||
public Task ExecuteOnActionAsync(SocketMessageComponent smc)
|
||||
=> _onClick(smc);
|
||||
}
|
||||
|
||||
// this is all so wrong ...
|
@@ -1,43 +0,0 @@
|
||||
// namespace NadekoBot;
|
||||
//
|
||||
// public class NadekoButtonInteractionArray : NadekoButtonInteraction
|
||||
// {
|
||||
// private readonly ButtonBuilder[] _bbs;
|
||||
// private readonly NadekoButtonInteraction[] _inters;
|
||||
//
|
||||
// public NadekoButtonInteractionArray(params NadekoButtonInteraction[] inters)
|
||||
// : base(inters[0].Client)
|
||||
// {
|
||||
// _inters = inters;
|
||||
// _bbs = inters.Map(x => x.GetButtonBuilder());
|
||||
// }
|
||||
//
|
||||
// protected override string Name
|
||||
// => throw new NotSupportedException();
|
||||
// protected override IEmote Emote
|
||||
// => throw new NotSupportedException();
|
||||
//
|
||||
// protected override ValueTask<bool> Validate(SocketMessageComponent smc)
|
||||
// => new(true);
|
||||
//
|
||||
// public override Task ExecuteOnActionAsync(SocketMessageComponent smc)
|
||||
// {
|
||||
// for (var i = 0; i < _bbs.Length; i++)
|
||||
// {
|
||||
// if (_bbs[i].CustomId == smc.Data.CustomId)
|
||||
// return _inters[i].ExecuteOnActionAsync(smc);
|
||||
// }
|
||||
//
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
//
|
||||
// public override MessageComponent CreateComponent()
|
||||
// {
|
||||
// var comp = new ComponentBuilder();
|
||||
//
|
||||
// foreach (var bb in _bbs)
|
||||
// comp.WithButton(bb);
|
||||
//
|
||||
// return comp.Build();
|
||||
// }
|
||||
// }
|
@@ -1,42 +0,0 @@
|
||||
namespace NadekoBot;
|
||||
|
||||
/// <summary>
|
||||
/// Builder class for NadekoInteractions
|
||||
/// </summary>
|
||||
public class NadekoInteractionBuilder
|
||||
{
|
||||
private NadekoInteractionData? iData;
|
||||
private Func<SocketMessageComponent, Task>? action;
|
||||
// private bool isOwn;
|
||||
|
||||
public NadekoInteractionBuilder WithData<T>(in T data)
|
||||
where T : NadekoInteractionData
|
||||
{
|
||||
iData = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
// public NadekoOwnInteractionBuiler WithIsOwn(bool isOwn = true)
|
||||
// {
|
||||
// this.isOwn = isOwn;
|
||||
// return this;
|
||||
|
||||
// }
|
||||
|
||||
public NadekoInteractionBuilder WithAction(in Func<SocketMessageComponent, Task> fn)
|
||||
{
|
||||
this.action = fn;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NadekoButtonActionInteraction Build(DiscordSocketClient client, ulong userId)
|
||||
{
|
||||
if (iData is null)
|
||||
throw new InvalidOperationException("You have to specify the data before building the interaction");
|
||||
|
||||
if (action is null)
|
||||
throw new InvalidOperationException("You have to specify the action before building the interaction");
|
||||
|
||||
return new(client, userId, iData, action);
|
||||
}
|
||||
}
|
20
src/NadekoBot/Common/Interaction/NadekoInteractionService.cs
Normal file
20
src/NadekoBot/Common/Interaction/NadekoInteractionService.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace NadekoBot;
|
||||
|
||||
public class NadekoInteractionService : INadekoInteractionService, INService
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public NadekoInteractionService(DiscordSocketClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public NadekoInteraction Create<T>(
|
||||
ulong userId,
|
||||
SimpleInteraction<T> inter)
|
||||
=> new NadekoInteraction(_client,
|
||||
userId,
|
||||
inter.Button,
|
||||
inter.TriggerAsync,
|
||||
onlyAuthor: true);
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
namespace NadekoBot;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction which only the author can use
|
||||
/// </summary>
|
||||
public abstract class NadekoButtonOwnInteraction : NadekoButtonInteraction
|
||||
{
|
||||
protected readonly ulong _authorId;
|
||||
|
||||
protected NadekoButtonOwnInteraction(DiscordSocketClient client, ulong authorId) : base(client)
|
||||
=> _authorId = authorId;
|
||||
|
||||
protected override ValueTask<bool> Validate(SocketMessageComponent smc)
|
||||
=> new(smc.User.Id == _authorId);
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
namespace NadekoBot.Common;
|
||||
|
||||
public abstract class NInteraction
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly ulong _userId;
|
||||
private readonly Func<SocketMessageComponent, Task> _action;
|
||||
|
||||
protected abstract NadekoInteractionData Data { get; }
|
||||
|
||||
public NInteraction(
|
||||
DiscordSocketClient client,
|
||||
ulong userId,
|
||||
Func<SocketMessageComponent, Task> action)
|
||||
{
|
||||
_client = client;
|
||||
_userId = userId;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public NadekoButtonInteraction GetInteraction()
|
||||
=> new NadekoInteractionBuilder()
|
||||
.WithData(Data)
|
||||
.WithAction(_action)
|
||||
.Build(_client, _userId);
|
||||
}
|
@@ -18,6 +18,7 @@ public abstract class NadekoModule : ModuleBase
|
||||
public CommandHandler _cmdHandler { get; set; }
|
||||
public ILocalization _localization { get; set; }
|
||||
public IEmbedBuilderService _eb { get; set; }
|
||||
public INadekoInteractionService _inter { get; set; }
|
||||
|
||||
protected string prefix
|
||||
=> _cmdHandler.GetPrefix(ctx.Guild);
|
||||
@@ -36,7 +37,7 @@ public abstract class NadekoModule : ModuleBase
|
||||
string error,
|
||||
string url = null,
|
||||
string footer = null,
|
||||
NadekoButtonInteraction inter = null)
|
||||
NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendErrorAsync(_eb, title, error, url, footer);
|
||||
|
||||
public Task<IUserMessage> SendConfirmAsync(
|
||||
@@ -47,32 +48,32 @@ public abstract class NadekoModule : ModuleBase
|
||||
=> ctx.Channel.SendConfirmAsync(_eb, title, text, url, footer);
|
||||
|
||||
//
|
||||
public Task<IUserMessage> SendErrorAsync(string text, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> SendErrorAsync(string text, NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendAsync(_eb, text, MessageType.Error, inter);
|
||||
public Task<IUserMessage> SendConfirmAsync(string text, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> SendConfirmAsync(string text, NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendAsync(_eb, text, MessageType.Ok, inter);
|
||||
public Task<IUserMessage> SendPendingAsync(string text, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> SendPendingAsync(string text, NadekoInteraction inter = null)
|
||||
=> ctx.Channel.SendAsync(_eb, text, MessageType.Pending, inter);
|
||||
|
||||
|
||||
// localized normal
|
||||
public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> ErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendErrorAsync(GetText(str), inter);
|
||||
|
||||
public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> PendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendPendingAsync(GetText(str), inter);
|
||||
|
||||
public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> ConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendConfirmAsync(GetText(str), inter);
|
||||
|
||||
// localized replies
|
||||
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> ReplyErrorLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendErrorAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||
|
||||
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> ReplyPendingLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendPendingAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||
|
||||
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoButtonInteraction inter = null)
|
||||
public Task<IUserMessage> ReplyConfirmLocalizedAsync(LocStr str, NadekoInteraction inter = null)
|
||||
=> SendConfirmAsync($"{Format.Bold(ctx.User.ToString())} {GetText(str)}", inter);
|
||||
|
||||
public async Task<bool> PromptUserConfirmAsync(IEmbedBuilder embed)
|
||||
|
@@ -0,0 +1,94 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using NCalc;
|
||||
using OneOf;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public class BaseShmartInputAmountReader
|
||||
{
|
||||
private static readonly Regex _percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
||||
protected readonly DbService _db;
|
||||
protected readonly GamblingConfigService _gambling;
|
||||
|
||||
public BaseShmartInputAmountReader(DbService db, GamblingConfigService gambling)
|
||||
{
|
||||
_db = db;
|
||||
_gambling = gambling;
|
||||
}
|
||||
|
||||
public async ValueTask<OneOf<long, OneOf.Types.Error<string>>> ReadAsync(ICommandContext context, string input)
|
||||
{
|
||||
var i = input.Trim().ToUpperInvariant();
|
||||
|
||||
i = i.Replace("K", "000");
|
||||
|
||||
//can't add m because it will conflict with max atm
|
||||
|
||||
if (await TryHandlePercentage(context, i) is long num)
|
||||
{
|
||||
return num;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var expr = new Expression(i, EvaluateOptions.IgnoreCase);
|
||||
expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context).GetAwaiter().GetResult();
|
||||
return (long)decimal.Parse(expr.Evaluate().ToString()!);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new OneOf.Types.Error<string>($"Invalid input: {input}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EvaluateParam(string name, ParameterArgs args, ICommandContext ctx)
|
||||
{
|
||||
switch (name.ToUpperInvariant())
|
||||
{
|
||||
case "PI":
|
||||
args.Result = Math.PI;
|
||||
break;
|
||||
case "E":
|
||||
args.Result = Math.E;
|
||||
break;
|
||||
case "ALL":
|
||||
case "ALLIN":
|
||||
args.Result = await Cur(ctx);
|
||||
break;
|
||||
case "HALF":
|
||||
args.Result = await Cur(ctx) / 2;
|
||||
break;
|
||||
case "MAX":
|
||||
args.Result = await Max(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual async Task<long> Cur(ICommandContext ctx)
|
||||
{
|
||||
await using var uow = _db.GetDbContext();
|
||||
return await uow.DiscordUser.GetUserCurrencyAsync(ctx.User.Id);
|
||||
}
|
||||
|
||||
protected virtual async Task<long> Max(ICommandContext ctx)
|
||||
{
|
||||
var settings = _gambling.Data;
|
||||
var max = settings.MaxBet;
|
||||
return max == 0 ? await Cur(ctx) : max;
|
||||
}
|
||||
|
||||
private async Task<long?> TryHandlePercentage(ICommandContext ctx, string input)
|
||||
{
|
||||
var m = _percentRegex.Match(input);
|
||||
|
||||
if (m.Captures.Count == 0)
|
||||
return null;
|
||||
|
||||
if (!long.TryParse(m.Groups["num"].ToString(), out var percent))
|
||||
return null;
|
||||
|
||||
return (long)(await Cur(ctx) * (percent / 100.0f));
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.Gambling.Bank;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class ShmartBankAmountTypeReader : NadekoTypeReader<ShmartBankAmount>
|
||||
{
|
||||
private readonly IBankService _bank;
|
||||
private readonly ShmartBankInputAmountReader _tr;
|
||||
|
||||
public ShmartBankAmountTypeReader(IBankService bank, DbService db, GamblingConfigService gambling)
|
||||
{
|
||||
_bank = bank;
|
||||
_tr = new ShmartBankInputAmountReader(bank, db, gambling);
|
||||
}
|
||||
|
||||
public override async ValueTask<TypeReaderResult<ShmartBankAmount>> ReadAsync(ICommandContext ctx, string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return TypeReaderResult.FromError<ShmartBankAmount>(CommandError.ParseFailed, "Input is empty.");
|
||||
|
||||
var result = await _tr.ReadAsync(ctx, input);
|
||||
|
||||
if (result.TryPickT0(out var val, out var err))
|
||||
{
|
||||
return TypeReaderResult.FromSuccess<ShmartBankAmount>(new(val));
|
||||
}
|
||||
|
||||
return TypeReaderResult.FromError<ShmartBankAmount>(CommandError.Unsuccessful, err.Value);
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
using NadekoBot.Modules.Gambling.Bank;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class ShmartBankInputAmountReader : BaseShmartInputAmountReader
|
||||
{
|
||||
private readonly IBankService _bank;
|
||||
|
||||
public ShmartBankInputAmountReader(IBankService bank, DbService db, GamblingConfigService gambling)
|
||||
: base(db, gambling)
|
||||
{
|
||||
_bank = bank;
|
||||
}
|
||||
|
||||
protected override Task<long> Cur(ICommandContext ctx)
|
||||
=> _bank.GetBalanceAsync(ctx.User.Id);
|
||||
|
||||
protected override Task<long> Max(ICommandContext ctx)
|
||||
=> Cur(ctx);
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
||||
{
|
||||
private readonly BaseShmartInputAmountReader _tr;
|
||||
|
||||
public ShmartNumberTypeReader(DbService db, GamblingConfigService gambling)
|
||||
{
|
||||
_tr = new BaseShmartInputAmountReader(db, gambling);
|
||||
}
|
||||
|
||||
public override async ValueTask<TypeReaderResult<ShmartNumber>> ReadAsync(ICommandContext ctx, string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, "Input is empty.");
|
||||
|
||||
var result = await _tr.ReadAsync(ctx, input);
|
||||
|
||||
if (result.TryPickT0(out var val, out var err))
|
||||
{
|
||||
return TypeReaderResult.FromSuccess<ShmartNumber>(new(val));
|
||||
}
|
||||
|
||||
return TypeReaderResult.FromError<ShmartNumber>(CommandError.Unsuccessful, err.Value);
|
||||
}
|
||||
}
|
@@ -1,100 +0,0 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
using NCalc;
|
||||
using System.Text.RegularExpressions;
|
||||
using Nadeko.Common;
|
||||
|
||||
namespace NadekoBot.Common.TypeReaders;
|
||||
|
||||
public sealed class ShmartNumberTypeReader : NadekoTypeReader<ShmartNumber>
|
||||
{
|
||||
private static readonly Regex _percentRegex = new(@"^((?<num>100|\d{1,2})%)$", RegexOptions.Compiled);
|
||||
private readonly DbService _db;
|
||||
private readonly GamblingConfigService _gambling;
|
||||
|
||||
public ShmartNumberTypeReader(DbService db, GamblingConfigService gambling)
|
||||
{
|
||||
_db = db;
|
||||
_gambling = gambling;
|
||||
}
|
||||
|
||||
public override ValueTask<TypeReaderResult<ShmartNumber>> ReadAsync(ICommandContext context, string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return new(TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, "Input is empty."));
|
||||
|
||||
var i = input.Trim().ToUpperInvariant();
|
||||
|
||||
i = i.Replace("K", "000");
|
||||
|
||||
//can't add m because it will conflict with max atm
|
||||
|
||||
if (TryHandlePercentage(context, i, out var num))
|
||||
return new(TypeReaderResult.FromSuccess(new ShmartNumber(num, i)));
|
||||
try
|
||||
{
|
||||
var expr = new Expression(i, EvaluateOptions.IgnoreCase);
|
||||
expr.EvaluateParameter += (str, ev) => EvaluateParam(str, ev, context);
|
||||
var lon = (long)decimal.Parse(expr.Evaluate().ToString());
|
||||
return new(TypeReaderResult.FromSuccess(new ShmartNumber(lon, input)));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return ValueTask.FromResult(
|
||||
TypeReaderResult.FromError<ShmartNumber>(CommandError.ParseFailed, $"Invalid input: {input}"));
|
||||
}
|
||||
}
|
||||
|
||||
private void EvaluateParam(string name, ParameterArgs args, ICommandContext ctx)
|
||||
{
|
||||
switch (name.ToUpperInvariant())
|
||||
{
|
||||
case "PI":
|
||||
args.Result = Math.PI;
|
||||
break;
|
||||
case "E":
|
||||
args.Result = Math.E;
|
||||
break;
|
||||
case "ALL":
|
||||
case "ALLIN":
|
||||
args.Result = Cur(ctx);
|
||||
break;
|
||||
case "HALF":
|
||||
args.Result = Cur(ctx) / 2;
|
||||
break;
|
||||
case "MAX":
|
||||
args.Result = Max(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private long Cur(ICommandContext ctx)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.DiscordUser.GetUserCurrency(ctx.User.Id);
|
||||
}
|
||||
|
||||
private long Max(ICommandContext ctx)
|
||||
{
|
||||
var settings = _gambling.Data;
|
||||
var max = settings.MaxBet;
|
||||
return max == 0 ? Cur(ctx) : max;
|
||||
}
|
||||
|
||||
private bool TryHandlePercentage(ICommandContext ctx, string input, out long num)
|
||||
{
|
||||
num = 0;
|
||||
var m = _percentRegex.Match(input);
|
||||
if (m.Captures.Count != 0)
|
||||
{
|
||||
if (!long.TryParse(m.Groups["num"].ToString(), out var percent))
|
||||
return false;
|
||||
|
||||
num = (long)(Cur(ctx) * (percent / 100.0f));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -108,8 +108,8 @@ public static class DiscordUserExtensions
|
||||
.Take(count)
|
||||
.ToList();
|
||||
|
||||
public static long GetUserCurrency(this DbSet<DiscordUser> users, ulong userId)
|
||||
=> users.AsNoTracking().FirstOrDefault(x => x.UserId == userId)?.CurrencyAmount ?? 0;
|
||||
public static async Task<long> GetUserCurrencyAsync(this DbSet<DiscordUser> users, ulong userId)
|
||||
=> (await users.FirstOrDefaultAsyncLinqToDB(x => x.UserId == userId))?.CurrencyAmount ?? 0;
|
||||
|
||||
public static void RemoveFromMany(this DbSet<DiscordUser> users, IEnumerable<ulong> ids)
|
||||
{
|
||||
|
@@ -49,7 +49,8 @@ public enum PunishmentAction
|
||||
ChatMute,
|
||||
VoiceMute,
|
||||
AddRole,
|
||||
Warn
|
||||
Warn,
|
||||
TimeOut
|
||||
}
|
||||
|
||||
public class AntiSpamIgnore : DbEntity
|
||||
|
@@ -17,8 +17,6 @@ public class DiscordUser : DbEntity
|
||||
public bool IsClubAdmin { get; set; }
|
||||
|
||||
public long TotalXp { get; set; }
|
||||
public DateTime LastLevelUp { get; set; } = DateTime.UtcNow;
|
||||
public DateTime LastXpGain { get; set; } = DateTime.MinValue;
|
||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||
|
||||
public long CurrencyAmount { get; set; }
|
||||
|
@@ -8,7 +8,6 @@ public class UserXpStats : DbEntity
|
||||
public long Xp { get; set; }
|
||||
public long AwardedXp { get; set; }
|
||||
public XpNotificationLocation NotifyOnLevelUp { get; set; }
|
||||
public DateTime LastLevelUp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public enum XpNotificationLocation { None, Dm, Channel }
|
@@ -13,6 +13,6 @@ public class XpShopOwnedItem : DbEntity
|
||||
|
||||
public enum XpShopItemType
|
||||
{
|
||||
Background,
|
||||
Frame,
|
||||
Background = 0,
|
||||
Frame = 1,
|
||||
}
|
@@ -10,10 +10,6 @@ public sealed class MysqlContext : NadekoContext
|
||||
|
||||
protected override string CurrencyTransactionOtherIdDefaultValue
|
||||
=> "NULL";
|
||||
protected override string DiscordUserLastXpGainDefaultValue
|
||||
=> "(UTC_TIMESTAMP - INTERVAL 1 year)";
|
||||
protected override string LastLevelUpDefaultValue
|
||||
=> "(UTC_TIMESTAMP)";
|
||||
|
||||
public MysqlContext(string connStr = "Server=localhost", string version = "8.0")
|
||||
{
|
||||
|
@@ -65,8 +65,6 @@ public abstract class NadekoContext : DbContext
|
||||
#region Mandatory Provider-Specific Values
|
||||
|
||||
protected abstract string CurrencyTransactionOtherIdDefaultValue { get; }
|
||||
protected abstract string DiscordUserLastXpGainDefaultValue { get; }
|
||||
protected abstract string LastLevelUpDefaultValue { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -166,12 +164,6 @@ public abstract class NadekoContext : DbContext
|
||||
du.Property(x => x.NotifyOnLevelUp)
|
||||
.HasDefaultValue(XpNotificationLocation.None);
|
||||
|
||||
du.Property(x => x.LastXpGain)
|
||||
.HasDefaultValueSql(DiscordUserLastXpGainDefaultValue);
|
||||
|
||||
du.Property(x => x.LastLevelUp)
|
||||
.HasDefaultValueSql(LastLevelUpDefaultValue);
|
||||
|
||||
du.Property(x => x.TotalXp)
|
||||
.HasDefaultValue(0);
|
||||
|
||||
@@ -213,9 +205,6 @@ public abstract class NadekoContext : DbContext
|
||||
})
|
||||
.IsUnique();
|
||||
|
||||
xps.Property(x => x.LastLevelUp)
|
||||
.HasDefaultValueSql(LastLevelUpDefaultValue);
|
||||
|
||||
xps.HasIndex(x => x.UserId);
|
||||
xps.HasIndex(x => x.GuildId);
|
||||
xps.HasIndex(x => x.Xp);
|
||||
|
@@ -8,10 +8,6 @@ public sealed class PostgreSqlContext : NadekoContext
|
||||
|
||||
protected override string CurrencyTransactionOtherIdDefaultValue
|
||||
=> "NULL";
|
||||
protected override string DiscordUserLastXpGainDefaultValue
|
||||
=> "timezone('utc', now()) - interval '-1 year'";
|
||||
protected override string LastLevelUpDefaultValue
|
||||
=> "timezone('utc', now())";
|
||||
|
||||
public PostgreSqlContext(string connStr = "Host=localhost")
|
||||
{
|
||||
@@ -20,6 +16,8 @@ public sealed class PostgreSqlContext : NadekoContext
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||
|
||||
base.OnConfiguring(optionsBuilder);
|
||||
optionsBuilder
|
||||
.UseLowerCaseNamingConvention()
|
||||
|
@@ -9,10 +9,6 @@ public sealed class SqliteContext : NadekoContext
|
||||
|
||||
protected override string CurrencyTransactionOtherIdDefaultValue
|
||||
=> "NULL";
|
||||
protected override string DiscordUserLastXpGainDefaultValue
|
||||
=> "datetime('now', '-1 years')";
|
||||
protected override string LastLevelUpDefaultValue
|
||||
=> "datetime('now')";
|
||||
|
||||
public SqliteContext(string connectionString = "Data Source=data/NadekoBot.db", int commandTimeout = 60)
|
||||
{
|
||||
|
@@ -1,10 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace NadekoBot.Migrations;
|
||||
|
||||
public static class MigrationQueries
|
||||
{
|
||||
public static void MigrateRero(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsMySql())
|
||||
{
|
||||
migrationBuilder.Sql(
|
||||
@"INSERT IGNORE into reactionroles(guildid, channelid, messageid, emote, roleid, `group`, levelreq, dateadded)
|
||||
select guildid, channelid, messageid, emotename, roleid, exclusive, 0, reactionrolemessage.dateadded
|
||||
from reactionrole
|
||||
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
|
||||
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;");
|
||||
}
|
||||
else if (migrationBuilder.IsSqlite())
|
||||
{
|
||||
migrationBuilder.Sql(
|
||||
@"insert or ignore into reactionroles(guildid, channelid, messageid, emote, roleid, 'group', levelreq, dateadded)
|
||||
@@ -13,4 +25,18 @@ from reactionrole
|
||||
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
|
||||
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id;");
|
||||
}
|
||||
else if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.Sql(@"insert into reactionroles(guildid, channelid, messageid, emote, roleid, ""group"", levelreq, dateadded)
|
||||
select guildid, channelid, messageid, emotename, roleid, exclusive::int, 0, reactionrolemessage.dateadded
|
||||
from reactionrole
|
||||
left join reactionrolemessage on reactionrolemessage.id = reactionrole.reactionrolemessageid
|
||||
left join guildconfigs on reactionrolemessage.guildconfigid = guildconfigs.id
|
||||
ON CONFLICT DO NOTHING;");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("This database provider doesn't have an implementation for MigrateRero");
|
||||
}
|
||||
}
|
||||
}
|
3542
src/NadekoBot/Migrations/MySql/20220808141855_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
3542
src/NadekoBot/Migrations/MySql/20220808141855_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations.Mysql
|
||||
{
|
||||
public partial class removeobsoletexpcolumns : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "lastlevelup",
|
||||
table: "userxpstats");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "lastlevelup",
|
||||
table: "discorduser");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "lastxpgain",
|
||||
table: "discorduser");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "lastlevelup",
|
||||
table: "userxpstats",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValueSql: "(UTC_TIMESTAMP)");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "lastlevelup",
|
||||
table: "discorduser",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValueSql: "(UTC_TIMESTAMP)");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "lastxpgain",
|
||||
table: "discorduser",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValueSql: "(UTC_TIMESTAMP - INTERVAL 1 year)");
|
||||
}
|
||||
}
|
||||
}
|
@@ -168,18 +168,6 @@ namespace NadekoBot.Migrations.Mysql
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("isclubadmin");
|
||||
|
||||
b.Property<DateTime>("LastLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("lastlevelup")
|
||||
.HasDefaultValueSql("(UTC_TIMESTAMP)");
|
||||
|
||||
b.Property<DateTime>("LastXpGain")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("lastxpgain")
|
||||
.HasDefaultValueSql("(UTC_TIMESTAMP - INTERVAL 1 year)");
|
||||
|
||||
b.Property<int>("NotifyOnLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
@@ -2565,12 +2553,6 @@ namespace NadekoBot.Migrations.Mysql
|
||||
.HasColumnType("bigint unsigned")
|
||||
.HasColumnName("guildid");
|
||||
|
||||
b.Property<DateTime>("LastLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("datetime(6)")
|
||||
.HasColumnName("lastlevelup")
|
||||
.HasDefaultValueSql("(UTC_TIMESTAMP)");
|
||||
|
||||
b.Property<int>("NotifyOnLevelUp")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("notifyonlevelup");
|
||||
|
3686
src/NadekoBot/Migrations/Postgresql/20220808142559_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
3686
src/NadekoBot/Migrations/Postgresql/20220808142559_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("balance");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("UserId")
|
||||
@@ -101,7 +101,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("Description")
|
||||
@@ -163,7 +163,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("currencyamount");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("Discriminator")
|
||||
@@ -176,18 +176,6 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasDefaultValue(false)
|
||||
.HasColumnName("isclubadmin");
|
||||
|
||||
b.Property<DateTime>("LastLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("lastlevelup")
|
||||
.HasDefaultValueSql("timezone('utc', now())");
|
||||
|
||||
b.Property<DateTime>("LastXpGain")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("lastxpgain")
|
||||
.HasDefaultValueSql("timezone('utc', now()) - interval '-1 year'");
|
||||
|
||||
b.Property<int>("NotifyOnLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
@@ -243,7 +231,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -322,7 +310,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("amountcents");
|
||||
|
||||
b.Property<DateTime>("LastCharge")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("lastcharge");
|
||||
|
||||
b.Property<string>("UniquePlatformUserId")
|
||||
@@ -330,7 +318,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("uniqueplatformuserid");
|
||||
|
||||
b.Property<DateTime>("ValidThru")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("validthru");
|
||||
|
||||
b.HasKey("UserId")
|
||||
@@ -357,7 +345,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("MessageId")
|
||||
@@ -388,7 +376,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<bool>("IsUsing")
|
||||
@@ -471,7 +459,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("action");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("GuildConfigId")
|
||||
@@ -518,7 +506,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.HasKey("Id")
|
||||
@@ -544,7 +532,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("action");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("GuildConfigId")
|
||||
@@ -595,7 +583,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("commandtext");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal?>("GuildId")
|
||||
@@ -642,7 +630,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -676,7 +664,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("Source")
|
||||
@@ -710,7 +698,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -741,7 +729,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("ItemId")
|
||||
@@ -768,7 +756,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -806,7 +794,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("commandname");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -840,7 +828,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("amount");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("Extra")
|
||||
@@ -890,7 +878,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -924,7 +912,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("command");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal?>("GuildId")
|
||||
@@ -955,7 +943,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("ItemId")
|
||||
@@ -993,7 +981,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("GuildConfigId")
|
||||
@@ -1028,7 +1016,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -1054,7 +1042,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -1088,7 +1076,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -1118,7 +1106,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -1148,7 +1136,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -1174,7 +1162,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("GuildConfigId")
|
||||
@@ -1253,7 +1241,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("cleverbotenabled");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<bool>("DeleteMessageOnCommand")
|
||||
@@ -1381,7 +1369,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("ItemType")
|
||||
@@ -1420,7 +1408,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("LogSettingId")
|
||||
@@ -1450,7 +1438,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -1493,7 +1481,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelupdatedid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -1629,7 +1617,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("authorid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("Name")
|
||||
@@ -1652,7 +1640,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -1694,7 +1682,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("containsanywhere");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<bool>("DmResponse")
|
||||
@@ -1733,7 +1721,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -1763,7 +1751,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -1825,7 +1813,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -1867,7 +1855,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("MusicPlaylistId")
|
||||
@@ -1917,7 +1905,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -1948,7 +1936,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("Index")
|
||||
@@ -1982,7 +1970,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("PollId")
|
||||
@@ -2025,7 +2013,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("authorname");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -2068,7 +2056,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("Emote")
|
||||
@@ -2123,7 +2111,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<bool>("IsPrivate")
|
||||
@@ -2143,7 +2131,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("userid");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("when");
|
||||
|
||||
b.HasKey("Id")
|
||||
@@ -2169,7 +2157,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("channelid");
|
||||
|
||||
b.Property<DateTime>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
@@ -2216,11 +2204,11 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("amountrewardedthismonth");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<DateTime>("LastReward")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("lastreward");
|
||||
|
||||
b.Property<string>("PlatformUserId")
|
||||
@@ -2251,7 +2239,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("Status")
|
||||
@@ -2278,7 +2266,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("Group")
|
||||
@@ -2323,7 +2311,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("authorid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -2373,7 +2361,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("ShopEntryId")
|
||||
@@ -2403,7 +2391,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -2433,7 +2421,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -2463,7 +2451,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("StreamRoleSettingsId")
|
||||
@@ -2501,7 +2489,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("addroleid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
@@ -2540,7 +2528,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("StreamRoleSettingsId")
|
||||
@@ -2574,7 +2562,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -2582,7 +2570,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("guildconfigid");
|
||||
|
||||
b.Property<DateTime>("UnbanAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("unbanat");
|
||||
|
||||
b.Property<decimal>("UserId")
|
||||
@@ -2608,7 +2596,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -2616,7 +2604,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("guildconfigid");
|
||||
|
||||
b.Property<DateTime>("UnmuteAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("unmuteat");
|
||||
|
||||
b.Property<decimal>("UserId")
|
||||
@@ -2642,7 +2630,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -2654,7 +2642,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("roleid");
|
||||
|
||||
b.Property<DateTime>("UnbanAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("unbanat");
|
||||
|
||||
b.Property<decimal>("UserId")
|
||||
@@ -2684,19 +2672,13 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("awardedxp");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<decimal>("GuildId")
|
||||
.HasColumnType("numeric(20,0)")
|
||||
.HasColumnName("guildid");
|
||||
|
||||
b.Property<DateTime>("LastLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("lastlevelup")
|
||||
.HasDefaultValueSql("timezone('utc', now())");
|
||||
|
||||
b.Property<int>("NotifyOnLevelUp")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("notifyonlevelup");
|
||||
@@ -2741,7 +2723,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -2783,7 +2765,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("claimerid");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<long>("Price")
|
||||
@@ -2823,7 +2805,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<string>("ItemEmoji")
|
||||
@@ -2857,7 +2839,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("NewId")
|
||||
@@ -2901,7 +2883,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<bool>("Forgiven")
|
||||
@@ -2963,7 +2945,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("count");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int?>("GuildConfigId")
|
||||
@@ -3005,7 +2987,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
.HasColumnName("amount");
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("Level")
|
||||
@@ -3035,7 +3017,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("Level")
|
||||
@@ -3074,7 +3056,7 @@ namespace NadekoBot.Migrations.PostgreSql
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime?>("DateAdded")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnType("timestamp without time zone")
|
||||
.HasColumnName("dateadded");
|
||||
|
||||
b.Property<int>("GuildConfigId")
|
||||
|
2843
src/NadekoBot/Migrations/Sqlite/20220808141842_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
2843
src/NadekoBot/Migrations/Sqlite/20220808141842_remove-obsolete-xp-columns.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace NadekoBot.Migrations
|
||||
{
|
||||
public partial class removeobsoletexpcolumns : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastLevelUp",
|
||||
table: "UserXpStats");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastLevelUp",
|
||||
table: "DiscordUser");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastXpGain",
|
||||
table: "DiscordUser");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "LastLevelUp",
|
||||
table: "UserXpStats",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValueSql: "datetime('now')");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "LastLevelUp",
|
||||
table: "DiscordUser",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValueSql: "datetime('now')");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "LastXpGain",
|
||||
table: "DiscordUser",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValueSql: "datetime('now', '-1 years')");
|
||||
}
|
||||
}
|
||||
}
|
@@ -134,16 +134,6 @@ namespace NadekoBot.Migrations
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(false);
|
||||
|
||||
b.Property<DateTime>("LastLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("datetime('now')");
|
||||
|
||||
b.Property<DateTime>("LastXpGain")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("datetime('now', '-1 years')");
|
||||
|
||||
b.Property<int>("NotifyOnLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
@@ -2003,11 +1993,6 @@ namespace NadekoBot.Migrations
|
||||
b.Property<ulong>("GuildId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastLevelUp")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValueSql("datetime('now')");
|
||||
|
||||
b.Property<int>("NotifyOnLevelUp")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
@@ -344,4 +344,36 @@ public partial class Administration : NadekoModule<AdministrationService>
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(ChannelPermission.CreatePublicThreads)]
|
||||
[UserPerm(ChannelPermission.CreatePublicThreads)]
|
||||
public async Task ThreadCreate([Leftover] string name)
|
||||
{
|
||||
if (ctx.Channel is not SocketTextChannel stc)
|
||||
return;
|
||||
|
||||
await stc.CreateThreadAsync(name, message: ctx.Message.ReferencedMessage);
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(ChannelPermission.ManageThreads)]
|
||||
[UserPerm(ChannelPermission.ManageThreads)]
|
||||
public async Task ThreadDelete([Leftover] string name)
|
||||
{
|
||||
if (ctx.Channel is not SocketTextChannel stc)
|
||||
return;
|
||||
|
||||
var t = stc.Threads.FirstOrDefault(x => string.Equals(x.Name, name, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (t is null)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
await t.DeleteAsync();
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
}
|
@@ -38,10 +38,14 @@ public partial class Administration
|
||||
if (minAgeMinutes < 1 || punishTimeMinutes < 0)
|
||||
return;
|
||||
|
||||
var minutes = (int?)punishTime?.Time.TotalMinutes ?? 0;
|
||||
if (action is PunishmentAction.TimeOut && minutes < 1)
|
||||
minutes = 1;
|
||||
|
||||
await _service.StartAntiAltAsync(ctx.Guild.Id,
|
||||
minAgeMinutes,
|
||||
action,
|
||||
(int?)punishTime?.Time.TotalMinutes ?? 0);
|
||||
minutes);
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
@@ -56,6 +60,9 @@ public partial class Administration
|
||||
if (minAgeMinutes < 1)
|
||||
return;
|
||||
|
||||
if (action == PunishmentAction.TimeOut)
|
||||
return;
|
||||
|
||||
await _service.StartAntiAltAsync(ctx.Guild.Id, minAgeMinutes, action, roleId: role.Id);
|
||||
|
||||
await ctx.OkAsync();
|
||||
@@ -123,6 +130,9 @@ public partial class Administration
|
||||
if (time is < 0 or > 60 * 24)
|
||||
return;
|
||||
|
||||
if(action is PunishmentAction.TimeOut && time < 1)
|
||||
return;
|
||||
|
||||
var stats = await _service.StartAntiRaidAsync(ctx.Guild.Id, userThreshold, seconds, action, time);
|
||||
|
||||
if (stats is null)
|
||||
@@ -187,6 +197,9 @@ public partial class Administration
|
||||
if (time is < 0 or > 60 * 24)
|
||||
return;
|
||||
|
||||
if (action is PunishmentAction.TimeOut && time < 1)
|
||||
return;
|
||||
|
||||
var stats = await _service.StartAntiSpamAsync(ctx.Guild.Id, messageCount, action, time, role?.Id);
|
||||
|
||||
await SendConfirmAsync(GetText(strs.prot_enable("Anti-Spam")),
|
||||
|
@@ -58,7 +58,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
||||
}
|
||||
|
||||
private async Task<(IGuildUser, IRole)> GetUserAndRoleAsync(
|
||||
SocketReaction r,
|
||||
ulong userId,
|
||||
ReactionRoleV2 rero)
|
||||
{
|
||||
var guild = _client.GetGuild(rero.GuildId);
|
||||
@@ -67,8 +67,8 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
||||
if (role is null)
|
||||
return default;
|
||||
|
||||
var user = guild.GetUser(r.UserId) as IGuildUser
|
||||
?? await _client.Rest.GetGuildUserAsync(guild.Id, r.UserId);
|
||||
var user = guild.GetUser(userId) as IGuildUser
|
||||
?? await _client.Rest.GetGuildUserAsync(guild.Id, userId);
|
||||
|
||||
if (user is null)
|
||||
return default;
|
||||
@@ -77,20 +77,23 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
||||
}
|
||||
|
||||
private Task ClientOnReactionRemoved(
|
||||
Cacheable<IUserMessage, ulong> msg,
|
||||
Cacheable<IUserMessage, ulong> cmsg,
|
||||
Cacheable<IMessageChannel, ulong> ch,
|
||||
SocketReaction r)
|
||||
{
|
||||
if (!_cache.TryGetValue(msg.Id, out var reros))
|
||||
if (!_cache.TryGetValue(cmsg.Id, out var reros))
|
||||
return Task.CompletedTask;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var rero = reros.FirstOrDefault(x => x.Emote == r.Emote.Name || x.Emote == r.Emote.ToString());
|
||||
var emote = await GetFixedEmoteAsync(cmsg, r.Emote);
|
||||
|
||||
var rero = reros.FirstOrDefault(x => x.Emote == emote.Name
|
||||
|| x.Emote == emote.ToString());
|
||||
if (rero is null)
|
||||
return;
|
||||
|
||||
var (user, role) = await GetUserAndRoleAsync(r, rero);
|
||||
var (user, role) = await GetUserAndRoleAsync(r.UserId, rero);
|
||||
|
||||
if (user.IsBot)
|
||||
return;
|
||||
@@ -112,6 +115,24 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
// had to add this because for some reason, reactionremoved event's reaction doesn't have IsAnimated set,
|
||||
// causing the .ToString() to be wrong on animated custom emotes
|
||||
private async Task<IEmote> GetFixedEmoteAsync(
|
||||
Cacheable<IUserMessage, ulong> cmsg,
|
||||
IEmote inputEmote)
|
||||
{
|
||||
// this should only run for emote
|
||||
if (inputEmote is not Emote e)
|
||||
return inputEmote;
|
||||
|
||||
// try to get the message and pull
|
||||
var msg = await cmsg.GetOrDownloadAsync();
|
||||
|
||||
var emote = msg.Reactions.Keys.FirstOrDefault(x => e.Equals(x));
|
||||
return emote ?? inputEmote;
|
||||
}
|
||||
|
||||
private Task ClientOnReactionAdded(
|
||||
Cacheable<IUserMessage, ulong> msg,
|
||||
Cacheable<IMessageChannel, ulong> ch,
|
||||
@@ -126,7 +147,7 @@ public sealed class ReactionRolesService : IReadyExecutor, INService, IReactionR
|
||||
if (rero is null)
|
||||
return;
|
||||
|
||||
var (user, role) = await GetUserAndRoleAsync(r, rero);
|
||||
var (user, role) = await GetUserAndRoleAsync(r.UserId, rero);
|
||||
|
||||
if (user.IsBot)
|
||||
return;
|
||||
|
@@ -345,6 +345,10 @@ public partial class Administration
|
||||
if (punish is PunishmentAction.AddRole or PunishmentAction.Warn)
|
||||
return;
|
||||
|
||||
// you must specify the time for timeout
|
||||
if (punish is PunishmentAction.TimeOut && time is null)
|
||||
return;
|
||||
|
||||
var success = _service.WarnPunish(ctx.Guild.Id, number, punish, time);
|
||||
|
||||
if (!success)
|
||||
|
@@ -193,6 +193,9 @@ public class UserPunishService : INService, IReadyExecutor
|
||||
case PunishmentAction.Warn:
|
||||
await Warn(guild, user.Id, mod, 1, reason);
|
||||
break;
|
||||
case PunishmentAction.TimeOut:
|
||||
await user.SetTimeOutAsync(TimeSpan.FromMinutes(minutes));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,6 +227,8 @@ public class UserPunishService : INService, IReadyExecutor
|
||||
return botUser.GuildPermissions.MuteMembers;
|
||||
case PunishmentAction.AddRole:
|
||||
return botUser.GuildPermissions.ManageRoles;
|
||||
case PunishmentAction.TimeOut:
|
||||
return botUser.GuildPermissions.ModerateMembers;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -351,7 +356,7 @@ public class UserPunishService : INService, IReadyExecutor
|
||||
await uow.Warnings.ForgiveAll(guildId, userId, moderator);
|
||||
else
|
||||
toReturn = uow.Warnings.Forgive(guildId, userId, moderator, index - 1);
|
||||
uow.SaveChanges();
|
||||
await uow.SaveChangesAsync();
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
@@ -372,6 +377,9 @@ public class UserPunishService : INService, IReadyExecutor
|
||||
if (punish is PunishmentAction.AddRole && role is null)
|
||||
return false;
|
||||
|
||||
if (punish is PunishmentAction.TimeOut && time is null)
|
||||
return false;
|
||||
|
||||
using var uow = _db.GetDbContext();
|
||||
var ps = uow.GuildConfigsForId(guildId, set => set.Include(x => x.WarnPunishments)).WarnPunishments;
|
||||
var toDelete = ps.Where(x => x.Count == number);
|
||||
|
@@ -1,5 +1,4 @@
|
||||
using Nadeko.Common;
|
||||
using NadekoBot.Modules.Gambling.Bank;
|
||||
using NadekoBot.Modules.Gambling.Bank;
|
||||
using NadekoBot.Modules.Gambling.Common;
|
||||
using NadekoBot.Modules.Gambling.Services;
|
||||
|
||||
@@ -12,10 +11,14 @@ public partial class Gambling
|
||||
public partial class BankCommands : GamblingModule<IBankService>
|
||||
{
|
||||
private readonly IBankService _bank;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public BankCommands(GamblingConfigService gcs, IBankService bank) : base(gcs)
|
||||
public BankCommands(GamblingConfigService gcs,
|
||||
IBankService bank,
|
||||
DiscordSocketClient client) : base(gcs)
|
||||
{
|
||||
_bank = bank;
|
||||
_client = client;
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -35,7 +38,7 @@ public partial class Gambling
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task BankWithdraw(ShmartNumber amount)
|
||||
public async Task BankWithdraw(ShmartBankAmount amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
return;
|
||||
@@ -69,5 +72,50 @@ public partial class Gambling
|
||||
await ReplyErrorLocalizedAsync(strs.cant_dm);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BankTakeInternalAsync(long amount, ulong userId)
|
||||
{
|
||||
if (await _bank.TakeAsync(userId, amount))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount),
|
||||
_client.GetUser(userId)?.ToString()
|
||||
?? userId.ToString(),
|
||||
CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
private async Task BankAwardInternalAsync(long amount, ulong userId)
|
||||
{
|
||||
if (await _bank.AwardAsync(userId, amount))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.take_fail(N(amount),
|
||||
_client.GetUser(userId)?.ToString()
|
||||
?? userId.ToString(),
|
||||
CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
[Priority(1)]
|
||||
public async Task BankTake(long amount, [Leftover] IUser user)
|
||||
=> await BankTakeInternalAsync(amount, user.Id);
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
[Priority(0)]
|
||||
public async Task BankTake(long amount, ulong userId)
|
||||
=> await BankTakeInternalAsync(amount, userId);
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task BankAward(long amount, [Leftover] IUser user)
|
||||
=> await BankAwardInternalAsync(amount, user.Id);
|
||||
}
|
||||
}
|
@@ -15,6 +15,48 @@ public sealed class BankService : IBankService, INService
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<bool> AwardAsync(ulong userId, long amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
await using var ctx = _db.GetDbContext();
|
||||
await ctx.BankUsers
|
||||
.ToLinqToDBTable()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
{
|
||||
UserId = userId,
|
||||
Balance = amount
|
||||
},
|
||||
(old) => new()
|
||||
{
|
||||
Balance = old.Balance + amount
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> TakeAsync(ulong userId, long amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(amount));
|
||||
|
||||
await using var ctx = _db.GetDbContext();
|
||||
var rows = await ctx.BankUsers
|
||||
.ToLinqToDBTable()
|
||||
.Where(x => x.UserId == userId && x.Balance >= amount)
|
||||
.UpdateAsync((old) => new()
|
||||
{
|
||||
Balance = old.Balance - amount
|
||||
});
|
||||
|
||||
return rows > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> DepositAsync(ulong userId, long amount)
|
||||
{
|
||||
if (amount <= 0)
|
||||
|
@@ -5,4 +5,6 @@ public interface IBankService
|
||||
Task<bool> DepositAsync(ulong userId, long amount);
|
||||
Task<bool> WithdrawAsync(ulong userId, long amount);
|
||||
Task<long> GetBalanceAsync(ulong userId);
|
||||
Task<bool> AwardAsync(ulong userId, long amount);
|
||||
Task<bool> TakeAsync(ulong userId, long amount);
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Gambling;
|
||||
|
||||
public class CashInteraction : NInteraction
|
||||
{
|
||||
protected override NadekoInteractionData Data
|
||||
=> new NadekoInteractionData(new Emoji("🏦"), "cash:bank_show_balance");
|
||||
|
||||
public CashInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action)
|
||||
: base(client, userId, action)
|
||||
{
|
||||
}
|
||||
}
|
@@ -99,26 +99,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
PrettyName = "Timely"
|
||||
};
|
||||
|
||||
public class RemindMeInteraction : NInteraction
|
||||
{
|
||||
public RemindMeInteraction(
|
||||
[NotNull] DiscordSocketClient client,
|
||||
ulong userId,
|
||||
[NotNull] Func<SocketMessageComponent, Task> action)
|
||||
: base(client, userId, action)
|
||||
{
|
||||
}
|
||||
|
||||
protected override NadekoInteractionData Data
|
||||
=> new NadekoInteractionData(
|
||||
Emote: Emoji.Parse("⏰"),
|
||||
CustomId: "timely:remind_me",
|
||||
Text: "Remind me"
|
||||
);
|
||||
}
|
||||
|
||||
private Func<SocketMessageComponent, Task> RemindTimelyAction(DateTime when)
|
||||
=> async smc =>
|
||||
private async Task RemindTimelyAction(SocketMessageComponent smc, DateTime when)
|
||||
{
|
||||
var tt = TimestampTag.FromDateTime(when, TimestampTagStyles.Relative);
|
||||
|
||||
@@ -130,7 +111,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
GetText(strs.timely_time));
|
||||
|
||||
await smc.RespondConfirmAsync(_eb, GetText(strs.remind_timely(tt)), ephemeral: true);
|
||||
};
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task Timely()
|
||||
@@ -147,7 +128,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var relativeTag = TimestampTag.FromDateTime(now.Add(rem), TimestampTagStyles.Relative);
|
||||
await ReplyErrorLocalizedAsync(strs.timely_already_claimed(relativeTag));
|
||||
await ReplyPendingLocalizedAsync(strs.timely_already_claimed(relativeTag));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,11 +138,17 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
|
||||
await _cs.AddAsync(ctx.User.Id, val, new("timely", "claim"));
|
||||
|
||||
var inter = new RemindMeInteraction(_client,
|
||||
ctx.User.Id,
|
||||
RemindTimelyAction(DateTime.UtcNow.Add(TimeSpan.FromHours(period))));
|
||||
var inter = _inter
|
||||
.Create(ctx.User.Id,
|
||||
new SimpleInteraction<DateTime>(
|
||||
new ButtonBuilder(
|
||||
label: "Remind me",
|
||||
emote: Emoji.Parse("⏰"),
|
||||
customId: "timely:remind_me"),
|
||||
RemindTimelyAction,
|
||||
DateTime.UtcNow.Add(TimeSpan.FromHours(period))));
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.timely(N(val), period), inter.GetInteraction());
|
||||
await ReplyConfirmLocalizedAsync(strs.timely(N(val), period), inter);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
@@ -362,7 +349,7 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
await ReplyConfirmLocalizedAsync(strs.has(Format.Code(userId.ToString()), cur));
|
||||
}
|
||||
|
||||
private async Task BankAction(SocketMessageComponent smc)
|
||||
private async Task BankAction(SocketMessageComponent smc, object _)
|
||||
{
|
||||
var balance = await _bank.GetBalanceAsync(ctx.User.Id);
|
||||
|
||||
@@ -372,8 +359,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
.Pipe(text => smc.RespondConfirmAsync(_eb, text, ephemeral: true));
|
||||
}
|
||||
|
||||
private NadekoButtonInteraction CreateCashInteraction()
|
||||
=> new CashInteraction(_client, ctx.User.Id, BankAction).GetInteraction();
|
||||
private NadekoInteraction CreateCashInteraction()
|
||||
=> _inter.Create<object>(ctx.User.Id,
|
||||
new(new(
|
||||
customId: "cash:bank_show_balance",
|
||||
emote: new Emoji("🏦")),
|
||||
BankAction));
|
||||
|
||||
[Cmd]
|
||||
[Priority(1)]
|
||||
@@ -663,7 +654,6 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
if (win > 0)
|
||||
{
|
||||
str = GetText(strs.br_win(N(win), result.Threshold + (result.Roll == 100 ? " 👑" : "")));
|
||||
await _cs.AddAsync(ctx.User, win, new("betroll", "win"));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -876,11 +866,12 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||
public enum GambleTestTarget
|
||||
{
|
||||
Slot,
|
||||
Betroll,
|
||||
Betflip,
|
||||
BetflipT,
|
||||
BetDraw,
|
||||
BetDrawHL,
|
||||
BetDrawRB,
|
||||
Betflip,
|
||||
BetflipT,
|
||||
Lula,
|
||||
Rps,
|
||||
}
|
||||
@@ -921,6 +912,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))
|
||||
};
|
||||
|
||||
|
@@ -142,7 +142,7 @@ public class GamblingService : INService, IReadyExecutor
|
||||
var onePercent = uow.DiscordUser.GetTopOnePercentCurrency(_client.CurrentUser.Id);
|
||||
decimal planted = uow.PlantedCurrency.AsQueryable().Sum(x => x.Amount);
|
||||
var waifus = uow.WaifuInfo.GetTotalValue();
|
||||
var bot = uow.DiscordUser.GetUserCurrency(_client.CurrentUser.Id);
|
||||
var bot = await uow.DiscordUser.GetUserCurrencyAsync(_client.CurrentUser.Id);
|
||||
decimal bank = await uow.GetTable<BankUser>()
|
||||
.SumAsyncLinqToDB(x => x.Balance);
|
||||
|
||||
|
@@ -27,12 +27,6 @@ public partial class Gambling
|
||||
private static decimal totalBet;
|
||||
private static decimal totalPaidOut;
|
||||
|
||||
private static readonly ConcurrentHashSet<ulong> _runningUsers = new();
|
||||
|
||||
//here is a payout chart
|
||||
//https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
|
||||
//thanks to judge for helping me with this
|
||||
|
||||
private readonly IImageCache _images;
|
||||
private readonly FontProvider _fonts;
|
||||
private readonly DbService _db;
|
||||
@@ -73,16 +67,6 @@ public partial class Gambling
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
public sealed class SlotInteraction : NInteraction
|
||||
{
|
||||
public SlotInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action) : base(client, userId, action)
|
||||
{
|
||||
}
|
||||
|
||||
protected override NadekoInteractionData Data { get; } = new(Emoji.Parse("🔁"),
|
||||
"slot:again",
|
||||
"Pull Again");
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task Slot(ShmartNumber amount)
|
||||
@@ -94,18 +78,13 @@ public partial class Gambling
|
||||
|
||||
await ctx.Channel.TriggerTypingAsync();
|
||||
|
||||
if (!_runningUsers.Add(ctx.User.Id))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (await InternalSlotAsync(amount) is not SlotResult result)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = GetSlotMessageInternal(result);
|
||||
var text = GetSlotMessageTextInternal(result);
|
||||
|
||||
using var image = await GenerateSlotImageAsync(amount, result);
|
||||
await using var imgStream = await image.ToStreamAsync();
|
||||
@@ -113,24 +92,20 @@ public partial class Gambling
|
||||
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithAuthor(ctx.User)
|
||||
.WithDescription(Format.Bold(msg))
|
||||
.WithDescription(Format.Bold(text))
|
||||
.WithImageUrl($"attachment://result.png")
|
||||
.WithOkColor();
|
||||
|
||||
// var inter = slotInteraction.GetInteraction();
|
||||
await ctx.Channel.SendFileAsync(imgStream,
|
||||
"result.png",
|
||||
embed: eb.Build()
|
||||
// components: inter.CreateComponent()
|
||||
);
|
||||
var bb = new ButtonBuilder(emote: Emoji.Parse("🔁"), customId: "slot:again", label: "Pull Again");
|
||||
var si = new SimpleInteraction<ShmartNumber>(bb, (_, amount) => Slot(amount), amount);
|
||||
|
||||
// await inter.RunAsync(resMsg);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
_runningUsers.TryRemove(ctx.User.Id);
|
||||
}
|
||||
var inter = _inter.Create(ctx.User.Id, si);
|
||||
var msg = await ctx.Channel.SendFileAsync(imgStream,
|
||||
"result.png",
|
||||
embed: eb.Build(),
|
||||
components: inter.CreateComponent()
|
||||
);
|
||||
await inter.RunAsync(msg);
|
||||
}
|
||||
|
||||
// private SlotInteraction CreateSlotInteractionIntenal(long amount)
|
||||
@@ -181,7 +156,7 @@ public partial class Gambling
|
||||
// });
|
||||
// }
|
||||
|
||||
private string GetSlotMessageInternal(SlotResult result)
|
||||
private string GetSlotMessageTextInternal(SlotResult result)
|
||||
{
|
||||
var multi = result.Multiplier.ToString("0.##");
|
||||
var msg = result.WinType switch
|
||||
@@ -201,7 +176,6 @@ public partial class Gambling
|
||||
|
||||
if (!maybeResult.TryPickT0(out var result, out var error))
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_enough(CurrencySign));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -235,7 +209,7 @@ public partial class Gambling
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrappingLength = 140,
|
||||
Origin = new(227, 92)
|
||||
Origin = new(298, 100)
|
||||
},
|
||||
((long)result.Won).ToString(),
|
||||
fontColor));
|
||||
@@ -247,7 +221,7 @@ public partial class Gambling
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrappingLength = 135,
|
||||
Origin = new(129, 472)
|
||||
Origin = new(196, 480)
|
||||
},
|
||||
amount.ToString(),
|
||||
fontColor));
|
||||
@@ -256,8 +230,7 @@ public partial class Gambling
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Center,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
WrappingLength = 135,
|
||||
Origin = new(325, 472)
|
||||
Origin = new(393, 480)
|
||||
},
|
||||
ownedAmount.ToString(),
|
||||
fontColor));
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
@@ -424,7 +424,7 @@ public partial class Help : NadekoModule<HelpService>
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Log.Information("No old version list found. Creating a new one.");
|
||||
Log.Information("No old version list found. Creating a new one");
|
||||
}
|
||||
|
||||
var versionList = JsonSerializer.Deserialize<List<string>>(versionListString);
|
||||
@@ -469,7 +469,7 @@ public partial class Help : NadekoModule<HelpService>
|
||||
"https://nadekobot.readthedocs.io/en/latest/"));
|
||||
|
||||
|
||||
private Task SelfhostAction(SocketMessageComponent smc)
|
||||
private Task SelfhostAction(SocketMessageComponent smc, object _)
|
||||
=> smc.RespondConfirmAsync(_eb,
|
||||
@"- In case you don't want or cannot Donate to NadekoBot project, but you
|
||||
- NadekoBot is a completely free and fully [open source](https://gitlab.com/kwoth/nadekobot) project which means you can run your own ""selfhosted"" instance on your computer or server for free.
|
||||
@@ -484,7 +484,13 @@ public partial class Help : NadekoModule<HelpService>
|
||||
[OnlyPublicBot]
|
||||
public async Task Donate()
|
||||
{
|
||||
var selfhostInter = new DonateSelfhostingInteraction(_client, ctx.User.Id, SelfhostAction);
|
||||
// => new NadekoInteractionData(new Emoji("🖥️"), "donate:selfhosting", "Selfhosting");
|
||||
var selfhostInter = _inter.Create(ctx.User.Id,
|
||||
new SimpleInteraction<object>(new ButtonBuilder(
|
||||
emote: new Emoji("🖥️"),
|
||||
customId: "donate:selfhosting",
|
||||
label: "Selfhosting"),
|
||||
SelfhostAction));
|
||||
|
||||
var eb = _eb.Create(ctx)
|
||||
.WithOkColor()
|
||||
@@ -525,7 +531,7 @@ Nadeko will DM you the welcome instructions, and you may start using the patron-
|
||||
|
||||
try
|
||||
{
|
||||
await (await ctx.User.CreateDMChannelAsync()).EmbedAsync(eb, inter: selfhostInter.GetInteraction());
|
||||
await (await ctx.User.CreateDMChannelAsync()).EmbedAsync(eb, inter: selfhostInter);
|
||||
_ = ctx.OkAsync();
|
||||
}
|
||||
catch
|
||||
|
@@ -1,12 +0,0 @@
|
||||
namespace NadekoBot.Modules.Help;
|
||||
|
||||
public class DonateSelfhostingInteraction : NInteraction
|
||||
{
|
||||
protected override NadekoInteractionData Data
|
||||
=> new NadekoInteractionData(new Emoji("🖥️"), "donate:selfhosting", "Selfhosting");
|
||||
|
||||
public DonateSelfhostingInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action)
|
||||
: base(client, userId, action)
|
||||
{
|
||||
}
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
namespace NadekoBot.Modules.Help;
|
||||
|
||||
public class DonateTroubleshootInteraction : NInteraction
|
||||
{
|
||||
protected override NadekoInteractionData Data
|
||||
=> new NadekoInteractionData(new Emoji("❓"), "donate:troubleshoot", "Troubleshoot");
|
||||
|
||||
public DonateTroubleshootInteraction(DiscordSocketClient client, ulong userId, Func<SocketMessageComponent, Task> action)
|
||||
: base(client, userId, action)
|
||||
{
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -98,6 +98,33 @@ public partial class Utility
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireUserPermission(GuildPermission.ManageRoles)]
|
||||
public async Task RoleInfo([Leftover] SocketRole role)
|
||||
{
|
||||
if (role.IsEveryone)
|
||||
return;
|
||||
|
||||
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)
|
||||
.AddMilliseconds(role.Id >> 22);
|
||||
var usercount = role.Members.LongCount();
|
||||
var embed = _eb.Create()
|
||||
.WithTitle(role.Name.TrimTo(128))
|
||||
.WithDescription(role.Permissions.ToList().Join(" | "))
|
||||
.AddField(GetText(strs.id), role.Id.ToString(), true)
|
||||
.AddField(GetText(strs.created_at), $"{createdAt:dd.MM.yyyy HH:mm}", true)
|
||||
.AddField(GetText(strs.users), usercount.ToString(), true)
|
||||
.AddField(GetText(strs.color), $"#{role.Color.R:X2}{role.Color.G:X2}{role.Color.B:X2}", true)
|
||||
.AddField(GetText(strs.mentionable), role.IsMentionable.ToString(), true)
|
||||
.AddField(GetText(strs.hoisted), role.IsHoisted.ToString(), true)
|
||||
.WithOkColor();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(role.GetIconUrl()))
|
||||
embed = embed.WithThumbnailUrl(role.GetIconUrl());
|
||||
|
||||
await ctx.Channel.EmbedAsync(embed);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task UserInfo(IGuildUser usr = null)
|
||||
|
@@ -715,8 +715,8 @@ public sealed class PatronageService
|
||||
return new()
|
||||
{
|
||||
Name = key.PrettyName,
|
||||
Quota = defaultValue,
|
||||
IsPatronLimit = false,
|
||||
Quota = 0,
|
||||
IsPatronLimit = true,
|
||||
};
|
||||
|
||||
return new()
|
||||
|
@@ -367,6 +367,41 @@ public partial class Utility : NadekoModule
|
||||
await ConfirmLocalizedAsync(strs.emoji_added(em.ToString()));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
[BotPerm(GuildPerm.ManageEmojisAndStickers)]
|
||||
[UserPerm(GuildPerm.ManageEmojisAndStickers)]
|
||||
[Priority(0)]
|
||||
public async Task EmojiRemove(params Emote[] emotes)
|
||||
{
|
||||
if (emotes.Length == 0)
|
||||
return;
|
||||
|
||||
var g = (SocketGuild)ctx.Guild;
|
||||
|
||||
var fails = new List<Emote>();
|
||||
foreach (var emote in emotes)
|
||||
{
|
||||
var guildEmote = g.Emotes.FirstOrDefault(x => x.Id == emote.Id);
|
||||
if (guildEmote is null)
|
||||
{
|
||||
fails.Add(emote);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.Guild.DeleteEmoteAsync(guildEmote);
|
||||
}
|
||||
}
|
||||
|
||||
if (fails.Count > 0)
|
||||
{
|
||||
await ReplyPendingLocalizedAsync(strs.emoji_not_removed(fails.Select(x => x.ToString()).Join(" ")));
|
||||
return;
|
||||
}
|
||||
|
||||
await ctx.OkAsync();
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[OwnerOnly]
|
||||
public async Task ListServers(int page = 1)
|
||||
|
@@ -334,14 +334,15 @@ public partial class Xp : NadekoModule<XpService>
|
||||
|
||||
public enum XpShopInputType
|
||||
{
|
||||
F = 0,
|
||||
Frs = 0,
|
||||
Fs = 0,
|
||||
Frames = 0,
|
||||
B = 1,
|
||||
Bg = 1,
|
||||
Bgs = 1,
|
||||
Backgrounds = 1
|
||||
Backgrounds = 0,
|
||||
B = 0,
|
||||
Bg = 0,
|
||||
Bgs = 0,
|
||||
Frames = 1,
|
||||
F = 1,
|
||||
Fr = 1,
|
||||
Frs = 1,
|
||||
Fs = 1,
|
||||
}
|
||||
|
||||
[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())));
|
||||
@@ -423,8 +432,7 @@ public partial class Xp : NadekoModule<XpService>
|
||||
var button = new ButtonBuilder(ownedItem.IsUsing
|
||||
? GetText(strs.in_use)
|
||||
: GetText(strs.use),
|
||||
"XP_SHOP_USE",
|
||||
ButtonStyle.Primary,
|
||||
"xpshop:use",
|
||||
emote: Emoji.Parse("👐"),
|
||||
isDisabled: ownedItem.IsUsing);
|
||||
|
||||
@@ -438,8 +446,7 @@ public partial class Xp : NadekoModule<XpService>
|
||||
else
|
||||
{
|
||||
var button = new ButtonBuilder(GetText(strs.buy),
|
||||
"XP_SHOP_BUY",
|
||||
ButtonStyle.Primary,
|
||||
"xpshop:buy",
|
||||
emote: Emoji.Parse("💰"));
|
||||
|
||||
var inter = new SimpleInteraction<(string key, XpShopItemType type)?>(
|
||||
@@ -455,6 +462,53 @@ 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);
|
||||
|
||||
NadekoInteraction GetUseInteraction()
|
||||
{
|
||||
return _inter.Create(ctx.User.Id,
|
||||
new SimpleInteraction<object>(
|
||||
new ButtonBuilder(label: "Use", customId: "xpshop:use_item", emote: Emoji.Parse("👐")),
|
||||
async (smc, _) => await XpShopUse(type, key)
|
||||
));
|
||||
}
|
||||
|
||||
if (result != BuyResult.Success)
|
||||
{
|
||||
var _ = result switch
|
||||
{
|
||||
BuyResult.XpShopDisabled => await ReplyErrorLocalizedAsync(strs.xp_shop_disabled),
|
||||
BuyResult.InsufficientFunds => await ReplyErrorLocalizedAsync(strs.not_enough(_gss.Data.Currency.Sign)),
|
||||
BuyResult.AlreadyOwned => await ReplyErrorLocalizedAsync(strs.xpshop_already_owned, GetUseInteraction()),
|
||||
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()),
|
||||
GetUseInteraction());
|
||||
}
|
||||
|
||||
[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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -188,6 +188,13 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
},
|
||||
(_, n) => n);
|
||||
|
||||
await ctx.Clubs
|
||||
.Where(x => x.Members.Any(m => group.Contains(m.UserId)))
|
||||
.UpdateAsync(old => new()
|
||||
{
|
||||
Xp = old.Xp + (group.Key * old.Members.Count(m => group.Contains(m.UserId)))
|
||||
});
|
||||
|
||||
dus.AddRange(items);
|
||||
}
|
||||
|
||||
@@ -207,6 +214,41 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
(_, n) => n);
|
||||
|
||||
gxps.AddRange(items);
|
||||
|
||||
var missingUserIds = group.Where(userId => !items.Any(x => x.UserId == userId)).ToArray();
|
||||
foreach (var userId in missingUserIds)
|
||||
{
|
||||
await ctx
|
||||
.UserXpStats
|
||||
.ToLinqToDBTable()
|
||||
.InsertOrUpdateAsync(() => new UserXpStats()
|
||||
{
|
||||
UserId = userId,
|
||||
GuildId = guildId,
|
||||
Xp = group.Key,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
AwardedXp = 0,
|
||||
NotifyOnLevelUp = XpNotificationLocation.None
|
||||
},
|
||||
_ => new()
|
||||
{
|
||||
|
||||
},
|
||||
() => new()
|
||||
{
|
||||
UserId = userId
|
||||
});
|
||||
}
|
||||
|
||||
if (missingUserIds.Length > 0)
|
||||
{
|
||||
var missingItems = await ctx.UserXpStats
|
||||
.ToLinqToDBTable()
|
||||
.Where(x => missingUserIds.Contains(x.UserId))
|
||||
.ToArrayAsyncLinqToDB();
|
||||
|
||||
gxps.AddRange(missingItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -593,12 +635,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 +659,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 +677,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 +708,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 +719,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 +854,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 +974,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 +998,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 +1019,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 +1078,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 +1102,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 +1116,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 +1128,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 +1145,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 +1167,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
|
||||
@@ -1335,13 +1436,17 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
var conf = _xpConfig.Data;
|
||||
|
||||
if (!conf.Shop.IsEnabled)
|
||||
return BuyResult.UnknownItem;
|
||||
return BuyResult.XpShopDisabled;
|
||||
|
||||
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 +1573,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;
|
||||
@@ -1478,6 +1587,7 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
||||
public enum BuyResult
|
||||
{
|
||||
Success,
|
||||
XpShopDisabled,
|
||||
AlreadyOwned,
|
||||
InsufficientFunds,
|
||||
UnknownItem,
|
||||
|
@@ -27,4 +27,4 @@ if (args.Length > 0 && args[0] != "run")
|
||||
LogSetup.SetupLogger(shardId);
|
||||
Log.Information("Pid: {ProcessId}", pid);
|
||||
|
||||
await new Bot(shardId, totalShards).RunAndBlockAsync();
|
||||
await new Bot(shardId, totalShards, Environment.GetEnvironmentVariable("NadekoBot__creds")).RunAndBlockAsync();
|
@@ -67,6 +67,9 @@ public sealed class DiscordEmbedBuilderWrapper : IEmbedBuilder
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(color), "Unsupported EmbedColor type")
|
||||
};
|
||||
|
||||
public IEmbedBuilder WithDiscordColor(Color color)
|
||||
=> Wrap(embed.WithColor(color));
|
||||
|
||||
public Embed Build()
|
||||
=> embed.Build();
|
||||
|
||||
|
@@ -18,11 +18,9 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
||||
private const string CREDS_FILE_NAME = "creds.yml";
|
||||
private const string CREDS_EXAMPLE_FILE_NAME = "creds_example.yml";
|
||||
|
||||
private string CredsPath
|
||||
=> Path.Combine(Directory.GetCurrentDirectory(), CREDS_FILE_NAME);
|
||||
private string CredsPath { get; }
|
||||
|
||||
private string CredsExamplePath
|
||||
=> Path.Combine(Directory.GetCurrentDirectory(), CREDS_EXAMPLE_FILE_NAME);
|
||||
private string CredsExamplePath { get; }
|
||||
|
||||
private readonly int? _totalShards;
|
||||
|
||||
@@ -34,9 +32,21 @@ public sealed class BotCredsProvider : IBotCredsProvider
|
||||
private readonly object _reloadLock = new();
|
||||
private readonly IDisposable _changeToken;
|
||||
|
||||
public BotCredsProvider(int? totalShards = null)
|
||||
public BotCredsProvider(int? totalShards = null, string credPath = null)
|
||||
{
|
||||
_totalShards = totalShards;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(credPath))
|
||||
{
|
||||
CredsPath = credPath;
|
||||
CredsExamplePath = Path.Combine(Path.GetDirectoryName(credPath), CREDS_EXAMPLE_FILE_NAME);
|
||||
}
|
||||
else
|
||||
{
|
||||
CredsPath = Path.Combine(Directory.GetCurrentDirectory(), CREDS_FILE_NAME);
|
||||
CredsExamplePath = Path.Combine(Directory.GetCurrentDirectory(), CREDS_EXAMPLE_FILE_NAME);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(CredsExamplePath))
|
||||
|
@@ -7,7 +7,7 @@ namespace NadekoBot.Services;
|
||||
|
||||
public sealed class StatsService : IStatsService, IReadyExecutor, INService
|
||||
{
|
||||
public const string BOT_VERSION = "4.3.0";
|
||||
public const string BOT_VERSION = "4.3.5";
|
||||
|
||||
public string Author
|
||||
=> "Kwoth#2452";
|
||||
|
@@ -33,7 +33,7 @@ public static class MessageChannelExtensions
|
||||
public static async Task<IUserMessage> SendAsync(
|
||||
this IMessageChannel channel,
|
||||
string? plainText,
|
||||
NadekoButtonInteraction? inter,
|
||||
NadekoInteraction? inter,
|
||||
Embed? embed = null,
|
||||
IReadOnlyCollection<Embed>? embeds = null,
|
||||
bool sanitizeAll = false)
|
||||
@@ -72,7 +72,7 @@ public static class MessageChannelExtensions
|
||||
IEmbedBuilder? embed,
|
||||
string plainText = "",
|
||||
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
||||
NadekoButtonInteraction? inter = null)
|
||||
NadekoInteraction? inter = null)
|
||||
=> ch.SendAsync(plainText,
|
||||
inter,
|
||||
embed: embed?.Build(),
|
||||
@@ -83,7 +83,7 @@ public static class MessageChannelExtensions
|
||||
IEmbedBuilderService eb,
|
||||
string text,
|
||||
MessageType type,
|
||||
NadekoButtonInteraction? inter = null)
|
||||
NadekoInteraction? inter = null)
|
||||
{
|
||||
var builder = eb.Create().WithDescription(text);
|
||||
|
||||
|
@@ -56,7 +56,7 @@ public static class SocketMessageComponentExtensions
|
||||
IEmbedBuilder? embed,
|
||||
string plainText = "",
|
||||
IReadOnlyCollection<IEmbedBuilder>? embeds = null,
|
||||
NadekoButtonInteraction? inter = null,
|
||||
NadekoInteraction? inter = null,
|
||||
bool ephemeral = false)
|
||||
=> smc.RespondAsync(plainText,
|
||||
embed: embed?.Build(),
|
||||
@@ -69,7 +69,7 @@ public static class SocketMessageComponentExtensions
|
||||
string text,
|
||||
MessageType type,
|
||||
bool ephemeral = false,
|
||||
NadekoButtonInteraction? inter = null)
|
||||
NadekoInteraction? inter = null)
|
||||
{
|
||||
var builder = eb.Create().WithDescription(text);
|
||||
|
||||
|
@@ -174,6 +174,14 @@ settopic:
|
||||
setchanlname:
|
||||
- setchanlname
|
||||
- schn
|
||||
# thread stuff
|
||||
threadcreate:
|
||||
- threadcreate
|
||||
- thcr
|
||||
threaddelete:
|
||||
- threaddelete
|
||||
- thdel
|
||||
- thrm
|
||||
prune:
|
||||
- prune
|
||||
- clear
|
||||
@@ -209,6 +217,9 @@ serverinfo:
|
||||
channelinfo:
|
||||
- channelinfo
|
||||
- cinfo
|
||||
roleinfo:
|
||||
- roleinfo
|
||||
- rinfo
|
||||
userinfo:
|
||||
- userinfo
|
||||
- uinfo
|
||||
@@ -374,6 +385,7 @@ award:
|
||||
- award
|
||||
take:
|
||||
- take
|
||||
- seize
|
||||
betroll:
|
||||
- betroll
|
||||
- br
|
||||
@@ -710,6 +722,13 @@ showemojis:
|
||||
emojiadd:
|
||||
- emojiadd
|
||||
- ea
|
||||
emojiremove:
|
||||
- emojiremove
|
||||
- emojirm
|
||||
- er
|
||||
- ed
|
||||
- emojidel
|
||||
- emojidelete
|
||||
deckshuffle:
|
||||
- deckshuffle
|
||||
- dsh
|
||||
@@ -1064,6 +1083,10 @@ xpadd:
|
||||
- xpadd
|
||||
xpshop:
|
||||
- xpshop
|
||||
xpshopbuy:
|
||||
- xpshopbuy
|
||||
xpshopuse:
|
||||
- xpshopuse
|
||||
clubcreate:
|
||||
- clubcreate
|
||||
clubtransfer:
|
||||
@@ -1304,6 +1327,7 @@ medusalist:
|
||||
medusainfo:
|
||||
- medusainfo
|
||||
- meinfo
|
||||
# Bank stuff
|
||||
bankdeposit:
|
||||
- deposit
|
||||
- d
|
||||
@@ -1316,6 +1340,11 @@ bankbalance:
|
||||
- balance
|
||||
- b
|
||||
- bal
|
||||
banktake:
|
||||
- take
|
||||
- seize
|
||||
bankaward:
|
||||
- award
|
||||
# Patron
|
||||
patron:
|
||||
- patron
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 13 KiB |
@@ -423,6 +423,10 @@ channelinfo:
|
||||
desc: "Shows info about the channel. If no channel is supplied, it defaults to current one."
|
||||
args:
|
||||
- "#some-channel"
|
||||
roleinfo:
|
||||
desc: "Shows info about the specified role."
|
||||
args:
|
||||
- "Gamers"
|
||||
userinfo:
|
||||
desc: "Shows info about the user. If no user is supplied, it defaults a user running the command."
|
||||
args:
|
||||
@@ -1217,6 +1221,10 @@ emojiadd:
|
||||
- ":someonesCustomEmoji:"
|
||||
- "MyEmojiName :someonesCustomEmoji:"
|
||||
- "owoNice https://cdn.discordapp.com/emojis/587930873811173386.png?size=128"
|
||||
emojiremove:
|
||||
desc: "Removes the specified emoji or emojis from this server."
|
||||
args:
|
||||
- ":eagleWarrior: :plumedArcher:"
|
||||
deckshuffle:
|
||||
desc: "Reshuffles all cards back into the deck."
|
||||
args:
|
||||
@@ -2128,6 +2136,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:
|
||||
@@ -2227,6 +2245,14 @@ bankbalance:
|
||||
desc: "Shows your current bank balance available for withdrawal."
|
||||
args:
|
||||
- ""
|
||||
banktake:
|
||||
desc: "Takes the specified amount of currency from a user's bank"
|
||||
args:
|
||||
- "500 @MoniLaunder"
|
||||
bankaward:
|
||||
desc: "Award the specified amount of currency to a user's bank"
|
||||
args:
|
||||
- "99999 @Bestie"
|
||||
patron:
|
||||
desc: "Check your patronage status and command usage quota. Bot owners can check targeted user's patronage status."
|
||||
args:
|
||||
@@ -2268,3 +2294,11 @@ bettest:
|
||||
- ""
|
||||
- "betflip 1000"
|
||||
- "slot 2000"
|
||||
threadcreate:
|
||||
desc: "Create a public thread with the specified title. You may optionally reply to a message to have it as a starting point."
|
||||
args:
|
||||
- "Q&A"
|
||||
threaddelete:
|
||||
desc: "Delete a thread with the specified name in this channel. Case insensitive."
|
||||
args:
|
||||
- "Q&A"
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"api_key_missing": "Der API Key der für diesen Befehl benötigt wird ist nicht angegeben.",
|
||||
"api_key_missing": "Der API Key fehlt.",
|
||||
"quote_deleted": "Zitat gelöscht",
|
||||
"redacted_too_long": "Wurde entfernt da es zu lang ist.",
|
||||
"trigger": "Auslöser",
|
||||
@@ -10,7 +10,7 @@
|
||||
"banned_user": "Nutzer gebannt",
|
||||
"byedel_off": "Die automatische Löschung der Verabschiedungs Nachrichten wurde deaktiviert.",
|
||||
"byedel_on": "Verabschiedungs Nachrichten werden nach {0} Sekunden gelöscht.",
|
||||
"byemsg_cur": "Derzeitige Verabschiedungs Nachricht",
|
||||
"byemsg_cur": "Derzeitige Verabschiedungs Nachricht: {0}",
|
||||
"byemsg_enable": "Beim schreiben von {0} werden die Verabschiedungs Nachrichten aktiviert.",
|
||||
"byemsg_new": "Neue Verabschiedungs Nachricht gesetzt",
|
||||
"bye_off": "Abschiedansagen ausgeschaltet.",
|
||||
@@ -922,9 +922,9 @@
|
||||
"pages": "Seiten",
|
||||
"favorites": "Favoriten",
|
||||
"tags": "Stichworte",
|
||||
"invalid_emoji_link": "",
|
||||
"emoji_add_error": "",
|
||||
"emoji_added": "",
|
||||
"invalid_emoji_link": "Der angegebene Link is entweder kein Bild, oder größer als 256KB.",
|
||||
"emoji_add_error": "Fehler beim hinzufügen des Emoji. Es sind keine Emoji slots mehr vorhanden, oder die Bildgröße ist unangemessen.",
|
||||
"emoji_added": "Neuen Emoji hinzugefügt: {0}",
|
||||
"boost_on": "",
|
||||
"boost_off": "",
|
||||
"boostmsg_cur": "",
|
||||
@@ -932,16 +932,16 @@
|
||||
"boostmsg_new": "",
|
||||
"boostdel_off": "",
|
||||
"boostdel_on": "",
|
||||
"log_ignored_channels": "",
|
||||
"log_ignored_users": "",
|
||||
"log_ignore_user": "",
|
||||
"log_not_ignore_user": "",
|
||||
"log_ignore_chan": "",
|
||||
"log_not_ignore_chan": "",
|
||||
"log_ignored_channels": "Ignorierte Kanäle",
|
||||
"log_ignored_users": "Ignorierte User",
|
||||
"log_ignore_user": "Logging wird User {0} ignorieren",
|
||||
"log_not_ignore_user": "Logging wird nicht länger User {0} ignorieren",
|
||||
"log_ignore_chan": "Logging wird Kanal {0} ignorieren",
|
||||
"log_not_ignore_chan": "Logging wird nicht länger Kanal {0} ignorieren",
|
||||
"streams_cleared": "",
|
||||
"warn_weight": "",
|
||||
"warn_count": "",
|
||||
"mass_ban_in_progress": "",
|
||||
"warn_weight": "Gewicht: {0}",
|
||||
"warn_count": "{0} momentan, {1} insgesamt",
|
||||
"mass_ban_in_progress": "Es werden {0} User gebannt...",
|
||||
"mass_ban_completed": "",
|
||||
"reminder_server_list": "",
|
||||
"imageonly_enable": "",
|
||||
@@ -975,5 +975,77 @@
|
||||
"exprs_cleared": "",
|
||||
"expr_reset": "",
|
||||
"expr_set": "",
|
||||
"expr_edited": ""
|
||||
"expr_edited": "",
|
||||
"stream_online_delete_enabled": "",
|
||||
"stream_online_delete_disabled": "",
|
||||
"club_create_error_name": "",
|
||||
"club_desc_update": "",
|
||||
"bank_accounts": "",
|
||||
"module_description_medusa": "",
|
||||
"list_of_medusae": "",
|
||||
"list_of_unloaded": "",
|
||||
"medusa_name_not_found": "",
|
||||
"medusa_info": "",
|
||||
"sneks_count": "",
|
||||
"commands_count": "",
|
||||
"no_medusa_loaded": "",
|
||||
"no_medusa_available": "",
|
||||
"loaded_medusae": "",
|
||||
"medusa_not_loaded": "",
|
||||
"medusa_possibly_cant_unload": "",
|
||||
"medusa_loaded": "",
|
||||
"medusa_unloaded": "",
|
||||
"medusa_empty": "",
|
||||
"medusa_already_loaded": "",
|
||||
"medusa_invalid_not_found": "",
|
||||
"bank_balance": "",
|
||||
"bank_deposited": "",
|
||||
"bank_withdrew": "",
|
||||
"bank_withdraw_insuff": "",
|
||||
"cmd_group_commands": "",
|
||||
"limit_reached": "",
|
||||
"feature_limit_reached_you": "",
|
||||
"feature_limit_reached_owner": "",
|
||||
"feature_limit_reached_either": "",
|
||||
"tier": "",
|
||||
"pledge": "",
|
||||
"expires": "",
|
||||
"commands": "",
|
||||
"groups": "",
|
||||
"modules": "",
|
||||
"no_quota_found": "",
|
||||
"patron_info": "",
|
||||
"quotas": "",
|
||||
"patron_not_enabled": "",
|
||||
"results_in": "",
|
||||
"patron_msg_sent": "",
|
||||
"cards": "",
|
||||
"hand_value": "",
|
||||
"roll2": "",
|
||||
"rolls": "",
|
||||
"available_tests": "",
|
||||
"test_results_for": "",
|
||||
"multiplier": "",
|
||||
"trivia_ended": "",
|
||||
"card": "",
|
||||
"guess": "",
|
||||
"repeater_skip_next": "",
|
||||
"repeater_dont_skip_next": "",
|
||||
"remind_timely": "",
|
||||
"xp_shop_disabled": "",
|
||||
"timely_time": "",
|
||||
"buy": "",
|
||||
"use": "",
|
||||
"in_use": "",
|
||||
"xp_shop_item_cant_use": "",
|
||||
"linkonly_enable": "",
|
||||
"linkonly_disable": "",
|
||||
"xp_shop_buy_required_tier": "",
|
||||
"available_commands": "",
|
||||
"xpadd_users": "",
|
||||
"xpshop_buy_success": "",
|
||||
"patron_insuff_tier": "",
|
||||
"xpshop_already_owned": "",
|
||||
"xpshop_item_not_found": "",
|
||||
"xpshop_website": ""
|
||||
}
|
@@ -12,6 +12,7 @@
|
||||
"invalid_emoji_link": "Specified link is either not an image or exceeds 256KB.",
|
||||
"emoji_add_error": "Error adding emoji. You either ran out of emoji slots, or image size is inadequate.",
|
||||
"emoji_added": "Added a new emoji: {0}",
|
||||
"emoji_not_removed": "The following emojis were not removed: {0}",
|
||||
"fw_cleared": "Removed all filtered words and filtered words channel settings.",
|
||||
"aliases_cleared": "All {0} aliases on this server have been removed.",
|
||||
"no_results": "No results found.",
|
||||
@@ -141,6 +142,10 @@
|
||||
"rar_err": "Failed to remove roles. I have insufficient permissions.",
|
||||
"rc": "Color of {0} role has been changed.",
|
||||
"rc_perms": "Error occurred due to invalid color or insufficient permissions.",
|
||||
"color": "Color",
|
||||
"icon": "Icon",
|
||||
"hoisted": "Hoisted",
|
||||
"mentionable": "Mentionable",
|
||||
"remrole": "Successfully removed role {0} from user {1}",
|
||||
"remrole_err": "Failed to remove role. I have insufficient permissions.",
|
||||
"renrole": "Role renamed.",
|
||||
@@ -1003,7 +1008,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 +1022,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>"
|
||||
}
|
||||
|
@@ -975,5 +975,77 @@
|
||||
"exprs_cleared": "L'ensemble des {0} expressions du serveur ont été retirées.",
|
||||
"expr_reset": "L'expression avec l'id {0} ne rajoutera plus de réactions.",
|
||||
"expr_set": "L'expression avec l'id {0} ajoutera automatiquement ces réactions au message de réponse : {1}",
|
||||
"expr_edited": "Expression éditée"
|
||||
"expr_edited": "Expression éditée",
|
||||
"stream_online_delete_enabled": "Aucune méduse de ce nom n'a été trouvée ou le fichier était invalide",
|
||||
"stream_online_delete_disabled": "Les notifications de diffusions en ligne ne seront plus supprimées quand la diffusion sera hors ligne.",
|
||||
"club_create_error_name": "Échec de la création du club. Un club existe du même nom existe déjà.",
|
||||
"club_desc_update": "La description du club a été mise à jour",
|
||||
"bank_accounts": "Comptes bancaires",
|
||||
"module_description_medusa": "**Unique au propriétaire du bot.** Charger, décharger et maintenez des modules dynamiques. Découvrez en plus [ici](https://nadekobot.readthedocs.io/en/latest/medusa/creating-a-medusa/)",
|
||||
"list_of_medusae": "Liste des méduses",
|
||||
"list_of_unloaded": "Liste des méduses disponible",
|
||||
"medusa_name_not_found": "Une méduse de ce nom n'existe pas ou n'est pas chargée.",
|
||||
"medusa_info": "Information de la méduse",
|
||||
"sneks_count": "Sneks ({0})",
|
||||
"commands_count": "Commandes ({0})",
|
||||
"no_medusa_loaded": "Aucunes méduses de chargées.",
|
||||
"no_medusa_available": "Aucune méduse de disponible.",
|
||||
"loaded_medusae": "Méduses chargées",
|
||||
"medusa_not_loaded": "La méduse de ce nom n'est pas chargée.",
|
||||
"medusa_possibly_cant_unload": "La méduse n'est probablement pas entièrement chargée. Redémarrez le bot si le problème persiste.",
|
||||
"medusa_loaded": "La méduse {0} a été chargée.",
|
||||
"medusa_unloaded": "La méduse {0} a été déchargée.",
|
||||
"medusa_empty": "La méduse n'a pas été chargée puisqu'elle ne contient aucun Sneks.",
|
||||
"medusa_already_loaded": "La méduse {0} est déjà chargée",
|
||||
"medusa_invalid_not_found": "Aucune méduse de ce nom n'a été trouvée ou le fichier était invalide",
|
||||
"bank_balance": "Vous disposez de {0} sur votre compte bancaire.",
|
||||
"bank_deposited": "Vous avez déposé {0} sur votre compte bancaire.",
|
||||
"bank_withdrew": "Vous avez retiré {0} de votre compte bancaire.",
|
||||
"bank_withdraw_insuff": "Vous ne disposez pas suffisamment de {0} sur votre compte bancaire.",
|
||||
"cmd_group_commands": "Groupe de commande '{0}'",
|
||||
"limit_reached": "La limite des fonctionnalités de {0} a été atteinte.",
|
||||
"feature_limit_reached_you": "Vous avez atteint la limite de {0} pour la fonctionnalité {1}. Vous pouvez augmenter cette limite en augmentant votre niveau de contribution.",
|
||||
"feature_limit_reached_owner": "Le propriétaire du serveur a atteint la limite de {0} pour la fonctionnalité {1}. Ce dernier peut augmenter cette limite en augmentant son niveau de contribution.",
|
||||
"feature_limit_reached_either": "La limite de {0} pour la fonctionnalité {1} a été atteinte. Soit vous ou le propriétaire du serveur peut augmenter cette limite en augmentant le niveau de contribution.",
|
||||
"tier": "Niveau",
|
||||
"pledge": "Promesse",
|
||||
"expires": "Expire",
|
||||
"commands": "Commandes",
|
||||
"groups": "Groupes",
|
||||
"modules": "Modules",
|
||||
"no_quota_found": "Aucun quota de trouvé.",
|
||||
"patron_info": "Info de la·e contributeur·rice",
|
||||
"quotas": "<<< Quotas >>>",
|
||||
"patron_not_enabled": "Le système de contribution est désactivé.",
|
||||
"results_in": "{0} résulte en {1}s",
|
||||
"patron_msg_sent": "L'envoi de messages aux contributeur·rice·s de niveau {1} et au-delà a été effectué. {1} ont été envoyés avec succès et {2} ont échoué.",
|
||||
"cards": "Cartes",
|
||||
"hand_value": "Valeur de la main",
|
||||
"roll2": "Lancé",
|
||||
"rolls": "Lancés",
|
||||
"available_tests": "Tests disponibles",
|
||||
"test_results_for": "Résultats du test pour {0}",
|
||||
"multiplier": "Multiplier",
|
||||
"trivia_ended": "Le jeu de culture générale est terminé",
|
||||
"card": "Carte",
|
||||
"guess": "Deviner",
|
||||
"repeater_skip_next": "Le prochain rappel de ce répéteur sera ignoré.",
|
||||
"repeater_dont_skip_next": "Le prochain rappel de ce répéteur ne sera pas ignoré.",
|
||||
"remind_timely": "Je vous notifierai à propos de votre récompense opportune {0}",
|
||||
"xp_shop_disabled": "Le magasin d'XP est désactivé par le propriétaire, ou il n'y a aucun item à vendre.",
|
||||
"timely_time": "Il est l'heure de votre récompense opportune.",
|
||||
"buy": "Acheter",
|
||||
"use": "Utiliser",
|
||||
"in_use": "En utilisation",
|
||||
"xp_shop_item_cant_use": "Vous ne pouvez pas utiliser cet item car il ne vous appartient pas ou il n'existe pas.",
|
||||
"linkonly_enable": "Ce channel est désormais limité aux liens.",
|
||||
"linkonly_disable": "Ce channel n'est désormais plus limité aux liens.",
|
||||
"xp_shop_buy_required_tier": "Acheter des items de ce magasin requiert d'être Tier Patron {0} ou au-delà.",
|
||||
"available_commands": "Commandes disponibles",
|
||||
"xpadd_users": "Ajout de {0} XP du serveur à l'utilisateur {1}.",
|
||||
"xpshop_buy_success": "Vous avez acheté avec succès `{0}/{1}`",
|
||||
"patron_insuff_tier": "Votre Tier Patron est insuffisant pour réaliser cette action.",
|
||||
"xpshop_already_owned": "Vous disposez déjà de cet item.",
|
||||
"xpshop_item_not_found": "Aucun item avec cette clé n'existe.",
|
||||
"xpshop_website": "Vous pouvez voir la liste de tous les items du magasin d'XP ici: <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