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>
<packageSources>
<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>
</configuration>

View File

@ -125,6 +125,14 @@ Command and aliases | Description | Usage
### Gambling
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`
`$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`
@ -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`
`$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`
`$startevent` | Starts one of the events seen on public nadeko. **Bot Owner only.** | `$startevent flowerreaction`
`$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`
`$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`
`!!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`
`!!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`
`!!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`
@ -226,7 +234,7 @@ 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`
`~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`
`~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`
@ -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`
`;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`
`;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`
`;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`

View File

@ -1,5 +1,5 @@
@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
SET root=%~dp0
CD /D %root%
@ -24,30 +24,43 @@ ECHO Downloading Nadeko...
ECHO.
git clone -b dev --recursive --depth 1 --progress https://github.com/Kwoth/NadekoBot.git >nul
IF %ERRORLEVEL% EQU 128 (GOTO :giterror)
TITLE Installing NadekoBot, please wait
TITLE Installing NadekoBot, please wait...
ECHO.
ECHO Installing...
ECHO Installing Discord.Net(1/4)...
::Building Nadeko
CD /D %build1%
dotnet restore >nul 2>&1
ECHO Installing Discord.Net(2/4)...
CD /D %build2%
dotnet restore >nul 2>&1
ECHO Installing Discord.Net(3/4)...
CD /D %build3%
dotnet restore >nul 2>&1
ECHO Installing Discord.Net(4/4)...
CD /D %build4%
dotnet restore >nul 2>&1
ECHO.
ECHO Discord.Net installation completed successfully...
ECHO.
ECHO Installing NadekoBot...
CD /D %build5%
dotnet restore >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
IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
:freshinstall
::Moves the NadekoBot folder to keep things tidy
ECHO.
ECHO Moving files, Please wait...
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end
IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
: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
ROBOCOPY "%root%NadekoBot" "%root%NadekoBot_Old" /MIR >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
@ -70,7 +83,7 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
RMDIR "%root%NadekoBot\" /S /Q >nul 2>&1
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end
IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
:dotnet
::Terminates the batch script if it can't run dotnet --version
TITLE Error!
@ -102,6 +115,25 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
PAUSE >nul 2>&1
CD /D "%root%"
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
::Normal execution of end of script
TITLE Installation complete!

View File

@ -1,5 +1,5 @@
@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
SET root=%~dp0
CD /D %root%
@ -24,30 +24,43 @@ ECHO Downloading Nadeko...
ECHO.
git clone -b master --recursive --depth 1 --progress https://github.com/Kwoth/NadekoBot.git >nul
IF %ERRORLEVEL% EQU 128 (GOTO :giterror)
TITLE Installing NadekoBot, please wait
TITLE Installing NadekoBot, please wait...
ECHO.
ECHO Installing...
ECHO Installing Discord.Net(1/4)...
::Building Nadeko
CD /D %build1%
dotnet restore >nul 2>&1
ECHO Installing Discord.Net(2/4)...
CD /D %build2%
dotnet restore >nul 2>&1
ECHO Installing Discord.Net(3/4)...
CD /D %build3%
dotnet restore >nul 2>&1
ECHO Installing Discord.Net(4/4)...
CD /D %build4%
dotnet restore >nul 2>&1
ECHO.
ECHO Discord.Net installation completed successfully...
ECHO.
ECHO Installing NadekoBot...
CD /D %build5%
dotnet restore >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
IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
:freshinstall
::Moves the NadekoBot folder to keep things tidy
ECHO.
ECHO Moving files, Please wait...
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end
IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
: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
ROBOCOPY "%root%NadekoBot" "%root%NadekoBot_Old" /MIR >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
@ -70,7 +83,7 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
RMDIR "%root%NadekoBot\" /S /Q >nul 2>&1
ROBOCOPY "%root%NadekoInstall_Temp" "%rootdir%" /E /MOVE >nul 2>&1
IF %ERRORLEVEL% GEQ 8 (GOTO :copyerror)
GOTO :end
IF EXIST "%PROGRAMFILES(X86)%" (GOTO 64BIT) ELSE (GOTO 32BIT)
:dotnet
::Terminates the batch script if it can't run dotnet --version
TITLE Error!
@ -102,6 +115,25 @@ IF EXIST "%root%NadekoBot\" (GOTO :backupinstall)
PAUSE >nul 2>&1
CD /D "%root%"
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
::Normal execution of end of script
TITLE Installation complete!

View File

@ -30,7 +30,7 @@ namespace NadekoBot.Migrations
name: "Betroll91Multiplier",
table: "BotConfig",
nullable: false,
defaultValue: 3f);
defaultValue: 4f);
migrationBuilder.AddColumn<int>(
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");
});
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 =>
{
b.Property<int>("Id")
@ -817,6 +837,55 @@ namespace NadekoBot.Migrations
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 =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig")
@ -982,6 +1051,38 @@ namespace NadekoBot.Migrations
.WithMany("RaceAnimals")
.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;
if (channel == null)
return;
if (DeleteMessagesOnCommand.Contains(channel.Guild.Id))
if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
await msg.DeleteAsync().ConfigureAwait(false);
}
catch (Exception ex)

View File

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

View File

@ -1,5 +1,6 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
@ -9,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration
@ -21,8 +23,8 @@ namespace NadekoBot.Modules.Administration
private static Logger _log { get; }
public static List<PlayingStatus> RotatingStatusMessages { get; }
public static bool RotatingStatuses { get; private set; } = false;
//todo wtf is with this while(true) in constructor
private static Timer _t { get; }
static PlayingRotateCommands()
{
_log = LogManager.GetCurrentClassLogger();
@ -30,46 +32,50 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
var t = Task.Run(async () =>
_t = new Timer(async (_) =>
{
var index = 0;
do
try
{
try
if (!RotatingStatuses)
return;
else
{
if (!RotatingStatuses)
continue;
else
{
if (index >= RotatingStatusMessages.Count)
index = 0;
if (index >= RotatingStatusMessages.Count)
index = 0;
if (!RotatingStatusMessages.Any())
continue;
var status = RotatingStatusMessages[index++].Status;
if (string.IsNullOrWhiteSpace(status))
continue;
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
await NadekoBot.Client.SetGameAsync(status).ConfigureAwait(false);
if (!RotatingStatusMessages.Any())
return;
var status = RotatingStatusMessages[index++].Status;
if (string.IsNullOrWhiteSpace(status))
return;
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
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)
{
_log.Warn("Rotating playing status errored.\n" + ex);
}
finally
{
await Task.Delay(TimeSpan.FromMinutes(1));
}
} while (true);
});
}
catch (Exception ex)
{
_log.Warn("Rotating playing status errored.\n" + ex);
}
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
new Dictionary<string, Func<string>> {
{"%servers%", () => NadekoBot.Client.GetGuildCount().ToString()},
{"%users%", () => NadekoBot.Client.GetGuilds().Sum(s => s.Users.Count).ToString()},
{"%playing%", () => {
{ "%servers%", () => NadekoBot.Client.GetGuildCount().ToString()},
{ "%users%", () => NadekoBot.Client.GetGuilds().Sum(s => s.Users.Count).ToString()},
{ "%playing%", () => {
var cnt = Music.Music.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
if (cnt != 1) return cnt.ToString();
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]
@ -153,4 +167,4 @@ namespace NadekoBot.Modules.Administration
}
}
}
}
}

View File

@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Administration
if (ids[1].ToUpperInvariant().StartsWith("C:"))
{
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)
{
return;
@ -159,9 +159,7 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly]
public async Task Announce([Remainder] string message)
{
var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g =>
g.GetDefaultChannelAsync()
)).ConfigureAwait(false);
var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray();
if (channels == null)
return;
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.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
@ -17,24 +19,76 @@ namespace NadekoBot.Modules.Administration
[Group]
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 ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache { get; } = new ConcurrentDictionary<ulong, GreetSettings>();
static ServerGreetCommands()
{
NadekoBot.Client.UserJoined += UserJoined;
NadekoBot.Client.UserLeft += UserLeft;
_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
private static async Task UserLeft(IGuildUser user)
{
try
{
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
var conf = GetOrAddSettingsForGuild(user.GuildId);
if (!conf.SendChannelByeMessage) return;
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
@ -62,11 +116,7 @@ namespace NadekoBot.Modules.Administration
{
try
{
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
var conf = GetOrAddSettingsForGuild(user.GuildId);
if (conf.SendChannelGreetMessage)
{
@ -133,6 +183,9 @@ namespace NadekoBot.Modules.Administration
var conf = uow.GuildConfigs.For(id, set => set);
conf.AutoDeleteGreetMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false);
}
}
@ -159,6 +212,9 @@ namespace NadekoBot.Modules.Administration
enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage;
conf.GreetMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false);
}
return enabled;
@ -201,6 +257,9 @@ namespace NadekoBot.Modules.Administration
conf.ChannelGreetMessageText = message;
greetMsgEnabled = conf.SendChannelGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete();
}
return greetMsgEnabled;
@ -227,6 +286,9 @@ namespace NadekoBot.Modules.Administration
var conf = uow.GuildConfigs.For(guildId, set => set);
enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false);
}
return enabled;
@ -269,6 +331,9 @@ namespace NadekoBot.Modules.Administration
conf.DmGreetMessageText = message;
greetMsgEnabled = conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete();
}
return greetMsgEnabled;
@ -296,6 +361,9 @@ namespace NadekoBot.Modules.Administration
enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage;
conf.ByeMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync();
}
return enabled;
@ -338,6 +406,9 @@ namespace NadekoBot.Modules.Administration
conf.ChannelByeMessageText = message;
byeMsgEnabled = conf.SendChannelByeMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete();
}
return byeMsgEnabled;
@ -356,16 +427,19 @@ namespace NadekoBot.Modules.Administration
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)
return;
using (var uow = DbHandler.UnitOfWork())
{
var conf = uow.GuildConfigs.For(id, set => set);
var conf = uow.GuildConfigs.For(guildId, set => set);
conf.AutoDeleteByeMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false);
}
}

View File

@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Administration
{
try
{
await (await guild.GetOwnerAsync()).SendErrorAsync(
await guild.Owner.SendErrorAsync(
"⚠️ I don't have **manage server** and/or **manage channels** permission," +
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
}
@ -75,16 +75,16 @@ namespace NadekoBot.Modules.Administration
var beforeVch = before.VoiceChannel;
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)
await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false);
}
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())
.FirstOrDefault();
if (textChannel == null)

View File

@ -36,10 +36,8 @@ namespace NadekoBot.Modules.ClashOfClans
.GetAllWars()
.Select(cw =>
{
cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId)
?.GetTextChannelAsync(cw.ChannelId)
.GetAwaiter()
.GetResult();
cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId)?
.GetTextChannel(cw.ChannelId);
return cw;
})
.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)
{
var channel = await NadekoBot.Client.GetGuild(serverId)?.GetTextChannelAsync(channelId);
var channel = NadekoBot.Client.GetGuild(serverId)?.GetTextChannel(channelId);
using (var uow = DbHandler.UnitOfWork())
{
var cw = new ClashWar

View File

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

View File

@ -1,9 +1,11 @@
using Discord;
using Discord.WebSocket;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
@ -25,9 +27,13 @@ namespace NadekoBot.Modules.CustomReactions
if(ch == null)
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(); } }
};

View File

@ -9,6 +9,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.WebSocket;
using NadekoBot.Services.Database;
namespace NadekoBot.Modules.Gambling
{
@ -19,15 +21,26 @@ namespace NadekoBot.Modules.Gambling
{
public enum CurrencyEvent
{
FlowerReaction
FlowerReaction,
SneakyGameStatus
}
//flower reaction event
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]
[RequireContext(ContextType.Guild)]
[OwnerOnly]
public async Task StartEvent(CurrencyEvent e)
public async Task StartEvent(CurrencyEvent e, int arg = -1)
{
var channel = (ITextChannel)Context.Channel;
try
@ -38,6 +51,9 @@ namespace NadekoBot.Modules.Gambling
case CurrencyEvent.FlowerReaction:
await FlowerReactionEvent(Context).ConfigureAwait(false);
break;
case CurrencyEvent.SneakyGameStatus:
await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
break;
default:
break;
}
@ -45,6 +61,63 @@ namespace NadekoBot.Modules.Gambling
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)
{
@ -59,14 +132,14 @@ namespace NadekoBot.Modules.Gambling
catch
{
try { await msg.DeleteAsync().ConfigureAwait(false); }
catch { }
catch { return; }
}
}
using (msg.OnReaction(async (r) =>
{
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 { }
}

View File

@ -184,7 +184,10 @@ namespace NadekoBot.Modules.Gambling
}
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;
}
Interlocked.Add(ref totalBet, amount);
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 membersArray = members as IUser[] ?? members.ToArray();
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]
@ -67,14 +67,14 @@ namespace NadekoBot.Modules.Gambling
{
if (amount <= 0 || Context.User.Id == receiver.Id)
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)
{
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false);
return;
}
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]
@ -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 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]
@ -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);
}
//[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]
public async Task BetRoll(long amount)
{
@ -193,22 +248,33 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases]
public async Task Leaderboard()
{
IEnumerable<Currency> richest = new List<Currency>();
var richest = new List<Currency>();
using (var uow = DbHandler.UnitOfWork())
{
richest = uow.Currency.GetTopRichest(10);
richest = uow.Currency.GetTopRichest(9).ToList();
}
if (!richest.Any())
return;
await Context.Channel.SendMessageAsync(
richest.Aggregate(new StringBuilder(
$@"```xl
Id $$$
"),
(cur, cs) => cur.AppendLine($@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━┫
{(Context.Guild.GetUserAsync(cs.UserId).GetAwaiter().GetResult()?.Username?.TrimTo(18, true) ?? cs.UserId.ToString()),-20} {cs.Amount,6} ")
).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━┛```").ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithTitle(NadekoBot.BotConfig.CurrencySign + " Leaderboard");
for (var i = 0; i < richest.Count; i++)
{
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>();
public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions;
private readonly ConcurrentHashSet<ulong> usersWhoSubmitted = new ConcurrentHashSet<ulong>();
private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>();
private int spamCount = 0;
@ -190,10 +191,6 @@ namespace NadekoBot.Modules.Games
try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
catch { }
}
//user didn't input something already
IGuildUser throwaway;
if (submissions.TryGetValue(input, out throwaway))
return;
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
@ -207,9 +204,15 @@ namespace NadekoBot.Modules.Games
return;
}
if (!usersWhoSubmitted.Add(guildUser.Id))
return;
//try adding it to the list of answers
if (!submissions.TryAdd(input, guildUser))
{
usersWhoSubmitted.TryRemove(guildUser.Id);
return;
}
// all good. valid input. answer recorded
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); }
}
public string GetHangman() => $@"\_\_\_\_\_\_\_\_\_
| |
| |
{(Errors > 0 ? "😲" : " ")} |
{(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} |
{(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} |
/-\";
public string GetHangman() => $@". ┌─────┐
................
................
.{(Errors > 0 ? ".............😲" : "")}
.{(Errors > 1 ? "............./" : "")} {(Errors > 2 ? "|" : "")} {(Errors > 3 ? "\\" : "")}
.{(Errors > 4 ? "............../" : "")} {(Errors > 5 ? "\\" : "")}
/-\";
public void Dispose()
{

View File

@ -61,7 +61,7 @@ namespace NadekoBot.Modules.Games
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]
[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)
{
await Context.Channel.SendErrorAsync($"You don't have any {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false);
@ -160,7 +163,7 @@ namespace NadekoBot.Modules.Games
IUserMessage msg;
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)
{
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);
}
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]

View File

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

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Games
[NadekoModule("Games", ">")]
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]
@ -37,7 +37,7 @@ namespace NadekoBot.Modules.Games
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.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]

View File

@ -71,6 +71,7 @@ namespace NadekoBot.Modules.Music.Classes
public event Action<bool> OnPauseChanged = delegate { };
public IVoiceChannel PlaybackVoiceChannel { get; private set; }
public ITextChannel OutputTextChannel { get; set; }
private bool Destroyed { get; 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 MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume)
public MusicPlayer(IVoiceChannel startingVoiceChannel, ITextChannel outputChannel, float? defaultVolume)
{
if (startingVoiceChannel == null)
throw new ArgumentNullException(nameof(startingVoiceChannel));
OutputTextChannel = outputChannel;
Volume = defaultVolume ?? 1.0f;
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"
Provider = "YouTube",
Uri = video.Uri,
Uri = await video.GetUriAsync().ConfigureAwait(false),
Query = link,
ProviderType = musicType,
});

View File

@ -211,30 +211,30 @@ namespace NadekoBot.Modules.Music
{
int startAt = itemsPerPage * (curPage - 1);
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()
.WithAuthor(eab => eab.WithName($"Player Queue - Page {curPage}/{lastPage + 1}")
.WithMusicIcon())
.WithDescription(string.Join("\n", musicPlayer.Playlist
.Skip(startAt)
.Take(itemsPerPage)
.Select(v => $"`{++number}.` {v.PrettyFullName}")))
.WithDescription(desc)
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
(musicPlayer.FairPlay ? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
.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;
};
await Context.Channel.SendPaginatedConfirmAsync(page, printAction, lastPage, false).ConfigureAwait(false);
@ -711,14 +711,11 @@ namespace NadekoBot.Modules.Music
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
//todo only author or owner
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task DeletePlaylist([Remainder] int id)
{
bool success = false;
MusicPlaylist pl = null;
try
@ -747,7 +744,7 @@ namespace NadekoBot.Modules.Music
}
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);
}
[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)
{
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;
}
var mp = new MusicPlayer(voiceCh, vol);
var mp = new MusicPlayer(voiceCh, textCh, vol);
IUserMessage playingMessage = null;
IUserMessage lastFinishedMessage = null;
mp.OnCompleted += async (s, song) =>
@ -828,7 +842,7 @@ namespace NadekoBot.Modules.Music
if (lastFinishedMessage != null)
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())
.WithDescription(song.PrettyName)
.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)
{
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 { }
@ -853,7 +874,7 @@ namespace NadekoBot.Modules.Music
if (playingMessage != null)
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())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
@ -862,22 +883,21 @@ namespace NadekoBot.Modules.Music
catch { }
};
mp.OnPauseChanged += async (paused) =>
{
try
{
IUserMessage msg;
if (paused)
msg = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
else
msg = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
if (msg != null)
msg.DeleteAfter(10);
}
catch { }
};
{
try
{
IUserMessage msg;
if (paused)
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
else
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
if (msg != null)
msg.DeleteAfter(10);
}
catch { }
};
mp.SongRemoved += async (song, index) =>
{
try
@ -888,7 +908,7 @@ namespace NadekoBot.Modules.Music
.WithFooter(ef => ef.WithText(song.PrettyInfo))
.WithErrorColor();
await textCh.EmbedAsync(embed).ConfigureAwait(false);
await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false);
}
catch { }

View File

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

View File

@ -65,12 +65,12 @@ namespace NadekoBot.Modules.Permissions
{
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>());
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);
}
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);
}
}

View File

@ -1,4 +1,7 @@
using Discord;
using AngleSharp;
using AngleSharp.Dom.Html;
using AngleSharp.Extensions;
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
@ -8,6 +11,7 @@ using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
@ -52,6 +56,116 @@ namespace NadekoBot.Modules.Searches
}, 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]
public async Task Anime([Remainder] string query)
{

View File

@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Searches
MemoryStream ms = new MemoryStream();
res.CopyTo(ms);
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)
{

View File

@ -1,102 +1,97 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json;
using NLog;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches
{
public partial class Searches
{
[Group]
public class OverwatchCommands : ModuleBase
{
private readonly Logger _log;
public OverwatchCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Overwatch(string region, [Remainder] string query = null)
{
if (string.IsNullOrWhiteSpace(query))
return;
var battletag = Regex.Replace(query, "#", "-", RegexOptions.IgnoreCase);
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
try
{
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
var model = await GetProfile(region, battletag);
var rankimg = $"{model.Competitive.rank_img}";
var rank = $"{model.Competitive.rank}";
var competitiveplay = $"{model.Games.Competitive.played}";
if (string.IsNullOrWhiteSpace(rank))
{
var embed = new EmbedBuilder()
.WithAuthor(eau => eau.WithName($"{model.username}")
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}"))
.WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png")
.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("**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 Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
.WithOkColor();
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
else
{
var embed = new EmbedBuilder()
.WithAuthor(eau => eau.WithName($"{model.username}")
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}"))
.WithThumbnailUrl(rankimg)
.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("**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(rank).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))
.WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
return;
}
}
catch
{
await Context.Channel.SendErrorAsync("Found no user! Please check the **Region** and **BattleTag** before trying again.");
}
}
public async Task<OverwatchApiModel.OverwatchPlayer.Data> GetProfile(string region, string battletag)
{
try
{
using (var http = new HttpClient())
{
var Url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile");
var model = JsonConvert.DeserializeObject<OverwatchApiModel.OverwatchPlayer>(Url);
return model.data;
}
}
catch
{
return null;
}
}
}
}
}
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json;
using NLog;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Searches
{
public partial class Searches
{
[Group]
public class OverwatchCommands : ModuleBase
{
private readonly Logger _log;
public OverwatchCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Overwatch(string region, [Remainder] string query = null)
{
if (string.IsNullOrWhiteSpace(query))
return;
var battletag = Regex.Replace(query, "#", "-", RegexOptions.IgnoreCase);
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
try
{
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
var model = await GetProfile(region, battletag);
var rankimg = $"{model.Competitive.rank_img}";
var rank = $"{model.Competitive.rank}";
var competitiveplay = $"{model.Games.Competitive.played}";
if (string.IsNullOrWhiteSpace(rank))
{
var embed = new EmbedBuilder()
.WithAuthor(eau => eau.WithName($"{model.username}")
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}"))
.WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png")
.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("**Competitive Rank**").WithValue("0").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
.WithOkColor();
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
else
{
var embed = new EmbedBuilder()
.WithAuthor(eau => eau.WithName($"{model.username}")
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}"))
.WithThumbnailUrl(rankimg)
.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("**Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").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 Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))
.WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
return;
}
}
catch
{
await Context.Channel.SendErrorAsync("Found no user! Please check the **Region** and **BattleTag** before trying again.");
}
}
public async Task<OverwatchApiModel.OverwatchPlayer.Data> GetProfile(string region, string battletag)
{
try
{
using (var http = new HttpClient())
{
var Url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile");
var model = JsonConvert.DeserializeObject<OverwatchApiModel.OverwatchPlayer>(Url);
return model.data;
}
}
catch
{
return null;
}
}
}
}
}

View File

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

View File

@ -117,31 +117,49 @@ namespace NadekoBot.Modules.Searches
terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
var fullQueryLink = $"http://imgur.com/search?q={ terms }";
var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
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 elems = document.QuerySelectorAll("a.image-list-link");
var fullQueryLink = $"http://imgur.com/search?q={ terms }";
var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
if (!elems.Any())
return;
var elems = document.QuerySelectorAll("a.image-list-link");
var img = (elems.FirstOrDefault()?.Children?.FirstOrDefault() as IHtmlImageElement);
if (!elems.Any())
return;
if (img?.Source == null)
return;
var img = (elems.FirstOrDefault()?.Children?.FirstOrDefault() as IHtmlImageElement);
var source = img.Source.Replace("b.", ".");
if (img?.Source == null)
return;
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl(fullQueryLink)
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
.WithDescription(source)
.WithImageUrl(source)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
var source = img.Source.Replace("b.", ".");
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl(fullQueryLink)
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
.WithDescription(source)
.WithImageUrl(source)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -150,34 +168,51 @@ namespace NadekoBot.Modules.Searches
terms = terms?.Trim();
if (string.IsNullOrWhiteSpace(terms))
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(' ', '+');
var fullQueryLink = $"http://imgur.com/search?q={ terms }";
var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
var fullQueryLink = $"http://imgur.com/search?q={ terms }";
var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
var elems = document.QuerySelectorAll("a.image-list-link").ToList();
var elems = document.QuerySelectorAll("a.image-list-link").ToList();
if (!elems.Any())
return;
if (!elems.Any())
return;
var img = (elems.ElementAtOrDefault(new NadekoRandom().Next(0, elems.Count))?.Children?.FirstOrDefault() as IHtmlImageElement);
var img = (elems.ElementAtOrDefault(new NadekoRandom().Next(0, elems.Count))?.Children?.FirstOrDefault() as IHtmlImageElement);
if (img?.Source == null)
return;
if (img?.Source == null)
return;
var source = img.Source.Replace("b.", ".");
var source = img.Source.Replace("b.", ".");
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl(fullQueryLink)
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
.WithDescription(source)
.WithImageUrl(source)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl(fullQueryLink)
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
.WithDescription(source)
.WithImageUrl(source)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -260,7 +295,7 @@ namespace NadekoBot.Modules.Searches
.WithTitle(Context.User.Mention)
.WithFooter(efb => efb.WithText(totalResults));
var desc = await Task.WhenAll(results.Select(async res =>
var desc = await Task.WhenAll(results.Select(async res =>
$"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n"))
.ConfigureAwait(false);
await Context.Channel.EmbedAsync(embed.WithDescription(String.Concat(desc))).ConfigureAwait(false);
@ -285,10 +320,10 @@ namespace NadekoBot.Modules.Searches
.ConfigureAwait(false);
try
{
var items = JArray.Parse(response).Shuffle().ToList();
if (items == null)
var items = JArray.Parse(response).ToArray();
if (items == null || items.Length == 0)
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 cost = item["cost"].ToString();
var desc = item["text"].ToString();
@ -344,13 +379,16 @@ namespace NadekoBot.Modules.Searches
throw new KeyNotFoundException("Cannot find a card by that name");
foreach (var item in items.Where(item => item.HasValues && item["img"] != null).Take(4))
{
using (var sr = await http.GetStreamAsync(item["img"].ToString()))
await Task.Run(async () =>
{
var imgStream = new MemoryStream();
await sr.CopyToAsync(imgStream);
imgStream.Position = 0;
images.Add(new ImageSharp.Image(imgStream));
}
using (var sr = await http.GetStreamAsync(item["img"].ToString()))
{
var imgStream = new MemoryStream();
await sr.CopyToAsync(imgStream);
imgStream.Position = 0;
images.Add(new ImageSharp.Image(imgStream));
}
}).ConfigureAwait(false);
}
string msg = null;
if (items.Count > 4)
@ -358,7 +396,7 @@ namespace NadekoBot.Modules.Searches
msg = "⚠ Found over 4 images. Showing random 4.";
}
var ms = new MemoryStream();
images.AsEnumerable().Merge().SaveAsPng(ms);
await Task.Run(() => images.AsEnumerable().Merge().SaveAsPng(ms));
ms.Position = 0;
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) =>
$"**{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>>();
private static Logger _log { get; }

View File

@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Utility
.WithColor(NadekoBot.OkColor);
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);
}
@ -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 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))
.WithThumbnailUrl(user.RealAvatarUrl())
.WithColor(NadekoBot.OkColor);
if (user.AvatarId != null)
embed.WithThumbnailUrl(user.RealAvatarUrl());
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
}

View File

@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Utility
{
_log = LogManager.GetCurrentClassLogger();
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)
return;
Task.Run(Run);
@ -69,12 +69,12 @@ namespace NadekoBot.Modules.Utility
{
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);
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);
return;

View File

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

View File

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

View File

@ -15,6 +15,8 @@ using System.Threading;
using ImageSharp;
using System.Collections.Generic;
using Newtonsoft.Json;
using Discord.WebSocket;
using NadekoBot.Services;
namespace NadekoBot.Modules.Utility
{
@ -25,6 +27,7 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)]
[OwnerOnly]
public async Task RotateRoleColor(int timeout, IRole role, params string[] hexes)
{
@ -112,18 +115,29 @@ namespace NadekoBot.Modules.Utility
game = game.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(game))
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)
.Select(u => u.Username)
.ToList();
.OrderBy(x => rng.Next())
.Take(60)
.ToArray()).ConfigureAwait(false);
int i = 0;
if (!arr.Any())
if (arr.Length == 0)
await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false);
else
{
await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2)
.Select(ig => string.Concat(ig.Select(el => $"• {el,-27}")))) + "\n```")
.ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -344,4 +358,4 @@ namespace NadekoBot.Modules.Utility
await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false), title, title).ConfigureAwait(false);
}
}
}
}

View File

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

View File

@ -177,7 +177,7 @@ namespace NadekoBot.Resources {
}
/// <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>
public static string addplaying_desc {
get {
@ -879,7 +879,7 @@ namespace NadekoBot.Resources {
}
/// <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>
public static string betflip_desc {
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>
/// Looks up a localized string similar to donadd.
/// </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>
/// Looks up a localized string similar to manga mang mq.
/// </summary>
@ -5091,7 +5145,7 @@ namespace NadekoBot.Resources {
}
/// <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>
public static string plant_desc {
get {
@ -5100,7 +5154,7 @@ namespace NadekoBot.Resources {
}
/// <summary>
/// Looks up a localized string similar to `{0}plant`.
/// Looks up a localized string similar to `{0}plant` or `{0}plant 5`.
/// </summary>
public static string plant_usage {
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>
/// Looks up a localized string similar to setmuterole.
/// </summary>
@ -8286,7 +8367,7 @@ namespace NadekoBot.Resources {
}
/// <summary>
/// Looks up a localized string similar to `{0}voice+text`.
/// Looks up a localized string similar to `{0}v+t`.
/// </summary>
public static string voiceplustext_usage {
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>
/// Looks up a localized string similar to weather we.
/// </summary>

View File

@ -292,7 +292,7 @@
<value>addplaying adpl</value>
</data>
<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 name="addplaying_usage" xml:space="preserve">
<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>
</data>
<data name="voiceplustext_usage" xml:space="preserve">
<value>`{0}voice+text`</value>
<value>`{0}v+t`</value>
</data>
<data name="scsc_cmd" xml:space="preserve">
<value>scsc</value>
@ -1183,7 +1183,7 @@
<value>betflip bf</value>
</data>
<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 name="betflip_usage" xml:space="preserve">
<value>`{0}bf 5 heads` or `{0}bf 3 t`</value>
@ -1372,10 +1372,10 @@
<value>plant</value>
</data>
<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 name="plant_usage" xml:space="preserve">
<value>`{0}plant`</value>
<value>`{0}plant` or `{0}plant 5`</value>
</data>
<data name="gencurrency_cmd" xml:space="preserve">
<value>gencurrency gc</value>
@ -2979,4 +2979,67 @@
<data name="slot_usage" xml:space="preserve">
<value>`{0}slot 5`</value>
</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>

View File

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

View File

@ -159,8 +159,8 @@ namespace NadekoBot.Services
private async Task<bool> WordFiltered(IGuild guild, SocketUserMessage usrMsg)
{
var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id);
var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id);
var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
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.
return;
#if !GLOBAL_NADEKO
// track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
#endif
var channel = msg.Channel as SocketTextChannel;
var guild = channel?.Guild;
@ -215,19 +217,20 @@ namespace NadekoBot.Services
if (IsBlacklisted(guild, usrMsg))
return;
var cleverBotRan = await TryRunCleverbot(usrMsg, guild).ConfigureAwait(false);
var cleverBotRan = await Task.Run(() => TryRunCleverbot(usrMsg, guild)).ConfigureAwait(false);
if (cleverBotRan)
return;
// 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
return;
string messageContent = usrMsg.Content;
// 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;
if (exec.Result.IsSuccess)

View File

@ -4,6 +4,7 @@ using Discord;
using NadekoBot.Extensions;
using NadekoBot.Modules.Gambling;
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Database;
namespace NadekoBot.Services
{
@ -19,26 +20,36 @@ namespace NadekoBot.Services
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)
throw new ArgumentNullException(nameof(amount));
using (var uow = DbHandler.UnitOfWork())
if (uow == null)
{
var success = uow.Currency.TryUpdateState(authorId, -amount);
if (!success)
return false;
uow.CurrencyTransactions.Add(new CurrencyTransaction()
using (uow = DbHandler.UnitOfWork())
{
UserId = authorId,
Reason = reason,
Amount = -amount,
});
await uow.CompleteAsync().ConfigureAwait(false);
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);
if (!success)
return false;
uow.CurrencyTransactions.Add(new CurrencyTransaction()
{
UserId = authorId,
Reason = reason,
Amount = -amount,
});
return true;
}
@ -50,22 +61,29 @@ namespace NadekoBot.Services
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)
throw new ArgumentNullException(nameof(amount));
var transaction = new CurrencyTransaction()
{
UserId = receiverId,
Reason = reason,
Amount = amount,
};
using (var uow = DbHandler.UnitOfWork())
if (uow == null)
using (uow = DbHandler.UnitOfWork())
{
uow.Currency.TryUpdateState(receiverId, amount);
uow.CurrencyTransactions.Add(transaction);
await uow.CompleteAsync();
}
else
{
uow.Currency.TryUpdateState(receiverId, amount);
uow.CurrencyTransactions.Add(new CurrencyTransaction()
{
UserId = receiverId,
Reason = reason,
Amount = amount,
});
await uow.CompleteAsync();
uow.CurrencyTransactions.Add(transaction);
}
}
}

View File

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

View File

@ -29,7 +29,7 @@ namespace NadekoBot.Services.Database.Models
public float BetflipMultiplier { get; set; } = 1.95f;
public int CurrencyDropAmount { get; set; } = 1;
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 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 NadekoBot.Services.Database.Models;
using NadekoBot.Extensions;
using Microsoft.EntityFrameworkCore.Infrastructure;
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 DbSet<Quote> Quotes { get; set; }
@ -22,6 +39,7 @@ namespace NadekoBot.Services.Database
public DbSet<CustomReaction> CustomReactions { get; set; }
public DbSet<CurrencyTransaction> CurrencyTransactions { get; set; }
public DbSet<UserPokeTypes> PokeGame { get; set; }
public DbSet<WaifuUpdate> WaifuUpdates { get; set; }
//logging
public DbSet<LogSetting> LogSettings { get; set; }
@ -33,23 +51,15 @@ namespace NadekoBot.Services.Database
public DbSet<RaceAnimal> RaceAnimals { get; set; }
public DbSet<ModulePrefix> ModulePrefixes { get; set; }
public NadekoContext()
public NadekoContext() : base()
{
this.Database.Migrate();
}
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()
{
if (!BotConfig.Any())
@ -244,6 +254,24 @@ namespace NadekoBot.Services.Database
// .HasIndex(cp => cp.CommandName)
// .IsUnique();
#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;
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)
{
_context = context;

View File

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

View File

@ -8,13 +8,19 @@ using System.Text.RegularExpressions;
using Google.Apis.Urlshortener.v1;
using Google.Apis.Urlshortener.v1.Data;
using NLog;
using Google.Apis.Customsearch.v1;
using Google.Apis.Customsearch.v1.Data;
namespace NadekoBot.Services.Impl
{
public class GoogleApiService : IGoogleApiService
{
const string search_engine_id = "018084019232060951019:hs5piey28-e";
private YouTubeService yt;
private UrlshortenerService sh;
private CustomsearchService cs;
private Logger _log { get; }
public GoogleApiService()
@ -22,13 +28,14 @@ namespace NadekoBot.Services.Impl
var bcs = new BaseClientService.Initializer
{
ApplicationName = "Nadeko Bot",
ApiKey = NadekoBot.Credentials.GoogleApiKey
ApiKey = NadekoBot.Credentials.GoogleApiKey,
};
_log = LogManager.GetCurrentClassLogger();
yt = new YouTubeService(bcs);
sh = new UrlshortenerService(bcs);
cs = new CustomsearchService(bcs);
}
public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1)
{
@ -150,7 +157,7 @@ namespace NadekoBot.Services.Impl
return toReturn;
}
//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();
@ -179,5 +186,34 @@ namespace NadekoBot.Services.Impl
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 DateTime started;
public const string BotVersion = "1.1.3";
public const string BotVersion = "1.1.5a";
public string Author => "Kwoth#2560";
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");
}
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 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",
"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",

View File

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