mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
add: added addrolereward and removerolereward events for .notify
add: added .notify with no params showing events with descriptions add: .winlb docs: updated docs dev: updated discord.net, redid migrations
This commit is contained in:
38
CHANGELOG.md
38
CHANGELOG.md
@@ -2,6 +2,44 @@
|
|||||||
|
|
||||||
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.0.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
|
## [5.3.0] - 07.12.2024
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added `.minesweeper` / `.mw` command - spoiler-based minesweeper minigame. Just for fun
|
||||||
|
- Added `.temprole` command - add a role to a user for a certain amount of time, after which the role will be removed
|
||||||
|
- Added `.xplevelset` - you can now set a level for a user in your server
|
||||||
|
- Added `.winlb` command - leaderboard of top gambling wins
|
||||||
|
- Added `.notify` command
|
||||||
|
- Specify an event to be notified about, and the bot will post the specified message in the current channel when the event occurs
|
||||||
|
- A few events supported right now:
|
||||||
|
- `UserLevelUp` when user levels up in the server
|
||||||
|
- `AddRoleReward` when a role is added to a user through .xpreward system
|
||||||
|
- `RemoveRoleReward` when a role is removed from a user through .xpreward system
|
||||||
|
- `Protection` when antialt, antiraid or antispam protection is triggered
|
||||||
|
- Added `.banner` command to see someone's banner
|
||||||
|
- Selfhosters:
|
||||||
|
- Added `.dmmod` and `.dmcmd` - you can now disable or enable whether commands or modules can be executed in bot's DMs
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Giveaway improvements
|
||||||
|
- Now mentions winners in a separate message
|
||||||
|
- Shows the timestamp of when the giveaway ends
|
||||||
|
- Xp Changes
|
||||||
|
- Removed awarded xp (the number in the brackets on the xp card)
|
||||||
|
- Awarded xp, (or the new level set) now directly apply to user's real xp
|
||||||
|
- Server xp notifications are now set by the server admin/manager in a specified channel
|
||||||
|
- `.sclr show` will now show hex code of the current color
|
||||||
|
- Queueing a song will now restart the playback if the queue is on the last track and stopped (there were no more tracks to play)
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- .setstream and .setactivity will now pause .ropl (rotating statuses)
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
|
||||||
## [5.2.4] - 27.11.2024
|
## [5.2.4] - 27.11.2024
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# Config
|
# Config
|
||||||
`.config` is the new `.bce`, it gives you a fast and easy way to edit most bot settings/values. Use `.h .config` for explanation.
|
`.config` gives you a fast and easy way to edit most bot settings/values. Use `.h .config` for explanation.
|
||||||
|
|
||||||
Use `.config` to see the list of editable config files
|
Use `.config` to see the list of editable config files
|
||||||
Use `.config <config-name>` to see the list of settable properties on that config
|
Use `.config <config-name>` to see the list of settable properties on that config
|
||||||
|
@@ -9,7 +9,7 @@ Donating to us also gives you the following benefits:
|
|||||||
|
|
||||||
- A hoisted **Donators role** in our [Discord server][discord-server]
|
- A hoisted **Donators role** in our [Discord server][discord-server]
|
||||||
- Access to exclusive **#noticed** text and voice channels
|
- Access to exclusive **#noticed** text and voice channels
|
||||||
- **1000 flowers** on the public bot per dollar donated (after fees)
|
- **3000 flowers** on the public bot per dollar donated (after fees)
|
||||||
- **Expressions** on the public bot for [Patreon pledges][patreon] of $5 or higher
|
- **Expressions** on the public bot for [Patreon pledges][patreon] of $5 or higher
|
||||||
|
|
||||||
## Patreon
|
## Patreon
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
## Setting Up NadekoBot on Windows With the Updater
|
## Setting Up NadekoBot on Windows With the Updater
|
||||||
|
|
||||||
| Table of Contents|
|
| Table of Contents|
|
||||||
| :---------------------------------------------------------------------------------------------------------------------------|
|
| :-|
|
||||||
| [Prerequisites](#prerequisites) |
|
| [Prerequisites](#prerequisites) |
|
||||||
| [Setup](#setup) |
|
| [Setup](#setup) |
|
||||||
| [Starting the Bot](#starting-the-bot) |
|
| [Starting the Bot](#starting-the-bot) |
|
||||||
| [Updating Nadeko](#updating-nadeko) |
|
| [Updating Nadeko](#updating-nadeko) |
|
||||||
| [Manually Installing the Prerequisites from the Updater](#music-prerequisites) |
|
| [Manually Installing the Prerequisites from the Updater](#music-prerequisites) |
|
||||||
|
|
||||||
*Note: If you want to make changes to Nadeko's source code, please follow the [From Source](#windows-from-source) guide instead.*
|
*Note: If you want to make changes to Nadeko's source code, please follow the [From Source](#windows-from-source) guide instead.*
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
#### Prerequisites
|
#### Prerequisites
|
||||||
|
|
||||||
- Windows 10 or later (64-bit)
|
- Windows 10 or later (64-bit)
|
||||||
- [Create a Discord Bot application and invite the bot to your server](../creds-guide.md)
|
|
||||||
|
|
||||||
**Optional**
|
**Optional**
|
||||||
|
|
||||||
@@ -29,8 +28,7 @@
|
|||||||

|

|
||||||
- Click on **`DOWNLOAD`** at the lower right
|
- Click on **`DOWNLOAD`** at the lower right
|
||||||

|

|
||||||
- **Note: Redis is optional. install Redis manually here: [Redis] Download and run the **`.msi`** file.**
|
- If you want to use the music module, click on **`Install`** next to **`FFMPEG`** and **`Youtube-DLP`**.
|
||||||
- If you will use the music module, click on **`Install`** next to **`FFMPEG`** and **`Youtube-DLP`**.
|
|
||||||
- If any dependencies fail to install, you can temporarily disable your Windows Defender/AV until you install them. If you don't want to, then read [the last section of this guide](#Manual-Prerequisite-Installation).
|
- If any dependencies fail to install, you can temporarily disable your Windows Defender/AV until you install them. If you don't want to, then read [the last section of this guide](#Manual-Prerequisite-Installation).
|
||||||
- When installation is finished, click on **`CREDS`** to the left of **`RUN`** at the lower right.
|
- When installation is finished, click on **`CREDS`** to the left of **`RUN`** at the lower right.
|
||||||
- Follow the guide on how to [Set up the creds.yml](../../creds-guide) file.
|
- Follow the guide on how to [Set up the creds.yml](../../creds-guide) file.
|
||||||
@@ -52,71 +50,6 @@
|
|||||||
- Launch the bot
|
- Launch the bot
|
||||||
- You've updated and are running again, easy as that!
|
- You've updated and are running again, easy as that!
|
||||||
|
|
||||||
#### Manual Prerequisite Installation
|
|
||||||
|
|
||||||
You can still install them manually:
|
|
||||||
|
|
||||||
- [Redis] (OPTIONAL) - Download and run the **`.msi`** file
|
|
||||||
- [ffmpeg-32bit] | [ffmpeg-64bit] - Download the **appropriate version** for your system (32 bit if you're running a 32 bit OS, or 64 if you're running a 64bit OS). Unzip it, and move `ffmpeg.exe` to a path that's in your PATH environment variable. If you don't know what that is, then just move the `ffmpeg.exe` file to NadekoBot/system
|
|
||||||
- [youtube-dlp] - Click to download the `yt-dlp.exe` file then put `yt-dlp.exe` in a path that's in your PATH environment variable. If you don't know what that is, then just move the `yt-dlp.exe` file to NadekoBot/system
|
|
||||||
|
|
||||||
## **⚠ IF YOU ARE FOLLOWING THE GUIDE ABOVE, IGNORE THIS SECTION ⚠**
|
|
||||||
|
|
||||||
### Windows From Source
|
|
||||||
|
|
||||||
##### Prerequisites
|
|
||||||
|
|
||||||
**Install these before proceeding or your bot will not work!**
|
|
||||||
- [.net 8](https://dotnet.microsoft.com/en-us/download) - needed to compile and run the bot
|
|
||||||
- [git](https://git-scm.com/downloads) - needed to clone the repository (you can also download the zip manually and extract it, but this guide assumes you're using git)
|
|
||||||
- [Redis] (OPTIONAL)- to cache things needed by some features and persist through restarts
|
|
||||||
|
|
||||||
##### Installation Instructions
|
|
||||||
|
|
||||||
Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and navigate to the location where you want to install the bot (for example `cd ~/Desktop/`)
|
|
||||||
|
|
||||||
1. `git clone https://gitlab.com/kwoth/nadekobot -b v5 --depth 1`
|
|
||||||
2. `cd nadekobot`
|
|
||||||
3. `dotnet publish -c Release -o output/ src/NadekoBot/`
|
|
||||||
4. `cd output`
|
|
||||||
5. `cp creds_example.yml creds.yml`
|
|
||||||
6. Open `creds.yml` with your favorite text editor (Please don't use Notepad or WordPad. You can use Notepad++, VSCode, Atom, Sublime, or something similar)
|
|
||||||
7. [Enter your bot's token](#creds-guide)
|
|
||||||
8. Run the bot `dotnet NadekoBot.dll`
|
|
||||||
9. 🎉
|
|
||||||
|
|
||||||
##### Update Instructions
|
|
||||||
|
|
||||||
Open PowerShell as described above and run the following commands:
|
|
||||||
|
|
||||||
1. Stop the bot
|
|
||||||
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
|
|
||||||
2. Navigate to your bot's folder, example:
|
|
||||||
- `cd ~/Desktop/nadekobot`
|
|
||||||
3. Pull the new version, and make sure you're on the v5 branch
|
|
||||||
- *⚠️ the first 3 lines can be omitted if you're already on v5. If you're updating from v4, you must run them*
|
|
||||||
- `git remote set-branches origin '*'`
|
|
||||||
- `git fetch -v --depth=1`
|
|
||||||
- `git checkout v5`
|
|
||||||
- `git pull`
|
|
||||||
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
|
|
||||||
4. **Backup** old output in case your data is overwritten
|
|
||||||
- `cp -r -fo output/ output-old`
|
|
||||||
5. Build the bot again
|
|
||||||
- `dotnet publish -c Release -o output/ src/NadekoBot/`
|
|
||||||
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
|
|
||||||
- ⚠ If you've modified said files, back them up instead
|
|
||||||
- `rm output-old/data/aliases.yml`
|
|
||||||
- `rm -r output-old/data/strings`
|
|
||||||
7. Copy old data
|
|
||||||
- `cp -Recurse .\output-old\data\ .\output\ -Force`
|
|
||||||
8. Copy creds.yml
|
|
||||||
- `cp output-old/creds.yml output/`
|
|
||||||
9. Run the bot
|
|
||||||
- `cd output`
|
|
||||||
- `dotnet NadekoBot.dll`
|
|
||||||
|
|
||||||
🎉 Enjoy
|
|
||||||
|
|
||||||
#### Music prerequisites
|
#### Music prerequisites
|
||||||
In order to use music commands, you need ffmpeg and yt-dlp installed.
|
In order to use music commands, you need ffmpeg and yt-dlp installed.
|
||||||
|
77
docs/guides/windows-source-guide.md
Normal file
77
docs/guides/windows-source-guide.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
## Setting Up NadekoBot on Windows from source
|
||||||
|
|
||||||
|
1. Prerequisites
|
||||||
|
|
||||||
|
- Windows 10 or later (64-bit)
|
||||||
|
- [.net 8 sdk](https://dotnet.microsoft.com/download/dotnet/8.0)
|
||||||
|
- If you want nadeko to play music: [Visual C++ 2010 (x86)] and [Visual C++ 2017 (x64)] (both are required, you may install them later)
|
||||||
|
- [git](https://git-scm.com/downloads) - needed to clone the repository (you can also download the zip manually and extract it, but this guide assumes you're using git)
|
||||||
|
- **Optional** Any code editor, for example [Visual Studio Code](https://code.visualstudio.com/Download)
|
||||||
|
- You'll need to at least modify creds.yml, notepad is inadequate
|
||||||
|
|
||||||
|
|
||||||
|
##### Installation Instructions
|
||||||
|
|
||||||
|
Open PowerShell (press windows button on your keyboard and type powershell, it should show up; alternatively, right click the start menu and select Windows PowerShell), and
|
||||||
|
|
||||||
|
|
||||||
|
0. Navigate to the location where you want to install the bot
|
||||||
|
- for example, type `cd ~/Desktop/` and press enter
|
||||||
|
1. `git clone https://gitlab.com/kwoth/nadekobot -b v5 --depth 1`
|
||||||
|
2. `cd nadekobot`
|
||||||
|
3. `dotnet publish -c Release -o output/ src/NadekoBot/`
|
||||||
|
4. `cd output`
|
||||||
|
5. `cp creds_example.yml creds.yml`
|
||||||
|
6. "You're done installing, you may now proceed to set up your bot's credentials by following the [#creds-guide]
|
||||||
|
- Once done, come back here and run the last command
|
||||||
|
8. Run the bot `dotnet NadekoBot.dll`
|
||||||
|
9. 🎉 Enjoy
|
||||||
|
|
||||||
|
##### Update Instructions
|
||||||
|
|
||||||
|
Open PowerShell as described above and run the following commands:
|
||||||
|
|
||||||
|
1. Stop the bot
|
||||||
|
- ⚠️ Make sure you don't have your database, credentials or any other nadekobot folder open in some application, this might prevent some of the steps from executing succesfully
|
||||||
|
2. Navigate to your bot's folder, example:
|
||||||
|
- `cd ~/Desktop/nadekobot`
|
||||||
|
3. Pull the new version, and make sure you're on the v5 branch
|
||||||
|
- *⚠️ If you're on v4, you must run these commands, if not, you may skip them.*
|
||||||
|
- `git remote set-branches origin '*'`
|
||||||
|
- `git fetch -v --depth=1`
|
||||||
|
- `git checkout v5`
|
||||||
|
- `git pull`
|
||||||
|
- ⚠️ If this fails, you may want to stash or remove your code changes if you don't know how to resolve merge conflicts
|
||||||
|
4. **Backup** old output in case your data is overwritten
|
||||||
|
- `cp -r -fo output/ output-old`
|
||||||
|
5. Build the bot again
|
||||||
|
- `dotnet publish -c Release -o output/ src/NadekoBot/`
|
||||||
|
6. Remove old strings and aliases to avoid overwriting the updated versions of those files
|
||||||
|
- ⚠ If you've modified said files, back them up instead
|
||||||
|
- `rm output-old/data/aliases.yml`
|
||||||
|
- `rm -r output-old/data/strings`
|
||||||
|
7. Copy old data, and new strings
|
||||||
|
- `cp -Recurse -Force .\output-old\data\ .\output\`
|
||||||
|
- `cp -Recurse -Force src/NadekoBot/data/strings/ output/data/`
|
||||||
|
8. Copy creds.yml
|
||||||
|
- `cp output-old/creds.yml output/`
|
||||||
|
9. Run the bot
|
||||||
|
- `cd output`
|
||||||
|
- `dotnet NadekoBot.dll`
|
||||||
|
|
||||||
|
🎉 Enjoy
|
||||||
|
|
||||||
|
#### Music prerequisites
|
||||||
|
In order to use music commands, you need ffmpeg and yt-dlp installed.
|
||||||
|
- [ffmpeg-32bit] | [ffmpeg-64bit] - Download the **appropriate version** for your system (32 bit if you're running a 32 bit OS, or 64 if you're running a 64bit OS). Unzip it, and move `ffmpeg.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `ffmpeg.exe` file to `NadekoBot/output`.
|
||||||
|
- [youtube-dlp] - Click to download the `yt-dlp.exe` file, then move `yt-dlp.exe` to a path that's in your PATH environment variable. If you don't know what that is, just move the `yt-dlp.exe` file to `NadekoBot/system`.
|
||||||
|
|
||||||
|
[Updater]: https://dl.nadeko.bot/v3/
|
||||||
|
[Notepad++]: https://notepad-plus-plus.org/
|
||||||
|
[.net]: https://dotnet.microsoft.com/download/dotnet/8.0
|
||||||
|
[Redis]: https://github.com/MicrosoftArchive/redis/releases/download/win-3.0.504/Redis-x64-3.0.504.msi
|
||||||
|
[Visual C++ 2010 (x86)]: https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe
|
||||||
|
[Visual C++ 2017 (x64)]: https://aka.ms/vs/15/release/vc_redist.x64.exe
|
||||||
|
[ffmpeg-32bit]: https://cdn.nadeko.bot/dl/ffmpeg-32.zip
|
||||||
|
[ffmpeg-64bit]: https://cdn.nadeko.bot/dl/ffmpeg-64.zip
|
||||||
|
[youtube-dlp]: https://github.com/yt-dlp/yt-dlp/releases
|
@@ -16,6 +16,7 @@ To self-host your own Nadeko, use the guides below:
|
|||||||
- [:material-microsoft-windows: Windows guide][windows-guide]
|
- [:material-microsoft-windows: Windows guide][windows-guide]
|
||||||
- [:material-linux: Linux guide][linux-guide]
|
- [:material-linux: Linux guide][linux-guide]
|
||||||
- [:material-apple: Mac OS guide][macos-guide]
|
- [:material-apple: Mac OS guide][macos-guide]
|
||||||
|
- [:material-microsoft-windows: Windows (from source) guide][windows-source-guide]
|
||||||
|
|
||||||
In case you need any help, join our [Discord server][discord-server] where we may provide support.
|
In case you need any help, join our [Discord server][discord-server] where we may provide support.
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ If you're unsure whether something is an issue, ask in our support server first.
|
|||||||
[invite]: https://invite.nadeko.bot/
|
[invite]: https://invite.nadeko.bot/
|
||||||
[commands]: https://nadeko.bot/commands/
|
[commands]: https://nadeko.bot/commands/
|
||||||
[windows-guide]: ./guides/windows-guide.md
|
[windows-guide]: ./guides/windows-guide.md
|
||||||
|
[windows-source-guide]: ./guides/windows-source-guide.md
|
||||||
[linux-guide]: ./guides/linux-guide.md
|
[linux-guide]: ./guides/linux-guide.md
|
||||||
[macos-guide]: ./guides/osx-guide.md
|
[macos-guide]: ./guides/osx-guide.md
|
||||||
[from-source-guide]: ./guides/from-source.md
|
[from-source-guide]: ./guides/from-source.md
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net.Core" Version="3.15.3" />
|
<PackageReference Include="Discord.Net.Core" Version="3.16.0" />
|
||||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||||
<PackageReference Include="YamlDotNet" Version="15.1.4" />
|
<PackageReference Include="YamlDotNet" Version="15.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -19,4 +19,6 @@ public enum NotifyType
|
|||||||
{
|
{
|
||||||
LevelUp = 0,
|
LevelUp = 0,
|
||||||
Protection = 1, Prot = 1,
|
Protection = 1, Prot = 1,
|
||||||
|
AddRoleReward = 2,
|
||||||
|
RemoveRoleReward = 3,
|
||||||
}
|
}
|
@@ -164,13 +164,18 @@ public abstract class NadekoContext : DbContext
|
|||||||
|
|
||||||
#region UserBetStats
|
#region UserBetStats
|
||||||
|
|
||||||
modelBuilder.Entity<UserBetStats>()
|
modelBuilder.Entity<UserBetStats>(ubs =>
|
||||||
.HasIndex(x => new
|
{
|
||||||
{
|
ubs.HasIndex(x => new
|
||||||
x.UserId,
|
{
|
||||||
x.Game
|
x.UserId,
|
||||||
})
|
x.Game
|
||||||
.IsUnique();
|
})
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
ubs.HasIndex(x => x.MaxWin)
|
||||||
|
.IsUnique(false);
|
||||||
|
});
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@@ -5,6 +5,16 @@ namespace NadekoBot.Migrations;
|
|||||||
|
|
||||||
public static class MigrationQueries
|
public static class MigrationQueries
|
||||||
{
|
{
|
||||||
|
public static void MergeAwardedXp(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql("""
|
||||||
|
UPDATE UserXpStats
|
||||||
|
SET Xp = AwardedXp + Xp,
|
||||||
|
AwardedXp = 0
|
||||||
|
WHERE AwardedXp > 0;
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
public static void MigrateSar(MigrationBuilder migrationBuilder)
|
public static void MigrateSar(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.Sql("""
|
migrationBuilder.Sql("""
|
||||||
|
@@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace NadekoBot.Migrations.PostgreSql
|
namespace NadekoBot.Migrations.PostgreSql
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PostgreSqlContext))]
|
[DbContext(typeof(PostgreSqlContext))]
|
||||||
[Migration("20241205052146_awardedxp-temprole-notify")]
|
[Migration("20241207150050_awardedxp-temprole-notify")]
|
||||||
partial class awardedxptemprolenotify
|
partial class awardedxptemprolenotify
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -3485,6 +3485,9 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_userbetstats");
|
.HasName("pk_userbetstats");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin")
|
||||||
|
.HasDatabaseName("ix_userbetstats_maxwin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_userbetstats_userid_game");
|
.HasDatabaseName("ix_userbetstats_userid_game");
|
@@ -16,6 +16,8 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
name: "ix_userxpstats_awardedxp",
|
name: "ix_userxpstats_awardedxp",
|
||||||
table: "userxpstats");
|
table: "userxpstats");
|
||||||
|
|
||||||
|
MigrationQueries.MergeAwardedXp(migrationBuilder);
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(
|
||||||
name: "awardedxp",
|
name: "awardedxp",
|
||||||
table: "userxpstats");
|
table: "userxpstats");
|
||||||
@@ -59,6 +61,11 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
table.UniqueConstraint("ak_temprole_guildid_userid_roleid", x => new { x.guildid, x.userid, x.roleid });
|
table.UniqueConstraint("ak_temprole_guildid_userid_roleid", x => new { x.guildid, x.userid, x.roleid });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_userbetstats_maxwin",
|
||||||
|
table: "userbetstats",
|
||||||
|
column: "maxwin");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "ix_temprole_expiresat",
|
name: "ix_temprole_expiresat",
|
||||||
table: "temprole",
|
table: "temprole",
|
||||||
@@ -74,6 +81,10 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "temprole");
|
name: "temprole");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_userbetstats_maxwin",
|
||||||
|
table: "userbetstats");
|
||||||
|
|
||||||
migrationBuilder.AddColumn<long>(
|
migrationBuilder.AddColumn<long>(
|
||||||
name: "awardedxp",
|
name: "awardedxp",
|
||||||
table: "userxpstats",
|
table: "userxpstats",
|
@@ -3482,6 +3482,9 @@ namespace NadekoBot.Migrations.PostgreSql
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_userbetstats");
|
.HasName("pk_userbetstats");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin")
|
||||||
|
.HasDatabaseName("ix_userbetstats_maxwin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("ix_userbetstats_userid_game");
|
.HasDatabaseName("ix_userbetstats_userid_game");
|
||||||
|
@@ -11,7 +11,7 @@ using NadekoBot.Db;
|
|||||||
namespace NadekoBot.Migrations
|
namespace NadekoBot.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteContext))]
|
[DbContext(typeof(SqliteContext))]
|
||||||
[Migration("20241205052137_awardedxp-temprole-notify")]
|
[Migration("20241207150041_awardedxp-temprole-notify")]
|
||||||
partial class awardedxptemprolenotify
|
partial class awardedxptemprolenotify
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -2593,6 +2593,8 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
@@ -15,6 +15,7 @@ namespace NadekoBot.Migrations
|
|||||||
name: "IX_UserXpStats_AwardedXp",
|
name: "IX_UserXpStats_AwardedXp",
|
||||||
table: "UserXpStats");
|
table: "UserXpStats");
|
||||||
|
|
||||||
|
MigrationQueries.MergeAwardedXp(migrationBuilder);
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(
|
||||||
name: "AwardedXp",
|
name: "AwardedXp",
|
||||||
table: "UserXpStats");
|
table: "UserXpStats");
|
||||||
@@ -58,6 +59,11 @@ namespace NadekoBot.Migrations
|
|||||||
table.UniqueConstraint("AK_TempRole_GuildId_UserId_RoleId", x => new { x.GuildId, x.UserId, x.RoleId });
|
table.UniqueConstraint("AK_TempRole_GuildId_UserId_RoleId", x => new { x.GuildId, x.UserId, x.RoleId });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_UserBetStats_MaxWin",
|
||||||
|
table: "UserBetStats",
|
||||||
|
column: "MaxWin");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_TempRole_ExpiresAt",
|
name: "IX_TempRole_ExpiresAt",
|
||||||
table: "TempRole",
|
table: "TempRole",
|
||||||
@@ -73,6 +79,10 @@ namespace NadekoBot.Migrations
|
|||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "TempRole");
|
name: "TempRole");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_UserBetStats_MaxWin",
|
||||||
|
table: "UserBetStats");
|
||||||
|
|
||||||
migrationBuilder.AddColumn<long>(
|
migrationBuilder.AddColumn<long>(
|
||||||
name: "AwardedXp",
|
name: "AwardedXp",
|
||||||
table: "UserXpStats",
|
table: "UserXpStats",
|
@@ -2590,6 +2590,8 @@ namespace NadekoBot.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MaxWin");
|
||||||
|
|
||||||
b.HasIndex("UserId", "Game")
|
b.HasIndex("UserId", "Game")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
public record struct AddRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong UserId, long Level) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.reward.addrole";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.AddRoleReward;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var model = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.user%", g => g.GetUser(model.UserId)?.ToString() ?? model.UserId.ToString() },
|
||||||
|
{ "%event.role%", g => g.GetRole(model.RoleId)?.ToString() ?? model.RoleId.ToString() },
|
||||||
|
{ "%event.level%", g => model.Level.ToString() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -20,6 +20,7 @@ public record struct LevelUpNotifyModel(
|
|||||||
return new Dictionary<string, Func<SocketGuild, string>>()
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
{
|
{
|
||||||
{ "%event.level%", g => data.Level.ToString() },
|
{ "%event.level%", g => data.Level.ToString() },
|
||||||
|
{ "%event.user%", g => g.GetUser(data.UserId)?.ToString() ?? data.UserId.ToString() },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,11 +35,4 @@ public record struct LevelUpNotifyModel(
|
|||||||
userId = UserId;
|
userId = UserId;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static class INotifyModelExtensions
|
|
||||||
{
|
|
||||||
public static TypedKey<T> GetTypedKey<T>(this T model)
|
|
||||||
where T : struct, INotifyModel
|
|
||||||
=> new(T.KeyName);
|
|
||||||
}
|
}
|
@@ -0,0 +1,34 @@
|
|||||||
|
#nullable disable
|
||||||
|
using NadekoBot.Db.Models;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
|
public record struct ProtectionNotifyModel(ulong GuildId, ProtectionType ProtType, ulong UserId) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.protection";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.Protection;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var data = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.type%", g => data.ProtType.ToString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
namespace NadekoBot.Modules.Xp.Services;
|
||||||
|
|
||||||
|
public record struct RemoveRoleRewardNotifyModel(ulong GuildId, ulong RoleId, ulong UserId, long Level) : INotifyModel
|
||||||
|
{
|
||||||
|
public static string KeyName
|
||||||
|
=> "notify.reward.removerole";
|
||||||
|
|
||||||
|
public static NotifyType NotifyType
|
||||||
|
=> NotifyType.RemoveRoleReward;
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
||||||
|
{
|
||||||
|
var model = this;
|
||||||
|
return new Dictionary<string, Func<SocketGuild, string>>()
|
||||||
|
{
|
||||||
|
{ "%event.user%", g => g.GetUser(model.UserId)?.ToString() ?? model.UserId.ToString() },
|
||||||
|
{ "%event.role%", g => g.GetRole(model.RoleId)?.ToString() ?? model.RoleId.ToString() },
|
||||||
|
{ "%event.level%", g => model.Level.ToString() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetUserId(out ulong userId)
|
||||||
|
{
|
||||||
|
userId = UserId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetGuildId(out ulong guildId)
|
||||||
|
{
|
||||||
|
guildId = GuildId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration;
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
@@ -6,19 +7,108 @@ public partial class Administration
|
|||||||
{
|
{
|
||||||
public class NotifyCommands : NadekoModule<NotifyService>
|
public class NotifyCommands : NadekoModule<NotifyService>
|
||||||
{
|
{
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task Notify()
|
||||||
|
{
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.Items(Enum.GetValues<NotifyType>())
|
||||||
|
.PageSize(5)
|
||||||
|
.Page((items, page) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.notify_available));
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
eb.AddField(item.ToString(), GetText(GetDescription(item)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocStr GetDescription(NotifyType item)
|
||||||
|
=> item switch
|
||||||
|
{
|
||||||
|
NotifyType.LevelUp => strs.notify_desc_levelup,
|
||||||
|
NotifyType.Protection => strs.notify_desc_protection,
|
||||||
|
NotifyType.AddRoleReward => strs.notify_desc_addrolerew,
|
||||||
|
NotifyType.RemoveRoleReward => strs.notify_desc_removerolerew,
|
||||||
|
_ => strs.notify_desc_not_found
|
||||||
|
};
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
[OwnerOnly]
|
[OwnerOnly]
|
||||||
public async Task Notify(NotifyType nType, [Leftover] string? message = null)
|
public async Task Notify(NotifyType nType, [Leftover] string? message = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(message))
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
{
|
{
|
||||||
await _service.DisableAsync(ctx.Guild.Id, nType);
|
// show msg
|
||||||
await Response().Confirm(strs.notify_off(nType)).SendAsync();
|
var conf = await _service.GetNotifyAsync(ctx.Guild.Id, nType);
|
||||||
|
if (conf is null)
|
||||||
|
{
|
||||||
|
await Response().Confirm(strs.notify_msg_not_set).SendAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.WithTitle(GetText(strs.notify_msg))
|
||||||
|
.WithDescription(conf.Message.TrimTo(2048))
|
||||||
|
.AddField(GetText(strs.notify_type), conf.Type.ToString(), true)
|
||||||
|
.AddField(GetText(strs.channel),
|
||||||
|
$"""
|
||||||
|
<#{conf.ChannelId}>
|
||||||
|
`{conf.ChannelId}`
|
||||||
|
""",
|
||||||
|
true);
|
||||||
|
|
||||||
|
await Response().Embed(eb).SendAsync();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nType, message);
|
await _service.EnableAsync(ctx.Guild.Id, ctx.Channel.Id, nType, message);
|
||||||
await Response().Confirm(strs.notify_on(nType.ToString())).SendAsync();
|
await Response().Confirm(strs.notify_on($"<#{ctx.Channel.Id}>", Format.Bold(nType.ToString()))).SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task NotifyList(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var notifs = await _service.GetForGuildAsync(ctx.Guild.Id);
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var notif in notifs)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"""
|
||||||
|
- **{notif.Type}**
|
||||||
|
<#{notif.ChannelId}> `{notif.ChannelId}`
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifs.Count == 0)
|
||||||
|
sb.AppendLine(GetText(strs.notify_none));
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Confirm(GetText(strs.notify_list), text: sb.ToString())
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[OwnerOnly]
|
||||||
|
public async Task NotifyClear(NotifyType nType)
|
||||||
|
{
|
||||||
|
await _service.DisableAsync(ctx.Guild.Id, nType);
|
||||||
|
await Response().Confirm(strs.notify_off(nType)).SendAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
|
public static class NotifyModelExtensions
|
||||||
|
{
|
||||||
|
public static TypedKey<T> GetTypedKey<T>(this T model)
|
||||||
|
where T : struct, INotifyModel
|
||||||
|
=> new(T.KeyName);
|
||||||
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using NadekoBot.Common.ModuleBehaviors;
|
using NadekoBot.Common.ModuleBehaviors;
|
||||||
using NadekoBot.Db.Models;
|
using NadekoBot.Db.Models;
|
||||||
|
using NadekoBot.Generators;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Administration;
|
namespace NadekoBot.Modules.Administration;
|
||||||
|
|
||||||
@@ -199,4 +200,27 @@ public sealed class NotifyService : IReadyExecutor, INotifySubscriber, INService
|
|||||||
|
|
||||||
guildsDict.TryRemove(guildId, out _);
|
guildsDict.TryRemove(guildId, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyCollection<Notify>> GetForGuildAsync(ulong guildId, int page = 0)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(page);
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
var list = await ctx.GetTable<Notify>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.OrderBy(x => x.Type)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Notify?> GetNotifyAsync(ulong guildId, NotifyType nType)
|
||||||
|
{
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
return await ctx.GetTable<Notify>()
|
||||||
|
.Where(x => x.GuildId == guildId && x.Type == nType)
|
||||||
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -5,36 +5,6 @@ using System.Threading.Channels;
|
|||||||
|
|
||||||
namespace NadekoBot.Modules.Administration.Services;
|
namespace NadekoBot.Modules.Administration.Services;
|
||||||
|
|
||||||
public record struct ProtectionNotifyModel(ulong GuildId, ProtectionType ProtType, ulong UserId) : INotifyModel
|
|
||||||
{
|
|
||||||
public static string KeyName
|
|
||||||
=> "notify.protection";
|
|
||||||
|
|
||||||
public static NotifyType NotifyType
|
|
||||||
=> NotifyType.Protection;
|
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, Func<SocketGuild, string>> GetReplacements()
|
|
||||||
{
|
|
||||||
var data = this;
|
|
||||||
return new Dictionary<string, Func<SocketGuild, string>>()
|
|
||||||
{
|
|
||||||
{ "%event.type%", g => data.ProtType.ToString() },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetUserId(out ulong userId)
|
|
||||||
{
|
|
||||||
userId = UserId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetGuildId(out ulong guildId)
|
|
||||||
{
|
|
||||||
guildId = GuildId;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ProtectionService : INService
|
public class ProtectionService : INService
|
||||||
{
|
{
|
||||||
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate
|
public event Func<PunishmentAction, ProtectionType, IGuildUser[], Task> OnAntiProtectionTriggered = delegate
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using NadekoBot.Modules.Gambling.Common;
|
using NadekoBot.Modules.Gambling.Common;
|
||||||
using NadekoBot.Modules.Gambling.Services;
|
using NadekoBot.Modules.Gambling.Services;
|
||||||
|
using NadekoBot.Modules.Xp.Services;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Gambling;
|
namespace NadekoBot.Modules.Gambling;
|
||||||
|
|
||||||
@@ -10,13 +11,19 @@ public partial class Gambling
|
|||||||
public sealed class BetStatsCommands : GamblingModule<UserBetStatsService>
|
public sealed class BetStatsCommands : GamblingModule<UserBetStatsService>
|
||||||
{
|
{
|
||||||
private readonly GamblingTxTracker _gamblingTxTracker;
|
private readonly GamblingTxTracker _gamblingTxTracker;
|
||||||
|
private readonly IBotCache _cache;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public BetStatsCommands(
|
public BetStatsCommands(
|
||||||
GamblingTxTracker gamblingTxTracker,
|
GamblingTxTracker gamblingTxTracker,
|
||||||
GamblingConfigService gcs)
|
GamblingConfigService gcs,
|
||||||
|
IBotCache cache,
|
||||||
|
IUserService userService)
|
||||||
: base(gcs)
|
: base(gcs)
|
||||||
{
|
{
|
||||||
_gamblingTxTracker = gamblingTxTracker;
|
_gamblingTxTracker = gamblingTxTracker;
|
||||||
|
_cache = cache;
|
||||||
|
_userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
@@ -25,12 +32,12 @@ public partial class Gambling
|
|||||||
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
|
var price = await _service.GetResetStatsPriceAsync(ctx.User.Id, game);
|
||||||
|
|
||||||
var result = await PromptUserConfirmAsync(CreateEmbed()
|
var result = await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
$"""
|
$"""
|
||||||
Are you sure you want to reset your bet stats for **{GetGameName(game)}**?
|
Are you sure you want to reset your bet stats for **{GetGameName(game)}**?
|
||||||
|
|
||||||
It will cost you {N(price)}
|
It will cost you {N(price)}
|
||||||
"""));
|
"""));
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return;
|
return;
|
||||||
@@ -88,15 +95,15 @@ public partial class Gambling
|
|||||||
};
|
};
|
||||||
|
|
||||||
var eb = CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithAuthor(user)
|
.WithAuthor(user)
|
||||||
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
|
.AddField("Total Won", N(stats.Sum(x => x.PaidOut)), true)
|
||||||
.AddField("Biggest Win", N(stats.Max(x => x.MaxWin)), true)
|
.AddField("Biggest Win", N(stats.Max(x => x.MaxWin)), true)
|
||||||
.AddField("Biggest Bet", N(stats.Max(x => x.MaxBet)), true)
|
.AddField("Biggest Bet", N(stats.Max(x => x.MaxBet)), true)
|
||||||
.AddField("# Bets", stats.Sum(x => x.WinCount + x.LoseCount), true)
|
.AddField("# Bets", stats.Sum(x => x.WinCount + x.LoseCount), true)
|
||||||
.AddField("Payout",
|
.AddField("Payout",
|
||||||
(stats.Sum(x => x.PaidOut) / stats.Sum(x => x.TotalBet)).ToString("P2", Culture),
|
(stats.Sum(x => x.PaidOut) / stats.Sum(x => x.TotalBet)).ToString("P2", Culture),
|
||||||
true);
|
true);
|
||||||
if (game == null)
|
if (game == null)
|
||||||
{
|
{
|
||||||
var favGame = stats.MaxBy(x => x.WinCount + x.LoseCount);
|
var favGame = stats.MaxBy(x => x.WinCount + x.LoseCount);
|
||||||
@@ -115,13 +122,75 @@ public partial class Gambling
|
|||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly record struct WinLbStat(
|
||||||
|
int Rank,
|
||||||
|
string User,
|
||||||
|
GamblingGame Game,
|
||||||
|
long MaxWin);
|
||||||
|
|
||||||
|
private TypedKey<List<WinLbStat>> GetWinLbKey(int page)
|
||||||
|
=> new($"winlb:{page}");
|
||||||
|
|
||||||
|
private async Task<IReadOnlyCollection<WinLbStat>> GetCachedWinLbAsync(int page)
|
||||||
|
{
|
||||||
|
return await _cache.GetOrAddAsync(GetWinLbKey(page),
|
||||||
|
async () =>
|
||||||
|
{
|
||||||
|
var items = await _service.GetWinLbAsync(page);
|
||||||
|
|
||||||
|
if (items.Count == 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var outputItems = new List<WinLbStat>(items.Count);
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var x = items[i];
|
||||||
|
var user = (await ctx.Client.GetUserAsync(x.UserId, CacheMode.CacheOnly))?.ToString()
|
||||||
|
?? (await _userService.GetUserAsync(x.UserId))?.Username
|
||||||
|
?? x.UserId.ToString();
|
||||||
|
|
||||||
|
outputItems.Add(new WinLbStat(i + 1 + (page * 10), user, x.Game, x.MaxWin));
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputItems;
|
||||||
|
},
|
||||||
|
expiry: TimeSpan.FromMinutes(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
public async Task WinLb(int page = 1)
|
||||||
|
{
|
||||||
|
if (--page < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Paginated()
|
||||||
|
.PageItems(p => GetCachedWinLbAsync(p))
|
||||||
|
.PageSize(10)
|
||||||
|
.Page((items, curPage) =>
|
||||||
|
{
|
||||||
|
var eb = CreateEmbed()
|
||||||
|
.WithOkColor();
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
var item = items[i];
|
||||||
|
eb.AddField($"#{item.Rank} {item.User}",
|
||||||
|
$"[{item.Game}]{N(item.MaxWin)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return eb;
|
||||||
|
})
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task GambleStats()
|
public async Task GambleStats()
|
||||||
{
|
{
|
||||||
var stats = await _gamblingTxTracker.GetAllAsync();
|
var stats = await _gamblingTxTracker.GetAllAsync();
|
||||||
|
|
||||||
var eb = CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
var str = "` Feature `|` Bet `|`Paid Out`|` RoI `\n";
|
||||||
str += "――――――――――――――――――――\n";
|
str += "――――――――――――――――――――\n";
|
||||||
@@ -157,13 +226,13 @@ public partial class Gambling
|
|||||||
public async Task GambleStatsReset()
|
public async Task GambleStatsReset()
|
||||||
{
|
{
|
||||||
if (!await PromptUserConfirmAsync(CreateEmbed()
|
if (!await PromptUserConfirmAsync(CreateEmbed()
|
||||||
.WithDescription(
|
.WithDescription(
|
||||||
"""
|
"""
|
||||||
Are you sure?
|
Are you sure?
|
||||||
This will completely reset Gambling Stats.
|
This will completely reset Gambling Stats.
|
||||||
|
|
||||||
This action is irreversible.
|
This action is irreversible.
|
||||||
""")))
|
""")))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await GambleStats();
|
await GambleStats();
|
||||||
|
@@ -42,7 +42,7 @@ public sealed class UserBetStatsService : INService
|
|||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
await ctx.GetTable<UserBetStats>()
|
await ctx.GetTable<UserBetStats>()
|
||||||
.DeleteAsync(x => x.UserId == userId && (game == null || x.Game == game));
|
.DeleteAsync(x => x.UserId == userId && (game == null || x.Game == game));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,4 +52,16 @@ public sealed class UserBetStatsService : INService
|
|||||||
await ctx.GetTable<GamblingStats>()
|
await ctx.GetTable<GamblingStats>()
|
||||||
.DeleteAsync();
|
.DeleteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IReadOnlyList<UserBetStats>> GetWinLbAsync(int page)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNegative(page);
|
||||||
|
|
||||||
|
await using var ctx = _db.GetDbContext();
|
||||||
|
return await ctx.GetTable<UserBetStats>()
|
||||||
|
.OrderByDescending(x => x.MaxWin)
|
||||||
|
.Skip(page * 10)
|
||||||
|
.Take(10)
|
||||||
|
.ToArrayAsyncLinqToDB();
|
||||||
|
}
|
||||||
}
|
}
|
@@ -103,11 +103,11 @@ public partial class Searches : NadekoModule<SearchesService>
|
|||||||
}
|
}
|
||||||
|
|
||||||
var eb = CreateEmbed()
|
var eb = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(GetText(strs.time_new))
|
.WithTitle(GetText(strs.time_new))
|
||||||
.WithDescription(Format.Code(data.Time.ToString(Culture)))
|
.WithDescription(Format.Code(data.Time.ToString(Culture)))
|
||||||
.AddField(GetText(strs.location), string.Join('\n', data.Address.Split(", ")), true)
|
.AddField(GetText(strs.location), string.Join('\n', data.Address.Split(", ")), true)
|
||||||
.AddField(GetText(strs.timezone), data.TimeZoneName, true);
|
.AddField(GetText(strs.timezone), data.TimeZoneName, true);
|
||||||
|
|
||||||
await Response().Embed(eb).SendAsync();
|
await Response().Embed(eb).SendAsync();
|
||||||
}
|
}
|
||||||
@@ -129,16 +129,16 @@ public partial class Searches : NadekoModule<SearchesService>
|
|||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(movie.Title)
|
.WithTitle(movie.Title)
|
||||||
.WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/")
|
.WithUrl($"https://www.imdb.com/title/{movie.ImdbId}/")
|
||||||
.WithDescription(movie.Plot.TrimTo(1000))
|
.WithDescription(movie.Plot.TrimTo(1000))
|
||||||
.AddField("Rating", movie.ImdbRating, true)
|
.AddField("Rating", movie.ImdbRating, true)
|
||||||
.AddField("Genre", movie.Genre, true)
|
.AddField("Genre", movie.Genre, true)
|
||||||
.AddField("Year", movie.Year, true)
|
.AddField("Year", movie.Year, true)
|
||||||
.WithImageUrl(Uri.IsWellFormedUriString(movie.Poster, UriKind.Absolute)
|
.WithImageUrl(Uri.IsWellFormedUriString(movie.Poster, UriKind.Absolute)
|
||||||
? movie.Poster
|
? movie.Poster
|
||||||
: null))
|
: null))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,9 +191,9 @@ public partial class Searches : NadekoModule<SearchesService>
|
|||||||
|
|
||||||
await Response()
|
await Response()
|
||||||
.Embed(CreateEmbed()
|
.Embed(CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.AddField(GetText(strs.original_url), $"<{query}>")
|
.AddField(GetText(strs.original_url), $"<{query}>")
|
||||||
.AddField(GetText(strs.short_url), $"<{shortLink}>"))
|
.AddField(GetText(strs.short_url), $"<{shortLink}>"))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,13 +214,13 @@ public partial class Searches : NadekoModule<SearchesService>
|
|||||||
}
|
}
|
||||||
|
|
||||||
var embed = CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithTitle(card.Name)
|
.WithTitle(card.Name)
|
||||||
.WithDescription(card.Description)
|
.WithDescription(card.Description)
|
||||||
.WithImageUrl(card.ImageUrl)
|
.WithImageUrl(card.ImageUrl)
|
||||||
.AddField(GetText(strs.store_url), card.StoreUrl, true)
|
.AddField(GetText(strs.store_url), card.StoreUrl, true)
|
||||||
.AddField(GetText(strs.cost), card.ManaCost, true)
|
.AddField(GetText(strs.cost), card.ManaCost, true)
|
||||||
.AddField(GetText(strs.types), card.Types, true);
|
.AddField(GetText(strs.types), card.Types, true);
|
||||||
|
|
||||||
await Response().Embed(embed).SendAsync();
|
await Response().Embed(embed).SendAsync();
|
||||||
}
|
}
|
||||||
@@ -281,10 +281,10 @@ public partial class Searches : NadekoModule<SearchesService>
|
|||||||
{
|
{
|
||||||
var item = items[0];
|
var item = items[0];
|
||||||
return CreateEmbed()
|
return CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.WithUrl(item.Permalink)
|
.WithUrl(item.Permalink)
|
||||||
.WithTitle(item.Word)
|
.WithTitle(item.Word)
|
||||||
.WithDescription(item.Definition);
|
.WithDescription(item.Definition);
|
||||||
})
|
})
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
@@ -312,11 +312,11 @@ public partial class Searches : NadekoModule<SearchesService>
|
|||||||
{
|
{
|
||||||
var model = items.First();
|
var model = items.First();
|
||||||
var embed = CreateEmbed()
|
var embed = CreateEmbed()
|
||||||
.WithDescription(ctx.User.Mention)
|
.WithDescription(ctx.User.Mention)
|
||||||
.AddField(GetText(strs.word), model.Word, true)
|
.AddField(GetText(strs.word), model.Word, true)
|
||||||
.AddField(GetText(strs._class), model.WordType, true)
|
.AddField(GetText(strs._class), model.WordType, true)
|
||||||
.AddField(GetText(strs.definition), model.Definition)
|
.AddField(GetText(strs.definition), model.Definition)
|
||||||
.WithOkColor();
|
.WithOkColor();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(model.Example))
|
if (!string.IsNullOrWhiteSpace(model.Example))
|
||||||
embed.AddField(GetText(strs.example), model.Example);
|
embed.AddField(GetText(strs.example), model.Example);
|
||||||
@@ -404,10 +404,28 @@ public partial class Searches : NadekoModule<SearchesService>
|
|||||||
await Response()
|
await Response()
|
||||||
.Embed(
|
.Embed(
|
||||||
CreateEmbed()
|
CreateEmbed()
|
||||||
.WithOkColor()
|
.WithOkColor()
|
||||||
.AddField("Username", usr.ToString())
|
.AddField("Username", usr.ToString())
|
||||||
.AddField("Avatar Url", avatarUrl)
|
.AddField("Avatar Url", avatarUrl)
|
||||||
.WithThumbnailUrl(avatarUrl.ToString()))
|
.WithThumbnailUrl(avatarUrl.ToString()))
|
||||||
|
.SendAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Cmd]
|
||||||
|
[RequireContext(ContextType.Guild)]
|
||||||
|
public async Task Banner([Leftover] IGuildUser? usr = null)
|
||||||
|
{
|
||||||
|
usr ??= (IGuildUser)ctx.User;
|
||||||
|
|
||||||
|
var bannerUrl = usr.GetGuildBannerUrl();
|
||||||
|
|
||||||
|
await Response()
|
||||||
|
.Embed(
|
||||||
|
CreateEmbed()
|
||||||
|
.WithOkColor()
|
||||||
|
.AddField("Username", usr.ToString())
|
||||||
|
.AddField("Banner Url", bannerUrl)
|
||||||
|
.WithThumbnailUrl(bannerUrl))
|
||||||
.SendAsync();
|
.SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -344,9 +344,45 @@ public class XpService : INService, IReadyExecutor, IExecNoCommand
|
|||||||
if (role is not null && user is not null)
|
if (role is not null && user is not null)
|
||||||
{
|
{
|
||||||
if (rrew.Remove)
|
if (rrew.Remove)
|
||||||
_ = user.RemoveRoleAsync(role);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await user.RemoveRoleAsync(role);
|
||||||
|
await _notifySub.NotifyAsync(new RemoveRoleRewardNotifyModel(guild.Id,
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
newLevel),
|
||||||
|
isShardLocal: true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex,
|
||||||
|
"Unable to remove role {RoleId} from user {UserId}: {Message}",
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
_ = user.AddRoleAsync(role);
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await user.AddRoleAsync(role);
|
||||||
|
await _notifySub.NotifyAsync(new AddRoleRewardNotifyModel(guild.Id,
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
newLevel),
|
||||||
|
isShardLocal: true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex,
|
||||||
|
"Unable to add role {RoleId} to user {UserId}: {Message}",
|
||||||
|
role.Id,
|
||||||
|
user.Id,
|
||||||
|
ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/>
|
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.6"/>
|
||||||
<PackageReference Include="CommandLineParser" Version="2.9.1"/>
|
<PackageReference Include="CommandLineParser" Version="2.9.1"/>
|
||||||
<PackageReference Include="Discord.Net" Version="3.15.3"/>
|
<PackageReference Include="Discord.Net" Version="3.16.0" />
|
||||||
<PackageReference Include="CoreCLR-NCalc" Version="3.1.246"/>
|
<PackageReference Include="CoreCLR-NCalc" Version="3.1.246"/>
|
||||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/>
|
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/>
|
||||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414"/>
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.68.0.3414"/>
|
||||||
|
@@ -157,6 +157,9 @@ public sealed class DoAsUserMessage : IUserMessage
|
|||||||
public MessageCallData? CallData
|
public MessageCallData? CallData
|
||||||
=> _msg.CallData;
|
=> _msg.CallData;
|
||||||
|
|
||||||
|
public IReadOnlyCollection<MessageSnapshot> ForwardedMessages
|
||||||
|
=> _msg.ForwardedMessages;
|
||||||
|
|
||||||
public Task ModifyAsync(Action<MessageProperties> func, RequestOptions? options = null)
|
public Task ModifyAsync(Action<MessageProperties> func, RequestOptions? options = null)
|
||||||
{
|
{
|
||||||
return _msg.ModifyAsync(func, options);
|
return _msg.ModifyAsync(func, options);
|
||||||
|
@@ -12,7 +12,7 @@ public sealed class UserService : IUserService, INService
|
|||||||
_db = db;
|
_db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<DiscordUser> GetUserAsync(ulong userId)
|
public async Task<DiscordUser?> GetUserAsync(ulong userId)
|
||||||
{
|
{
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var user = await uow
|
var user = await uow
|
||||||
|
@@ -715,6 +715,8 @@ color:
|
|||||||
avatar:
|
avatar:
|
||||||
- avatar
|
- avatar
|
||||||
- av
|
- av
|
||||||
|
banner:
|
||||||
|
- banner
|
||||||
translate:
|
translate:
|
||||||
- translate
|
- translate
|
||||||
- trans
|
- trans
|
||||||
@@ -1549,4 +1551,15 @@ temprole:
|
|||||||
- temprole
|
- temprole
|
||||||
notify:
|
notify:
|
||||||
- notify
|
- notify
|
||||||
- nfy
|
- nfy
|
||||||
|
notifylist:
|
||||||
|
- notifylist
|
||||||
|
- notifyl
|
||||||
|
notifyclear:
|
||||||
|
- notifyclear
|
||||||
|
- notifyremove
|
||||||
|
- notifyrm
|
||||||
|
- notifclr
|
||||||
|
winlb:
|
||||||
|
- winlb
|
||||||
|
- wins
|
@@ -2170,6 +2170,13 @@ avatar:
|
|||||||
params:
|
params:
|
||||||
- usr:
|
- usr:
|
||||||
desc: "The user whose avatar is being displayed."
|
desc: "The user whose avatar is being displayed."
|
||||||
|
banner:
|
||||||
|
desc: Shows a mentioned person's banner.
|
||||||
|
ex:
|
||||||
|
- '@Someone'
|
||||||
|
params:
|
||||||
|
- usr:
|
||||||
|
desc: "The user whose banner is being displayed."
|
||||||
translate:
|
translate:
|
||||||
desc: Translates text from the given language to the destination language.
|
desc: Translates text from the given language to the destination language.
|
||||||
ex:
|
ex:
|
||||||
@@ -4857,10 +4864,38 @@ minesweeper:
|
|||||||
notify:
|
notify:
|
||||||
desc: |-
|
desc: |-
|
||||||
Sends a message to the current channel once the specified event occurs.
|
Sends a message to the current channel once the specified event occurs.
|
||||||
|
Provide no parameters to see all available events.
|
||||||
ex:
|
ex:
|
||||||
- 'levelup Congratulations to user %user.name% for reaching level %event.level%'
|
- 'levelup Congratulations to user %user.name% for reaching level %event.level%'
|
||||||
params:
|
params:
|
||||||
|
- { }
|
||||||
- event:
|
- event:
|
||||||
desc: "The event to notify on."
|
desc: "The event to notify on."
|
||||||
- message:
|
- event:
|
||||||
desc: "The message to send."
|
desc: "The event to notify on."
|
||||||
|
message:
|
||||||
|
desc: "The message to send."
|
||||||
|
notifylist:
|
||||||
|
desc: |-
|
||||||
|
Lists all active notifications in this server.
|
||||||
|
ex:
|
||||||
|
- ''
|
||||||
|
params:
|
||||||
|
- { }
|
||||||
|
notifyclear:
|
||||||
|
desc: |-
|
||||||
|
Removes the specified notify event.
|
||||||
|
ex:
|
||||||
|
- 'levelup'
|
||||||
|
params:
|
||||||
|
- event:
|
||||||
|
desc: "The notify event to clear."
|
||||||
|
winlb:
|
||||||
|
desc: |-
|
||||||
|
Shows the biggest wins leaderboard
|
||||||
|
ex:
|
||||||
|
- ''
|
||||||
|
- '5'
|
||||||
|
params:
|
||||||
|
- page:
|
||||||
|
desc: "The optional page to display."
|
@@ -1087,7 +1087,7 @@
|
|||||||
"giveaway_starting": "Starting giveaway...",
|
"giveaway_starting": "Starting giveaway...",
|
||||||
"winner": "Winner",
|
"winner": "Winner",
|
||||||
"giveaway_list": "List of active giveways",
|
"giveaway_list": "List of active giveways",
|
||||||
"todo_list_empty": "Your todo list is empty." ,
|
"todo_list_empty": "Your todo list is empty.",
|
||||||
"todo_list": "Todo List",
|
"todo_list": "Todo List",
|
||||||
"todo_stats": "{0} items | {1} completed | {2} remaining",
|
"todo_stats": "{0} items | {1} completed | {2} remaining",
|
||||||
"todo_add_max_limit": "You'reached the maximum amount of todos you can have.",
|
"todo_add_max_limit": "You'reached the maximum amount of todos you can have.",
|
||||||
@@ -1102,7 +1102,7 @@
|
|||||||
"search_results": "Search results",
|
"search_results": "Search results",
|
||||||
"queue_search_results": "Type the number of the search result to queue up that track.",
|
"queue_search_results": "Type the number of the search result to queue up that track.",
|
||||||
"overloads": "Overloads",
|
"overloads": "Overloads",
|
||||||
"honeypot_on": "Honeypot enabled on this channel." ,
|
"honeypot_on": "Honeypot enabled on this channel.",
|
||||||
"honeypot_off": "Honeypot disabled.",
|
"honeypot_off": "Honeypot disabled.",
|
||||||
"afk_set": "AFK message set. Type a message in any channel to clear.",
|
"afk_set": "AFK message set. Type a message in any channel to clear.",
|
||||||
"rero_message_not_found": "The specified message wasn't found. Make sure you've specified the message from this channel.",
|
"rero_message_not_found": "The specified message wasn't found. Make sure you've specified the message from this channel.",
|
||||||
@@ -1115,7 +1115,7 @@
|
|||||||
"nc_hint": "Use `{0}nczoom x y` command to zoom in. Image is {1}x{2} pixels.",
|
"nc_hint": "Use `{0}nczoom x y` command to zoom in. Image is {1}x{2} pixels.",
|
||||||
"nc_insuff_payment": "Invalid payment.",
|
"nc_insuff_payment": "Invalid payment.",
|
||||||
"invalid_img_size": "Image must to be {0}x{1} pixels.",
|
"invalid_img_size": "Image must to be {0}x{1} pixels.",
|
||||||
"no_attach_found": "No attachment found. Please send the image along with this command." ,
|
"no_attach_found": "No attachment found. Please send the image along with this command.",
|
||||||
"trfl_enabled": "Flag translation enabled on this channel. Reacting to a message with a flag will translate it to that language.",
|
"trfl_enabled": "Flag translation enabled on this channel. Reacting to a message with a flag will translate it to that language.",
|
||||||
"trfl_disabled": "Flag translation disabled.",
|
"trfl_disabled": "Flag translation disabled.",
|
||||||
"rakeback_claimed": "You've claimed {0} as rakeback!",
|
"rakeback_claimed": "You've claimed {0} as rakeback!",
|
||||||
@@ -1125,10 +1125,10 @@
|
|||||||
"self_assign_group_role_req": "Users can now self-assign a role from group {0} only if they have {1} role.",
|
"self_assign_group_role_req": "Users can now self-assign a role from group {0} only if they have {1} role.",
|
||||||
"sar_group_not_found": "Group with that number doesn't exist.",
|
"sar_group_not_found": "Group with that number doesn't exist.",
|
||||||
"sar_group_deleted": "Group {0} deleted.",
|
"sar_group_deleted": "Group {0} deleted.",
|
||||||
"choose_one": "Choose one" ,
|
"choose_one": "Choose one",
|
||||||
"requires_role": "Requires role: {0}",
|
"requires_role": "Requires role: {0}",
|
||||||
"invalid_message_id": "Invalid Message Id.",
|
"invalid_message_id": "Invalid Message Id.",
|
||||||
"invalid_message_link": "The message link must be from this server.",
|
"invalid_message_link": "The message link must be this Bot's message. The bot can't add buttons to other users' messages.",
|
||||||
"btnrole_message_max": "Limit reached. You may have up to 25 button roles per message.",
|
"btnrole_message_max": "Limit reached. You may have up to 25 button roles per message.",
|
||||||
"btnrole_not_found": "No button role found on that message.",
|
"btnrole_not_found": "No button role found on that message.",
|
||||||
"btnrole_none": "There are no button roles on this page.",
|
"btnrole_none": "There are no button roles on this page.",
|
||||||
@@ -1145,6 +1145,17 @@
|
|||||||
"level_set": "Level of user {0} set to {1} on this server.",
|
"level_set": "Level of user {0} set to {1} on this server.",
|
||||||
"temp_role_added": "User {0} has been given {1} role temporarily. The role expires {2}",
|
"temp_role_added": "User {0} has been given {1} role temporarily. The role expires {2}",
|
||||||
"user_afk": "User {0} is AFK.",
|
"user_afk": "User {0} is AFK.",
|
||||||
"notify_on":"Notification message will be sent on this channel when {0} event triggers.",
|
"notify_on": "Notification message will be sent in {0} channel when {1} event triggers.",
|
||||||
"notify_off":"Notification message will no longer be sent when {0} event triggers."
|
"notify_off": "Notification message will no longer be sent when {0} event triggers.",
|
||||||
|
"notify_none": "No notifications on this page.",
|
||||||
|
"notify_msg_not_set": "Notification message is not set for this event.",
|
||||||
|
"notify_list": "Notify List",
|
||||||
|
"notify_type": "Type",
|
||||||
|
"notify_msg": "Notify Message",
|
||||||
|
"notify_available": "List of available notify events",
|
||||||
|
"notify_desc_levelup": "Triggers when a user levels up on this server.",
|
||||||
|
"notify_desc_protection": "Triggers when antialt, antispam or antiraid is triggered.",
|
||||||
|
"notify_desc_addrolerew": "Triggers when a user gets a role as a reward for reaching a level (xprew).",
|
||||||
|
"notify_desc_removerolerew": "Triggers when a user loses a role as a reward for reaching a level (xprew).",
|
||||||
|
"notify_desc_not_found": "No description found for this notify event. Please report this."
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user