Merge pull request #1031 from Kwoth/dev

1.1.5a
This commit is contained in:
Master Kwoth 2017-02-01 08:07:50 +01:00 committed by GitHub
commit 65bcda21c1
71 changed files with 3389 additions and 441 deletions

@ -1 +1 @@
Subproject commit 58766448d79ac9adec228f341f258aa262a3f278 Subproject commit 80384323790471d254c7db5c237a49dc62624378

View File

@ -2,5 +2,6 @@
<configuration> <configuration>
<packageSources> <packageSources>
<add key="ImageSharp" value="https://www.myget.org/F/imagesharp/api/v3/index.json" /> <add key="ImageSharp" value="https://www.myget.org/F/imagesharp/api/v3/index.json" />
<add key="Kwoth" value="https://www.myget.org/F/kwoth/api/v3/index.json" />
</packageSources> </packageSources>
</configuration> </configuration>

View File

@ -125,6 +125,14 @@ Command and aliases | Description | Usage
### Gambling ### Gambling
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`$claimwaifu` `$claim` | Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `$affinity` towards you. | `$claim 50 @Himesama`
`$divorce` | Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. | `$divorce @CheatingSloot`
`$affinity` | Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `$claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. | `$affinity @MyHusband` or `$affinity`
`$waifus` `$waifulb` | Shows top 9 waifus. | `$waifus`
`$waifuinfo` `$waifustats` | Shows waifu stats for a target person. Defaults to you if no user is provided. | `$waifuinfo @MyCrush` or `$waifuinfo`
`$slotstats` | Shows the total stats of the slot command for this bot's session. **Bot Owner only.** | `$slotstats`
`$slottest` | Tests to see how much slots payout for X number of plays. **Bot Owner only.** | `$slottest 1000`
`$slot` | Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. | `$slot 5`
`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3` `$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`
`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t` `$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t`
`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` `$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5`
@ -132,6 +140,7 @@ Command and aliases | Description | Usage
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF` `$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF`
`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` `$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5`
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` `$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
`$startevent` | Starts one of the events seen on public nadeko. **Bot Owner only.** | `$startevent flowerreaction`
`$race` | Starts a new animal race. | `$race` `$race` | Starts a new animal race. | `$race`
`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` `$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName` `$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName`
@ -206,7 +215,6 @@ Command and aliases | Description | Usage
`!!localplaylst` `!!lopl` | Queues all songs from a directory. **Bot Owner only.** | `!!lopl C:/music/classical` `!!localplaylst` `!!lopl` | Queues all songs from a directory. **Bot Owner only.** | `!!lopl C:/music/classical`
`!!radio` `!!ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) | `!!ra radio link here` `!!radio` `!!ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) | `!!ra radio link here`
`!!local` `!!lo` | Queues a local file by specifying a full path. **Bot Owner only.** | `!!lo C:/music/mysong.mp3` `!!local` `!!lo` | Queues a local file by specifying a full path. **Bot Owner only.** | `!!lo C:/music/mysong.mp3`
`!!move` `!!mv` | Moves the bot to your voice channel. (works only if music is already playing) | `!!mv`
`!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5` `!!remove` `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5`
`!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3` `!!movesong` `!!ms` | Moves a song from one position to another. | `!!ms 5>3`
`!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq` `!!setmaxqueue` `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq 50` or `!!smq`
@ -226,7 +234,7 @@ Command and aliases | Description | Usage
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri` `~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri`
`~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. | `~autohentai 30 yuri|tail|long_hair` or `~autohentai` `~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. **Requires ManageMessages channel permission.** | `~autohentai 30 yuri|tail|long_hair` or `~autohentai`
`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri` `~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri`
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing` `~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing`
`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2` `~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2`
@ -249,6 +257,7 @@ Command and aliases | Description | Usage
`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw` `;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw`
`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop` `;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop`
`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw` `;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw`
`;cmdcosts` | Shows a list of command costs. Paginated with 9 command per page. | `;cmdcosts` or `;cmdcosts 2`
`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5` `;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5`
`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds` `;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds`
`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313` `;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313`

View File

@ -1,5 +1,5 @@
@ECHO off @ECHO off
TITLE Downloading NadekoBot, please wait TITLE Downloading Latest Build of NadekoBot...
::Setting convenient to read variables which don't delete the windows temp folder ::Setting convenient to read variables which don't delete the windows temp folder
SET root=%~dp0 SET root=%~dp0
CD /D %root% CD /D %root%
@ -24,30 +24,43 @@ ECHO Downloading Nadeko...
ECHO. ECHO.
git clone -b dev --recursive --depth 1 --progress https://github.com/Kwoth/NadekoBot.git >nul git clone -b dev --recursive --depth 1 --progress https://github.com/Kwoth/NadekoBot.git >nul
IF %ERRORLEVEL% EQU 128 (GOTO :giterror) IF %ERRORLEVEL% EQU 128 (GOTO :giterror)
TITLE Installing NadekoBot, please wait TITLE Installing NadekoBot, please wait...
ECHO. ECHO.
ECHO Installing... ECHO Installing Discord.Net(1/4)...
::Building Nadeko ::Building Nadeko
CD /D %build1% CD /D %build1%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO Installing Discord.Net(2/4)...
CD /D %build2% CD /D %build2%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO Installing Discord.Net(3/4)...
CD /D %build3% CD /D %build3%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO Installing Discord.Net(4/4)...
CD /D %build4% CD /D %build4%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO.
ECHO Discord.Net installation completed successfully...
ECHO.
ECHO Installing NadekoBot...
CD /D %build5% CD /D %build5%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
dotnet build --configuration Release >nul 2>&1 dotnet build --configuration Release >nul 2>&1
ECHO.
ECHO NadekoBot installation completed successfully...
::Attempts to backup old files if they currently exist in the same folder as the batch file ::Attempts to backup old files if they currently exist in the same folder as the batch file
IF EXIST "%root%NadekoBot\" (GOTO :backupinstall) IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
:freshinstall :freshinstall
::Moves the NadekoBot folder to keep things tidy ::Moves the NadekoBot folder to keep things tidy
ECHO.
ECHO Moving files, Please wait...
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1 ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror) IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
:backupinstall :backupinstall
TITLE Backing up old files TITLE Backing up old files...
ECHO.
ECHO Moving and Backing up old files...
::Recursively copies all files and folders from NadekoBot to NadekoBot_Old ::Recursively copies all files and folders from NadekoBot to NadekoBot_Old
ROBOCOPY "%root%NadekoBot" "%root%NadekoBot_Old" /MIR >nul 2>&1 ROBOCOPY "%root%NadekoBot" "%root%NadekoBot_Old" /MIR >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror) IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
@ -70,7 +83,7 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
RMDIR "%root%NadekoBot\" /S /Q >nul 2>&1 RMDIR "%root%NadekoBot\" /S /Q >nul 2>&1
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1 ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror) IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
:dotnet :dotnet
::Terminates the batch script if it can't run dotnet --version ::Terminates the batch script if it can't run dotnet --version
TITLE Error! TITLE Error!
@ -102,6 +115,25 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
PAUSE >nul 2>&1 PAUSE >nul 2>&1
CD /D "%root%" CD /D "%root%"
GOTO :EOF GOTO :EOF
:64BIT
ECHO.
ECHO Your System Architecture is 64bit...
GOTO end
:32BIT
ECHO.
ECHO Your System Architecture is 32bit...
timeout /t 5
ECHO.
ECHO Downloading libsodium.dll and opus.dll...
SET "FILENAME=%~dp0\NadekoBot\src\NadekoBot\libsodium.dll"
bitsadmin.exe /transfer "Downloading libsodium.dll" /priority high https://github.com/Kwoth/NadekoBot/raw/dev/src/NadekoBot/_libs/32/libsodium.dll "%FILENAME%"
ECHO libsodium.dll downloaded.
ECHO.
timeout /t 5
SET "FILENAME=%~dp0\NadekoBot\src\NadekoBot\opus.dll"
bitsadmin.exe /transfer "Downloading opus.dll" /priority high https://github.com/Kwoth/NadekoBot/raw/dev/src/NadekoBot/_libs/32/opus.dll "%FILENAME%"
ECHO opus.dll downloaded.
GOTO end
:end :end
::Normal execution of end of script ::Normal execution of end of script
TITLE Installation complete! TITLE Installation complete!

View File

@ -1,5 +1,5 @@
@ECHO off @ECHO off
TITLE Downloading NadekoBot, please wait TITLE Downloading Stable Build of NadekoBot...
::Setting convenient to read variables which don't delete the windows temp folder ::Setting convenient to read variables which don't delete the windows temp folder
SET root=%~dp0 SET root=%~dp0
CD /D %root% CD /D %root%
@ -24,30 +24,43 @@ ECHO Downloading Nadeko...
ECHO. ECHO.
git clone -b master --recursive --depth 1 --progress https://github.com/Kwoth/NadekoBot.git >nul git clone -b master --recursive --depth 1 --progress https://github.com/Kwoth/NadekoBot.git >nul
IF %ERRORLEVEL% EQU 128 (GOTO :giterror) IF %ERRORLEVEL% EQU 128 (GOTO :giterror)
TITLE Installing NadekoBot, please wait TITLE Installing NadekoBot, please wait...
ECHO. ECHO.
ECHO Installing... ECHO Installing Discord.Net(1/4)...
::Building Nadeko ::Building Nadeko
CD /D %build1% CD /D %build1%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO Installing Discord.Net(2/4)...
CD /D %build2% CD /D %build2%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO Installing Discord.Net(3/4)...
CD /D %build3% CD /D %build3%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO Installing Discord.Net(4/4)...
CD /D %build4% CD /D %build4%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
ECHO.
ECHO Discord.Net installation completed successfully...
ECHO.
ECHO Installing NadekoBot...
CD /D %build5% CD /D %build5%
dotnet restore >nul 2>&1 dotnet restore >nul 2>&1
dotnet build --configuration Release >nul 2>&1 dotnet build --configuration Release >nul 2>&1
ECHO.
ECHO NadekoBot installation completed successfully...
::Attempts to backup old files if they currently exist in the same folder as the batch file ::Attempts to backup old files if they currently exist in the same folder as the batch file
IF EXIST "%root%NadekoBot\" (GOTO :backupinstall) IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
:freshinstall :freshinstall
::Moves the NadekoBot folder to keep things tidy ::Moves the NadekoBot folder to keep things tidy
ECHO.
ECHO Moving files, Please wait...
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1 ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror) IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
:backupinstall :backupinstall
TITLE Backing up old files TITLE Backing up old files...
ECHO.
ECHO Moving and Backing up old files...
::Recursively copies all files and folders from NadekoBot to NadekoBot_Old ::Recursively copies all files and folders from NadekoBot to NadekoBot_Old
ROBOCOPY "%root%NadekoBot" "%root%NadekoBot_Old" /MIR >nul 2>&1 ROBOCOPY "%root%NadekoBot" "%root%NadekoBot_Old" /MIR >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror) IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
@ -70,7 +83,7 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
RMDIR "%root%NadekoBot\" /S /Q >nul 2>&1 RMDIR "%root%NadekoBot\" /S /Q >nul 2>&1
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1 ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror) IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
:dotnet :dotnet
::Terminates the batch script if it can't run dotnet --version ::Terminates the batch script if it can't run dotnet --version
TITLE Error! TITLE Error!
@ -102,6 +115,25 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
PAUSE >nul 2>&1 PAUSE >nul 2>&1
CD /D "%root%" CD /D "%root%"
GOTO :EOF GOTO :EOF
:64BIT
ECHO.
ECHO Your System Architecture is 64bit...
GOTO end
:32BIT
ECHO.
ECHO Your System Architecture is 32bit...
timeout /t 5
ECHO.
ECHO Downloading libsodium.dll and opus.dll...
SET "FILENAME=%~dp0\NadekoBot\src\NadekoBot\libsodium.dll"
bitsadmin.exe /transfer "Downloading libsodium.dll" /priority high https://github.com/Kwoth/NadekoBot/raw/dev/src/NadekoBot/_libs/32/libsodium.dll "%FILENAME%"
ECHO libsodium.dll downloaded.
ECHO.
timeout /t 5
SET "FILENAME=%~dp0\NadekoBot\src\NadekoBot\opus.dll"
bitsadmin.exe /transfer "Downloading opus.dll" /priority high https://github.com/Kwoth/NadekoBot/raw/dev/src/NadekoBot/_libs/32/opus.dll "%FILENAME%"
ECHO opus.dll downloaded.
GOTO end
:end :end
::Normal execution of end of script ::Normal execution of end of script
TITLE Installation complete! TITLE Installation complete!

View File

@ -30,7 +30,7 @@ namespace NadekoBot.Migrations
name: "Betroll91Multiplier", name: "Betroll91Multiplier",
table: "BotConfig", table: "BotConfig",
nullable: false, nullable: false,
defaultValue: 3f); defaultValue: 4f);
migrationBuilder.AddColumn<int>( migrationBuilder.AddColumn<int>(
name: "CurrencyDropAmount", name: "CurrencyDropAmount",

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class waifus : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "DiscordUser",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
AvatarId = table.Column<string>(nullable: true),
Discriminator = table.Column<string>(nullable: true),
UserId = table.Column<ulong>(nullable: false),
Username = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_DiscordUser", x => x.Id);
table.UniqueConstraint("AK_DiscordUser_UserId", x => x.UserId);
});
migrationBuilder.CreateTable(
name: "WaifuInfo",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
AffinityId = table.Column<int>(nullable: true),
ClaimerId = table.Column<int>(nullable: true),
Price = table.Column<int>(nullable: false),
WaifuId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_WaifuInfo", x => x.Id);
table.ForeignKey(
name: "FK_WaifuInfo_DiscordUser_AffinityId",
column: x => x.AffinityId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_WaifuInfo_DiscordUser_ClaimerId",
column: x => x.ClaimerId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_WaifuInfo_DiscordUser_WaifuId",
column: x => x.WaifuId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "WaifuUpdates",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
NewId = table.Column<int>(nullable: true),
OldId = table.Column<int>(nullable: true),
UpdateType = table.Column<int>(nullable: false),
UserId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_WaifuUpdates", x => x.Id);
table.ForeignKey(
name: "FK_WaifuUpdates_DiscordUser_NewId",
column: x => x.NewId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_WaifuUpdates_DiscordUser_OldId",
column: x => x.OldId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_WaifuUpdates_DiscordUser_UserId",
column: x => x.UserId,
principalTable: "DiscordUser",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_WaifuInfo_AffinityId",
table: "WaifuInfo",
column: "AffinityId");
migrationBuilder.CreateIndex(
name: "IX_WaifuInfo_ClaimerId",
table: "WaifuInfo",
column: "ClaimerId");
migrationBuilder.CreateIndex(
name: "IX_WaifuInfo_WaifuId",
table: "WaifuInfo",
column: "WaifuId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_WaifuUpdates_NewId",
table: "WaifuUpdates",
column: "NewId");
migrationBuilder.CreateIndex(
name: "IX_WaifuUpdates_OldId",
table: "WaifuUpdates",
column: "OldId");
migrationBuilder.CreateIndex(
name: "IX_WaifuUpdates_UserId",
table: "WaifuUpdates",
column: "UserId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "WaifuInfo");
migrationBuilder.DropTable(
name: "WaifuUpdates");
migrationBuilder.DropTable(
name: "DiscordUser");
}
}
}

View File

@ -299,6 +299,26 @@ namespace NadekoBot.Migrations
b.ToTable("CustomReactions"); b.ToTable("CustomReactions");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("AvatarId");
b.Property<string>("Discriminator");
b.Property<ulong>("UserId");
b.Property<string>("Username");
b.HasKey("Id");
b.HasAlternateKey("UserId");
b.ToTable("DiscordUser");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -817,6 +837,55 @@ namespace NadekoBot.Migrations
b.ToTable("PokeGame"); b.ToTable("PokeGame");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("AffinityId");
b.Property<int?>("ClaimerId");
b.Property<int>("Price");
b.Property<int>("WaifuId");
b.HasKey("Id");
b.HasIndex("AffinityId");
b.HasIndex("ClaimerId");
b.HasIndex("WaifuId")
.IsUnique();
b.ToTable("WaifuInfo");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("NewId");
b.Property<int?>("OldId");
b.Property<int>("UpdateType");
b.Property<int>("UserId");
b.HasKey("Id");
b.HasIndex("NewId");
b.HasIndex("OldId");
b.HasIndex("UserId");
b.ToTable("WaifuUpdates");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b =>
{ {
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
@ -982,6 +1051,38 @@ namespace NadekoBot.Migrations
.WithMany("RaceAnimals") .WithMany("RaceAnimals")
.HasForeignKey("BotConfigId"); .HasForeignKey("BotConfigId");
}); });
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Affinity")
.WithMany()
.HasForeignKey("AffinityId");
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Claimer")
.WithMany()
.HasForeignKey("ClaimerId");
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Waifu")
.WithOne()
.HasForeignKey("NadekoBot.Services.Database.Models.WaifuInfo", "WaifuId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "New")
.WithMany()
.HasForeignKey("NewId");
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Old")
.WithMany()
.HasForeignKey("OldId");
b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
} }
} }
} }

View File

@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Administration
var channel = msg.Channel as SocketTextChannel; var channel = msg.Channel as SocketTextChannel;
if (channel == null) if (channel == null)
return; return;
if (DeleteMessagesOnCommand.Contains(channel.Guild.Id)) if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
await msg.DeleteAsync().ConfigureAwait(false); await msg.DeleteAsync().ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -25,7 +25,6 @@ namespace NadekoBot.Modules.Administration
static AutoAssignRoleCommands() static AutoAssignRoleCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0) AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId)); .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
@ -46,9 +45,6 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
}; };
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -1,5 +1,6 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
@ -9,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
@ -21,8 +23,8 @@ namespace NadekoBot.Modules.Administration
private static Logger _log { get; } private static Logger _log { get; }
public static List<PlayingStatus> RotatingStatusMessages { get; } public static List<PlayingStatus> RotatingStatusMessages { get; }
public static bool RotatingStatuses { get; private set; } = false; public static bool RotatingStatuses { get; private set; } = false;
private static Timer _t { get; }
//todo wtf is with this while(true) in constructor
static PlayingRotateCommands() static PlayingRotateCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
@ -30,46 +32,50 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages; RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses; RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
var t = Task.Run(async () =>
_t = new Timer(async (_) =>
{ {
var index = 0; var index = 0;
do
{
try try
{ {
if (!RotatingStatuses) if (!RotatingStatuses)
continue; return;
else else
{ {
if (index >= RotatingStatusMessages.Count) if (index >= RotatingStatusMessages.Count)
index = 0; index = 0;
if (!RotatingStatusMessages.Any()) if (!RotatingStatusMessages.Any())
continue; return;
var status = RotatingStatusMessages[index++].Status; var status = RotatingStatusMessages[index++].Status;
if (string.IsNullOrWhiteSpace(status)) if (string.IsNullOrWhiteSpace(status))
continue; return;
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value())); PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
await NadekoBot.Client.SetGameAsync(status).ConfigureAwait(false); var shards = NadekoBot.Client.Shards;
for (int i = 0; i < shards.Count; i++)
{
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(shards.ElementAt(i))));
try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
catch (Exception ex)
{
_log.Warn(ex);
}
}
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_log.Warn("Rotating playing status errored.\n" + ex); _log.Warn("Rotating playing status errored.\n" + ex);
} }
finally }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
{
await Task.Delay(TimeSpan.FromMinutes(1));
}
} while (true);
});
} }
public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } = public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
new Dictionary<string, Func<string>> { new Dictionary<string, Func<string>> {
{"%servers%", () => NadekoBot.Client.GetGuildCount().ToString()}, { "%servers%", () => NadekoBot.Client.GetGuildCount().ToString()},
{"%users%", () => NadekoBot.Client.GetGuilds().Sum(s => s.Users.Count).ToString()}, { "%users%", () => NadekoBot.Client.GetGuilds().Sum(s => s.Users.Count).ToString()},
{"%playing%", () => { { "%playing%", () => {
var cnt = Music.Music.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null); var cnt = Music.Music.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
if (cnt != 1) return cnt.ToString(); if (cnt != 1) return cnt.ToString();
try { try {
@ -81,7 +87,15 @@ namespace NadekoBot.Modules.Administration
} }
} }
}, },
{"%queued%", () => Music.Music.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()} { "%queued%", () => Music.Music.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
{ "%time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
{ "%shardcount%", () => NadekoBot.Client.Shards.Count.ToString() },
};
public static Dictionary<string, Func<DiscordSocketClient, string>> ShardSpecificPlaceholders { get; } =
new Dictionary<string, Func<DiscordSocketClient, string>> {
{ "%shardid%", (client) => client.ShardId.ToString()},
{ "%shardguilds%", (client) => client.Guilds.Count.ToString()},
}; };
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Administration
if (ids[1].ToUpperInvariant().StartsWith("C:")) if (ids[1].ToUpperInvariant().StartsWith("C:"))
{ {
var cid = ulong.Parse(ids[1].Substring(2)); var cid = ulong.Parse(ids[1].Substring(2));
var ch = (await server.GetTextChannelsAsync()).Where(c => c.Id == cid).FirstOrDefault(); var ch = server.TextChannels.Where(c => c.Id == cid).FirstOrDefault();
if (ch == null) if (ch == null)
{ {
return; return;
@ -159,9 +159,7 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task Announce([Remainder] string message) public async Task Announce([Remainder] string message)
{ {
var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g => var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray();
g.GetDefaultChannelAsync()
)).ConfigureAwait(false);
if (channels == null) if (channels == null)
return; return;
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message))) await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message)))

View File

@ -4,9 +4,11 @@ using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,24 +19,76 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class ServerGreetCommands : ModuleBase public class ServerGreetCommands : ModuleBase
{ {
//make this to a field in the guildconfig table
class GreetSettings
{
public int AutoDeleteGreetMessagesTimer { get; set; }
public int AutoDeleteByeMessagesTimer { get; set; }
public ulong GreetMessageChannelId { get; set; }
public ulong ByeMessageChannelId { get; set; }
public bool SendDmGreetMessage { get; set; }
public string DmGreetMessageText { get; set; }
public bool SendChannelGreetMessage { get; set; }
public string ChannelGreetMessageText { get; set; }
public bool SendChannelByeMessage { get; set; }
public string ChannelByeMessageText { get; set; }
public static GreetSettings Create(GuildConfig g) => new GreetSettings()
{
AutoDeleteByeMessagesTimer = g.AutoDeleteByeMessagesTimer,
AutoDeleteGreetMessagesTimer = g.AutoDeleteGreetMessagesTimer,
GreetMessageChannelId = g.GreetMessageChannelId,
ByeMessageChannelId = g.ByeMessageChannelId,
SendDmGreetMessage = g.SendDmGreetMessage,
DmGreetMessageText = g.DmGreetMessageText,
SendChannelGreetMessage = g.SendChannelGreetMessage,
ChannelGreetMessageText = g.ChannelGreetMessageText,
SendChannelByeMessage = g.SendChannelByeMessage,
ChannelByeMessageText = g.ChannelByeMessageText,
};
}
private static Logger _log { get; } private static Logger _log { get; }
private static ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache { get; } = new ConcurrentDictionary<ulong, GreetSettings>();
static ServerGreetCommands() static ServerGreetCommands()
{ {
NadekoBot.Client.UserJoined += UserJoined; NadekoBot.Client.UserJoined += UserJoined;
NadekoBot.Client.UserLeft += UserLeft; NadekoBot.Client.UserLeft += UserLeft;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, (g) => GreetSettings.Create(g)));
} }
private static GreetSettings GetOrAddSettingsForGuild(ulong guildId)
{
GreetSettings settings;
GuildConfigsCache.TryGetValue(guildId, out settings);
if (settings != null)
return settings;
using (var uow = DbHandler.UnitOfWork())
{
var gc = uow.GuildConfigs.For(guildId, set => set);
settings = GreetSettings.Create(gc);
}
GuildConfigsCache.TryAdd(guildId, settings);
return settings;
}
//todo optimize ASAP //todo optimize ASAP
private static async Task UserLeft(IGuildUser user) private static async Task UserLeft(IGuildUser user)
{ {
try try
{ {
GuildConfig conf; var conf = GetOrAddSettingsForGuild(user.GuildId);
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (!conf.SendChannelByeMessage) return; if (!conf.SendChannelByeMessage) return;
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId); var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
@ -62,11 +116,7 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
GuildConfig conf; var conf = GetOrAddSettingsForGuild(user.GuildId);
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (conf.SendChannelGreetMessage) if (conf.SendChannelGreetMessage)
{ {
@ -133,6 +183,9 @@ namespace NadekoBot.Modules.Administration
var conf = uow.GuildConfigs.For(id, set => set); var conf = uow.GuildConfigs.For(id, set => set);
conf.AutoDeleteGreetMessagesTimer = timer; conf.AutoDeleteGreetMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
} }
@ -159,6 +212,9 @@ namespace NadekoBot.Modules.Administration
enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage; enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage;
conf.GreetMessageChannelId = channelId; conf.GreetMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
return enabled; return enabled;
@ -201,6 +257,9 @@ namespace NadekoBot.Modules.Administration
conf.ChannelGreetMessageText = message; conf.ChannelGreetMessageText = message;
greetMsgEnabled = conf.SendChannelGreetMessage; greetMsgEnabled = conf.SendChannelGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
return greetMsgEnabled; return greetMsgEnabled;
@ -227,6 +286,9 @@ namespace NadekoBot.Modules.Administration
var conf = uow.GuildConfigs.For(guildId, set => set); var conf = uow.GuildConfigs.For(guildId, set => set);
enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage; enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
return enabled; return enabled;
@ -269,6 +331,9 @@ namespace NadekoBot.Modules.Administration
conf.DmGreetMessageText = message; conf.DmGreetMessageText = message;
greetMsgEnabled = conf.SendDmGreetMessage; greetMsgEnabled = conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
return greetMsgEnabled; return greetMsgEnabled;
@ -296,6 +361,9 @@ namespace NadekoBot.Modules.Administration
enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage; enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage;
conf.ByeMessageChannelId = channelId; conf.ByeMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
return enabled; return enabled;
@ -338,6 +406,9 @@ namespace NadekoBot.Modules.Administration
conf.ChannelByeMessageText = message; conf.ChannelByeMessageText = message;
byeMsgEnabled = conf.SendChannelByeMessage; byeMsgEnabled = conf.SendChannelByeMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
return byeMsgEnabled; return byeMsgEnabled;
@ -356,16 +427,19 @@ namespace NadekoBot.Modules.Administration
await Context.Channel.SendConfirmAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false); await Context.Channel.SendConfirmAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false);
} }
private static async Task SetByeDel(ulong id, int timer) private static async Task SetByeDel(ulong guildId, int timer)
{ {
if (timer < 0 || timer > 600) if (timer < 0 || timer > 600)
return; return;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(id, set => set); var conf = uow.GuildConfigs.For(guildId, set => set);
conf.AutoDeleteByeMessagesTimer = timer; conf.AutoDeleteByeMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
} }

View File

@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
await (await guild.GetOwnerAsync()).SendErrorAsync( await guild.Owner.SendErrorAsync(
"⚠️ I don't have **manage server** and/or **manage channels** permission," + "⚠️ I don't have **manage server** and/or **manage channels** permission," +
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false); $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
} }
@ -75,16 +75,16 @@ namespace NadekoBot.Modules.Administration
var beforeVch = before.VoiceChannel; var beforeVch = before.VoiceChannel;
if (beforeVch != null) if (beforeVch != null)
{ {
var textChannel = (await guild.GetTextChannelsAsync()).Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault(); var textChannel = guild.TextChannels.Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault();
if (textChannel != null) if (textChannel != null)
await textChannel.AddPermissionOverwriteAsync(user, await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Deny, new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false); sendMessages: PermValue.Deny)).ConfigureAwait(false);
} }
var afterVch = after.VoiceChannel; var afterVch = after.VoiceChannel;
if (afterVch != null && guild.AFKChannelId != afterVch.Id) if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
{ {
var textChannel = (await guild.GetTextChannelsAsync()) ITextChannel textChannel = guild.TextChannels
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) .Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant())
.FirstOrDefault(); .FirstOrDefault();
if (textChannel == null) if (textChannel == null)

View File

@ -36,10 +36,8 @@ namespace NadekoBot.Modules.ClashOfClans
.GetAllWars() .GetAllWars()
.Select(cw => .Select(cw =>
{ {
cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId) cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId)?
?.GetTextChannelAsync(cw.ChannelId) .GetTextChannel(cw.ChannelId);
.GetAwaiter()
.GetResult();
return cw; return cw;
}) })
.Where(cw => cw.Channel != null) .Where(cw => cw.Channel != null)
@ -322,7 +320,7 @@ namespace NadekoBot.Modules.ClashOfClans
public static async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId) public static async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId)
{ {
var channel = await NadekoBot.Client.GetGuild(serverId)?.GetTextChannelAsync(channelId); var channel = NadekoBot.Client.GetGuild(serverId)?.GetTextChannel(channelId);
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var cw = new ClashWar var cw = new ClashWar

View File

@ -10,14 +10,16 @@ using NadekoBot.Extensions;
using NLog; using NLog;
using System.Diagnostics; using System.Diagnostics;
using Discord.WebSocket; using Discord.WebSocket;
using System;
namespace NadekoBot.Modules.CustomReactions namespace NadekoBot.Modules.CustomReactions
{ {
[NadekoModule("CustomReactions", ".")] [NadekoModule("CustomReactions", ".")]
public class CustomReactions : DiscordModule public class CustomReactions : DiscordModule
{ {
public static ConcurrentHashSet<CustomReaction> GlobalReactions { get; } = new ConcurrentHashSet<CustomReaction>(); private static CustomReaction[] _globalReactions = new CustomReaction[] { };
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>> GuildReactions { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(); public static CustomReaction[] GlobalReactions => _globalReactions;
public static ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>();
public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>(); public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
@ -30,8 +32,8 @@ namespace NadekoBot.Modules.CustomReactions
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var items = uow.CustomReactions.GetAll(); var items = uow.CustomReactions.GetAll();
GuildReactions = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new ConcurrentHashSet<CustomReaction>(g))); GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray()));
GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null || g.GuildId == 0)); _globalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
} }
sw.Stop(); sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
@ -46,17 +48,24 @@ namespace NadekoBot.Modules.CustomReactions
return false; return false;
var content = umsg.Content.Trim().ToLowerInvariant(); var content = umsg.Content.Trim().ToLowerInvariant();
ConcurrentHashSet<CustomReaction> reactions; CustomReaction[] reactions;
GuildReactions.TryGetValue(channel.Guild.Id, out reactions); GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
if (reactions != null && reactions.Any()) if (reactions != null && reactions.Any())
{ {
var reaction = reactions.Where(cr => var rs = reactions.Where(cr =>
{ {
if (cr == null)
return false;
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant(); var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault(); }).ToArray();
if (rs.Length != 0)
{
var reaction = rs[new NadekoRandom().Next(0, rs.Length)];
if (reaction != null) if (reaction != null)
{ {
if (reaction.Response != "-") if (reaction.Response != "-")
@ -66,12 +75,19 @@ namespace NadekoBot.Modules.CustomReactions
return true; return true;
} }
} }
var greaction = GlobalReactions.Where(cr => }
var grs = GlobalReactions.Where(cr =>
{ {
if (cr == null)
return false;
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant(); var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault(); }).ToArray();
if (grs.Length == 0)
return false;
var greaction = grs[new NadekoRandom().Next(0, grs.Length)];
if (greaction != null) if (greaction != null)
{ {
@ -114,12 +130,19 @@ namespace NadekoBot.Modules.CustomReactions
if (channel == null) if (channel == null)
{ {
GlobalReactions.Add(cr); Array.Resize(ref _globalReactions, _globalReactions.Length + 1);
_globalReactions[_globalReactions.Length - 1] = cr;
} }
else else
{ {
var reactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); var reactions = GuildReactions.AddOrUpdate(Context.Guild.Id,
reactions.Add(cr); Array.Empty<CustomReaction>(),
(k, old) =>
{
Array.Resize(ref old, old.Length + 1);
old[old.Length - 1] = cr;
return old;
});
} }
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
@ -136,17 +159,17 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (page < 1 || page > 1000) if (page < 1 || page > 1000)
return; return;
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions.Where(cr => cr != null).ToArray();
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else else
{ {
var lastPage = customReactions.Count / 20; var lastPage = customReactions.Length / 20;
await Context.Channel.SendPaginatedConfirmAsync(page, curPage => await Context.Channel.SendPaginatedConfirmAsync(page, curPage =>
new EmbedBuilder().WithOkColor() new EmbedBuilder().WithOkColor()
.WithTitle("Custom reactions") .WithTitle("Custom reactions")
@ -167,11 +190,11 @@ namespace NadekoBot.Modules.CustomReactions
[Priority(1)] [Priority(1)]
public async Task ListCustReact(All x) public async Task ListCustReact(All x)
{ {
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions.Where(cr => cr != null).ToArray();
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
@ -195,11 +218,11 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (page < 1 || page > 10000) if (page < 1 || page > 10000)
return; return;
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions.Where(cr => cr != null).ToArray();
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
@ -225,13 +248,13 @@ namespace NadekoBot.Modules.CustomReactions
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task ShowCustReact(int id) public async Task ShowCustReact(int id)
{ {
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions;
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ });
var found = customReactions.FirstOrDefault(cr => cr.Id == id); var found = customReactions.FirstOrDefault(cr => cr?.Id == id);
if (found == null) if (found == null)
await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
@ -265,13 +288,17 @@ namespace NadekoBot.Modules.CustomReactions
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null) if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
{ {
uow.CustomReactions.Remove(toDelete); uow.CustomReactions.Remove(toDelete);
GlobalReactions.RemoveWhere(cr => cr.Id == toDelete.Id); //todo i can dramatically improve performance of this, if Ids are ordered.
_globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
success = true; success = true;
} }
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId) else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
{ {
uow.CustomReactions.Remove(toDelete); uow.CustomReactions.Remove(toDelete);
GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()).RemoveWhere(cr => cr.Id == toDelete.Id); GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
{
return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
});
success = true; success = true;
} }
if (success) if (success)
@ -312,8 +339,10 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (page < 1) if (page < 1)
return; return;
var ordered = ReactionStats.OrderByDescending(x => x.Value).ToList(); var ordered = ReactionStats.OrderByDescending(x => x.Value).ToArray();
var lastPage = ordered.Count / 9; if (!ordered.Any())
return;
var lastPage = ordered.Length / 9;
await Context.Channel.SendPaginatedConfirmAsync(page, await Context.Channel.SendPaginatedConfirmAsync(page,
(curPage) => ordered.Skip((curPage - 1) * 9) (curPage) => ordered.Skip((curPage - 1) * 9)
.Take(9) .Take(9)

View File

@ -1,9 +1,11 @@
using Discord; using Discord;
using Discord.WebSocket;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -25,9 +27,13 @@ namespace NadekoBot.Modules.CustomReactions
if(ch == null) if(ch == null)
return ""; return "";
var usrs = (ch.Guild.GetUsersAsync().GetAwaiter().GetResult()); var g = ch.Guild as SocketGuild;
if(g == null)
return "";
return usrs.Skip(new NadekoRandom().Next(0,usrs.Count-1)).Shuffle().FirstOrDefault()?.Mention ?? ""; var users = g.Users.ToArray();
return users[new NadekoRandom().Next(0, users.Length-1)].Mention;
} } } }
//{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } } //{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } }
}; };

View File

@ -9,6 +9,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.WebSocket;
using NadekoBot.Services.Database;
namespace NadekoBot.Modules.Gambling namespace NadekoBot.Modules.Gambling
{ {
@ -19,15 +21,26 @@ namespace NadekoBot.Modules.Gambling
{ {
public enum CurrencyEvent public enum CurrencyEvent
{ {
FlowerReaction FlowerReaction,
SneakyGameStatus
} }
//flower reaction event //flower reaction event
public static readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>(); public static readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
public static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>();
private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10)
.Concat(Enumerable.Range(65, 26))
.Concat(Enumerable.Range(97, 26))
.Select(x => (char)x)
.ToArray();
private static string _secretCode = String.Empty;
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task StartEvent(CurrencyEvent e) public async Task StartEvent(CurrencyEvent e, int arg = -1)
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
try try
@ -38,6 +51,9 @@ namespace NadekoBot.Modules.Gambling
case CurrencyEvent.FlowerReaction: case CurrencyEvent.FlowerReaction:
await FlowerReactionEvent(Context).ConfigureAwait(false); await FlowerReactionEvent(Context).ConfigureAwait(false);
break; break;
case CurrencyEvent.SneakyGameStatus:
await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
break;
default: default:
break; break;
} }
@ -45,6 +61,63 @@ namespace NadekoBot.Modules.Gambling
catch { } catch { }
} }
public static async Task SneakyGameStatusEvent(CommandContext Context, int? arg)
{
int num;
if (arg == null || arg < 5)
num = 60;
else
num = arg.Value;
if (_secretCode != String.Empty)
return;
var rng = new NadekoRandom();
for (int i = 0; i < 5; i++)
{
_secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)];
}
await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName)
.ConfigureAwait(false);
try
{
await Context.Channel.SendConfirmAsync($"SneakyGameStatus event started",
$"Users must type a secret code to get 100 currency.\n" +
$"Lasts {num} seconds. Don't tell anyone. Shhh.")
.ConfigureAwait(false);
}
catch { }
NadekoBot.Client.MessageReceived += SneakyGameMessageReceivedEventHandler;
await Task.Delay(num * 1000);
NadekoBot.Client.MessageReceived -= SneakyGameMessageReceivedEventHandler;
_sneakyGameAwardedUsers.Clear();
_secretCode = String.Empty;
await NadekoBot.Client.SetGameAsync($"SneakyGame event ended.")
.ConfigureAwait(false);
}
private static Task SneakyGameMessageReceivedEventHandler(SocketMessage arg)
{
if (arg.Content == _secretCode &&
_sneakyGameAwardedUsers.Add(arg.Author.Id))
{
var _ = Task.Run(async () =>
{
await CurrencyHandler.AddCurrencyAsync(arg.Author, "Sneaky Game Event", 100, false)
.ConfigureAwait(false);
try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); }
catch { }
});
}
return Task.Delay(0);
}
public static async Task FlowerReactionEvent(CommandContext Context) public static async Task FlowerReactionEvent(CommandContext Context)
{ {
@ -59,14 +132,14 @@ namespace NadekoBot.Modules.Gambling
catch catch
{ {
try { await msg.DeleteAsync().ConfigureAwait(false); } try { await msg.DeleteAsync().ConfigureAwait(false); }
catch { } catch { return; }
} }
} }
using (msg.OnReaction(async (r) => using (msg.OnReaction(async (r) =>
{ {
try try
{ {
if (r.Emoji.Name == "🌸" && r.User.IsSpecified && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
{ {
try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { } try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { }
} }

View File

@ -184,7 +184,10 @@ namespace NadekoBot.Modules.Gambling
} }
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false)) if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false))
{
await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencySign}.").ConfigureAwait(false);
return; return;
}
Interlocked.Add(ref totalBet, amount); Interlocked.Add(ref totalBet, amount);
using (var bgFileStream = new MemoryStream(backgroundBuffer)) using (var bgFileStream = new MemoryStream(backgroundBuffer))
{ {

View File

@ -0,0 +1,516 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Gambling
{
public partial class Gambling
{
public enum ClaimTitles
{
Lonely,
Devoted,
Rookie,
Schemer,
Dilettante,
Intermediate,
Seducer,
Expert,
Veteran,
Incubis,
Harem_King,
Harem_God,
}
public enum AffinityTitles
{
Pure,
Faithful,
Defiled,
Cheater,
Tainted,
Corrupted,
Lewd,
Sloot,
Depraved,
Harlot
}
[Group]
public class WaifuClaimCommands : ModuleBase
{
private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
enum WaifuClaimResult
{
Success,
NotEnoughFunds,
InsufficientAmount
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task WaifuClaim(int amount, [Remainder]IUser target)
{
if (amount < 50)
{
await Context.Channel.SendErrorAsync($"{Context.User.Mention} No waifu is that cheap. You must pay at least 50{NadekoBot.BotConfig.CurrencySign} to get a waifu, even if their actual value is lower.").ConfigureAwait(false);
return;
}
if (target.Id == Context.User.Id)
{
await Context.Channel.SendErrorAsync(Context.User.Mention + " You can't claim yourself.").ConfigureAwait(false);
return;
}
WaifuClaimResult result = WaifuClaimResult.NotEnoughFunds;
int? oldPrice = null;
WaifuInfo w;
var isAffinity = false;
using (var uow = DbHandler.UnitOfWork())
{
w = uow.Waifus.ByWaifuUserId(target.Id);
isAffinity = (w?.Affinity?.UserId == Context.User.Id);
if (w == null)
{
var claimer = uow.DiscordUsers.GetOrCreate(Context.User);
var waifu = uow.DiscordUsers.GetOrCreate(target);
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false))
{
result = WaifuClaimResult.NotEnoughFunds;
}
else
{
uow.Waifus.Add(w = new WaifuInfo()
{
Waifu = waifu,
Claimer = claimer,
Affinity = null,
Price = amount
});
uow._context.WaifuUpdates.Add(new WaifuUpdate()
{
User = waifu,
Old = null,
New = claimer,
UpdateType = WaifuUpdateType.Claimed
});
result = WaifuClaimResult.Success;
}
}
else if (isAffinity && amount >= w.Price * 0.88f)
{
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false))
{
result = WaifuClaimResult.NotEnoughFunds;
}
else
{
var oldClaimer = w.Claimer;
w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User);
oldPrice = w.Price;
w.Price = amount + (amount / 4);
result = WaifuClaimResult.Success;
uow._context.WaifuUpdates.Add(new WaifuUpdate()
{
User = w.Waifu,
Old = oldClaimer,
New = w.Claimer,
UpdateType = WaifuUpdateType.Claimed
});
}
}
else if (amount >= w.Price * 1.1f) // if no affinity
{
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false))
{
result = WaifuClaimResult.NotEnoughFunds;
}
else
{
var oldClaimer = w.Claimer;
w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User);
oldPrice = w.Price;
w.Price = amount;
result = WaifuClaimResult.Success;
uow._context.WaifuUpdates.Add(new WaifuUpdate()
{
User = w.Waifu,
Old = oldClaimer,
New = w.Claimer,
UpdateType = WaifuUpdateType.Claimed
});
}
}
else
result = WaifuClaimResult.InsufficientAmount;
await uow.CompleteAsync().ConfigureAwait(false);
}
if (result == WaifuClaimResult.InsufficientAmount)
{
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You must pay {Math.Ceiling(w.Price * (isAffinity ? 0.88f : 1.1f))} or more to claim that waifu!").ConfigureAwait(false);
return;
}
else if (result == WaifuClaimResult.NotEnoughFunds)
{
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} you don't have {amount}{NadekoBot.BotConfig.CurrencySign}!")
.ConfigureAwait(false);
}
else
{
var msg = $"{Context.User.Mention} claimed {target.Mention} as their waifu for {amount}{NadekoBot.BotConfig.CurrencySign}!";
if (w.Affinity?.UserId == Context.User.Id)
msg += $"\n🎉 Their love is fulfilled! 🎉\n**{target}'s** new value is {w.Price}{NadekoBot.BotConfig.CurrencySign}!";
await Context.Channel.SendConfirmAsync(msg)
.ConfigureAwait(false);
}
}
public enum DivorceResult
{
Success,
SucessWithPenalty,
NotYourWife,
Cooldown
}
private static readonly TimeSpan DivorceLimit = TimeSpan.FromHours(6);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Divorce([Remainder]IUser target)
{
var channel = (ITextChannel)Context.Channel;
if (target.Id == Context.User.Id)
return;
var result = DivorceResult.NotYourWife;
TimeSpan difference = TimeSpan.Zero;
var amount = 0;
WaifuInfo w = null;
using (var uow = DbHandler.UnitOfWork())
{
w = uow.Waifus.ByWaifuUserId(target.Id);
var now = DateTime.UtcNow;
if (w == null || w.Claimer == null || w.Claimer.UserId != Context.User.Id)
result = DivorceResult.NotYourWife;
else if (_divorceCooldowns.AddOrUpdate(Context.User.Id,
now,
(key, old) => ((difference = now.Subtract(old)) > DivorceLimit) ? now : old) != now)
{
result = DivorceResult.Cooldown;
}
else
{
amount = w.Price / 2;
if (w.Affinity?.UserId == Context.User.Id)
{
await CurrencyHandler.AddCurrencyAsync(w.Waifu.UserId, "Waifu Compensation", amount, uow).ConfigureAwait(false);
w.Price = (int)Math.Floor(w.Price * 0.75f);
result = DivorceResult.SucessWithPenalty;
}
else
{
await CurrencyHandler.AddCurrencyAsync(Context.User.Id, "Waifu Refund", amount, uow).ConfigureAwait(false);
result = DivorceResult.Success;
}
var oldClaimer = w.Claimer;
w.Claimer = null;
uow._context.WaifuUpdates.Add(new WaifuUpdate()
{
User = w.Waifu,
Old = oldClaimer,
New = null,
UpdateType = WaifuUpdateType.Claimed
});
}
await uow.CompleteAsync().ConfigureAwait(false);
}
if (result == DivorceResult.SucessWithPenalty)
{
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} You have divorced a waifu who likes you. You heartless monster.\n{w.Waifu} received {amount}{NadekoBot.BotConfig.CurrencySign} as a compensation.").ConfigureAwait(false);
}
else if (result == DivorceResult.Success)
{
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} You have divorced a waifu who doesn't like you. You received {amount}{NadekoBot.BotConfig.CurrencySign} back.").ConfigureAwait(false);
}
else if (result == DivorceResult.NotYourWife)
{
await Context.Channel.SendErrorAsync($"{Context.User.Mention} That waifu is not yours.").ConfigureAwait(false);
}
else
{
var remaining = DivorceLimit.Subtract(difference);
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You divorced recently. You must wait **{remaining.Hours} hours and {remaining.Minutes} minutes** to divorce again.").ConfigureAwait(false);
}
}
private static readonly TimeSpan AffinityLimit = TimeSpan.FromMinutes(30);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task WaifuClaimerAffinity([Remainder]IUser u = null)
{
if (u?.Id == Context.User.Id)
{
await Context.Channel.SendErrorAsync($"{Context.User.Mention} you can't set affinity to yourself, you egomaniac.").ConfigureAwait(false);
return;
}
DiscordUser oldAff = null;
var sucess = false;
var cooldown = false;
TimeSpan difference = TimeSpan.Zero;
using (var uow = DbHandler.UnitOfWork())
{
var w = uow.Waifus.ByWaifuUserId(Context.User.Id);
var newAff = u == null ? null : uow.DiscordUsers.GetOrCreate(u);
var now = DateTime.UtcNow;
if (w?.Affinity?.UserId == u?.Id)
{
sucess = false;
}
else if (_affinityCooldowns.AddOrUpdate(Context.User.Id,
now,
(key, old) => ((difference = now.Subtract(old)) > AffinityLimit) ? now : old) != now)
{
sucess = false;
cooldown = true;
}
else if (w == null)
{
var thisUser = uow.DiscordUsers.GetOrCreate(Context.User);
uow.Waifus.Add(new WaifuInfo()
{
Affinity = newAff,
Waifu = thisUser,
Price = 1,
Claimer = null
});
sucess = true;
uow._context.WaifuUpdates.Add(new WaifuUpdate()
{
User = thisUser,
Old = null,
New = newAff,
UpdateType = WaifuUpdateType.AffinityChanged
});
}
else
{
if (w.Affinity != null)
oldAff = w.Affinity;
w.Affinity = newAff;
sucess = true;
uow._context.WaifuUpdates.Add(new WaifuUpdate()
{
User = w.Waifu,
Old = oldAff,
New = newAff,
UpdateType = WaifuUpdateType.AffinityChanged
});
}
await uow.CompleteAsync().ConfigureAwait(false);
}
if (!sucess)
{
if (cooldown)
{
var remaining = AffinityLimit.Subtract(difference);
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You must wait **{remaining.Hours} hours and {remaining.Minutes} minutes** in order to change your affinity again.").ConfigureAwait(false);
}
else
await Context.Channel.SendErrorAsync($"{Context.User.Mention} your affinity is already set to that waifu or you're trying to remove your affinity while not having one.").ConfigureAwait(false);
return;
}
if (u == null)
await Context.Channel.SendConfirmAsync("Affinity Reset", $"{Context.User.Mention} Your affinity is reset. You no longer have a person you like.").ConfigureAwait(false);
else if (oldAff == null)
await Context.Channel.SendConfirmAsync("Affinity Set", $"{Context.User.Mention} wants to be {u.Mention}'s waifu. Aww <3").ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync("Affinity Changed", $"{Context.User.Mention} changed their affinity from {oldAff} to {u.Mention}.\n\n*This is morally questionable.*🤔").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task WaifuLeaderboard()
{
IList<WaifuInfo> waifus;
using (var uow = DbHandler.UnitOfWork())
{
waifus = uow.Waifus.GetTop(9);
}
if (waifus.Count == 0)
{
await Context.Channel.SendConfirmAsync("No waifus have been claimed yet.").ConfigureAwait(false);
return;
}
var embed = new EmbedBuilder()
.WithTitle("Top Waifus")
.WithOkColor();
for (int i = 0; i < waifus.Count; i++)
{
var w = waifus[i];
embed.AddField(efb => efb.WithName("#" + (i + 1) + " - " + w.Price + NadekoBot.BotConfig.CurrencySign).WithValue(w.ToString()).WithIsInline(false));
}
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task WaifuInfo([Remainder]IUser target = null)
{
if (target == null)
target = Context.User;
WaifuInfo w;
IList<WaifuInfo> claims;
int divorces = 0;
using (var uow = DbHandler.UnitOfWork())
{
w = uow.Waifus.ByWaifuUserId(target.Id);
claims = uow.Waifus.ByClaimerUserId(target.Id);
divorces = uow._context.WaifuUpdates.Count(x => x.Old != null &&
x.Old.UserId == target.Id &&
x.UpdateType == WaifuUpdateType.Claimed &&
x.New == null);
if (w == null)
uow.Waifus.Add(w = new WaifuInfo()
{
Affinity = null,
Claimer = null,
Price = 1,
Waifu = uow.DiscordUsers.GetOrCreate(target),
});
await uow.CompleteAsync().ConfigureAwait(false);
}
var claimInfo = GetClaimTitle(target.Id);
var affInfo = GetAffinityTitle(target.Id);
var embed = new EmbedBuilder()
.WithOkColor()
.WithTitle("Waifu " + w.Waifu.ToString() + " - \"the " + claimInfo.Title + "\"")
.AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true))
.AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true))
.AddField(efb => efb.WithName("Changes Of Heart").WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true))
.AddField(efb => efb.WithName("Divorces").WithValue(divorces.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? "Nobody" : string.Join("\n", claims.Select(x => x.Waifu))).WithIsInline(true));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
public struct WaifuProfileTitle
{
public int Count { get; }
public string Title { get; }
public WaifuProfileTitle(int count, string title)
{
Count = count;
Title = title;
}
}
private static WaifuProfileTitle GetClaimTitle(ulong userId)
{
int count = 0;
using (var uow = DbHandler.UnitOfWork())
{
count = uow.Waifus.ByClaimerUserId(userId).Count;
}
ClaimTitles title = ClaimTitles.Lonely;
if (count == 0)
title = ClaimTitles.Lonely;
else if (count == 1)
title = ClaimTitles.Devoted;
else if (count < 4)
title = ClaimTitles.Rookie;
else if (count < 6)
title = ClaimTitles.Schemer;
else if (count < 8)
title = ClaimTitles.Dilettante;
else if (count < 10)
title = ClaimTitles.Intermediate;
else if (count < 12)
title = ClaimTitles.Seducer;
else if (count < 15)
title = ClaimTitles.Expert;
else if (count < 17)
title = ClaimTitles.Veteran;
else if (count < 25)
title = ClaimTitles.Incubis;
else if (count < 50)
title = ClaimTitles.Harem_King;
else
title = ClaimTitles.Harem_God;
return new WaifuProfileTitle(count, title.ToString().Replace('_', ' '));
}
private static WaifuProfileTitle GetAffinityTitle(ulong userId)
{
int count = 0;
using (var uow = DbHandler.UnitOfWork())
{
count = uow._context.WaifuUpdates.Count(w => w.User.UserId == userId && w.UpdateType == WaifuUpdateType.AffinityChanged);
}
AffinityTitles title = AffinityTitles.Pure;
if (count < 1)
title = AffinityTitles.Pure;
else if (count < 2)
title = AffinityTitles.Faithful;
else if (count < 4)
title = AffinityTitles.Defiled;
else if (count < 7)
title = AffinityTitles.Cheater;
else if (count < 9)
title = AffinityTitles.Tainted;
else if (count < 11)
title = AffinityTitles.Corrupted;
else if (count < 13)
title = AffinityTitles.Lewd;
else if (count < 15)
title = AffinityTitles.Sloot;
else if (count < 17)
title = AffinityTitles.Depraved;
else
title = AffinityTitles.Harlot;
return new WaifuProfileTitle(count, title.ToString().Replace('_', ' '));
}
}
}
}

View File

@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Gambling
var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown); var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
var membersArray = members as IUser[] ?? members.ToArray(); var membersArray = members as IUser[] ?? members.ToArray();
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)]; var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}** ID: `{usr.Id}`").ConfigureAwait(false); await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -67,14 +67,14 @@ namespace NadekoBot.Modules.Gambling
{ {
if (amount <= 0 || Context.User.Id == receiver.Id) if (amount <= 0 || Context.User.Id == receiver.Id)
return; return;
var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, true).ConfigureAwait(false); var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false);
if (!success) if (!success)
{ {
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false); await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false);
return; return;
} }
await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully sent {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} to {receiver.Mention}!").ConfigureAwait(false); await Context.Channel.SendConfirmAsync($"{Context.User.Mention} gifted {amount}{CurrencySign} to {Format.Bold(receiver.ToString())}!").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -94,7 +94,7 @@ namespace NadekoBot.Modules.Gambling
await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} to <@{usrId}>!").ConfigureAwait(false); await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount}{CurrencySign} to <@{usrId}>!").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -145,6 +145,61 @@ namespace NadekoBot.Modules.Gambling
await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false); await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false);
} }
//[NadekoCommand, Usage, Description, Aliases]
//[OwnerOnly]
//public Task BrTest(int tests = 1000)
//{
// var t = Task.Run(async () =>
// {
// if (tests <= 0)
// return;
// //multi vs how many times it occured
// var dict = new Dictionary<int, int>();
// var generator = new NadekoRandom();
// for (int i = 0; i < tests; i++)
// {
// var rng = generator.Next(0, 101);
// var mult = 0;
// if (rng < 67)
// {
// mult = 0;
// }
// else if (rng < 91)
// {
// mult = 2;
// }
// else if (rng < 100)
// {
// mult = 4;
// }
// else
// mult = 10;
// if (dict.ContainsKey(mult))
// dict[mult] += 1;
// else
// dict.Add(mult, 1);
// }
// var sb = new StringBuilder();
// const int bet = 1;
// int payout = 0;
// foreach (var key in dict.Keys.OrderByDescending(x => x))
// {
// sb.AppendLine($"x{key} occured {dict[key]} times. {dict[key] * 1.0f / tests * 100}%");
// payout += key * dict[key];
// }
// try
// {
// await Context.Channel.SendConfirmAsync("BetRoll Test Results", sb.ToString(),
// footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%");
// }
// catch { }
// });
// return Task.CompletedTask;
//}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task BetRoll(long amount) public async Task BetRoll(long amount)
{ {
@ -193,22 +248,33 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Leaderboard() public async Task Leaderboard()
{ {
IEnumerable<Currency> richest = new List<Currency>(); var richest = new List<Currency>();
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
richest = uow.Currency.GetTopRichest(10); richest = uow.Currency.GetTopRichest(9).ToList();
} }
if (!richest.Any()) if (!richest.Any())
return; return;
await Context.Channel.SendMessageAsync(
richest.Aggregate(new StringBuilder(
$@"```xl var embed = new EmbedBuilder()
.WithOkColor()
Id $$$ .WithTitle(NadekoBot.BotConfig.CurrencySign + " Leaderboard");
"),
(cur, cs) => cur.AppendLine($@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━┫ for (var i = 0; i < richest.Count; i++)
{(Context.Guild.GetUserAsync(cs.UserId).GetAwaiter().GetResult()?.Username?.TrimTo(18, true) ?? cs.UserId.ToString()),-20} {cs.Amount,6} ") {
).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━┛```").ConfigureAwait(false); var x = richest[i];
var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false);
var usrStr = "";
if (usr == null)
usrStr = x.UserId.ToString();
else
usrStr = usr.Username?.TrimTo(20, true);
embed.AddField(efb => efb.WithName("#" + (i + 1) + " " + usrStr).WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign).WithIsInline(true));
}
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
} }
} }

View File

@ -69,6 +69,7 @@ namespace NadekoBot.Modules.Games
private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>(); private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>();
public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions; public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions;
private readonly ConcurrentHashSet<ulong> usersWhoSubmitted = new ConcurrentHashSet<ulong>();
private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>();
private int spamCount = 0; private int spamCount = 0;
@ -190,10 +191,6 @@ namespace NadekoBot.Modules.Games
try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
catch { } catch { }
} }
//user didn't input something already
IGuildUser throwaway;
if (submissions.TryGetValue(input, out throwaway))
return;
var inputWords = input.Split(' '); //get all words var inputWords = input.Split(' '); //get all words
if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters
@ -207,9 +204,15 @@ namespace NadekoBot.Modules.Games
return; return;
} }
if (!usersWhoSubmitted.Add(guildUser.Id))
return;
//try adding it to the list of answers //try adding it to the list of answers
if (!submissions.TryAdd(input, guildUser)) if (!submissions.TryAdd(input, guildUser))
{
usersWhoSubmitted.TryRemove(guildUser.Id);
return; return;
}
// all good. valid input. answer recorded // all good. valid input. answer recorded
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)"); await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)");

View File

@ -189,13 +189,13 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
} }
public string GetHangman() => $@"\_\_\_\_\_\_\_\_\_ public string GetHangman() => $@". ┌─────┐
| | ................
| | ................
{(Errors > 0 ? "😲" : " ")} | .{(Errors > 0 ? ".............😲" : "")}
{(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} | .{(Errors > 1 ? "............./" : "")} {(Errors > 2 ? "|" : "")} {(Errors > 3 ? "\\" : "")}
{(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} | .{(Errors > 4 ? "............../" : "")} {(Errors > 5 ? "\\" : "")}
/-\"; /-\";
public void Dispose() public void Dispose()
{ {

View File

@ -61,7 +61,7 @@ namespace NadekoBot.Modules.Games
return; return;
} }
await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord); await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman());
} }
} }
} }

View File

@ -147,9 +147,12 @@ namespace NadekoBot.Modules.Games
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Plant() public async Task Plant(int amount = 1)
{ {
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", 1, false).ConfigureAwait(false); if (amount < 1)
return;
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false);
if (!removed) if (!removed)
{ {
await Context.Channel.SendErrorAsync($"You don't have any {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false); await Context.Channel.SendErrorAsync($"You don't have any {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false);
@ -160,7 +163,7 @@ namespace NadekoBot.Modules.Games
IUserMessage msg; IUserMessage msg;
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]);
var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.BotConfig.CurrencyName}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick";
if (file == null) if (file == null)
{ {
msg = await Context.Channel.SendConfirmAsync(NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); msg = await Context.Channel.SendConfirmAsync(NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
@ -169,7 +172,15 @@ namespace NadekoBot.Modules.Games
{ {
msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), new FileInfo(file).Name, msgToSend).ConfigureAwait(false); msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), new FileInfo(file).Name, msgToSend).ConfigureAwait(false);
} }
plantedFlowers.AddOrUpdate(Context.Channel.Id, new List<IUserMessage>() { msg }, (id, old) => { old.Add(msg); return old; });
var msgs = new IUserMessage[amount];
msgs[0] = msg;
plantedFlowers.AddOrUpdate(Context.Channel.Id, msgs.ToList(), (id, old) =>
{
old.AddRange(msgs);
return old;
});
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -76,9 +76,9 @@ namespace NadekoBot.Modules.Games.Trivia
questionMessage = await channel.EmbedAsync(questionEmbed).ConfigureAwait(false); questionMessage = await channel.EmbedAsync(questionEmbed).ConfigureAwait(false);
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound ||
ex.StatusCode == System.Net.HttpStatusCode.Forbidden || ex.HttpCode == System.Net.HttpStatusCode.Forbidden ||
ex.StatusCode == System.Net.HttpStatusCode.BadRequest) ex.HttpCode == System.Net.HttpStatusCode.BadRequest)
{ {
return; return;
} }
@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Games.Trivia
await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build()) await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
.ConfigureAwait(false); .ConfigureAwait(false);
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden) catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound || ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
{ {
break; break;
} }

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Games
[NadekoModule("Games", ">")] [NadekoModule("Games", ">")]
public partial class Games : DiscordModule public partial class Games : DiscordModule
{ {
private static IEnumerable<string> _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text); private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -37,7 +37,7 @@ namespace NadekoBot.Modules.Games
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false)) .AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
.AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses.Shuffle().FirstOrDefault()).WithIsInline(false))); .AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false)));
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -71,6 +71,7 @@ namespace NadekoBot.Modules.Music.Classes
public event Action<bool> OnPauseChanged = delegate { }; public event Action<bool> OnPauseChanged = delegate { };
public IVoiceChannel PlaybackVoiceChannel { get; private set; } public IVoiceChannel PlaybackVoiceChannel { get; private set; }
public ITextChannel OutputTextChannel { get; set; }
private bool Destroyed { get; set; } = false; private bool Destroyed { get; set; } = false;
public bool RepeatSong { get; private set; } = false; public bool RepeatSong { get; private set; } = false;
@ -84,10 +85,12 @@ namespace NadekoBot.Modules.Music.Classes
public event Action<Song, int> SongRemoved = delegate { }; public event Action<Song, int> SongRemoved = delegate { };
public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume) public MusicPlayer(IVoiceChannel startingVoiceChannel, ITextChannel outputChannel, float? defaultVolume)
{ {
if (startingVoiceChannel == null) if (startingVoiceChannel == null)
throw new ArgumentNullException(nameof(startingVoiceChannel)); throw new ArgumentNullException(nameof(startingVoiceChannel));
OutputTextChannel = outputChannel;
Volume = defaultVolume ?? 1.0f; Volume = defaultVolume ?? 1.0f;
PlaybackVoiceChannel = startingVoiceChannel; PlaybackVoiceChannel = startingVoiceChannel;

View File

@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Music.Classes
{ {
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube" Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
Provider = "YouTube", Provider = "YouTube",
Uri = video.Uri, Uri = await video.GetUriAsync().ConfigureAwait(false),
Query = link, Query = link,
ProviderType = musicType, ProviderType = musicType,
}); });

View File

@ -211,30 +211,30 @@ namespace NadekoBot.Modules.Music
{ {
int startAt = itemsPerPage * (curPage - 1); int startAt = itemsPerPage * (curPage - 1);
var number = 0 + startAt; var number = 0 + startAt;
var desc = string.Join("\n", musicPlayer.Playlist
.Skip(startAt)
.Take(itemsPerPage)
.Select(v => $"`{++number}.` {v.PrettyFullName}"));
if (currentSong != null)
desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc;
if (musicPlayer.RepeatSong)
desc = "🔂 Repeating Current Song\n\n" + desc;
else if (musicPlayer.RepeatPlaylist)
desc = "🔁 Repeating Playlist\n\n" + desc;
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Player Queue - Page {curPage}/{lastPage + 1}") .WithAuthor(eab => eab.WithName($"Player Queue - Page {curPage}/{lastPage + 1}")
.WithMusicIcon()) .WithMusicIcon())
.WithDescription(string.Join("\n", musicPlayer.Playlist .WithDescription(desc)
.Skip(startAt)
.Take(itemsPerPage)
.Select(v => $"`{++number}.` {v.PrettyFullName}")))
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " + $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
(musicPlayer.FairPlay ? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit"))) (musicPlayer.FairPlay ? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
.WithOkColor(); .WithOkColor();
if (musicPlayer.RepeatSong)
{
embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}");
}
else if (musicPlayer.RepeatPlaylist)
{
embed.WithTitle("🔁 Repeating Playlist");
}
if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
{
embed.WithTitle("🎵 Song queue is full!");
}
return embed; return embed;
}; };
await Context.Channel.SendPaginatedConfirmAsync(page, printAction, lastPage, false).ConfigureAwait(false); await Context.Channel.SendPaginatedConfirmAsync(page, printAction, lastPage, false).ConfigureAwait(false);
@ -712,13 +712,10 @@ namespace NadekoBot.Modules.Music
} }
//todo only author or owner
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task DeletePlaylist([Remainder] int id) public async Task DeletePlaylist([Remainder] int id)
{ {
bool success = false; bool success = false;
MusicPlaylist pl = null; MusicPlaylist pl = null;
try try
@ -747,7 +744,7 @@ namespace NadekoBot.Modules.Music
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex); _log.Warn(ex);
} }
} }
@ -800,6 +797,23 @@ namespace NadekoBot.Modules.Music
await Context.Channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false); await Context.Channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task SetMusicChannel()
{
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
{
await Context.Channel.SendErrorAsync("Music must be playing before you set an ouput channel.").ConfigureAwait(false);
return;
}
musicPlayer.OutputTextChannel = (ITextChannel)Context.Channel;
await Context.Channel.SendConfirmAsync("I will now output playing, finished, paused and removed songs in this channel.").ConfigureAwait(false);
}
public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal)
{ {
if (voiceCh == null || voiceCh.Guild != textCh.Guild) if (voiceCh == null || voiceCh.Guild != textCh.Guild)
@ -818,7 +832,7 @@ namespace NadekoBot.Modules.Music
{ {
vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume; vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume;
} }
var mp = new MusicPlayer(voiceCh, vol); var mp = new MusicPlayer(voiceCh, textCh, vol);
IUserMessage playingMessage = null; IUserMessage playingMessage = null;
IUserMessage lastFinishedMessage = null; IUserMessage lastFinishedMessage = null;
mp.OnCompleted += async (s, song) => mp.OnCompleted += async (s, song) =>
@ -828,7 +842,7 @@ namespace NadekoBot.Modules.Music
if (lastFinishedMessage != null) if (lastFinishedMessage != null)
lastFinishedMessage.DeleteAfter(0); lastFinishedMessage.DeleteAfter(0);
lastFinishedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor() lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon()) .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
.WithDescription(song.PrettyName) .WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))) .WithFooter(ef => ef.WithText(song.PrettyInfo)))
@ -836,7 +850,14 @@ namespace NadekoBot.Modules.Music
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.ProviderType == MusicType.Normal) if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.ProviderType == MusicType.Normal)
{ {
await QueueSong(await queuer.Guild.GetCurrentUserAsync(), textCh, voiceCh, (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false); var relatedVideos = (await NadekoBot.Google.GetRelatedVideosAsync(song.SongInfo.Query, 4)).ToList();
if(relatedVideos.Count > 0)
await QueueSong(await queuer.Guild.GetCurrentUserAsync(),
textCh,
voiceCh,
relatedVideos[new NadekoRandom().Next(0, relatedVideos.Count)],
silent,
musicType).ConfigureAwait(false);
} }
} }
catch { } catch { }
@ -853,7 +874,7 @@ namespace NadekoBot.Modules.Music
if (playingMessage != null) if (playingMessage != null)
playingMessage.DeleteAfter(0); playingMessage.DeleteAfter(0);
playingMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor() playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon()) .WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
.WithDescription(song.PrettyName) .WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))) .WithFooter(ef => ef.WithText(song.PrettyInfo)))
@ -867,9 +888,9 @@ namespace NadekoBot.Modules.Music
{ {
IUserMessage msg; IUserMessage msg;
if (paused) if (paused)
msg = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false); msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
else else
msg = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
if (msg != null) if (msg != null)
msg.DeleteAfter(10); msg.DeleteAfter(10);
@ -877,7 +898,6 @@ namespace NadekoBot.Modules.Music
catch { } catch { }
}; };
mp.SongRemoved += async (song, index) => mp.SongRemoved += async (song, index) =>
{ {
try try
@ -888,7 +908,7 @@ namespace NadekoBot.Modules.Music
.WithFooter(ef => ef.WithText(song.PrettyInfo)) .WithFooter(ef => ef.WithText(song.PrettyInfo))
.WithErrorColor(); .WithErrorColor();
await textCh.EmbedAsync(embed).ConfigureAwait(false); await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch { }

View File

@ -289,6 +289,6 @@ namespace NadekoBot.Modules.NSFW
public static Task<string> GetRule34ImageLink(string tag) => public static Task<string> GetRule34ImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34); Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34);
}
#endif #endif
}
} }

View File

@ -65,12 +65,12 @@ namespace NadekoBot.Modules.Permissions
{ {
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>()); var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>());
activeCds.RemoveWhere(ac => ac.Command == command.Aliases.First().ToLowerInvariant()); activeCds.RemoveWhere(ac => ac.Command == command.Aliases.First().ToLowerInvariant());
await channel.SendConfirmAsync($"🚮 Command **{command}** has no coooldown now and all existing cooldowns have been cleared.") await channel.SendConfirmAsync($"🚮 Command **{command.Aliases.First()}** has no coooldown now and all existing cooldowns have been cleared.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
else else
{ {
await channel.SendConfirmAsync($"✅ Command **{command}** now has a **{secs} {"seconds".SnPl(secs)}** cooldown.") await channel.SendConfirmAsync($"✅ Command **{command.Aliases.First()}** now has a **{secs} {"seconds".SnPl(secs)}** cooldown.")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }

View File

@ -1,4 +1,7 @@
using Discord; using AngleSharp;
using AngleSharp.Dom.Html;
using AngleSharp.Extensions;
using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
@ -8,6 +11,7 @@ using Newtonsoft.Json.Linq;
using NLog; using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -52,6 +56,116 @@ namespace NadekoBot.Modules.Searches
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29)); }, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
} }
[NadekoCommand, Usage, Description, Aliases]
[Priority(1)]
public async Task Mal([Remainder] string name)
{
if (string.IsNullOrWhiteSpace(name))
return;
var fullQueryLink = "https://myanimelist.net/profile/" + name;
var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
var imageElem = document.QuerySelector("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-left > div.user-profile > div.user-image > img");
var imageUrl = ((IHtmlImageElement)imageElem)?.Source ?? "http://icecream.me/uploads/870b03f36b59cc16ebfe314ef2dde781.png";
var stats = document.QuerySelectorAll("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-right > div#statistics > div.user-statistics-stats > div.stats > div.clearfix > ul.stats-status > li > span").Select(x => x.InnerHtml).ToList();
var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc");
var favAnime = "No favorite anime yet";
if (favorites[0].QuerySelector("p") == null)
favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
.Shuffle()
.Take(3)
.Select(x =>
{
var elem = (IHtmlAnchorElement)x;
return $"[{elem.InnerHtml}]({elem.Href})";
}));
//var favManga = "No favorite manga yet.";
//if (favorites[1].QuerySelector("p") == null)
// favManga = string.Join("\n", favorites[1].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
// .Take(3)
// .Select(x =>
// {
// var elem = (IHtmlAnchorElement)x;
// return $"[{elem.InnerHtml}]({elem.Href})";
// }));
var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li")
.Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml))
.ToList();
var daysAndMean = document.QuerySelectorAll("div.anime:nth-child(1) > div:nth-child(2) > div")
.Select(x => x.TextContent.Split(':').Select(y => y.Trim()).ToArray())
.ToArray();
var embed = new EmbedBuilder()
.WithOkColor()
.WithTitle($"{name}'s MAL profile")
.AddField(efb => efb.WithName("💚 Watching").WithValue(stats[0]).WithIsInline(true))
.AddField(efb => efb.WithName("💙 Completed").WithValue(stats[1]).WithIsInline(true));
if (info.Count < 3)
embed.AddField(efb => efb.WithName("💛 On-Hold").WithValue(stats[2]).WithIsInline(true));
embed
.AddField(efb => efb.WithName("💔 Dropped").WithValue(stats[3]).WithIsInline(true))
.AddField(efb => efb.WithName("⚪ Plan to watch").WithValue(stats[4]).WithIsInline(true))
.AddField(efb => efb.WithName("🕐 " + daysAndMean[0][0]).WithValue(daysAndMean[0][1]).WithIsInline(true))
.AddField(efb => efb.WithName("📊 " + daysAndMean[1][0]).WithValue(daysAndMean[1][1]).WithIsInline(true))
.AddField(efb => efb.WithName(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1).WithValue(info[0].Item2.TrimTo(20)).WithIsInline(true))
.AddField(efb => efb.WithName(MalInfoToEmoji(info[1].Item1) + " " + info[1].Item1).WithValue(info[1].Item2.TrimTo(20)).WithIsInline(true));
if (info.Count > 2)
embed.AddField(efb => efb.WithName(MalInfoToEmoji(info[2].Item1) + " " + info[2].Item1).WithValue(info[2].Item2.TrimTo(20)).WithIsInline(true));
//if(info.Count > 3)
// embed.AddField(efb => efb.WithName(MalInfoToEmoji(info[3].Item1) + " " + info[3].Item1).WithValue(info[3].Item2).WithIsInline(true))
embed
.WithDescription($@"
** https://myanimelist.net/animelist/{ name } **
**Top 3 Favorite Anime:**
{favAnime}"
//**[Manga List](https://myanimelist.net/mangalist/{name})**
//💚`Reading:` {stats[5]}
//💙`Completed:` {stats[6]}
//💔`Dropped:` {stats[8]}
//⚪`Plan to read:` {stats[9]}
//**Top 3 Favorite Manga:**
//{favManga}"
)
.WithUrl(fullQueryLink)
.WithImageUrl(imageUrl);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
private static string MalInfoToEmoji(string info) {
info = info.Trim().ToLowerInvariant();
switch (info)
{
case "gender":
return "🚁";
case "location":
return "🗺";
case "last online":
return "👥";
case "birthday":
return "📆";
default:
return "❔";
}
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(0)]
public Task Mal(IUser usr) => Mal(usr.Username);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Anime([Remainder] string query) public async Task Anime([Remainder] string query)
{ {

View File

@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Searches
MemoryStream ms = new MemoryStream(); MemoryStream ms = new MemoryStream();
res.CopyTo(ms); res.CopyTo(ms);
ms.Position = 0; ms.Position = 0;
await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link: **https://osu.ppy.sh/u/{Uri.EscapeDataString(usr)}\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false); await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link:** <https://new.ppy.sh/u/{Uri.EscapeDataString(usr)}>\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -46,11 +46,7 @@ namespace NadekoBot.Modules.Searches
.WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png") .WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png")
.AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true)) .AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) .AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true)) .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
.WithOkColor(); .WithOkColor();
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -64,9 +60,9 @@ namespace NadekoBot.Modules.Searches
.WithThumbnailUrl(rankimg) .WithThumbnailUrl(rankimg)
.AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true)) .AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) .AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true)) .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
@ -96,7 +92,6 @@ namespace NadekoBot.Modules.Searches
return null; return null;
} }
} }
} }
} }
} }

View File

@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Searches
var server = NadekoBot.Client.GetGuild(fs.GuildId); var server = NadekoBot.Client.GetGuild(fs.GuildId);
if (server == null) if (server == null)
return; return;
var channel = await server.GetTextChannelAsync(fs.ChannelId); var channel = server.GetTextChannel(fs.ChannelId);
if (channel == null) if (channel == null)
return; return;
try try

View File

@ -117,6 +117,23 @@ namespace NadekoBot.Modules.Searches
terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
try
{
var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link)
.WithImageUrl(res.Link)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
catch
{
_log.Warn("Falling back to Imgur search.");
var fullQueryLink = $"http://imgur.com/search?q={ terms }"; var fullQueryLink = $"http://imgur.com/search?q={ terms }";
var config = Configuration.Default.WithDefaultLoader(); var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink); var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
@ -143,6 +160,7 @@ namespace NadekoBot.Modules.Searches
.WithTitle(Context.User.Mention); .WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task RandomImage([Remainder] string terms = null) public async Task RandomImage([Remainder] string terms = null)
@ -150,7 +168,23 @@ namespace NadekoBot.Modules.Searches
terms = terms?.Trim(); terms = terms?.Trim();
if (string.IsNullOrWhiteSpace(terms)) if (string.IsNullOrWhiteSpace(terms))
return; return;
terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
try
{
var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link)
.WithImageUrl(res.Link)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
catch
{
_log.Warn("Falling back to Imgur");
terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
var fullQueryLink = $"http://imgur.com/search?q={ terms }"; var fullQueryLink = $"http://imgur.com/search?q={ terms }";
@ -179,6 +213,7 @@ namespace NadekoBot.Modules.Searches
.WithTitle(Context.User.Mention); .WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Lmgtfy([Remainder] string ffs = null) public async Task Lmgtfy([Remainder] string ffs = null)
@ -285,10 +320,10 @@ namespace NadekoBot.Modules.Searches
.ConfigureAwait(false); .ConfigureAwait(false);
try try
{ {
var items = JArray.Parse(response).Shuffle().ToList(); var items = JArray.Parse(response).ToArray();
if (items == null) if (items == null || items.Length == 0)
throw new KeyNotFoundException("Cannot find a card by that name"); throw new KeyNotFoundException("Cannot find a card by that name");
var item = items[0]; var item = items[new NadekoRandom().Next(0, items.Length)];
var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString()); var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString());
var cost = item["cost"].ToString(); var cost = item["cost"].ToString();
var desc = item["text"].ToString(); var desc = item["text"].ToString();
@ -343,6 +378,8 @@ namespace NadekoBot.Modules.Searches
if (items == null) if (items == null)
throw new KeyNotFoundException("Cannot find a card by that name"); throw new KeyNotFoundException("Cannot find a card by that name");
foreach (var item in items.Where(item => item.HasValues && item["img"] != null).Take(4)) foreach (var item in items.Where(item => item.HasValues && item["img"] != null).Take(4))
{
await Task.Run(async () =>
{ {
using (var sr = await http.GetStreamAsync(item["img"].ToString())) using (var sr = await http.GetStreamAsync(item["img"].ToString()))
{ {
@ -351,6 +388,7 @@ namespace NadekoBot.Modules.Searches
imgStream.Position = 0; imgStream.Position = 0;
images.Add(new ImageSharp.Image(imgStream)); images.Add(new ImageSharp.Image(imgStream));
} }
}).ConfigureAwait(false);
} }
string msg = null; string msg = null;
if (items.Count > 4) if (items.Count > 4)
@ -358,7 +396,7 @@ namespace NadekoBot.Modules.Searches
msg = "⚠ Found over 4 images. Showing random 4."; msg = "⚠ Found over 4 images. Showing random 4.";
} }
var ms = new MemoryStream(); var ms = new MemoryStream();
images.AsEnumerable().Merge().SaveAsPng(ms); await Task.Run(() => images.AsEnumerable().Merge().SaveAsPng(ms));
ms.Position = 0; ms.Position = 0;
await Context.Channel.SendFileAsync(ms, arg + ".png", msg).ConfigureAwait(false); await Context.Channel.SendFileAsync(ms, arg + ".png", msg).ConfigureAwait(false);
} }

View File

@ -50,7 +50,7 @@ namespace NadekoBot.Modules.Utility
} }
private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) => private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) =>
$"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content; $"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions();
public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>(); public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
private static Logger _log { get; } private static Logger _log { get; }

View File

@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Utility
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
if (guild.Emojis.Count() > 0) if (guild.Emojis.Count() > 0)
{ {
embed.AddField(fb => fb.WithName("**Custom Emojis**").WithValue(string.Join(" ", guild.Emojis.Select(e => $"{e.Name} <:{e.Name}:{e.Id}>")))); embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>"))));
} }
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
@ -97,8 +97,10 @@ namespace NadekoBot.Modules.Utility
.AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
.WithThumbnailUrl(user.RealAvatarUrl())
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
if (user.AvatarId != null)
embed.WithThumbnailUrl(user.RealAvatarUrl());
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
} }

View File

@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Utility
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
this.Repeater = repeater; this.Repeater = repeater;
this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannelAsync(repeater.ChannelId).GetAwaiter().GetResult(); this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId);
if (Channel == null) if (Channel == null)
return; return;
Task.Run(Run); Task.Run(Run);
@ -69,12 +69,12 @@ namespace NadekoBot.Modules.Utility
{ {
oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false);
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Forbidden) catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
{ {
_log.Warn("Missing permissions. Repeater stopped. ChannelId : {0}", Channel?.Id); _log.Warn("Missing permissions. Repeater stopped. ChannelId : {0}", Channel?.Id);
return; return;
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound)
{ {
_log.Warn("Channel not found. Repeater stopped. ChannelId : {0}", Channel?.Id); _log.Warn("Channel not found. Repeater stopped. ChannelId : {0}", Channel?.Id);
return; return;

View File

@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Utility
keyword = keyword.ToUpperInvariant(); keyword = keyword.ToUpperInvariant();
Quote quote; Quote quote;
using (var uow = DbHandler.Instance.GetUnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
quote = await uow.Quotes.GetRandomQuoteByKeywordAsync(Context.Guild.Id, keyword).ConfigureAwait(false); quote = await uow.Quotes.GetRandomQuoteByKeywordAsync(Context.Guild.Id, keyword).ConfigureAwait(false);
} }
@ -93,24 +93,31 @@ namespace NadekoBot.Modules.Utility
var isAdmin = ((IGuildUser)Context.Message.Author).GuildPermissions.Administrator; var isAdmin = ((IGuildUser)Context.Message.Author).GuildPermissions.Administrator;
keyword = keyword.ToUpperInvariant(); keyword = keyword.ToUpperInvariant();
var sucess = false;
string response; string response;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var qs = uow.Quotes.GetAllQuotesByKeyword(Context.Guild.Id, keyword); var qs = uow.Quotes.GetAllQuotesByKeyword(Context.Guild.Id, keyword)?.Where(elem => isAdmin || elem.AuthorId == Context.Message.Author.Id).ToArray();
if (qs == null || !qs.Any()) if (qs == null || !qs.Any())
{ {
await Context.Channel.SendErrorAsync("No quotes found.").ConfigureAwait(false); sucess = false;
return; response = "No quotes found which you can remove.";
} }
else
var q = qs.Shuffle().FirstOrDefault(elem => isAdmin || elem.AuthorId == Context.Message.Author.Id); {
var q = qs[new NadekoRandom().Next(0, qs.Length)];
uow.Quotes.Remove(q); uow.Quotes.Remove(q);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
sucess = true;
response = "🗑 **Deleted a random quote.**"; response = "🗑 **Deleted a random quote.**";
} }
}
if(sucess)
await Context.Channel.SendConfirmAsync(response); await Context.Channel.SendConfirmAsync(response);
else
await Context.Channel.SendErrorAsync(response);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -126,7 +133,7 @@ namespace NadekoBot.Modules.Utility
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var quotes = uow.Quotes.GetAllQuotesByKeyword(Context.Guild.Id, keyword); var quotes = uow.Quotes.GetAllQuotesByKeyword(Context.Guild.Id, keyword);
//todo kwoth please don't be complete retard
uow.Quotes.RemoveRange(quotes.ToArray());//wtf?! uow.Quotes.RemoveRange(quotes.ToArray());//wtf?!
await uow.CompleteAsync(); await uow.CompleteAsync();

View File

@ -68,9 +68,7 @@ namespace NadekoBot.Modules.Utility
} }
else else
{ {
var t = NadekoBot.Client.GetGuild(r.ServerId)?.GetTextChannelAsync(r.ChannelId).ConfigureAwait(false); ch = NadekoBot.Client.GetGuild(r.ServerId)?.GetTextChannel(r.ChannelId);
if (t != null)
ch = await t.Value;
} }
if (ch == null) if (ch == null)
return; return;

View File

@ -15,6 +15,8 @@ using System.Threading;
using ImageSharp; using ImageSharp;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using Newtonsoft.Json;
using Discord.WebSocket;
using NadekoBot.Services;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
@ -25,6 +27,7 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[OwnerOnly] [OwnerOnly]
public async Task RotateRoleColor(int timeout, IRole role, params string[] hexes) public async Task RotateRoleColor(int timeout, IRole role, params string[] hexes)
{ {
@ -112,19 +115,30 @@ namespace NadekoBot.Modules.Utility
game = game.Trim().ToUpperInvariant(); game = game.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(game)) if (string.IsNullOrWhiteSpace(game))
return; return;
var arr = (await (Context.Channel as IGuildChannel).Guild.GetUsersAsync())
var socketGuild = Context.Guild as SocketGuild;
if (socketGuild == null) {
_log.Warn("Can't cast guild to socket guild.");
return;
}
var rng = new NadekoRandom();
var arr = await Task.Run(() => socketGuild.Users
.Where(u => u.Game?.Name?.ToUpperInvariant() == game) .Where(u => u.Game?.Name?.ToUpperInvariant() == game)
.Select(u => u.Username) .Select(u => u.Username)
.ToList(); .OrderBy(x => rng.Next())
.Take(60)
.ToArray()).ConfigureAwait(false);
int i = 0; int i = 0;
if (!arr.Any()) if (arr.Length == 0)
await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false); await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false);
else else
{
await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2) await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2)
.Select(ig => string.Concat(ig.Select(el => $"• {el,-27}")))) + "\n```") .Select(ig => string.Concat(ig.Select(el => $"• {el,-27}")))) + "\n```")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]

View File

@ -75,7 +75,8 @@ namespace NadekoBot
//initialize Services //initialize Services
CommandService = new CommandService(new CommandServiceConfig() { CommandService = new CommandService(new CommandServiceConfig() {
CaseSensitiveCommands = false CaseSensitiveCommands = false,
DefaultRunMode = RunMode.Sync
}); });
Google = new GoogleApiService(); Google = new GoogleApiService();
CommandHandler = new CommandHandler(Client, CommandService); CommandHandler = new CommandHandler(Client, CommandService);

View File

@ -177,7 +177,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%. /// Looks up a localized string similar to Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds%.
/// </summary> /// </summary>
public static string addplaying_desc { public static string addplaying_desc {
get { get {
@ -879,7 +879,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you&apos;ve bet.. /// Looks up a localized string similar to Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you&apos;ve bet (rounded up). Multiplier can be changed by the bot owner..
/// </summary> /// </summary>
public static string betflip_desc { public static string betflip_desc {
get { get {
@ -2489,6 +2489,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to divorce.
/// </summary>
public static string divorce_cmd {
get {
return ResourceManager.GetString("divorce_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Releases your claim on a specific waifu. You will get some of the money you&apos;ve spent back unless that waifu has an affinity towards you. 6 hours cooldown..
/// </summary>
public static string divorce_desc {
get {
return ResourceManager.GetString("divorce_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}divorce @CheatingSloot`.
/// </summary>
public static string divorce_usage {
get {
return ResourceManager.GetString("divorce_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to donadd. /// Looks up a localized string similar to donadd.
/// </summary> /// </summary>
@ -4379,6 +4406,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to mal.
/// </summary>
public static string mal_cmd {
get {
return ResourceManager.GetString("mal_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows basic info from myanimelist profile..
/// </summary>
public static string mal_desc {
get {
return ResourceManager.GetString("mal_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}mal straysocks`.
/// </summary>
public static string mal_usage {
get {
return ResourceManager.GetString("mal_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to manga mang mq. /// Looks up a localized string similar to manga mang mq.
/// </summary> /// </summary>
@ -5091,7 +5145,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost). /// Looks up a localized string similar to Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost).
/// </summary> /// </summary>
public static string plant_desc { public static string plant_desc {
get { get {
@ -5100,7 +5154,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to `{0}plant`. /// Looks up a localized string similar to `{0}plant` or `{0}plant 5`.
/// </summary> /// </summary>
public static string plant_usage { public static string plant_usage {
get { get {
@ -6728,6 +6782,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to setmusicchannel smch.
/// </summary>
public static string setmusicchannel_cmd {
get {
return ResourceManager.GetString("setmusicchannel_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in..
/// </summary>
public static string setmusicchannel_desc {
get {
return ResourceManager.GetString("setmusicchannel_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}smch`.
/// </summary>
public static string setmusicchannel_usage {
get {
return ResourceManager.GetString("setmusicchannel_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to setmuterole. /// Looks up a localized string similar to setmuterole.
/// </summary> /// </summary>
@ -8286,7 +8367,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to `{0}voice+text`. /// Looks up a localized string similar to `{0}v+t`.
/// </summary> /// </summary>
public static string voiceplustext_usage { public static string voiceplustext_usage {
get { get {
@ -8375,6 +8456,114 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to claimwaifu claim.
/// </summary>
public static string waifuclaim_cmd {
get {
return ResourceManager.GetString("waifuclaim_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `{0}affinity` towards you..
/// </summary>
public static string waifuclaim_desc {
get {
return ResourceManager.GetString("waifuclaim_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}claim 50 @Himesama`.
/// </summary>
public static string waifuclaim_usage {
get {
return ResourceManager.GetString("waifuclaim_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to affinity.
/// </summary>
public static string waifuclaimeraffinity_cmd {
get {
return ResourceManager.GetString("waifuclaimeraffinity_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `{0}claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown..
/// </summary>
public static string waifuclaimeraffinity_desc {
get {
return ResourceManager.GetString("waifuclaimeraffinity_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}affinity @MyHusband` or `{0}affinity`.
/// </summary>
public static string waifuclaimeraffinity_usage {
get {
return ResourceManager.GetString("waifuclaimeraffinity_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to waifuinfo waifustats.
/// </summary>
public static string waifuinfo_cmd {
get {
return ResourceManager.GetString("waifuinfo_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows waifu stats for a target person. Defaults to you if no user is provided..
/// </summary>
public static string waifuinfo_desc {
get {
return ResourceManager.GetString("waifuinfo_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}waifuinfo @MyCrush` or `{0}waifuinfo`.
/// </summary>
public static string waifuinfo_usage {
get {
return ResourceManager.GetString("waifuinfo_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to waifus waifulb.
/// </summary>
public static string waifuleaderboard_cmd {
get {
return ResourceManager.GetString("waifuleaderboard_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows top 9 waifus..
/// </summary>
public static string waifuleaderboard_desc {
get {
return ResourceManager.GetString("waifuleaderboard_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}waifus`.
/// </summary>
public static string waifuleaderboard_usage {
get {
return ResourceManager.GetString("waifuleaderboard_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to weather we. /// Looks up a localized string similar to weather we.
/// </summary> /// </summary>

View File

@ -292,7 +292,7 @@
<value>addplaying adpl</value> <value>addplaying adpl</value>
</data> </data>
<data name="addplaying_desc" xml:space="preserve"> <data name="addplaying_desc" xml:space="preserve">
<value>Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%</value> <value>Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds%</value>
</data> </data>
<data name="addplaying_usage" xml:space="preserve"> <data name="addplaying_usage" xml:space="preserve">
<value>`{0}adpl`</value> <value>`{0}adpl`</value>
@ -340,7 +340,7 @@
<value>Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless.</value> <value>Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless.</value>
</data> </data>
<data name="voiceplustext_usage" xml:space="preserve"> <data name="voiceplustext_usage" xml:space="preserve">
<value>`{0}voice+text`</value> <value>`{0}v+t`</value>
</data> </data>
<data name="scsc_cmd" xml:space="preserve"> <data name="scsc_cmd" xml:space="preserve">
<value>scsc</value> <value>scsc</value>
@ -1183,7 +1183,7 @@
<value>betflip bf</value> <value>betflip bf</value>
</data> </data>
<data name="betflip_desc" xml:space="preserve"> <data name="betflip_desc" xml:space="preserve">
<value>Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet.</value> <value>Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you've bet (rounded up). Multiplier can be changed by the bot owner.</value>
</data> </data>
<data name="betflip_usage" xml:space="preserve"> <data name="betflip_usage" xml:space="preserve">
<value>`{0}bf 5 heads` or `{0}bf 3 t`</value> <value>`{0}bf 5 heads` or `{0}bf 3 t`</value>
@ -1372,10 +1372,10 @@
<value>plant</value> <value>plant</value>
</data> </data>
<data name="plant_desc" xml:space="preserve"> <data name="plant_desc" xml:space="preserve">
<value>Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost)</value> <value>Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost)</value>
</data> </data>
<data name="plant_usage" xml:space="preserve"> <data name="plant_usage" xml:space="preserve">
<value>`{0}plant`</value> <value>`{0}plant` or `{0}plant 5`</value>
</data> </data>
<data name="gencurrency_cmd" xml:space="preserve"> <data name="gencurrency_cmd" xml:space="preserve">
<value>gencurrency gc</value> <value>gencurrency gc</value>
@ -2979,4 +2979,67 @@
<data name="slot_usage" xml:space="preserve"> <data name="slot_usage" xml:space="preserve">
<value>`{0}slot 5`</value> <value>`{0}slot 5`</value>
</data> </data>
<data name="waifuclaimeraffinity_cmd" xml:space="preserve">
<value>affinity</value>
</data>
<data name="waifuclaimeraffinity_desc" xml:space="preserve">
<value>Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `{0}claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown.</value>
</data>
<data name="waifuclaimeraffinity_usage" xml:space="preserve">
<value>`{0}affinity @MyHusband` or `{0}affinity`</value>
</data>
<data name="waifuclaim_cmd" xml:space="preserve">
<value>claimwaifu claim</value>
</data>
<data name="waifuclaim_desc" xml:space="preserve">
<value>Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `{0}affinity` towards you.</value>
</data>
<data name="waifuclaim_usage" xml:space="preserve">
<value>`{0}claim 50 @Himesama`</value>
</data>
<data name="waifuleaderboard_cmd" xml:space="preserve">
<value>waifus waifulb</value>
</data>
<data name="waifuleaderboard_desc" xml:space="preserve">
<value>Shows top 9 waifus.</value>
</data>
<data name="waifuleaderboard_usage" xml:space="preserve">
<value>`{0}waifus`</value>
</data>
<data name="divorce_cmd" xml:space="preserve">
<value>divorce</value>
</data>
<data name="divorce_desc" xml:space="preserve">
<value>Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown.</value>
</data>
<data name="divorce_usage" xml:space="preserve">
<value>`{0}divorce @CheatingSloot`</value>
</data>
<data name="waifuinfo_cmd" xml:space="preserve">
<value>waifuinfo waifustats</value>
</data>
<data name="waifuinfo_desc" xml:space="preserve">
<value>Shows waifu stats for a target person. Defaults to you if no user is provided.</value>
</data>
<data name="waifuinfo_usage" xml:space="preserve">
<value>`{0}waifuinfo @MyCrush` or `{0}waifuinfo`</value>
</data>
<data name="mal_cmd" xml:space="preserve">
<value>mal</value>
</data>
<data name="mal_desc" xml:space="preserve">
<value>Shows basic info from myanimelist profile.</value>
</data>
<data name="mal_usage" xml:space="preserve">
<value>`{0}mal straysocks`</value>
</data>
<data name="setmusicchannel_cmd" xml:space="preserve">
<value>setmusicchannel smch</value>
</data>
<data name="setmusicchannel_desc" xml:space="preserve">
<value>Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in.</value>
</data>
<data name="setmusicchannel_usage" xml:space="preserve">
<value>`{0}smch`</value>
</data>
</root> </root>

View File

@ -32,7 +32,7 @@ namespace Services.CleverBotApi
#if GLOBAL_NADEKO #if GLOBAL_NADEKO
var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=nadekobot"; var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=nadekobot";
#else #else
var url = "http://www.cleverbot.com/webservicemin?uc=3210"; var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=chatterbotapi";
#endif #endif
switch (type) switch (type)

View File

@ -159,8 +159,8 @@ namespace NadekoBot.Services
private async Task<bool> WordFiltered(IGuild guild, SocketUserMessage usrMsg) private async Task<bool> WordFiltered(IGuild guild, SocketUserMessage usrMsg)
{ {
var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id); var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id); var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' '); var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0) if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
{ {
@ -197,8 +197,10 @@ namespace NadekoBot.Services
if (usrMsg == null) //has to be an user message, not system/other messages. if (usrMsg == null) //has to be an user message, not system/other messages.
return; return;
#if !GLOBAL_NADEKO
// track how many messagges each user is sending // track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
#endif
var channel = msg.Channel as SocketTextChannel; var channel = msg.Channel as SocketTextChannel;
var guild = channel?.Guild; var guild = channel?.Guild;
@ -215,19 +217,20 @@ namespace NadekoBot.Services
if (IsBlacklisted(guild, usrMsg)) if (IsBlacklisted(guild, usrMsg))
return; return;
var cleverBotRan = await TryRunCleverbot(usrMsg, guild).ConfigureAwait(false); var cleverBotRan = await Task.Run(() => TryRunCleverbot(usrMsg, guild)).ConfigureAwait(false);
if (cleverBotRan) if (cleverBotRan)
return; return;
// maybe this message is a custom reaction // maybe this message is a custom reaction
var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false); // todo log custom reaction executions. return struct with info
var crExecuted = await Task.Run(() => CustomReactions.TryExecuteCustomReaction(usrMsg)).ConfigureAwait(false);
if (crExecuted) //if it was, don't execute the command if (crExecuted) //if it was, don't execute the command
return; return;
string messageContent = usrMsg.Content; string messageContent = usrMsg.Content;
// execute the command and measure the time it took // execute the command and measure the time it took
var exec = await ExecuteCommand(new CommandContext(_client, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best); var exec = await Task.Run(() => ExecuteCommand(new CommandContext(_client, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best)).ConfigureAwait(false);
execTime = Environment.TickCount - execTime; execTime = Environment.TickCount - execTime;
if (exec.Result.IsSuccess) if (exec.Result.IsSuccess)

View File

@ -4,6 +4,7 @@ using Discord;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Gambling; using NadekoBot.Modules.Gambling;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Database;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
@ -19,13 +20,26 @@ namespace NadekoBot.Services
return success; return success;
} }
public static async Task<bool> RemoveCurrencyAsync(ulong authorId, string reason, long amount) public static async Task<bool> RemoveCurrencyAsync(ulong authorId, string reason, long amount, IUnitOfWork uow = null)
{ {
if (amount < 0) if (amount < 0)
throw new ArgumentNullException(nameof(amount)); throw new ArgumentNullException(nameof(amount));
using (var uow = DbHandler.UnitOfWork()) if (uow == null)
{
using (uow = DbHandler.UnitOfWork())
{
var toReturn = InternalRemoveCurrency(authorId, reason, amount, uow);
await uow.CompleteAsync().ConfigureAwait(false);
return toReturn;
}
}
return InternalRemoveCurrency(authorId, reason, amount, uow);
}
private static bool InternalRemoveCurrency(ulong authorId, string reason, long amount, IUnitOfWork uow)
{ {
var success = uow.Currency.TryUpdateState(authorId, -amount); var success = uow.Currency.TryUpdateState(authorId, -amount);
if (!success) if (!success)
@ -36,9 +50,6 @@ namespace NadekoBot.Services
Reason = reason, Reason = reason,
Amount = -amount, Amount = -amount,
}); });
await uow.CompleteAsync().ConfigureAwait(false);
}
return true; return true;
} }
@ -50,23 +61,30 @@ namespace NadekoBot.Services
try { await author.SendConfirmAsync($"`You received:` {amount} {NadekoBot.BotConfig.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { } try { await author.SendConfirmAsync($"`You received:` {amount} {NadekoBot.BotConfig.CurrencySign}\n`Reason:` {reason}").ConfigureAwait(false); } catch { }
} }
public static async Task AddCurrencyAsync(ulong receiverId, string reason, long amount) public static async Task AddCurrencyAsync(ulong receiverId, string reason, long amount, IUnitOfWork uow = null)
{ {
if (amount < 0) if (amount < 0)
throw new ArgumentNullException(nameof(amount)); throw new ArgumentNullException(nameof(amount));
var transaction = new CurrencyTransaction()
using (var uow = DbHandler.UnitOfWork())
{
uow.Currency.TryUpdateState(receiverId, amount);
uow.CurrencyTransactions.Add(new CurrencyTransaction()
{ {
UserId = receiverId, UserId = receiverId,
Reason = reason, Reason = reason,
Amount = amount, Amount = amount,
}); };
if (uow == null)
using (uow = DbHandler.UnitOfWork())
{
uow.Currency.TryUpdateState(receiverId, amount);
uow.CurrencyTransactions.Add(transaction);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
else
{
uow.Currency.TryUpdateState(receiverId, amount);
uow.CurrencyTransactions.Add(transaction);
}
} }
} }
} }

View File

@ -21,6 +21,8 @@ namespace NadekoBot.Services.Database
ICurrencyTransactionsRepository CurrencyTransactions { get; } ICurrencyTransactionsRepository CurrencyTransactions { get; }
IMusicPlaylistRepository MusicPlaylists { get; } IMusicPlaylistRepository MusicPlaylists { get; }
IPokeGameRepository PokeGame { get; } IPokeGameRepository PokeGame { get; }
IWaifuRepository Waifus { get; }
IDiscordUserRepository DiscordUsers { get; }
int Complete(); int Complete();
Task<int> CompleteAsync(); Task<int> CompleteAsync();

View File

@ -29,7 +29,7 @@ namespace NadekoBot.Services.Database.Models
public float BetflipMultiplier { get; set; } = 1.95f; public float BetflipMultiplier { get; set; } = 1.95f;
public int CurrencyDropAmount { get; set; } = 1; public int CurrencyDropAmount { get; set; } = 1;
public float Betroll67Multiplier { get; set; } = 2; public float Betroll67Multiplier { get; set; } = 2;
public float Betroll91Multiplier { get; set; } = 3; public float Betroll91Multiplier { get; set; } = 4;
public float Betroll100Multiplier { get; set; } = 10; public float Betroll100Multiplier { get; set; } = 10;
//public HashSet<CommandCost> CommandCosts { get; set; } = new HashSet<CommandCost>(); //public HashSet<CommandCost> CommandCosts { get; set; } = new HashSet<CommandCost>();

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Database.Models
{
public class DiscordUser : DbEntity
{
public ulong UserId { get; set; }
public string Username { get; set; }
public string Discriminator { get; set; }
public string AvatarId { get; set; }
public override string ToString() =>
Username + "#" + Discriminator;
}
}

View File

@ -0,0 +1,49 @@
using NadekoBot.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Database.Models
{
public class WaifuInfo : DbEntity
{
public int WaifuId { get; set; }
public DiscordUser Waifu { get; set; }
public int? ClaimerId { get; set; }
public DiscordUser Claimer { get; set; }
public int? AffinityId { get; set; }
public DiscordUser Affinity { get; set; }
public int Price { get; set; }
public override string ToString()
{
var claimer = "no one";
var status = "";
var waifuUsername = Waifu.Username.TrimTo(20);
var claimerUsername = Claimer?.Username.TrimTo(20);
if (Claimer != null)
{
claimer = $"{ claimerUsername }#{Claimer.Discriminator}";
}
if (AffinityId == null)
{
status = $"... but {waifuUsername}'s heart is empty";
}
else if (AffinityId == ClaimerId)
{
status = $"... and {waifuUsername} likes {claimerUsername} too <3";
}
else {
status = $"... but {waifuUsername}'s heart belongs to {Affinity.Username.TrimTo(20)}#{Affinity.Discriminator}";
}
return $"**{waifuUsername}#{Waifu.Discriminator}** - claimed by **{claimer}**\n\t{status}";
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Database.Models
{
public class WaifuUpdate : DbEntity
{
public int UserId { get; set; }
public DiscordUser User { get; set; }
public WaifuUpdateType UpdateType { get; set; }
public int? OldId { get; set; }
public DiscordUser Old { get; set; }
public int? NewId { get; set; }
public DiscordUser New { get; set; }
}
public enum WaifuUpdateType
{
AffinityChanged,
Claimed
}
}

View File

@ -3,9 +3,26 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace NadekoBot.Services.Database namespace NadekoBot.Services.Database
{ {
public class NadekoContextFactory : IDbContextFactory<NadekoContext>
{
/// <summary>
/// :\ Used for migrations
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
public NadekoContext Create(DbContextFactoryOptions options)
{
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite("Filename=./data/NadekoBot.db");
return new NadekoContext(optionsBuilder.Options);
}
}
public class NadekoContext : DbContext public class NadekoContext : DbContext
{ {
public DbSet<Quote> Quotes { get; set; } public DbSet<Quote> Quotes { get; set; }
@ -22,6 +39,7 @@ namespace NadekoBot.Services.Database
public DbSet<CustomReaction> CustomReactions { get; set; } public DbSet<CustomReaction> CustomReactions { get; set; }
public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; } public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; }
public DbSet<UserPokeTypes> PokeGame { get; set; } public DbSet<UserPokeTypes> PokeGame { get; set; }
public DbSet<WaifuUpdate> WaifuUpdates { get; set; }
//logging //logging
public DbSet<LogSetting> LogSettings { get; set; } public DbSet<LogSetting> LogSettings { get; set; }
@ -33,23 +51,15 @@ namespace NadekoBot.Services.Database
public DbSet<RaceAnimal> RaceAnimals { get; set; } public DbSet<RaceAnimal> RaceAnimals { get; set; }
public DbSet<ModulePrefix> ModulePrefixes { get; set; } public DbSet<ModulePrefix> ModulePrefixes { get; set; }
public NadekoContext() public NadekoContext() : base()
{ {
this.Database.Migrate();
} }
public NadekoContext(DbContextOptions options) : base(options) public NadekoContext(DbContextOptions options) : base(options)
{ {
this.Database.Migrate();
EnsureSeedData();
} }
////Uncomment this to db initialisation with dotnet ef migration add [module]
//protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//{
// optionsBuilder.UseSqlite("Filename=./data/NadekoBot.db");
//}
public void EnsureSeedData() public void EnsureSeedData()
{ {
if (!BotConfig.Any()) if (!BotConfig.Any())
@ -244,6 +254,24 @@ namespace NadekoBot.Services.Database
// .HasIndex(cp => cp.CommandName) // .HasIndex(cp => cp.CommandName)
// .IsUnique(); // .IsUnique();
#endregion #endregion
#region Waifus
var wi = modelBuilder.Entity<WaifuInfo>();
wi.HasOne(x => x.Waifu)
.WithOne();
// //.HasForeignKey<WaifuInfo>(w => w.WaifuId)
// //.IsRequired(true);
//wi.HasOne(x => x.Claimer)
// .WithOne();
// //.HasForeignKey<WaifuInfo>(w => w.ClaimerId)
// //.IsRequired(false);
var du = modelBuilder.Entity<DiscordUser>();
du.HasAlternateKey(w => w.UserId);
#endregion
} }
} }
} }

View File

@ -0,0 +1,15 @@
using Discord;
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Database.Repositories
{
public interface IDiscordUserRepository : IRepository<DiscordUser>
{
DiscordUser GetOrCreate(IUser original);
}
}

View File

@ -0,0 +1,13 @@
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Generic;
namespace NadekoBot.Services.Database.Repositories
{
public interface IWaifuRepository : IRepository<WaifuInfo>
{
IList<WaifuInfo> GetTop(int count);
WaifuInfo ByWaifuUserId(ulong userId);
IList<WaifuInfo> ByClaimerUserId(ulong userId);
}
}

View File

@ -0,0 +1,36 @@
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Discord;
namespace NadekoBot.Services.Database.Repositories.Impl
{
public class DiscordUserRepository : Repository<DiscordUser>, IDiscordUserRepository
{
public DiscordUserRepository(DbContext context) : base(context)
{
}
public DiscordUser GetOrCreate(IUser original)
{
DiscordUser toReturn;
toReturn = _set.FirstOrDefault(u => u.UserId == original.Id);
if (toReturn == null)
_set.Add(toReturn = new DiscordUser()
{
AvatarId = original.AvatarId,
Discriminator = original.Discriminator,
UserId = original.Id,
Username = original.Username,
});
return toReturn;
}
}
}

View File

@ -0,0 +1,49 @@
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace NadekoBot.Services.Database.Repositories.Impl
{
public class WaifuRepository : Repository<WaifuInfo>, IWaifuRepository
{
public WaifuRepository(DbContext context) : base(context)
{
}
public WaifuInfo ByWaifuUserId(ulong userId)
{
return _set.Include(wi => wi.Waifu)
.Include(wi => wi.Affinity)
.Include(wi => wi.Claimer)
.FirstOrDefault(wi => wi.Waifu.UserId == userId);
}
public IList<WaifuInfo> ByClaimerUserId(ulong userId)
{
return _set.Include(wi => wi.Waifu)
.Include(wi => wi.Affinity)
.Include(wi => wi.Claimer)
.Where(wi => wi.Claimer != null && wi.Claimer.UserId == userId)
.ToList();
}
public IList<WaifuInfo> GetTop(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count));
if (count == 0)
return new List<WaifuInfo>();
return _set.Include(wi => wi.Waifu)
.Include(wi => wi.Affinity)
.Include(wi => wi.Claimer)
.OrderByDescending(wi => wi.Price)
.Take(count)
.ToList();
}
}
}

View File

@ -48,6 +48,12 @@ namespace NadekoBot.Services.Database
private IPokeGameRepository _pokegame; private IPokeGameRepository _pokegame;
public IPokeGameRepository PokeGame => _pokegame ?? (_pokegame = new PokeGameRepository(_context)); public IPokeGameRepository PokeGame => _pokegame ?? (_pokegame = new PokeGameRepository(_context));
private IWaifuRepository _waifus;
public IWaifuRepository Waifus => _waifus ?? (_waifus = new WaifuRepository(_context));
private IDiscordUserRepository _discordUsers;
public IDiscordUserRepository DiscordUsers => _discordUsers ?? (_discordUsers = new DiscordUserRepository(_context));
public UnitOfWork(NadekoContext context) public UnitOfWork(NadekoContext context)
{ {
_context = context; _context = context;

View File

@ -1,4 +1,6 @@
using Microsoft.EntityFrameworkCore; using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using NadekoBot.Services.Database; using NadekoBot.Services.Database;
namespace NadekoBot.Services namespace NadekoBot.Services
@ -13,7 +15,8 @@ namespace NadekoBot.Services
static DbHandler() { } static DbHandler() { }
private DbHandler() { private DbHandler()
{
connectionString = NadekoBot.Credentials.Db.ConnectionString; connectionString = NadekoBot.Credentials.Db.ConnectionString;
var optionsBuilder = new DbContextOptionsBuilder(); var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite(NadekoBot.Credentials.Db.ConnectionString); optionsBuilder.UseSqlite(NadekoBot.Credentials.Db.ConnectionString);
@ -32,10 +35,16 @@ namespace NadekoBot.Services
//} //}
} }
public NadekoContext GetDbContext() => public NadekoContext GetDbContext()
new NadekoContext(options); {
var context = new NadekoContext(options);
context.Database.Migrate();
context.EnsureSeedData();
public IUnitOfWork GetUnitOfWork() => return context;
}
private IUnitOfWork GetUnitOfWork() =>
new UnitOfWork(GetDbContext()); new UnitOfWork(GetDbContext());
public static IUnitOfWork UnitOfWork() => public static IUnitOfWork UnitOfWork() =>

View File

@ -8,13 +8,19 @@ using System.Text.RegularExpressions;
using Google.Apis.Urlshortener.v1; using Google.Apis.Urlshortener.v1;
using Google.Apis.Urlshortener.v1.Data; using Google.Apis.Urlshortener.v1.Data;
using NLog; using NLog;
using Google.Apis.Customsearch.v1;
using Google.Apis.Customsearch.v1.Data;
namespace NadekoBot.Services.Impl namespace NadekoBot.Services.Impl
{ {
public class GoogleApiService : IGoogleApiService public class GoogleApiService : IGoogleApiService
{ {
const string search_engine_id = "018084019232060951019:hs5piey28-e";
private YouTubeService yt; private YouTubeService yt;
private UrlshortenerService sh; private UrlshortenerService sh;
private CustomsearchService cs;
private Logger _log { get; } private Logger _log { get; }
public GoogleApiService() public GoogleApiService()
@ -22,13 +28,14 @@ namespace NadekoBot.Services.Impl
var bcs = new BaseClientService.Initializer var bcs = new BaseClientService.Initializer
{ {
ApplicationName = "Nadeko Bot", ApplicationName = "Nadeko Bot",
ApiKey = NadekoBot.Credentials.GoogleApiKey ApiKey = NadekoBot.Credentials.GoogleApiKey,
}; };
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
yt = new YouTubeService(bcs); yt = new YouTubeService(bcs);
sh = new UrlshortenerService(bcs); sh = new UrlshortenerService(bcs);
cs = new CustomsearchService(bcs);
} }
public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1) public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1)
{ {
@ -150,7 +157,7 @@ namespace NadekoBot.Services.Impl
return toReturn; return toReturn;
} }
//todo AsyncEnumerable //todo AsyncEnumerable
public async Task<IReadOnlyDictionary<string,TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds) public async Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds)
{ {
var videoIdsList = videoIds as List<string> ?? videoIds.ToList(); var videoIdsList = videoIds as List<string> ?? videoIds.ToList();
@ -179,5 +186,34 @@ namespace NadekoBot.Services.Impl
return toReturn; return toReturn;
} }
public struct ImageResult
{
public Result.ImageData Image { get; }
public string Link { get; }
public ImageResult(Result.ImageData image, string link)
{
this.Image = image;
this.Link = link;
}
}
public async Task<ImageResult> GetImageAsync(string query, int start = 1)
{
if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query));
var req = cs.Cse.List(query);
req.Cx = search_engine_id;
req.Num = 1;
req.Fields = "items(image(contextLink,thumbnailLink),link)";
req.SearchType = CseResource.ListRequest.SearchTypeEnum.Image;
req.Start = start;
var search = await req.ExecuteAsync().ConfigureAwait(false);
return new ImageResult(search.Items[0].Image, search.Items[0].Link);
}
} }
} }

View File

@ -15,7 +15,7 @@ namespace NadekoBot.Services.Impl
private DiscordShardedClient client; private DiscordShardedClient client;
private DateTime started; private DateTime started;
public const string BotVersion = "1.1.3"; public const string BotVersion = "1.1.5a";
public string Author => "Kwoth#2560"; public string Author => "Kwoth#2560";
public string Library => "Discord.Net"; public string Library => "Discord.Net";

View File

@ -103,6 +103,9 @@ namespace NadekoBot.Extensions
http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
} }
public static string GetInitials(this string txt, string glue = "") =>
string.Join(glue, txt.Split(' ').Select(x => x.FirstOrDefault()));
public static DateTime ToUnixTimestamp(this double number) => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(number); public static DateTime ToUnixTimestamp(this double number) => new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(number);
public static EmbedBuilder WithOkColor(this EmbedBuilder eb) => public static EmbedBuilder WithOkColor(this EmbedBuilder eb) =>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -237,7 +237,7 @@
}, },
{ {
"Title":"Careers in Psychology: Opportunities in a Changing World", "Title":"Careers in Psychology: Opportunities in a Changing World",
"Text":"This text addresses the growing need among students and faculty for information about the careers available in psychology at the bachelor’s and graduate level." "Text":"This text addresses the growing need among students and faculty for information about the careers available in psychology at the bachelors and graduate level."
}, },
{ {
"Title":"Philosophy of Psychology", "Title":"Philosophy of Psychology",

View File

@ -18,7 +18,7 @@
}, },
"dependencies": { "dependencies": {
"AngleSharp": "0.9.9", "AngleSharp": "0.9.9",
"VideoLibrary": "1.3.4", "libvideo": "1.0.0",
"CoreCLR-NCalc": "2.1.2", "CoreCLR-NCalc": "2.1.2",
"Google.Apis.Urlshortener.v1": "1.19.0.138", "Google.Apis.Urlshortener.v1": "1.19.0.138",
"Google.Apis.YouTube.v3": "1.20.0.701", "Google.Apis.YouTube.v3": "1.20.0.701",