Compare commits

...

23 Commits
release ... 1.4

Author SHA1 Message Date
ce783c3f75
Merge pull request #4 from mattburchett/revert-3-1.9
Revert "1.9 2017/11/30"
2017-11-30 19:16:55 -06:00
0589728f58
Revert "1.9 2017/11/30" 2017-11-30 19:16:48 -06:00
be9352bc03
Merge pull request #3 from mattburchett/1.9
1.9 2017/11/30
2017-11-30 19:15:58 -06:00
af26fcfa5f Merge pull request #2 from mattburchett/weather2
Weather2
2017-09-21 10:00:47 -07:00
Matt
7a73869a9f Changing commas to slashes 2017-09-21 10:23:10 -05:00
Matt
b1987be30c Fixing again. 2017-09-21 10:17:02 -05:00
Matt
cdd5928028 Adding a line break, maybe 2017-09-21 09:55:15 -05:00
c3a93b17dc Merge pull request #1 from mattburchett/weather2
Weather2
2017-09-20 19:27:57 -07:00
unknown
55c4691933 Fixing 2017-09-20 21:27:14 -05:00
unknown
cf2532af9a Typo 2017-09-20 21:27:03 -05:00
unknown
89a6adb389 Fixing typo. 2017-09-20 21:19:43 -05:00
unknown
b66ba51e0d Fixing? 2017-09-20 21:09:39 -05:00
unknown
f20ccd01f4 Fixing? 2017-09-20 21:06:44 -05:00
unknown
8842a86b30 Adding Weather Changes to include Fehrenheit as well as Celsius? 2017-09-20 20:32:25 -05:00
Master Kwoth
0234df0844 Merge pull request #1602 from shivaco/patch-2
Re-phrase, etc.
2017-09-16 21:10:09 +02:00
shivaco
68a92426c2 Re-phrased, added gif for using .permrole, etc. 2017-09-16 21:28:45 +06:00
shivaco
f5eaa4f335 Totally not noob at Markdowning 2017-09-16 21:23:56 +06:00
Master Kwoth
7923c32323 Merge pull request #1601 from shivaco/patch-2
Markdown edits and re-phrase
2017-09-16 10:36:03 +02:00
shivaco
cf5756e0a8 Some edits in Markdown and a bit of re-phrase 2017-09-16 14:12:53 +06:00
Master Kwoth
51f9ae9e3b Merge pull request #1593 from shivaco/patch-3
Fixed the mistype + added hyperlink
2017-09-15 19:38:19 +02:00
Master Kwoth
eef5ad0c36 Merge pull request #1598 from numbermaniac/1.4
FAQ.md: Grammar/spelling and sentence structure
2017-09-15 19:27:38 +02:00
numbermaniac
c3755055a1 FAQ.md: Grammar/spelling and sentence structure 2017-09-15 19:42:26 +10:00
shivaco
494d8405b8 Fixed the mistype + added hyperlink 2017-09-13 23:19:07 +06:00
58 changed files with 837 additions and 6861 deletions

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.3
VisualStudioVersion = 15.0.26430.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}"
EndProject
@ -33,7 +33,4 @@ Global
GlobalSection(NestedProjects) = preSolution
{45EC1473-C678-4857-A544-07DFE0D0B478} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F3F555C-855F-4BE8-B526-D062D3E8ACA4}
EndGlobalSection
EndGlobal

View File

@ -56,13 +56,13 @@ Follow this Detailed [Guide](http://discord.kongslien.net/guide.html).
###Question 12: I'm building NadekoBot from source, but I get hundreds of (namespace) errors without changing anything!?
-----
**Answer:** Using Visual Studio, you can solve these errors by going to `Tools` -> `NuGet Package Manager` -> `Manage NuGet Packages for Solution`. Go to the Installed tab, select the Packages that were missing (usually `Newtonsoft.json` and `RestSharp`) and install them for all projects
**Answer:** Using Visual Studio, you can solve these errors by going to `Tools` -> `NuGet Package Manager` -> `Manage NuGet Packages for Solution`. Go to the Installed tab, select the Packages that were missing (usually `Newtonsoft.json` and `RestSharp`) and install them for all projects.
###Question 13: My bot has all permissions but it's still saying, "Failed to add roles. Bot has insufficient permissions". How do I fix this?
----------
**Answer:** Discord has added few new features and the roles now follows the role hierarchy which means you need to place your bot's role above every-other role your server has to fix the role hierarchy issue. [Here's](https://support.discordapp.com/hc/en-us/articles/214836687-Role-Management-101) a link to Discords role management 101.
**Answer:** Discord has added a few new features and the roles now follow the role hierarchy, which means you need to place your bot's role above every other role on your server to fix the issue. [Here's](https://support.discordapp.com/hc/en-us/articles/214836687-Role-Management-101) a link to Discord's role management 101.
**Please Note:** *The bot can only set/add all roles below its own highest role. It can not assign it's "highest role" to anyone else.*
**Please Note:** *The bot can only set/add all roles below its own highest role. It cannot assign its "highest role" to anyone else.*
###Question 14: I've broken permissions and am stuck, can I reset permissions?
----------

View File

@ -1,9 +1,9 @@
Permissions Overview
===================
Have you ever felt confused or even overwhelmed when trying to set Nadeko's permissions? In this guide we will be explaining how to use the
permission commands correctly and even cover a few common questions! Every command we discuss here can be found in the [Commands List](http://nadekobot.readthedocs.io/en/1.0/Commands%20List/#permissions).
permission commands correctly and even cover a few common questions! Every command we discuss here can be found in the [Commands List](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#permissions).
**To see the old guide for versions 0.9 and below, see [here](http://nadekobot.readthedocs.io/en/latest/Permissions%20System/)**
**To see the guide, [click here](http://nadekobot.readthedocs.io/en/latest/Permissions%20System/)**
Why do we use the Permissions Commands?
------------------------------
@ -15,11 +15,14 @@ With the permissions system it possible to restrict who can skip the current son
First Time Setup
------------------
To change permissions you **must** meet the following requirement:
To change permissions you **must** meet the following requirements:
**Have the role specified by `.permrole` (By default, this is Nadeko)**
**Be the owner of the server**
**If you are NOT the server owner, get the role specified by `.permrole` (By default, this is Nadeko)**
If you have an existing role called `Nadeko` but can't assign it to yourself, create a new role called `Nadeko` and assign that to yourself.
![img0](https://i.imgur.com/5QKZqqy.gif)
If you would like to set a different role, such as `Admins`, to be the role required to edit permissions, do `.permrole Admins` (you must have the current permission role to be able to do this).
@ -35,7 +38,7 @@ To view this permissions chain, do `.listperms`, with the top of the chain being
If you want to remove a permission from the chain of permissions, do `.removeperm X` to remove rule number X and similarly, do `.moveperm X Y` to move rule number X to number Y (moving, not swapping!).
As an example, if you wanted to enable NSFW for a certain role, say "Lewd", you could do `.rolemdl NSFW enable Lewd`.
This adds the rule to the top of the permissions chain so even if the default `.sm NSFW disabled` rule exists, the "Lewd" role will be able to use the NSFW module.
This adds the rule to the top of the permissions chain so even if the default `.sm NSFW disable` rule exists, the "Lewd" role will be able to use the NSFW module.
If you want the bot to notify users why they can't use a command or module, use `.verbose true` and Nadeko will tell you what rule is preventing the command.

View File

@ -6,17 +6,17 @@ Prerequisites
*Clone the repo*
`git clone -b 1.4 https://github.com/Kwoth/NadekoBot`
`cd NadekoBot/src/NadekoBot`
Edit `credentials.json.` Read the JSON Exaplanations guide on the left if you don't know how to set it up
Edit `credentials.json.` Read the [JSON Explanations](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/) guide if you don't know how to set it up.
*run*
`dotnet restore`
`dotnet run -c Release`
*when you decide to updatein the future (might not work if you've made custom edits to the source, make sure you know how git works)*
*when you decide to update in the future (might not work if you've made custom edits to the source, make sure you know how git works)*
`git pull`
`dotnet restore`
`dotnet run -c Release`
[.netcore]: https://www.microsoft.com/net/download/core#/sdk
[ffmpeg]: http://ffmpeg.zeranoe.com/builds/
[git]: https://git-scm.com/downloads
[git]: https://git-scm.com/downloads

View File

@ -1,30 +1,30 @@
#### If you have NadekoBot 1.x on Windows
- Go to `NadekoBot\src\NadekoBot` and backup your `credentials.json` file; then go to `NadekoBot\src\NadekoBot\bin\Release\netcoreapp1.0` and backup your `data` folder.
- Follow the [Windows Guide](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/) and install the latest version of **NadekoBot**.
- Paste your `credentials.json` file into the `C:\Program Files\NadekoBot\system` folder.
- Paste your `data` folder into `C:\Program Files\NadekoBot\system` folder.
- Navigate to your **old** `Nadeko` folder and copy your `credentials.json` file and the `data` folder.
- Paste credentials into the **NadekoBot 1.4x+** `C:\Program Files\NadekoBot\system` folder.
- Paste your **old** `Nadeko` data folder into **NadekoBot 1.4x+** `C:\Program Files\NadekoBot\system` folder.
- If it asks you to overwrite files, it is fine to do so.
- Next launch your **new** Nadeko as the guide describes, if it is not already running.
#### If you are running Dockerised Nadeko
- Shutdown your existing container **docker stop nadeko**.
- Shutdown your existing container `docker stop nadeko`.
- Move you credentials and other files to another folder.
- Delete your container **docker rm nadeko**.
- Create a new container **docker create --name=nadeko -v /nadeko/:/root/nadeko uirel/nadeko:1.4**.
- Start the container **docker start nadeko** wait for it to complain about lacking credentials.
- Stop the container **docker stop nadeko** open the nadeko folder and replace the credentials, database and other files with your copies.
- Restart the container **docker start nadeko**.
- Delete your container `docker rm nadeko`.
- Create a new container `docker create --name=nadeko -v /nadeko/:/root/nadeko uirel/nadeko:1.4`.
- Start the container with `docker start nadeko`, and wait for it to complain about lacking credentials.
- Stop the container, `docker stop nadeko`, open the `Nadeko` folder and replace the credentials, database and other files with your copies.
- Restart the container `docker start nadeko`.
#### If you have NadekoBot 1.x on Linux or macOS
- Backup the `NadekoBot.db` from `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data`
- Backup the `credentials.json` from `NadekoBot/src/NadekoBot/`
- **For MacOS Users Only:** download and install the latest version of [.NET Core SDK](https://www.microsoft.com/net/core#macos)
- Backup the `NadekoBot.db` from `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data`.
- Backup the `credentials.json` from `NadekoBot/src/NadekoBot/`.
- **For MacOS Users Only:** download and install the latest version of [.NET Core SDK](https://www.microsoft.com/net/core#macos).
- Next, use the command `cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/1.4/linuxAIO.sh && bash linuxAIO.sh`
- **For Ubuntu, Debian and CentOS Users Only:** use the option `4. Auto-Install Prerequisites` to install the latest version of .NET Core SDK.
- Use option `1. Download NadekoBot` to update your NadekoBot to 1.4.x.
- Use option `1. Download NadekoBot` to update your NadekoBot to 1.4+ version.
- Next, just [run your NadekoBot.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot)
- *NOTE: 1.4.x uses `NadekoBot.db` file from `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data` folder.*
- *NOTE: 1.4+ version uses `NadekoBot.db` file from `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.1/data` folder.*

View File

@ -1,3 +1,3 @@
{
"sdk": { "version": "2.0.0" }
"sdk": { "version": "1.0.1" }
}

View File

@ -2,11 +2,12 @@
using System.Runtime.CompilerServices;
using Discord.Commands;
using NadekoBot.Services.Impl;
namespace NadekoBot.Common.Attributes
{
public class Aliases : AliasAttribute
{
public Aliases([CallerMemberName] string memberName = "") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Cmd.Split(' ').Skip(1).ToArray())
public Aliases([CallerMemberName] string memberName = "") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_cmd").Split(' ').Skip(1).ToArray())
{
}
}

View File

@ -6,7 +6,7 @@ namespace NadekoBot.Common.Attributes
{
public class Description : SummaryAttribute
{
public Description([CallerMemberName] string memberName="") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Desc)
public Description([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_desc"))
{
}

View File

@ -6,7 +6,7 @@ namespace NadekoBot.Common.Attributes
{
public class NadekoCommand : CommandAttribute
{
public NadekoCommand([CallerMemberName] string memberName="") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Cmd)
public NadekoCommand([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant() + "_cmd").Split(' ')[0])
{
}

View File

@ -6,7 +6,7 @@ namespace NadekoBot.Common.Attributes
{
public class Usage : RemarksAttribute
{
public Usage([CallerMemberName] string memberName="") : base(Localization.LoadCommand(memberName.ToLowerInvariant()).Usage)
public Usage([CallerMemberName] string memberName="") : base(Localization.LoadCommandString(memberName.ToLowerInvariant()+"_usage"))
{
}

View File

@ -1,9 +0,0 @@
namespace NadekoBot.Common
{
public class CommandData
{
public string Cmd { get; set; }
public string Usage { get; set; }
public string Desc { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class totalxp : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "TotalXp",
table: "DiscordUser",
nullable: false,
defaultValue: 0);
migrationBuilder.Sql(MigrationQueries.TotalXp);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TotalXp",
table: "DiscordUser");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class clubadmins : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsClubAdmin",
table: "DiscordUser",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsClubAdmin",
table: "DiscordUser");
}
}
}

View File

@ -34,9 +34,5 @@ INSERT INTO DiscordUser
FROM DiscordUser_tmp;
DROP TABLE DiscordUser_tmp;";
public static string TotalXp { get; } =
@"UPDATE DiscordUser
SET TotalXp = ifnull((SELECT SUM(Xp) FROM UserXpStats WHERE UserId = DiscordUser.UserId), 0)";
}
}

View File

@ -462,18 +462,14 @@ namespace NadekoBot.Migrations
b.Property<string>("Discriminator");
b.Property<bool>("IsClubAdmin");
b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 15, 5, 48, 8, 660, DateTimeKind.Local));
.HasDefaultValue(new DateTime(2017, 9, 11, 22, 0, 31, 236, DateTimeKind.Local));
b.Property<DateTime>("LastXpGain");
b.Property<int>("NotifyOnLevelUp");
b.Property<int>("TotalXp");
b.Property<ulong>("UserId");
b.Property<string>("Username");
@ -1366,7 +1362,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 15, 5, 48, 8, 665, DateTimeKind.Local));
.HasDefaultValue(new DateTime(2017, 9, 11, 22, 0, 31, 238, DateTimeKind.Local));
b.Property<int>("NotifyOnLevelUp");

View File

@ -30,9 +30,8 @@ namespace NadekoBot.Modules.Administration
private readonly IImagesService _images;
private readonly MusicService _music;
private readonly IBotConfigProvider _bc;
private readonly NadekoBot _bot;
public SelfCommands(DbService db, NadekoBot bot, DiscordSocketClient client,
public SelfCommands(DbService db, DiscordSocketClient client,
MusicService music, IImagesService images, IBotConfigProvider bc)
{
_db = db;
@ -40,7 +39,6 @@ namespace NadekoBot.Modules.Administration
_images = images;
_music = music;
_bc = bc;
_bot = bot;
}
[NadekoCommand, Usage, Description, Aliases]
@ -351,7 +349,7 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly]
public async Task SetGame([Remainder] string game = null)
{
await _bot.SetGameAsync(game).ConfigureAwait(false);
await _client.SetGameAsync(game).ConfigureAwait(false);
await ReplyConfirmLocalized("set_game").ConfigureAwait(false);
}

View File

@ -7,7 +7,6 @@ using NadekoBot.Modules.Music.Services;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration.Services
{
@ -17,7 +16,6 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DiscordSocketClient _client;
private readonly MusicService _music;
private readonly Logger _log;
private readonly IDataCache _cache;
private readonly Replacer _rep;
private readonly DbService _db;
private readonly IBotConfigProvider _bcp;
@ -29,60 +27,50 @@ namespace NadekoBot.Modules.Administration.Services
public int Index { get; set; }
}
public PlayingRotateService(DiscordSocketClient client, IBotConfigProvider bcp,
MusicService music, DbService db, IDataCache cache, NadekoBot bot)
public PlayingRotateService(DiscordSocketClient client, IBotConfigProvider bcp, MusicService music, DbService db)
{
_client = client;
_bcp = bcp;
_music = music;
_db = db;
_log = LogManager.GetCurrentClassLogger();
_cache = cache;
_rep = new ReplacementBuilder()
.WithClient(client)
.WithStats(client)
.WithMusic(music)
.Build();
if (client.ShardId == 0)
_t = new Timer(async (objState) =>
{
_rep = new ReplacementBuilder()
.WithClient(client)
.WithStats(client)
.WithMusic(music)
.Build();
_t = new Timer(async (objState) =>
try
{
try
{
bcp.Reload();
bcp.Reload();
var state = (TimerState)objState;
if (!BotConfig.RotatingStatuses)
return;
if (state.Index >= BotConfig.RotatingStatusMessages.Count)
state.Index = 0;
var state = (TimerState)objState;
if (!BotConfig.RotatingStatuses)
return;
if (state.Index >= BotConfig.RotatingStatusMessages.Count)
state.Index = 0;
if (!BotConfig.RotatingStatusMessages.Any())
return;
var status = BotConfig.RotatingStatusMessages[state.Index++].Status;
if (string.IsNullOrWhiteSpace(status))
return;
if (!BotConfig.RotatingStatusMessages.Any())
return;
var status = BotConfig.RotatingStatusMessages[state.Index++].Status;
if (string.IsNullOrWhiteSpace(status))
return;
status = _rep.Replace(status);
status = _rep.Replace(status);
try
{
await bot.SetGameAsync(status).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
try { await client.SetGameAsync(status).ConfigureAwait(false); }
catch (Exception ex)
{
_log.Warn("Rotating playing status errored.\n" + ex);
_log.Warn(ex);
}
}, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
}
catch (Exception ex)
{
_log.Warn("Rotating playing status errored.\n" + ex);
}
}, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
}
}

View File

@ -58,7 +58,8 @@ namespace NadekoBot.Modules.CustomReactions
if (channel == null)
{
await _service.AddGcr(cr).ConfigureAwait(false);
Array.Resize(ref _service.GlobalReactions, _service.GlobalReactions.Length + 1);
_service.GlobalReactions[_service.GlobalReactions.Length - 1] = cr;
}
else
{
@ -236,7 +237,8 @@ namespace NadekoBot.Modules.CustomReactions
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
{
uow.CustomReactions.Remove(toDelete);
await _service.DelGcr(toDelete.Id);
//todo 91 i can dramatically improve performance of this, if Ids are ordered.
_service.GlobalReactions = _service.GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
success = true;
}
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)

View File

@ -15,7 +15,6 @@ using NadekoBot.Modules.CustomReactions.Extensions;
using NadekoBot.Modules.Permissions.Common;
using NadekoBot.Modules.Permissions.Services;
using NadekoBot.Services.Impl;
using Newtonsoft.Json;
namespace NadekoBot.Modules.CustomReactions.Services
{
@ -33,11 +32,9 @@ namespace NadekoBot.Modules.CustomReactions.Services
private readonly CommandHandler _cmd;
private readonly IBotConfigProvider _bc;
private readonly NadekoStrings _strings;
private readonly IDataCache _cache;
public CustomReactionsService(PermissionService perms, DbService db, NadekoStrings strings,
DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow,
IDataCache cache)
DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow)
{
_log = LogManager.GetCurrentClassLogger();
_db = db;
@ -46,38 +43,12 @@ namespace NadekoBot.Modules.CustomReactions.Services
_cmd = cmd;
_bc = bc;
_strings = strings;
_cache = cache;
var sub = _cache.Redis.GetSubscriber();
sub.Subscribe("gcr.added", (ch, msg) =>
{
Array.Resize(ref GlobalReactions, GlobalReactions.Length + 1);
GlobalReactions[GlobalReactions.Length - 1] = JsonConvert.DeserializeObject<CustomReaction>(msg);
}, StackExchange.Redis.CommandFlags.FireAndForget);
sub.Subscribe("gcr.deleted", (ch, msg) =>
{
var id = int.Parse(msg);
GlobalReactions = GlobalReactions.Where(cr => cr?.Id != id).ToArray();
}, StackExchange.Redis.CommandFlags.FireAndForget);
var items = uow.CustomReactions.GetAll();
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();
}
public Task AddGcr(CustomReaction cr)
{
var sub = _cache.Redis.GetSubscriber();
return sub.PublishAsync("gcr.added", JsonConvert.SerializeObject(cr));
}
public Task DelGcr(int id)
{
var sub = _cache.Redis.GetSubscriber();
return sub.PublishAsync("gcr.deleted", id);
}
public void ClearStats() => ReactionStats.Clear();
public CustomReaction TryGetCustomReaction(IUserMessage umsg)

View File

@ -331,7 +331,7 @@ namespace NadekoBot.Modules.Gambling
var embed = new EmbedBuilder().WithOkColor();
if (entry.Type == ShopEntryType.Role)
return embed.AddField(efb => efb.WithName(GetText("name")).WithValue(GetText("shop_role", Format.Bold(Context.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE"))).WithIsInline(true))
return embed.AddField(efb => efb.WithName(GetText("name")).WithValue(GetText("shop_role", Format.Bold(entry.RoleName))).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("price")).WithValue(entry.Price.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("type")).WithValue(entry.Type.ToString()).WithIsInline(true));
else if (entry.Type == ShopEntryType.List)
@ -349,7 +349,7 @@ namespace NadekoBot.Modules.Gambling
{
if (entry.Type == ShopEntryType.Role)
{
return GetText("shop_role", Format.Bold(Context.Guild.GetRole(entry.RoleId)?.Name ?? "MISSING_ROLE"));
return GetText("shop_role", Format.Bold(entry.RoleName));
}
else if (entry.Type == ShopEntryType.List)
{

View File

@ -38,7 +38,7 @@ namespace NadekoBot.Modules.Games
}
[NadekoCommand, Usage, Description, Aliases]
public async Task EightBall([Remainder] string question = null)
public async Task _8Ball([Remainder] string question = null)
{
if (string.IsNullOrWhiteSpace(question))
return;

View File

@ -3,6 +3,7 @@ using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Common
{
@ -17,16 +18,27 @@ namespace NadekoBot.Modules.Music.Common
public string SongUri { get; private set; }
//private volatile bool restart = false;
public SongBuffer(string songUri, string skipTo, bool isLocal)
{
_log = LogManager.GetCurrentClassLogger();
//_log.Warn(songUri);
this.SongUri = songUri;
this._isLocal = isLocal;
try
{
this.p = StartFFmpegProcess(SongUri, 0);
var t = Task.Run(() =>
{
this.p.BeginErrorReadLine();
this.p.ErrorDataReceived += P_ErrorDataReceived;
this.p.WaitForExit();
});
this._outStream = this.p.StandardOutput.BaseStream;
}
catch (System.ComponentModel.Win32Exception)
{
@ -56,14 +68,113 @@ Check the guides for your platform on how to setup ffmpeg correctly:
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
RedirectStandardError = true,
CreateNoWindow = true,
});
}
private void P_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (string.IsNullOrWhiteSpace(e.Data))
return;
_log.Error(">>> " + e.Data);
if (e.Data?.Contains("Error in the pull function") == true)
{
_log.Error("Ignore this.");
//restart = true;
}
}
private readonly object locker = new object();
private readonly bool _isLocal;
public Task<bool> StartBuffering(CancellationToken cancelToken)
{
var toReturn = new TaskCompletionSource<bool>();
var _ = Task.Run(() =>
{
try
{
////int maxLoopsPerSec = 25;
//var sw = Stopwatch.StartNew();
////var delay = 1000 / maxLoopsPerSec;
//int currentLoops = 0;
//int _bytesSent = 0;
//try
//{
// //do
// //{
// // if (restart)
// // {
// // var cur = _bytesSent / 3840 / (1000 / 20.0f);
// // _log.Info("Restarting");
// // try { this.p.StandardOutput.Dispose(); } catch { }
// // try { this.p.Dispose(); } catch { }
// // this.p = StartFFmpegProcess(SongUri, cur);
// // }
// // restart = false;
// ++currentLoops;
// byte[] buffer = new byte[readSize];
// int bytesRead = 1;
// while (!cancelToken.IsCancellationRequested && !this.p.HasExited)
// {
// bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, readSize, cancelToken).ConfigureAwait(false);
// _bytesSent += bytesRead;
// if (bytesRead == 0)
// break;
// bool written;
// do
// {
// lock (locker)
// written = _outStream.Write(buffer, 0, bytesRead);
// if (!written)
// await Task.Delay(2000, cancelToken);
// }
// while (!written && !cancelToken.IsCancellationRequested);
// lock (locker)
// if (_outStream.Length > 200_000 || bytesRead == 0)
// if (toReturn.TrySetResult(true))
// _log.Info("Prebuffering finished in {0}", sw.Elapsed.TotalSeconds.ToString("F2"));
// //_log.Info(_outStream.Length);
// await Task.Delay(10);
// }
// //if (cancelToken.IsCancellationRequested)
// // _log.Info("Song canceled");
// //else if (p.HasExited)
// // _log.Info("Song buffered completely (FFmpeg exited)");
// //else if (bytesRead == 0)
// // _log.Info("Nothing read");
// //}
// //while (restart && !cancelToken.IsCancellationRequested);
//return Task.CompletedTask;
toReturn.TrySetResult(true);
}
catch (System.ComponentModel.Win32Exception)
{
_log.Error(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/OjKk8F
Linux Guide: https://goo.gl/ShjCUo");
}
catch (OperationCanceledException) { }
catch (InvalidOperationException) { } // when ffmpeg is disposed
catch (Exception ex)
{
_log.Info(ex);
}
finally
{
if (toReturn.TrySetResult(false))
_log.Info("Prebuffering failed");
}
}, cancelToken);
return toReturn.Task;
}
public int Read(byte[] b, int offset, int toRead)
{
lock (locker)
@ -92,4 +203,215 @@ Check the guides for your platform on how to setup ffmpeg correctly:
this.p.Dispose();
}
}
}
}
//namespace NadekoBot.Services.Music
//{
// /// <summary>
// /// Create a buffer for a song file. It will create multiples files to ensure, that radio don't fill up disk space.
// /// It also help for large music by deleting files that are already seen.
// /// </summary>
// class SongBuffer : Stream
// {
// public SongBuffer(MusicPlayer musicPlayer, string basename, SongInfo songInfo, int skipTo, int maxFileSize)
// {
// MusicPlayer = musicPlayer;
// Basename = basename;
// SongInfo = songInfo;
// SkipTo = skipTo;
// MaxFileSize = maxFileSize;
// CurrentFileStream = new FileStream(this.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
// _log = LogManager.GetCurrentClassLogger();
// }
// MusicPlayer MusicPlayer { get; }
// private string Basename { get; }
// private SongInfo SongInfo { get; }
// private int SkipTo { get; }
// private int MaxFileSize { get; } = 2.MiB();
// private long FileNumber = -1;
// private long NextFileToRead = 0;
// public bool BufferingCompleted { get; private set; } = false;
// private ulong CurrentBufferSize = 0;
// private FileStream CurrentFileStream;
// private Logger _log;
// public Task BufferSong(CancellationToken cancelToken) =>
// Task.Run(async () =>
// {
// Process p = null;
// FileStream outStream = null;
// try
// {
// p = Process.Start(new ProcessStartInfo
// {
// FileName = "ffmpeg",
// Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -vn -ac 2 pipe:1 -loglevel quiet",
// UseShellExecute = false,
// RedirectStandardOutput = true,
// RedirectStandardError = false,
// CreateNoWindow = true,
// });
// byte[] buffer = new byte[81920];
// int currentFileSize = 0;
// ulong prebufferSize = 100ul.MiB();
// outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
// while (!p.HasExited) //Also fix low bandwidth
// {
// int bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false);
// if (currentFileSize >= MaxFileSize)
// {
// try
// {
// outStream.Dispose();
// }
// catch { }
// outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
// currentFileSize = bytesRead;
// }
// else
// {
// currentFileSize += bytesRead;
// }
// CurrentBufferSize += Convert.ToUInt64(bytesRead);
// await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
// while (CurrentBufferSize > prebufferSize)
// await Task.Delay(100, cancelToken);
// }
// BufferingCompleted = true;
// }
// catch (System.ComponentModel.Win32Exception)
// {
// var oldclr = Console.ForegroundColor;
// Console.ForegroundColor = ConsoleColor.Red;
// Console.WriteLine(@"You have not properly installed or configured FFMPEG.
//Please install and configure FFMPEG to play music.
//Check the guides for your platform on how to setup ffmpeg correctly:
// Windows Guide: https://goo.gl/OjKk8F
// Linux Guide: https://goo.gl/ShjCUo");
// Console.ForegroundColor = oldclr;
// }
// catch (Exception ex)
// {
// Console.WriteLine($"Buffering stopped: {ex.Message}");
// }
// finally
// {
// if (outStream != null)
// outStream.Dispose();
// if (p != null)
// {
// try
// {
// p.Kill();
// }
// catch { }
// p.Dispose();
// }
// }
// });
// /// <summary>
// /// Return the next file to read, and delete the old one
// /// </summary>
// /// <returns>Name of the file to read</returns>
// private string GetNextFile()
// {
// string filename = Basename + "-" + NextFileToRead;
// if (NextFileToRead != 0)
// {
// try
// {
// CurrentBufferSize -= Convert.ToUInt64(new FileInfo(Basename + "-" + (NextFileToRead - 1)).Length);
// File.Delete(Basename + "-" + (NextFileToRead - 1));
// }
// catch { }
// }
// NextFileToRead++;
// return filename;
// }
// private bool IsNextFileReady()
// {
// return NextFileToRead <= FileNumber;
// }
// private void CleanFiles()
// {
// for (long i = NextFileToRead - 1; i <= FileNumber; i++)
// {
// try
// {
// File.Delete(Basename + "-" + i);
// }
// catch { }
// }
// }
// //Stream part
// public override bool CanRead => true;
// public override bool CanSeek => false;
// public override bool CanWrite => false;
// public override long Length => (long)CurrentBufferSize;
// public override long Position { get; set; } = 0;
// public override void Flush() { }
// public override int Read(byte[] buffer, int offset, int count)
// {
// int read = CurrentFileStream.Read(buffer, offset, count);
// if (read < count)
// {
// if (!BufferingCompleted || IsNextFileReady())
// {
// CurrentFileStream.Dispose();
// CurrentFileStream = new FileStream(GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
// read += CurrentFileStream.Read(buffer, read + offset, count - read);
// }
// if (read < count)
// Array.Clear(buffer, read, count - read);
// }
// return read;
// }
// public override long Seek(long offset, SeekOrigin origin)
// {
// throw new NotImplementedException();
// }
// public override void SetLength(long value)
// {
// throw new NotImplementedException();
// }
// public override void Write(byte[] buffer, int offset, int count)
// {
// throw new NotImplementedException();
// }
// public new void Dispose()
// {
// CurrentFileStream.Dispose();
// MusicPlayer.SongCancelSource.Cancel();
// CleanFiles();
// base.Dispose();
// }
// }
//}

View File

@ -16,13 +16,9 @@ using NadekoBot.Modules.NSFW.Exceptions;
namespace NadekoBot.Modules.NSFW
{
// thanks to halitalf for adding autoboob and autobutt features :D
public class NSFW : NadekoTopLevelModule<SearchesService>
{
private static readonly ConcurrentDictionary<ulong, Timer> _autoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
private static readonly ConcurrentDictionary<ulong, Timer> _autoBoobTimers = new ConcurrentDictionary<ulong, Timer>();
private static readonly ConcurrentDictionary<ulong, Timer> _autoButtTimers = new ConcurrentDictionary<ulong, Timer>();
private static readonly ConcurrentHashSet<ulong> _hentaiBombBlacklist = new ConcurrentHashSet<ulong>();
private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
@ -53,34 +49,10 @@ namespace NadekoBot.Modules.NSFW
.WithDescription($"[{GetText("tag")}: {tag}]({img})"))
.ConfigureAwait(false);
}
private async Task InternalBoobs(IMessageChannel Channel)
{
try
{
JToken obj;
obj = JArray.Parse(await _service.Http.GetStringAsync($"http://api.oboobs.ru/boobs/{new NadekoRandom().Next(0, 10330)}").ConfigureAwait(false))[0];
await Channel.SendMessageAsync($"http://media.oboobs.ru/{obj["preview"]}").ConfigureAwait(false);
}
catch (Exception ex)
{
await Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
}
}
private async Task InternalButts(IMessageChannel Channel)
{
try
{
JToken obj;
obj = JArray.Parse(await _service.Http.GetStringAsync($"http://api.obutts.ru/butts/{new NadekoRandom().Next(0, 4335)}").ConfigureAwait(false))[0];
await Channel.SendMessageAsync($"http://media.obutts.ru/{obj["preview"]}").ConfigureAwait(false);
}
catch (Exception ex)
{
await Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
public Task Hentai([Remainder] string tag = null) =>
InternalHentai(Context.Channel, tag, false);
#if !GLOBAL_NADEKO
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(ChannelPermission.ManageMessages)]
@ -93,7 +65,7 @@ namespace NadekoBot.Modules.NSFW
if (!_autoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return;
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
await ReplyConfirmLocalized("stopped").ConfigureAwait(false);
await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false);
return;
}
@ -127,90 +99,8 @@ namespace NadekoBot.Modules.NSFW
interval,
string.Join(", ", tagsArr)).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(ChannelPermission.ManageMessages)]
public async Task AutoBoobs(int interval = 0)
{
Timer t;
if (interval == 0)
{
if (!_autoBoobTimers.TryRemove(Context.Channel.Id, out t)) return;
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
await ReplyConfirmLocalized("stopped").ConfigureAwait(false);
return;
}
if (interval < 20)
return;
t = new Timer(async (state) =>
{
try
{
await InternalBoobs(Context.Channel).ConfigureAwait(false);
}
catch
{
// ignored
}
}, null, interval * 1000, interval * 1000);
_autoBoobTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return t;
});
await ReplyConfirmLocalized("started", interval).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(ChannelPermission.ManageMessages)]
public async Task AutoButts(int interval = 0)
{
Timer t;
if (interval == 0)
{
if (!_autoButtTimers.TryRemove(Context.Channel.Id, out t)) return;
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
await ReplyConfirmLocalized("stopped").ConfigureAwait(false);
return;
}
if (interval < 20)
return;
t = new Timer(async (state) =>
{
try
{
await InternalButts(Context.Channel).ConfigureAwait(false);
}
catch
{
// ignored
}
}, null, interval * 1000, interval * 1000);
_autoButtTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
{
old.Change(Timeout.Infinite, Timeout.Infinite);
return t;
});
await ReplyConfirmLocalized("started", interval).ConfigureAwait(false);
}
#endif
[NadekoCommand, Usage, Description, Aliases]
public Task Hentai([Remainder] string tag = null) =>
InternalHentai(Context.Channel, tag, false);
[NadekoCommand, Usage, Description, Aliases]
public async Task HentaiBomb([Remainder] string tag = null)
{
@ -309,7 +199,7 @@ namespace NadekoBot.Modules.NSFW
tag = tag.Trim().ToLowerInvariant();
var added = _service.ToggleBlacklistedTag(Context.Guild.Id, tag);
if (added)
if(added)
await ReplyConfirmLocalized("blacklisted_tag_add", tag).ConfigureAwait(false);
else
await ReplyConfirmLocalized("blacklisted_tag_remove", tag).ConfigureAwait(false);

View File

@ -135,11 +135,11 @@ namespace NadekoBot.Modules.Permissions.Services
{
var oldPrefixes = new[] { ".", ";", "!!", "!m", "!", "+", "-", "$", ">" };
uow._context.Database.ExecuteSqlCommand(
@"UPDATE Permissionv2
$@"UPDATE {nameof(Permissionv2)}
SET secondaryTargetName=trim(substr(secondaryTargetName, 3))
WHERE secondaryTargetName LIKE '!!%' OR secondaryTargetName LIKE '!m%';
UPDATE Permissionv2
UPDATE {nameof(Permissionv2)}
SET secondaryTargetName=substr(secondaryTargetName, 2)
WHERE secondaryTargetName LIKE '.%' OR
secondaryTargetName LIKE '~%' OR

View File

@ -76,13 +76,11 @@ namespace NadekoBot.Modules.Searches.Common
if (images.Length == 0)
return null;
var toReturn = images[_rng.Next(images.Length)];
#if !GLOBAL_NADEKO
foreach (var dledImg in images)
{
if(dledImg != toReturn)
_cache.Add(dledImg);
}
#endif
return toReturn;
}
}

View File

@ -43,16 +43,20 @@ namespace NadekoBot.Modules.Searches
string response;
response = await _service.Http.GetStringAsync($"http://api.openweathermap.org/data/2.5/weather?q={query}&appid=42cd627dd60debf25a5739e50a217d74&units=metric").ConfigureAwait(false);
string responsef;
responsef = await _service.Http.GetStringAsync($"http://api.openweathermap.org/data/2.5/weather?q={query}&appid=42cd627dd60debf25a5739e50a217d74&units=imperial").ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<WeatherData>(response);
var dataf = JsonConvert.DeserializeObject<WeatherData>(responsef);
var embed = new EmbedBuilder()
.AddField(fb => fb.WithName("🌍 " + Format.Bold(GetText("location"))).WithValue($"[{data.Name + ", " + data.Sys.Country}](https://openweathermap.org/city/{data.Id})").WithIsInline(true))
.AddField(fb => fb.WithName("📏 " + Format.Bold(GetText("latlong"))).WithValue($"{data.Coord.Lat}, {data.Coord.Lon}").WithIsInline(true))
.AddField(fb => fb.WithName("☁ " + Format.Bold(GetText("condition"))).WithValue(string.Join(", ", data.Weather.Select(w => w.Main))).WithIsInline(true))
.AddField(fb => fb.WithName("😓 " + Format.Bold(GetText("humidity"))).WithValue($"{data.Main.Humidity}%").WithIsInline(true))
.AddField(fb => fb.WithName("💨 " + Format.Bold(GetText("wind_speed"))).WithValue(data.Wind.Speed + " m/s").WithIsInline(true))
.AddField(fb => fb.WithName("🌡 " + Format.Bold(GetText("temperature"))).WithValue(data.Main.Temp + "°C").WithIsInline(true))
.AddField(fb => fb.WithName("🔆 " + Format.Bold(GetText("min_max"))).WithValue($"{data.Main.TempMin}°C - {data.Main.TempMax}°C").WithIsInline(true))
.AddField(fb => fb.WithName("💨 " + Format.Bold(GetText("wind_speed"))).WithValue(data.Wind.Speed + " m/s" + " / " + dataf.Wind.Speed + " mph").WithIsInline(true))
.AddField(fb => fb.WithName("🌡 " + Format.Bold(GetText("temperature"))).WithValue(data.Main.Temp + "°C" + " / " + dataf.Main.Temp + "°F").WithIsInline(true))
.AddField(fb => fb.WithName("🔆 " + Format.Bold(GetText("min_max"))).WithValue($"{data.Main.TempMin}°C - {data.Main.TempMax}°C" + "\n" + $"{dataf.Main.TempMin}°F - {dataf.Main.TempMax}°F").WithIsInline(true))
.AddField(fb => fb.WithName("🌄 " + Format.Bold(GetText("sunrise"))).WithValue($"{data.Sys.Sunrise.ToUnixTimestamp():HH:mm} UTC").WithIsInline(true))
.AddField(fb => fb.WithName("🌇 " + Format.Bold(GetText("sunset"))).WithValue($"{data.Sys.Sunset.ToUnixTimestamp():HH:mm} UTC").WithIsInline(true))
.WithOkColor()

View File

@ -11,14 +11,10 @@ namespace NadekoBot.Modules.Searches.Services
public class AnimeSearchService : INService
{
private readonly Logger _log;
private readonly IDataCache _cache;
private readonly HttpClient _http;
public AnimeSearchService(IDataCache cache)
public AnimeSearchService()
{
_log = LogManager.GetCurrentClassLogger();
_cache = cache;
_http = new HttpClient();
}
public async Task<AnimeResult> GetAnimeData(string query)
@ -29,16 +25,11 @@ namespace NadekoBot.Modules.Searches.Services
{
var link = "https://aniapi.nadekobot.me/anime/" + Uri.EscapeDataString(query.Replace("/", " "));
link = link.ToLowerInvariant();
var (ok, data) = await _cache.TryGetAnimeDataAsync(link).ConfigureAwait(false);
if (!ok)
using (var http = new HttpClient())
{
data = await _http.GetStringAsync(link).ConfigureAwait(false);
await _cache.SetAnimeDataAsync(link, data).ConfigureAwait(false);
var res = await http.GetStringAsync(link).ConfigureAwait(false);
return JsonConvert.DeserializeObject<AnimeResult>(res);
}
return JsonConvert.DeserializeObject<AnimeResult>(data);
}
catch
{
@ -53,17 +44,12 @@ namespace NadekoBot.Modules.Searches.Services
try
{
var link = "https://aniapi.nadekobot.me/manga/" + Uri.EscapeDataString(query.Replace("/", " "));
link = link.ToLowerInvariant();
var (ok, data) = await _cache.TryGetAnimeDataAsync(link).ConfigureAwait(false);
if (!ok)
var link = "https://aniapi.nadekobot.me/manga/" + Uri.EscapeDataString(query.Replace("/", " "));
using (var http = new HttpClient())
{
data = await _http.GetStringAsync(link).ConfigureAwait(false);
await _cache.SetAnimeDataAsync(link, data).ConfigureAwait(false);
var res = await http.GetStringAsync(link).ConfigureAwait(false);
return JsonConvert.DeserializeObject<MangaResult>(res);
}
return JsonConvert.DeserializeObject<MangaResult>(data);
}
catch
{

View File

@ -26,27 +26,6 @@ namespace NadekoBot.Modules.Xp
_client = client;
}
[NadekoCommand, Usage, Description, Aliases]
public async Task ClubAdmin([Remainder]IUser toAdmin)
{
bool admin;
try
{
admin = _service.ToggleAdmin(Context.User, toAdmin);
}
catch (InvalidOperationException)
{
await ReplyErrorLocalized("club_admin_error").ConfigureAwait(false);
return;
}
if(admin)
await ReplyConfirmLocalized("club_admin_add", Format.Bold(toAdmin.ToString())).ConfigureAwait(false);
else
await ReplyConfirmLocalized("club_admin_remove", Format.Bold(toAdmin.ToString())).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task ClubCreate([Remainder]string clubName)
{
@ -118,14 +97,7 @@ namespace NadekoBot.Modules.Xp
.AddField("Level Req.", club.MinimumLevelReq.ToString(), true)
.AddField("Members", string.Join("\n", club.Users
.Skip(page * 10)
.Take(10)
.OrderByDescending(x => x.IsClubAdmin)
.Select(x =>
{
if (x.IsClubAdmin)
return x.ToString() + "⭐";
return x.ToString();
})), false);
.Take(10)), false);
if (Uri.IsWellFormedUriString(club.ImageUrl, UriKind.Absolute))
return embed.WithThumbnailUrl(club.ImageUrl);
@ -140,7 +112,7 @@ namespace NadekoBot.Modules.Xp
if (--page < 0)
return Task.CompletedTask;
var club = _service.GetClubWithBansAndApplications(Context.User.Id);
var club = _service.GetBansAndApplications(Context.User.Id);
if (club == null)
return ReplyErrorLocalized("club_not_exists");
@ -159,8 +131,7 @@ namespace NadekoBot.Modules.Xp
return new EmbedBuilder()
.WithTitle(GetText("club_bans_for", club.ToString()))
.WithDescription(toShow)
.WithOkColor();
.WithDescription(toShow);
}, bans.Length / 10);
}
@ -172,11 +143,11 @@ namespace NadekoBot.Modules.Xp
if (--page < 0)
return Task.CompletedTask;
var club = _service.GetClubWithBansAndApplications(Context.User.Id);
var club = _service.GetBansAndApplications(Context.User.Id);
if (club == null)
return ReplyErrorLocalized("club_not_exists");
var apps = club
var bans = club
.Applicants
.Select(x => x.User)
.ToArray();
@ -184,17 +155,16 @@ namespace NadekoBot.Modules.Xp
return Context.Channel.SendPaginatedConfirmAsync(_client, page,
curPage =>
{
var toShow = string.Join("\n", apps
var toShow = string.Join("\n", bans
.Skip(page * 10)
.Take(10)
.Select(x => x.ToString()));
return new EmbedBuilder()
.WithTitle(GetText("club_apps_for", club.ToString()))
.WithDescription(toShow)
.WithOkColor();
.WithDescription(toShow);
}, apps.Length / 10);
}, bans.Length / 10);
}
[NadekoCommand, Usage, Description, Aliases]
@ -312,7 +282,7 @@ namespace NadekoBot.Modules.Xp
}
else
{
await ReplyErrorLocalized("club_disband_error").ConfigureAwait(false);
await ReplyErrorLocalized("club_disaband_error").ConfigureAwait(false);
}
}

View File

@ -27,11 +27,10 @@ namespace NadekoBot.Modules.Xp.Services
{
var du = uow.DiscordUsers.GetOrCreate(user);
uow._context.SaveChanges();
var xp = new LevelStats(du.TotalXp);
var xp = new LevelStats(uow.Xp.GetTotalUserXp(user.Id));
if (xp.Level >= 5 && du.Club == null)
{
du.IsClubAdmin = true;
du.Club = new ClubInfo()
{
Name = clubName,
@ -53,27 +52,6 @@ namespace NadekoBot.Modules.Xp.Services
return true;
}
public bool ToggleAdmin(IUser owner, IUser toAdmin)
{
bool newState;
using (var uow = _db.UnitOfWork)
{
var club = uow.Clubs.GetByOwner(owner.Id);
var adminUser = uow.DiscordUsers.GetOrCreate(toAdmin);
if (club.OwnerId == adminUser.Id)
return true;
if (club == null || club.Owner.UserId != owner.Id ||
!club.Users.Contains(adminUser))
throw new InvalidOperationException();
newState = adminUser.IsClubAdmin = !adminUser.IsClubAdmin;
uow.Complete();
}
return newState;
}
public ClubInfo GetClubByMember(IUser user)
{
using (var uow = _db.UnitOfWork)
@ -129,7 +107,7 @@ namespace NadekoBot.Modules.Xp.Services
uow._context.SaveChanges();
if (du.Club != null
|| new LevelStats(du.TotalXp).Level < club.MinimumLevelReq
|| new LevelStats(uow.Xp.GetTotalUserXp(user.Id)).Level < club.MinimumLevelReq
|| club.Bans.Any(x => x.UserId == du.Id)
|| club.Applicants.Any(x => x.UserId == du.Id))
{
@ -156,7 +134,11 @@ namespace NadekoBot.Modules.Xp.Services
discordUser = null;
using (var uow = _db.UnitOfWork)
{
var club = uow.Clubs.GetByOwnerOrAdmin(clubOwnerUserId);
var club = uow.Clubs.GetByOwner(clubOwnerUserId,
set => set.Include(x => x.Applicants)
.ThenInclude(x => x.Club)
.Include(x => x.Applicants)
.ThenInclude(x => x.User));
if (club == null)
return false;
@ -165,7 +147,6 @@ namespace NadekoBot.Modules.Xp.Services
return false;
applicant.User.Club = club;
applicant.User.IsClubAdmin = false;
club.Applicants.Remove(applicant);
//remove that user's all other applications
@ -178,11 +159,15 @@ namespace NadekoBot.Modules.Xp.Services
return true;
}
public ClubInfo GetClubWithBansAndApplications(ulong ownerUserId)
public ClubInfo GetBansAndApplications(ulong ownerUserId)
{
using (var uow = _db.UnitOfWork)
{
return uow.Clubs.GetByOwnerOrAdmin(ownerUserId);
return uow.Clubs.GetByOwner(ownerUserId,
x => x.Include(y => y.Bans)
.ThenInclude(y => y.User)
.Include(y => y.Applicants)
.ThenInclude(y => y.User));
}
}
@ -195,7 +180,6 @@ namespace NadekoBot.Modules.Xp.Services
return false;
du.Club = null;
du.IsClubAdmin = false;
uow.Complete();
}
return true;
@ -237,7 +221,9 @@ namespace NadekoBot.Modules.Xp.Services
{
using (var uow = _db.UnitOfWork)
{
club = uow.Clubs.GetByOwnerOrAdmin(ownerUserId);
club = uow.Clubs.GetByOwner(ownerUserId,
set => set.Include(x => x.Applicants)
.ThenInclude(x => x.User));
if (club == null)
return false;
@ -270,7 +256,9 @@ namespace NadekoBot.Modules.Xp.Services
{
using (var uow = _db.UnitOfWork)
{
club = uow.Clubs.GetByOwnerOrAdmin(ownerUserId);
club = uow.Clubs.GetByOwner(ownerUserId,
set => set.Include(x => x.Bans)
.ThenInclude(x => x.User));
if (club == null)
return false;
@ -289,7 +277,7 @@ namespace NadekoBot.Modules.Xp.Services
{
using (var uow = _db.UnitOfWork)
{
club = uow.Clubs.GetByOwnerOrAdmin(ownerUserId);
club = uow.Clubs.GetByOwner(ownerUserId);
if (club == null)
return false;

View File

@ -36,7 +36,6 @@ namespace NadekoBot.Modules.Xp.Services
private readonly IImagesService _images;
private readonly Logger _log;
private readonly NadekoStrings _strings;
private readonly IDataCache _cache;
private readonly FontCollection _fonts = new FontCollection();
public const int XP_REQUIRED_LVL_1 = 36;
@ -55,6 +54,9 @@ namespace NadekoBot.Modules.Xp.Services
private readonly ConcurrentQueue<UserCacheItem> _addMessageXp
= new ConcurrentQueue<UserCacheItem>();
private readonly ConcurrentDictionary<string, byte[]> _imageStreams
= new ConcurrentDictionary<string, byte[]>();
private readonly Timer updateXpTimer;
private readonly HttpClient http = new HttpClient();
private FontFamily _usernameFontFamily;
@ -67,7 +69,7 @@ namespace NadekoBot.Modules.Xp.Services
public XpService(CommandHandler cmd, IBotConfigProvider bc,
IEnumerable<GuildConfig> allGuildConfigs, IImagesService images,
DbService db, NadekoStrings strings, IDataCache cache)
DbService db, NadekoStrings strings)
{
_db = db;
_cmd = cmd;
@ -75,7 +77,6 @@ namespace NadekoBot.Modules.Xp.Services
_images = images;
_log = LogManager.GetCurrentClassLogger();
_strings = strings;
_cache = cache;
//load settings
allGuildConfigs = allGuildConfigs.Where(x => x.XpSettings != null);
@ -144,13 +145,12 @@ namespace NadekoBot.Modules.Xp.Services
du.LastXpGain = DateTime.UtcNow;
var globalXp = du.TotalXp;
var globalXp = uow.Xp.GetTotalUserXp(item.Key.User.Id);
var oldGlobalLevelData = new LevelStats(globalXp);
var newGlobalLevelData = new LevelStats(globalXp + xp);
var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
usr.Xp += xp;
du.TotalXp += xp;
if (du.Club != null)
du.Club.Xp += xp;
var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
@ -308,11 +308,11 @@ namespace NadekoBot.Modules.Xp.Services
}
}
public DiscordUser[] GetUserXps(int page)
public (ulong UserId, int TotalXp)[] GetUserXps(int page)
{
using (var uow = _db.UnitOfWork)
{
return uow.DiscordUsers.GetUsersXpLeaderboardFor(page);
return uow.Xp.GetUsersFor(page);
}
}
@ -403,6 +403,17 @@ namespace NadekoBot.Modules.Xp.Services
return _rewardedUsers.Add(userId);
}
public LevelStats GetGlobalUserStats(ulong userId)
{
int totalXp;
using (var uow = _db.UnitOfWork)
{
totalXp = uow.Xp.GetTotalUserXp(userId);
}
return new LevelStats(totalXp);
}
public FullUserStats GetUserStats(IGuildUser user)
{
DiscordUser du;
@ -414,8 +425,8 @@ namespace NadekoBot.Modules.Xp.Services
{
du = uow.DiscordUsers.GetOrCreate(user);
stats = uow.Xp.GetOrCreateUser(user.GuildId, user.Id);
totalXp = du.TotalXp;
globalRank = uow.DiscordUsers.GetUserGlobalRanking(user.Id);
totalXp = uow.Xp.GetTotalUserXp(user.Id);
globalRank = uow.Xp.GetUserGlobalRanking(user.Id);
guildRank = uow.Xp.GetUserGuildRanking(user.Id, user.GuildId);
}
@ -539,7 +550,7 @@ namespace NadekoBot.Modules.Xp.Services
}
}
public Task<MemoryStream> GenerateImageAsync(IGuildUser user)
public Task<Image<Rgba32>> GenerateImageAsync(IGuildUser user)
{
return GenerateImageAsync(GetUserStats(user));
}
@ -555,166 +566,170 @@ namespace NadekoBot.Modules.Xp.Services
_timeFont = _fonts.Find("Whitney-Bold").CreateFont(20);
}
public Task<MemoryStream> GenerateImageAsync(FullUserStats stats) => Task.Run(async () =>
public Task<Image<Rgba32>> GenerateImageAsync(FullUserStats stats) => Task.Run(async () =>
{
using (var img = Image.Load(_images.XpCard.ToArray()))
var img = Image.Load(_images.XpCard.ToArray());
var username = stats.User.ToString();
var usernameFont = _usernameFontFamily
.CreateFont(username.Length <= 6
? 50
: 50 - username.Length);
img.DrawText("@" + username, usernameFont, Rgba32.White,
new PointF(130, 5));
// level
img.DrawText(stats.Global.Level.ToString(), _levelFont, Rgba32.White,
new PointF(47, 137));
img.DrawText(stats.Guild.Level.ToString(), _levelFont, Rgba32.White,
new PointF(47, 285));
//club name
var clubName = stats.User.Club?.ToString() ?? "-";
var clubFont = _clubFontFamily
.CreateFont(clubName.Length <= 8
? 35
: 35 - (clubName.Length / 2));
img.DrawText(clubName, clubFont, Rgba32.White,
new PointF(650 - clubName.Length * 10, 40));
var pen = new Pen<Rgba32>(Rgba32.Black, 1);
var brush = Brushes.Solid<Rgba32>(Rgba32.White);
var xpBgBrush = Brushes.Solid<Rgba32>(new Rgba32(0, 0, 0, 0.4f));
var global = stats.Global;
var guild = stats.Guild;
//xp bar
img.FillPolygon(xpBgBrush, new[] {
new PointF(321, 104),
new PointF(321 + (450 * (global.LevelXp / (float)global.RequiredXp)), 104),
new PointF(286 + (450 * (global.LevelXp / (float)global.RequiredXp)), 235),
new PointF(286, 235),
});
img.DrawText($"{global.LevelXp}/{global.RequiredXp}", _xpFont, brush, pen,
new PointF(430, 130));
img.FillPolygon(xpBgBrush, new[] {
new PointF(282, 248),
new PointF(282 + (450 * (guild.LevelXp / (float)guild.RequiredXp)), 248),
new PointF(247 + (450 * (guild.LevelXp / (float)guild.RequiredXp)), 379),
new PointF(247, 379),
});
img.DrawText($"{guild.LevelXp}/{guild.RequiredXp}", _xpFont, brush, pen,
new PointF(400, 270));
if (stats.FullGuildStats.AwardedXp != 0)
{
var username = stats.User.ToString();
var usernameFont = _usernameFontFamily
.CreateFont(username.Length <= 6
? 50
: 50 - username.Length);
img.DrawText("@" + username, usernameFont, Rgba32.White,
new PointF(130, 5));
// level
img.DrawText(stats.Global.Level.ToString(), _levelFont, Rgba32.White,
new PointF(47, 137));
img.DrawText(stats.Guild.Level.ToString(), _levelFont, Rgba32.White,
new PointF(47, 285));
//club name
var clubName = stats.User.Club?.ToString() ?? "-";
var clubFont = _clubFontFamily
.CreateFont(clubName.Length <= 8
? 35
: 35 - (clubName.Length / 2));
img.DrawText(clubName, clubFont, Rgba32.White,
new PointF(650 - clubName.Length * 10, 40));
var pen = new Pen<Rgba32>(Rgba32.Black, 1);
var brush = Brushes.Solid<Rgba32>(Rgba32.White);
var xpBgBrush = Brushes.Solid<Rgba32>(new Rgba32(0, 0, 0, 0.4f));
var global = stats.Global;
var guild = stats.Guild;
//xp bar
img.FillPolygon(xpBgBrush, new[] {
new PointF(321, 104),
new PointF(321 + (450 * (global.LevelXp / (float)global.RequiredXp)), 104),
new PointF(286 + (450 * (global.LevelXp / (float)global.RequiredXp)), 235),
new PointF(286, 235),
});
img.DrawText($"{global.LevelXp}/{global.RequiredXp}", _xpFont, brush, pen,
new PointF(430, 130));
img.FillPolygon(xpBgBrush, new[] {
new PointF(282, 248),
new PointF(282 + (450 * (guild.LevelXp / (float)guild.RequiredXp)), 248),
new PointF(247 + (450 * (guild.LevelXp / (float)guild.RequiredXp)), 379),
new PointF(247, 379),
});
img.DrawText($"{guild.LevelXp}/{guild.RequiredXp}", _xpFont, brush, pen,
new PointF(400, 270));
if (stats.FullGuildStats.AwardedXp != 0)
{
var sign = stats.FullGuildStats.AwardedXp > 0
? "+ "
: "";
img.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})", _awardedFont, brush, pen,
new PointF(445 - (Math.Max(0, (stats.FullGuildStats.AwardedXp.ToString().Length - 2)) * 5), 335));
}
//ranking
img.DrawText(stats.GlobalRanking.ToString(), _rankFont, Rgba32.White,
new PointF(148, 170));
img.DrawText(stats.GuildRanking.ToString(), _rankFont, Rgba32.White,
new PointF(148, 317));
//time on this level
string GetTimeSpent(DateTime time)
{
var offset = DateTime.UtcNow - time;
return $"{offset.Days}d{offset.Hours}h{offset.Minutes}m";
}
img.DrawText(GetTimeSpent(stats.User.LastLevelUp), _timeFont, Rgba32.White,
new PointF(50, 197));
img.DrawText(GetTimeSpent(stats.FullGuildStats.LastLevelUp), _timeFont, Rgba32.White,
new PointF(50, 344));
//avatar
if (stats.User.AvatarId != null)
{
try
{
var avatarUrl = stats.User.RealAvatarUrl();
var (succ, data) = await _cache.TryGetImageDataAsync(avatarUrl);
if (!succ)
{
using (var temp = await http.GetStreamAsync(avatarUrl))
using (var tempDraw = Image.Load(temp).Resize(69, 70))
{
ApplyRoundedCorners(tempDraw, 35);
data = tempDraw.ToStream().ToArray();
}
await _cache.SetImageDataAsync(avatarUrl, data);
}
var toDraw = Image.Load(data);
img.DrawImage(toDraw,
1,
new Size(69, 70),
new Point(32, 10));
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
//club image
if (!string.IsNullOrWhiteSpace(stats.User.Club?.ImageUrl))
{
var imgUrl = stats.User.Club.ImageUrl;
try
{
var (succ, data) = await _cache.TryGetImageDataAsync(imgUrl);
if (!succ)
{
using (var temp = await http.GetStreamAsync(imgUrl))
using (var tempDraw = Image.Load(temp).Resize(45, 45))
{
ApplyRoundedCorners(tempDraw, 22.5f);
data = tempDraw.ToStream().ToArray();
}
await _cache.SetImageDataAsync(imgUrl, data);
}
var toDraw = Image.Load(data);
img.DrawImage(toDraw,
1,
new Size(45, 45),
new Point(722, 25));
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
return img.Resize(432, 211).ToStream();
var sign = stats.FullGuildStats.AwardedXp > 0
? "+ "
: "";
img.DrawText($"({sign}{stats.FullGuildStats.AwardedXp})", _awardedFont, brush, pen,
new PointF(445 - (Math.Max(0, (stats.FullGuildStats.AwardedXp.ToString().Length - 2)) * 5), 335));
}
//ranking
img.DrawText(stats.GlobalRanking.ToString(), _rankFont, Rgba32.White,
new PointF(148, 170));
img.DrawText(stats.GuildRanking.ToString(), _rankFont, Rgba32.White,
new PointF(148, 317));
//time on this level
string GetTimeSpent(DateTime time)
{
var offset = DateTime.UtcNow - time;
return $"{offset.Days}d{offset.Hours}h{offset.Minutes}m";
}
img.DrawText(GetTimeSpent(stats.User.LastLevelUp), _timeFont, Rgba32.White,
new PointF(50, 197));
img.DrawText(GetTimeSpent(stats.FullGuildStats.LastLevelUp), _timeFont, Rgba32.White,
new PointF(50, 344));
//avatar
if (stats.User.AvatarId != null)
{
try
{
var avatarUrl = stats.User.RealAvatarUrl();
byte[] s;
if (!_imageStreams.TryGetValue(avatarUrl, out s))
{
using (var temp = await http.GetStreamAsync(avatarUrl))
{
var tempDraw = Image.Load(temp);
tempDraw = tempDraw.Resize(69, 70);
ApplyRoundedCorners(tempDraw, 35);
s = tempDraw.ToStream().ToArray();
}
_imageStreams.AddOrUpdate(avatarUrl, s, (k, v) => s);
}
var toDraw = Image.Load(s);
img.DrawImage(toDraw,
1,
new Size(69, 70),
new Point(32, 10));
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
//club image
if (!string.IsNullOrWhiteSpace(stats.User.Club?.ImageUrl))
{
var imgUrl = stats.User.Club.ImageUrl;
try
{
byte[] s;
if (!_imageStreams.TryGetValue(imgUrl, out s))
{
using (var temp = await http.GetStreamAsync(imgUrl))
{
var tempDraw = Image.Load(temp);
tempDraw = tempDraw.Resize(45, 45);
ApplyRoundedCorners(tempDraw, 22.5f);
s = tempDraw.ToStream().ToArray();
}
_imageStreams.AddOrUpdate(imgUrl, s, (k, v) => s);
}
var toDraw = Image.Load(s);
img.DrawImage(toDraw,
1,
new Size(45, 45),
new Point(722, 25));
}
catch (Exception ex)
{
_log.Warn(ex);
}
}
var arr = img.ToStream().ToArray();
//_log.Info("{0:F2} KB", arr.Length * 1.0f / 1.KB());
return img;
});

View File

@ -1,12 +1,10 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Common;
using NadekoBot.Common.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Xp.Common;
using NadekoBot.Modules.Xp.Services;
using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using System.Diagnostics;
using System.Linq;
@ -17,55 +15,23 @@ namespace NadekoBot.Modules.Xp
public partial class Xp : NadekoTopLevelModule<XpService>
{
private readonly DiscordSocketClient _client;
private readonly DbService _db;
public Xp(DiscordSocketClient client,DbService db)
public Xp(DiscordSocketClient client)
{
_client = client;
_db = db;
}
//[NadekoCommand, Usage, Description, Aliases]
//[RequireContext(ContextType.Guild)]
//[OwnerOnly]
//public async Task Populate()
//{
// var rng = new NadekoRandom();
// using (var uow = _db.UnitOfWork)
// {
// for (var i = 0ul; i < 1000000; i++)
// {
// uow.DiscordUsers.Add(new DiscordUser()
// {
// AvatarId = i.ToString(),
// Discriminator = "1234",
// UserId = i,
// Username = i.ToString(),
// Club = null,
// });
// var xp = uow.Xp.GetOrCreateUser(Context.Guild.Id, i);
// xp.Xp = rng.Next(100, 100000);
// }
// uow.Complete();
// }
//}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
//[Ratelimit(30)]
public async Task Experience([Remainder]IUser user = null)
{
user = user ?? Context.User;
var sw = Stopwatch.StartNew();
await Context.Channel.TriggerTypingAsync();
var img = await _service.GenerateImageAsync((IGuildUser)user);
sw.Stop();
_log.Info("Generating finished in {0:F2}s", sw.Elapsed.TotalSeconds);
sw.Restart();
await Context.Channel.SendFileAsync(img, $"{user.Id}_xp.png")
await Context.Channel.SendFileAsync(img.ToStream(), $"{user.Id}_xp.png")
.ConfigureAwait(false);
sw.Stop();
_log.Info("Sending finished in {0:F2}s", sw.Elapsed.TotalSeconds);
}
[NadekoCommand, Usage, Description, Aliases]
@ -74,7 +40,7 @@ namespace NadekoBot.Modules.Xp
{
page--;
if (page < 0 || page > 100)
if (page < 0)
return Task.CompletedTask;
var roles = _service.GetRoleRewards(Context.Guild.Id)
@ -203,7 +169,7 @@ namespace NadekoBot.Modules.Xp
[RequireContext(ContextType.Guild)]
public Task XpLeaderboard(int page = 1)
{
if (--page < 0 || page > 100)
if (--page < 0)
return Task.CompletedTask;
return Context.Channel.SendPaginatedConfirmAsync(_client, page, async (curPage) =>
@ -244,28 +210,32 @@ namespace NadekoBot.Modules.Xp
[RequireContext(ContextType.Guild)]
public async Task XpGlobalLeaderboard(int page = 1)
{
if (--page < 0 || page > 100)
if (--page < 0)
return;
var users = _service.GetUserXps(page);
var embed = new EmbedBuilder()
.WithTitle(GetText("global_leaderboard"))
.WithOkColor();
if (!users.Any())
embed.WithDescription("-");
else
await Context.Channel.SendPaginatedConfirmAsync(_client, page, async (curPage) =>
{
for (int i = 0; i < users.Length; i++)
{
var user = users[i];
embed.AddField(
$"#{(i + 1 + page * 9)} {(user.ToString())}",
$"{GetText("level_x", LevelStats.FromXp(users[i].TotalXp).Level)} - {users[i].TotalXp}xp");
}
}
var users = _service.GetUserXps(curPage);
await Context.Channel.EmbedAsync(embed);
var embed = new EmbedBuilder()
.WithTitle(GetText("global_leaderboard"))
.WithOkColor();
if (!users.Any())
return embed.WithDescription("-");
else
{
for (int i = 0; i < users.Length; i++)
{
var user = await Context.Guild.GetUserAsync(users[i].UserId).ConfigureAwait(false);
embed.AddField(
$"#{(i + 1 + curPage * 9)} {(user?.ToString() ?? users[i].UserId.ToString())}",
$"{GetText("level_x", LevelStats.FromXp(users[i].TotalXp).Level)} - {users[i].TotalXp}xp");
}
return embed;
}
}, addPaginatedFooter: false);
}
[NadekoCommand, Usage, Description, Aliases]

View File

@ -20,8 +20,6 @@ using NadekoBot.Common.ShardCom;
using NadekoBot.Common.TypeReaders;
using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Services.Database;
using StackExchange.Redis;
using Newtonsoft.Json;
namespace NadekoBot
{
@ -141,7 +139,6 @@ namespace NadekoBot
.AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this
.AddManual<NadekoBot>(this)
.AddManual<IUnitOfWork>(uow)
.AddManual<IDataCache>(new RedisCache())
.LoadFrom(Assembly.GetEntryAssembly())
.Build();
@ -242,6 +239,13 @@ namespace NadekoBot
#if GLOBAL_NADEKO
isPublicNadeko = true;
#endif
//_log.Info(string.Join(", ", CommandService.Commands
// .Distinct(x => x.Name + x.Module.Name)
// .SelectMany(x => x.Aliases)
// .GroupBy(x => x)
// .Where(x => x.Count() > 1)
// .Select(x => x.Key + $"({x.Count()})")));
//unload modules which are not available on the public bot
if(isPublicNadeko)
@ -252,7 +256,6 @@ namespace NadekoBot
.ForEach(x => CommandService.RemoveModuleAsync(x));
Ready.TrySetResult(true);
HandleStatusChanges();
_log.Info($"Shard {Client.ShardId} ready.");
//_log.Info(await stats.Print().ConfigureAwait(false));
}
@ -315,51 +318,5 @@ namespace NadekoBot
}
})).Start();
}
private void HandleStatusChanges()
{
var sub = Services.GetService<IDataCache>().Redis.GetSubscriber();
sub.Subscribe("status.game_set", async (ch, game) =>
{
try
{
var obj = new { Name = default(string) };
obj = JsonConvert.DeserializeAnonymousType(game, obj);
await Client.SetGameAsync(obj.Name).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
}
}, CommandFlags.FireAndForget);
sub.Subscribe("status.stream_set", async (ch, streamData) =>
{
try
{
var obj = new { Name = "", Url = "" };
obj = JsonConvert.DeserializeAnonymousType(streamData, obj);
await Client.SetGameAsync(obj.Name, obj.Url, StreamType.Twitch).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.Warn(ex);
}
}, CommandFlags.FireAndForget);
}
public Task SetGameAsync(string game)
{
var obj = new { Name = game };
var sub = Services.GetService<IDataCache>().Redis.GetSubscriber();
return sub.PublishAsync("status.game_set", JsonConvert.SerializeObject(obj));
}
public Task SetStreamAsync(string name, string url)
{
var obj = new { Name = name, Url = url };
var sub = Services.GetService<IDataCache>().Redis.GetSubscriber();
return sub.PublishAsync("status.game_set", JsonConvert.SerializeObject(obj));
}
}
}

View File

@ -1,18 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<OutputType>exe</OutputType>
<AssetTargetFallback>$(AssetTargetFallback);dnxcore50;portable-net45+win8+wpa81</AssetTargetFallback>
<Description>General purpose Discord bot written in C#.</Description>
<Copyright>Kwoth</Copyright>
<Authors>Kwoth</Authors>
<PublisherName>Kwoth</PublisherName>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyName>NadekoBot</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>NadekoBot</PackageId>
<RuntimeFrameworkVersion>1.1.1</RuntimeFrameworkVersion>
<PackageTargetFallback>$(PackageTargetFallback);dnxcore50;portable-net45+win8+wpa81</PackageTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<ApplicationIcon>nadeko_icon.ico</ApplicationIcon>
<RuntimeIdentifiers>win7-x64<!--;ubuntu.14.04-x64;osx.10.10-x64 --></RuntimeIdentifiers>
<Configurations>Debug;Release;global_nadeko</Configurations>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Version)' == '' ">
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">1.9.1</VersionPrefix>
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">1.4.1</VersionPrefix>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
<Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version>
</PropertyGroup>
@ -47,24 +59,27 @@
<ItemGroup>
<PackageReference Include="AngleSharp" Version="0.9.9" />
<PackageReference Include="Discord.Net" Version="2.0.0-alpha-build-00828" />
<PackageReference Include="CoreCLR-NCalc" Version="2.1.3" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.29.1.138" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.29.1.976" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.29.1.896" />
<PackageReference Include="libvideo" Version="1.0.1" />
<PackageReference Include="CoreCLR-NCalc" Version="2.1.2" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.20.0.701" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.20.0.466" />
<PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00194" />
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha9-00189" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="NLog" Version="5.0.0-beta10" />
<PackageReference Include="StackExchange.Redis" Version="1.2.6" />
<PackageReference Include="System.ValueTuple" Version="4.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="NLog" Version="5.0.0-beta03" />
<PackageReference Include="NYoutubeDL" Version="0.4.4" />
<PackageReference Include="System.ValueTuple" Version="4.4.0-preview1-25305-02" />
<PackageReference Include="System.Xml.XPath" Version="4.3.0" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'GlobalNadeko' ">
@ -72,12 +87,22 @@
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\CommandStrings.Designer.cs">
<SubType>Designer</SubType>
</Compile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>NadekoBot</id>
<version>1.4.0-2$suffix$</version>
<title>NadekoBot</title>
<authors>Kwoth</authors>
<owners>Kwoth</owners>
<description>General purpose discord chat bot written in C#.</description>
<tags>nadeko;bot;nadekobot;discord bot</tags>
<projectUrl>https://github.com/Kwoth/NadekoBot</projectUrl>
<licenseUrl>https://choosealicense.com/licenses/unlicense/</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
</metadata>
</package>

View File

@ -0,0 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cadministration_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgambling_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgames_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cpermissions_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Csearches_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cutility_005Ccommands/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NadekoBot")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyInformationalVersion("1.0")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f8225ac4-3cbc-40b4-bcf3-1cacf276bf29")]

View File

@ -3589,7 +3589,7 @@
<value>`{0}xpex Role Excluded-Role` `{0}xpex Server`</value>
</data>
<data name="xpexclude_desc" xml:space="preserve">
<value>Exclude a channel, role or current server from the xp system.</value>
<value>Exclude a user or a role from the xp system, or whole current server.</value>
</data>
<data name="xpnotify_cmd" xml:space="preserve">
<value>xpnotify xpn</value>
@ -3780,31 +3780,4 @@
<data name="nsfwclearcache_desc" xml:space="preserve">
<value>Clears nsfw cache.</value>
</data>
<data name="clubadmin_cmd" xml:space="preserve">
<value>clubadmin</value>
</data>
<data name="clubadmin_usage" xml:space="preserve">
<value>`{0}clubadmin`</value>
</data>
<data name="clubadmin_desc" xml:space="preserve">
<value>Assigns (or unassigns) staff role to the member of the club. Admins can ban, kick and accept applications.</value>
</data>
<data name="autoboobs_cmd" xml:space="preserve">
<value>autoboobs</value>
</data>
<data name="autoboobs_desc" xml:space="preserve">
<value>Posts a boobs every X seconds. 20 seconds minimum. Provide no arguments to disable.</value>
</data>
<data name="autoboobs_usage" xml:space="preserve">
<value>`{0}autoboobs 30` or `{0}autoboobs`</value>
</data>
<data name="autobutts_cmd" xml:space="preserve">
<value>autobutts</value>
</data>
<data name="autobutts_desc" xml:space="preserve">
<value>Posts a butts every X seconds. 20 seconds minimum. Provide no arguments to disable.</value>
</data>
<data name="autobutts_usage" xml:space="preserve">
<value>`{0}autobutts 30` or `{0}autobutts`</value>
</data>
</root>

View File

@ -1,5 +1,4 @@
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.RegularExpressions;
namespace NadekoBot.Services.Database.Models
@ -7,9 +6,7 @@ namespace NadekoBot.Services.Database.Models
public class CustomReaction : DbEntity
{
public ulong? GuildId { get; set; }
[NotMapped]
[JsonIgnore]
public Regex Regex { get; set; }
public string Response { get; set; }
public string Trigger { get; set; }
@ -19,7 +16,6 @@ namespace NadekoBot.Services.Database.Models
public bool AutoDeleteTrigger { get; set; }
public bool DmResponse { get; set; }
[JsonIgnore]
public bool IsGlobal => !GuildId.HasValue;
public bool ContainsAnywhere { get; set; }

View File

@ -10,9 +10,6 @@ namespace NadekoBot.Services.Database.Models
public string AvatarId { get; set; }
public ClubInfo Club { get; set; }
public bool IsClubAdmin { get; set; }
public int TotalXp { get; set; }
public DateTime LastLevelUp { get; set; } = DateTime.UtcNow;
public DateTime LastXpGain { get; set; } = DateTime.MinValue;
public XpNotificationType NotifyOnLevelUp { get; set; }

View File

@ -3,21 +3,23 @@ using System.Collections.Generic;
using System.Linq;
using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Data.Sqlite;
using System.IO;
namespace NadekoBot.Services.Database
{
public class NadekoContextFactory : IDesignTimeDbContextFactory<NadekoContext>
{
public NadekoContext CreateDbContext(string[] args)
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<NadekoContext>();
var builder = new SqliteConnectionStringBuilder("Data Source=data/NadekoBot.db");
builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource);
optionsBuilder.UseSqlite(builder.ToString());
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite("Filename=./data/NadekoBot.db");
var ctx = new NadekoContext(optionsBuilder.Options);
ctx.Database.SetCommandTimeout(60);
return ctx;
@ -56,7 +58,12 @@ namespace NadekoBot.Services.Database
public DbSet<ModulePrefix> ModulePrefixes { get; set; }
public DbSet<RewardedUser> RewardedUsers { get; set; }
public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
public NadekoContext() : base()
{
}
public NadekoContext(DbContextOptions options) : base(options)
{
}
@ -224,7 +231,7 @@ namespace NadekoBot.Services.Database
musicPlaylistEntity
.HasMany(p => p.Songs)
.WithOne()
.OnDelete(DeleteBehavior.Cascade);
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Cascade);
#endregion
@ -322,7 +329,7 @@ namespace NadekoBot.Services.Database
#region ClubManytoMany
modelBuilder.Entity<ClubApplicants>()
.HasKey(t => new { t.ClubId, t.UserId });
.HasKey(t => new { t.ClubId, t.UserId });
modelBuilder.Entity<ClubApplicants>()
.HasOne(pt => pt.User)
@ -333,7 +340,7 @@ namespace NadekoBot.Services.Database
.WithMany(x => x.Applicants);
modelBuilder.Entity<ClubBans>()
.HasKey(t => new { t.ClubId, t.UserId });
.HasKey(t => new { t.ClubId, t.UserId });
modelBuilder.Entity<ClubBans>()
.HasOne(pt => pt.User)

View File

@ -10,7 +10,6 @@ namespace NadekoBot.Services.Database.Repositories
int GetNextDiscrim(string clubName);
ClubInfo GetByName(string v, int discrim, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null);
ClubInfo GetByOwner(ulong userId, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null);
ClubInfo GetByOwnerOrAdmin(ulong userId);
ClubInfo GetByMember(ulong userId, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null);
ClubInfo[] GetClubLeaderboardPage(int page);
}

View File

@ -6,7 +6,5 @@ namespace NadekoBot.Services.Database.Repositories
public interface IDiscordUserRepository : IRepository<DiscordUser>
{
DiscordUser GetOrCreate(IUser original);
int GetUserGlobalRanking(ulong id);
DiscordUser[] GetUsersXpLeaderboardFor(int page);
}
}

View File

@ -5,7 +5,10 @@ namespace NadekoBot.Services.Database.Repositories
public interface IXpRepository : IRepository<UserXpStats>
{
UserXpStats GetOrCreateUser(ulong guildId, ulong userId);
int GetUserGuildRanking(ulong userId, ulong guildId);
int GetTotalUserXp(ulong userId);
UserXpStats[] GetUsersFor(ulong guildId, int page);
(ulong UserId, int TotalXp)[] GetUsersFor(int page);
int GetUserGlobalRanking(ulong userId);
int GetUserGuildRanking(ulong userId, ulong guildId);
}
}

View File

@ -24,30 +24,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl
return func(_set).FirstOrDefault(x => x.Owner.UserId == userId);
}
public ClubInfo GetByOwnerOrAdmin(ulong userId)
{
return _set
.Include(x => x.Bans)
.ThenInclude(x => x.User)
.Include(x => x.Applicants)
.ThenInclude(x => x.User)
.Include(x => x.Owner)
.FirstOrDefault(x => x.Owner.UserId == userId) ??
_context.Set<DiscordUser>()
.Include(x => x.Club)
.ThenInclude(x => x.Users)
.Include(x => x.Club)
.ThenInclude(x => x.Bans)
.ThenInclude(x => x.User)
.Include(x => x.Club)
.ThenInclude(x => x.Applicants)
.ThenInclude(x => x.User)
.Include(x => x.Club)
.ThenInclude(x => x.Owner)
.FirstOrDefault(x => x.UserId == userId && x.IsClubAdmin)
?.Club;
}
public ClubInfo GetByName(string name, int discrim, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null)
{
if (func == null)

View File

@ -37,23 +37,5 @@ namespace NadekoBot.Services.Database.Repositories.Impl
return toReturn;
}
public int GetUserGlobalRanking(ulong id)
{
return _set.Count(x => x.TotalXp >
_set.Where(y => y.UserId == id)
.DefaultIfEmpty()
.Sum(y => y.TotalXp));
}
public DiscordUser[] GetUsersXpLeaderboardFor(int page)
{
return _set
.OrderByDescending(x => x.TotalXp)
.Skip(page * 9)
.Take(9)
.AsEnumerable()
.ToArray();
}
}
}

View File

@ -29,6 +29,11 @@ namespace NadekoBot.Services.Database.Repositories.Impl
return usr;
}
public int GetTotalUserXp(ulong userId)
{
return _set.Where(x => x.UserId == userId).Sum(x => x.Xp);
}
public UserXpStats[] GetUsersFor(ulong guildId, int page)
{
return _set.Where(x => x.GuildId == guildId)
@ -38,6 +43,15 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.ToArray();
}
public int GetUserGlobalRanking(ulong userId)
{
return _set
.GroupBy(x => x.UserId)
.Count(x => x.Sum(y => y.Xp) > _set
.Where(y => y.UserId == userId)
.Sum(y => y.Xp)) + 1;
}
public int GetUserGuildRanking(ulong userId, ulong guildId)
{
return _set
@ -48,5 +62,16 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.DefaultIfEmpty()
.Sum())) + 1;
}
public (ulong UserId, int TotalXp)[] GetUsersFor(int page)
{
return _set.GroupBy(x => x.UserId)
.OrderByDescending(x => x.Sum(y => y.Xp))
.Skip(page * 9)
.Take(9)
.AsEnumerable()
.Select(x => (x.Key, x.Sum(y => y.Xp)))
.ToArray();
}
}
}

View File

@ -1,28 +1,25 @@
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database;
using System;
using System.IO;
using System.Linq;
namespace NadekoBot.Services
{
public class DbService
{
private readonly DbContextOptions<NadekoContext> options;
private readonly DbContextOptions<NadekoContext> migrateOptions;
private readonly DbContextOptions options;
private readonly DbContextOptions migrateOptions;
private readonly string _connectionString;
public DbService(IBotCredentials creds)
{
var builder = new SqliteConnectionStringBuilder(creds.Db.ConnectionString);
builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource);
var optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
optionsBuilder.UseSqlite(builder.ToString());
_connectionString = creds.Db.ConnectionString;
var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite(creds.Db.ConnectionString);
options = optionsBuilder.Options;
optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
optionsBuilder.UseSqlite(builder.ToString(), x => x.SuppressForeignKeyEnforcement());
optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlite(creds.Db.ConnectionString, x => x.SuppressForeignKeyEnforcement());
migrateOptions = optionsBuilder.Options;
}

View File

@ -1,18 +0,0 @@
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services
{
public interface IDataCache
{
ConnectionMultiplexer Redis { get; }
Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key);
Task<(bool Success, string Data)> TryGetAnimeDataAsync(string key);
Task SetImageDataAsync(string key, byte[] data);
Task SetAnimeDataAsync(string link, string data);
}
}

View File

@ -90,13 +90,18 @@ namespace NadekoBot.Services.Impl
ulong.TryParse(data[nameof(ClientId)], out ulong clId);
ClientId = clId;
//var scId = data[nameof(SoundCloudClientId)];
//SoundCloudClientId = scId;
//SoundCloudClientId = string.IsNullOrWhiteSpace(scId)
// ?
// : scId;
CarbonKey = data[nameof(CarbonKey)];
var dbSection = data.GetSection("db");
Db = new DBConfig(string.IsNullOrWhiteSpace(dbSection["Type"])
? "sqlite"
: dbSection["Type"],
string.IsNullOrWhiteSpace(dbSection["ConnectionString"])
? "Data Source=data/NadekoBot.db"
? "Filename=./data/NadekoBot.db"
: dbSection["ConnectionString"]);
}
catch (Exception ex)
@ -120,7 +125,7 @@ namespace NadekoBot.Services.Impl
public string SoundCloudClientId { get; set; } = "";
public string CleverbotApiKey { get; } = "";
public string CarbonKey { get; set; } = "";
public DBConfig Db { get; set; } = new DBConfig("sqlite", "Data Source=data/NadekoBot.db");
public DBConfig Db { get; set; } = new DBConfig("sqlite", "Filename=./data/NadekoBot.db");
public int TotalShards { get; set; } = 1;
public string PatreonAccessToken { get; set; } = "";
public string PatreonCampaignId { get; set; } = "334038";

View File

@ -5,9 +5,6 @@ using System.Linq;
using Discord;
using NLog;
using NadekoBot.Services.Database.Models;
using NadekoBot.Common;
using Newtonsoft.Json;
using System.IO;
namespace NadekoBot.Services.Impl
{
@ -19,14 +16,6 @@ namespace NadekoBot.Services.Impl
public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; }
public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture;
private static readonly Dictionary<string, CommandData> _commandData;
static Localization()
{
_commandData = JsonConvert.DeserializeObject<Dictionary<string, CommandData>>(
File.ReadAllText("./data/command_strings.json"));
}
private Localization() { }
public Localization(IBotConfigProvider bcp, IEnumerable<GuildConfig> gcs, DbService db)
{
@ -128,19 +117,10 @@ namespace NadekoBot.Services.Impl
return info ?? DefaultCultureInfo;
}
public static CommandData LoadCommand(string key)
public static string LoadCommandString(string key)
{
_commandData.TryGetValue(key, out var toReturn);
if (toReturn == null)
return new CommandData
{
Cmd = key,
Desc = key,
Usage = key,
};
return toReturn;
string toReturn = Resources.CommandStrings.ResourceManager.GetString(key);
return string.IsNullOrWhiteSpace(toReturn) ? key : toReturn;
}
}
}

View File

@ -1,40 +0,0 @@
using StackExchange.Redis;
using System.Threading.Tasks;
namespace NadekoBot.Services.Impl
{
public class RedisCache : IDataCache
{
public ConnectionMultiplexer Redis { get; }
private readonly IDatabase _db;
public RedisCache()
{
Redis = ConnectionMultiplexer.Connect("127.0.0.1");
Redis.PreserveAsyncOrder = false;
_db = Redis.GetDatabase();
}
public async Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key)
{
byte[] x = await _db.StringGetAsync("image_" + key);
return (x != null, x);
}
public Task SetImageDataAsync(string key, byte[] data)
{
return _db.StringSetAsync("image_" + key, data);
}
public async Task<(bool Success, string Data)> TryGetAnimeDataAsync(string key)
{
string x = await _db.StringGetAsync("anime_" + key);
return (x != null, x);
}
public Task SetAnimeDataAsync(string key, string data)
{
return _db.StringSetAsync("anime_" + key, data);
}
}
}

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Services.Impl
private readonly IBotCredentials _creds;
private readonly DateTime _started;
public const string BotVersion = "1.9.0";
public const string BotVersion = "1.8.4";
public string Author => "Kwoth#2560";
public string Library => "Discord.Net";

View File

@ -11,7 +11,7 @@ namespace NadekoBot.Services
var logConfig = new LoggingConfiguration();
var consoleTarget = new ColoredConsoleTarget()
{
Layout = @"${date:format=HH\:mm\:ss} ${logger:shortName=True} | ${message}"
Layout = @"${date:format=HH\:mm\:ss} ${logger} | ${message}"
};
logConfig.AddTarget("Console", consoleTarget);

View File

@ -13,6 +13,7 @@
"customreactions_stats_not_found": "No stats for that trigger found, no action taken.",
"customreactions_trigger": "Trigger",
"customreactions_redacted_too_long": "Redecated because it's too long.",
"nsfw_autohentai_stopped": "Autohentai stopped.",
"nsfw_not_found": "No results found.",
"nsfw_blacklisted_tag_list": "List of blacklisted tags:",
"nsfw_blacklisted_tag": "One or more tags you've used are blacklisted",
@ -867,10 +868,5 @@
"xp_club_icon_set": "New club icon set.",
"xp_club_bans_for": "Bans for {0} club",
"xp_club_apps_for": "Applicants for {0} club",
"xp_club_leaderboard": "Club leaderboard - page {0}",
"xp_club_admin_add": "{0} is now a club admin.",
"xp_club_admin_remove": "{0} is no longer club admin.",
"xp_club_admin_error": "Error. You are either not the owner of the club, or that user is not in your club.",
"nsfw_started": "Started. Reposting every {0}s.",
"nsfw_stopped": "Stopped reposting."
"xp_club_leaderboard": "Club leaderboard - page {0}"
}

View File

@ -13,7 +13,7 @@
"CarbonKey": "",
"Db": {
"Type": "sqlite",
"ConnectionString": "Data Source=data/NadekoBot.db"
"ConnectionString": "Filename=./data/NadekoBot.db"
},
"TotalShards": 1,
"PatreonAccessToken": "",

File diff suppressed because it is too large Load Diff