Merge pull request #3 from mattburchett/1.9

1.9 2017/11/30
This commit is contained in:
Matt Burchett 2017-11-30 19:15:58 -06:00 committed by GitHub
commit be9352bc03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 6811 additions and 780 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,9 @@
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

@ -0,0 +1,27 @@
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

@ -0,0 +1,25 @@
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,5 +34,9 @@ INSERT INTO DiscordUser
FROM DiscordUser_tmp; FROM DiscordUser_tmp;
DROP TABLE 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,14 +462,18 @@ namespace NadekoBot.Migrations
b.Property<string>("Discriminator"); b.Property<string>("Discriminator");
b.Property<bool>("IsClubAdmin");
b.Property<DateTime>("LastLevelUp") b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 11, 22, 0, 31, 236, DateTimeKind.Local)); .HasDefaultValue(new DateTime(2017, 9, 15, 5, 48, 8, 660, DateTimeKind.Local));
b.Property<DateTime>("LastXpGain"); b.Property<DateTime>("LastXpGain");
b.Property<int>("NotifyOnLevelUp"); b.Property<int>("NotifyOnLevelUp");
b.Property<int>("TotalXp");
b.Property<ulong>("UserId"); b.Property<ulong>("UserId");
b.Property<string>("Username"); b.Property<string>("Username");
@ -1362,7 +1366,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime>("LastLevelUp") b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 11, 22, 0, 31, 238, DateTimeKind.Local)); .HasDefaultValue(new DateTime(2017, 9, 15, 5, 48, 8, 665, DateTimeKind.Local));
b.Property<int>("NotifyOnLevelUp"); b.Property<int>("NotifyOnLevelUp");

View File

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

View File

@ -7,6 +7,7 @@ using NadekoBot.Modules.Music.Services;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration.Services namespace NadekoBot.Modules.Administration.Services
{ {
@ -16,6 +17,7 @@ namespace NadekoBot.Modules.Administration.Services
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly MusicService _music; private readonly MusicService _music;
private readonly Logger _log; private readonly Logger _log;
private readonly IDataCache _cache;
private readonly Replacer _rep; private readonly Replacer _rep;
private readonly DbService _db; private readonly DbService _db;
private readonly IBotConfigProvider _bcp; private readonly IBotConfigProvider _bcp;
@ -27,13 +29,19 @@ namespace NadekoBot.Modules.Administration.Services
public int Index { get; set; } public int Index { get; set; }
} }
public PlayingRotateService(DiscordSocketClient client, IBotConfigProvider bcp, MusicService music, DbService db) public PlayingRotateService(DiscordSocketClient client, IBotConfigProvider bcp,
MusicService music, DbService db, IDataCache cache, NadekoBot bot)
{ {
_client = client; _client = client;
_bcp = bcp; _bcp = bcp;
_music = music; _music = music;
_db = db; _db = db;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_cache = cache;
if (client.ShardId == 0)
{
_rep = new ReplacementBuilder() _rep = new ReplacementBuilder()
.WithClient(client) .WithClient(client)
.WithStats(client) .WithStats(client)
@ -60,7 +68,10 @@ namespace NadekoBot.Modules.Administration.Services
status = _rep.Replace(status); status = _rep.Replace(status);
try { await client.SetGameAsync(status).ConfigureAwait(false); } try
{
await bot.SetGameAsync(status).ConfigureAwait(false);
}
catch (Exception ex) catch (Exception ex)
{ {
_log.Warn(ex); _log.Warn(ex);
@ -74,3 +85,4 @@ namespace NadekoBot.Modules.Administration.Services
} }
} }
} }
}

View File

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

View File

@ -15,6 +15,7 @@ using NadekoBot.Modules.CustomReactions.Extensions;
using NadekoBot.Modules.Permissions.Common; using NadekoBot.Modules.Permissions.Common;
using NadekoBot.Modules.Permissions.Services; using NadekoBot.Modules.Permissions.Services;
using NadekoBot.Services.Impl; using NadekoBot.Services.Impl;
using Newtonsoft.Json;
namespace NadekoBot.Modules.CustomReactions.Services namespace NadekoBot.Modules.CustomReactions.Services
{ {
@ -32,9 +33,11 @@ namespace NadekoBot.Modules.CustomReactions.Services
private readonly CommandHandler _cmd; private readonly CommandHandler _cmd;
private readonly IBotConfigProvider _bc; private readonly IBotConfigProvider _bc;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly IDataCache _cache;
public CustomReactionsService(PermissionService perms, DbService db, NadekoStrings strings, public CustomReactionsService(PermissionService perms, DbService db, NadekoStrings strings,
DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow) DiscordSocketClient client, CommandHandler cmd, IBotConfigProvider bc, IUnitOfWork uow,
IDataCache cache)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_db = db; _db = db;
@ -43,12 +46,38 @@ namespace NadekoBot.Modules.CustomReactions.Services
_cmd = cmd; _cmd = cmd;
_bc = bc; _bc = bc;
_strings = strings; _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(); 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())); 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(); 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 void ClearStats() => ReactionStats.Clear();
public CustomReaction TryGetCustomReaction(IUserMessage umsg) public CustomReaction TryGetCustomReaction(IUserMessage umsg)

View File

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

View File

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

View File

@ -3,7 +3,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Common namespace NadekoBot.Modules.Music.Common
{ {
@ -18,27 +17,16 @@ namespace NadekoBot.Modules.Music.Common
public string SongUri { get; private set; } public string SongUri { get; private set; }
//private volatile bool restart = false;
public SongBuffer(string songUri, string skipTo, bool isLocal) public SongBuffer(string songUri, string skipTo, bool isLocal)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
//_log.Warn(songUri);
this.SongUri = songUri; this.SongUri = songUri;
this._isLocal = isLocal; this._isLocal = isLocal;
try try
{ {
this.p = StartFFmpegProcess(SongUri, 0); 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; this._outStream = this.p.StandardOutput.BaseStream;
} }
catch (System.ComponentModel.Win32Exception) catch (System.ComponentModel.Win32Exception)
{ {
@ -68,113 +56,14 @@ Check the guides for your platform on how to setup ffmpeg correctly:
Arguments = args, Arguments = args,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true, RedirectStandardOutput = true,
RedirectStandardError = true, RedirectStandardError = false,
CreateNoWindow = 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 object locker = new object();
private readonly bool _isLocal; 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) public int Read(byte[] b, int offset, int toRead)
{ {
lock (locker) lock (locker)
@ -204,214 +93,3 @@ Check the guides for your platform on how to setup ffmpeg correctly:
} }
} }
} }
//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,9 +16,13 @@ using NadekoBot.Modules.NSFW.Exceptions;
namespace NadekoBot.Modules.NSFW namespace NadekoBot.Modules.NSFW
{ {
// thanks to halitalf for adding autoboob and autobutt features :D
public class NSFW : NadekoTopLevelModule<SearchesService> public class NSFW : NadekoTopLevelModule<SearchesService>
{ {
private static readonly ConcurrentDictionary<ulong, Timer> _autoHentaiTimers = new ConcurrentDictionary<ulong, Timer>(); 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 static readonly ConcurrentHashSet<ulong> _hentaiBombBlacklist = new ConcurrentHashSet<ulong>();
private async Task InternalHentai(IMessageChannel channel, string tag, bool noError) private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
@ -49,10 +53,34 @@ namespace NadekoBot.Modules.NSFW
.WithDescription($"[{GetText("tag")}: {tag}]({img})")) .WithDescription($"[{GetText("tag")}: {tag}]({img})"))
.ConfigureAwait(false); .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 #if !GLOBAL_NADEKO
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(ChannelPermission.ManageMessages)] [RequireUserPermission(ChannelPermission.ManageMessages)]
@ -65,7 +93,7 @@ namespace NadekoBot.Modules.NSFW
if (!_autoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return; if (!_autoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return;
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false); await ReplyConfirmLocalized("stopped").ConfigureAwait(false);
return; return;
} }
@ -99,8 +127,90 @@ namespace NadekoBot.Modules.NSFW
interval, interval,
string.Join(", ", tagsArr)).ConfigureAwait(false); 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 #endif
[NadekoCommand, Usage, Description, Aliases]
public Task Hentai([Remainder] string tag = null) =>
InternalHentai(Context.Channel, tag, false);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task HentaiBomb([Remainder] string tag = null) public async Task HentaiBomb([Remainder] string tag = null)
{ {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -36,6 +36,7 @@ namespace NadekoBot.Modules.Xp.Services
private readonly IImagesService _images; private readonly IImagesService _images;
private readonly Logger _log; private readonly Logger _log;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly IDataCache _cache;
private readonly FontCollection _fonts = new FontCollection(); private readonly FontCollection _fonts = new FontCollection();
public const int XP_REQUIRED_LVL_1 = 36; public const int XP_REQUIRED_LVL_1 = 36;
@ -54,9 +55,6 @@ namespace NadekoBot.Modules.Xp.Services
private readonly ConcurrentQueue<UserCacheItem> _addMessageXp private readonly ConcurrentQueue<UserCacheItem> _addMessageXp
= new ConcurrentQueue<UserCacheItem>(); = new ConcurrentQueue<UserCacheItem>();
private readonly ConcurrentDictionary<string, byte[]> _imageStreams
= new ConcurrentDictionary<string, byte[]>();
private readonly Timer updateXpTimer; private readonly Timer updateXpTimer;
private readonly HttpClient http = new HttpClient(); private readonly HttpClient http = new HttpClient();
private FontFamily _usernameFontFamily; private FontFamily _usernameFontFamily;
@ -69,7 +67,7 @@ namespace NadekoBot.Modules.Xp.Services
public XpService(CommandHandler cmd, IBotConfigProvider bc, public XpService(CommandHandler cmd, IBotConfigProvider bc,
IEnumerable<GuildConfig> allGuildConfigs, IImagesService images, IEnumerable<GuildConfig> allGuildConfigs, IImagesService images,
DbService db, NadekoStrings strings) DbService db, NadekoStrings strings, IDataCache cache)
{ {
_db = db; _db = db;
_cmd = cmd; _cmd = cmd;
@ -77,6 +75,7 @@ namespace NadekoBot.Modules.Xp.Services
_images = images; _images = images;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_strings = strings; _strings = strings;
_cache = cache;
//load settings //load settings
allGuildConfigs = allGuildConfigs.Where(x => x.XpSettings != null); allGuildConfigs = allGuildConfigs.Where(x => x.XpSettings != null);
@ -145,12 +144,13 @@ namespace NadekoBot.Modules.Xp.Services
du.LastXpGain = DateTime.UtcNow; du.LastXpGain = DateTime.UtcNow;
var globalXp = uow.Xp.GetTotalUserXp(item.Key.User.Id); var globalXp = du.TotalXp;
var oldGlobalLevelData = new LevelStats(globalXp); var oldGlobalLevelData = new LevelStats(globalXp);
var newGlobalLevelData = new LevelStats(globalXp + xp); var newGlobalLevelData = new LevelStats(globalXp + xp);
var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp); var oldGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
usr.Xp += xp; usr.Xp += xp;
du.TotalXp += xp;
if (du.Club != null) if (du.Club != null)
du.Club.Xp += xp; du.Club.Xp += xp;
var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp); var newGuildLevelData = new LevelStats(usr.Xp + usr.AwardedXp);
@ -308,11 +308,11 @@ namespace NadekoBot.Modules.Xp.Services
} }
} }
public (ulong UserId, int TotalXp)[] GetUserXps(int page) public DiscordUser[] GetUserXps(int page)
{ {
using (var uow = _db.UnitOfWork) using (var uow = _db.UnitOfWork)
{ {
return uow.Xp.GetUsersFor(page); return uow.DiscordUsers.GetUsersXpLeaderboardFor(page);
} }
} }
@ -403,17 +403,6 @@ namespace NadekoBot.Modules.Xp.Services
return _rewardedUsers.Add(userId); 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) public FullUserStats GetUserStats(IGuildUser user)
{ {
DiscordUser du; DiscordUser du;
@ -425,8 +414,8 @@ namespace NadekoBot.Modules.Xp.Services
{ {
du = uow.DiscordUsers.GetOrCreate(user); du = uow.DiscordUsers.GetOrCreate(user);
stats = uow.Xp.GetOrCreateUser(user.GuildId, user.Id); stats = uow.Xp.GetOrCreateUser(user.GuildId, user.Id);
totalXp = uow.Xp.GetTotalUserXp(user.Id); totalXp = du.TotalXp;
globalRank = uow.Xp.GetUserGlobalRanking(user.Id); globalRank = uow.DiscordUsers.GetUserGlobalRanking(user.Id);
guildRank = uow.Xp.GetUserGuildRanking(user.Id, user.GuildId); guildRank = uow.Xp.GetUserGuildRanking(user.Id, user.GuildId);
} }
@ -550,7 +539,7 @@ namespace NadekoBot.Modules.Xp.Services
} }
} }
public Task<Image<Rgba32>> GenerateImageAsync(IGuildUser user) public Task<MemoryStream> GenerateImageAsync(IGuildUser user)
{ {
return GenerateImageAsync(GetUserStats(user)); return GenerateImageAsync(GetUserStats(user));
} }
@ -566,9 +555,10 @@ namespace NadekoBot.Modules.Xp.Services
_timeFont = _fonts.Find("Whitney-Bold").CreateFont(20); _timeFont = _fonts.Find("Whitney-Bold").CreateFont(20);
} }
public Task<Image<Rgba32>> GenerateImageAsync(FullUserStats stats) => Task.Run(async () => public Task<MemoryStream> 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 username = stats.User.ToString();
var usernameFont = _usernameFontFamily var usernameFont = _usernameFontFamily
@ -665,20 +655,19 @@ namespace NadekoBot.Modules.Xp.Services
{ {
var avatarUrl = stats.User.RealAvatarUrl(); var avatarUrl = stats.User.RealAvatarUrl();
byte[] s; var (succ, data) = await _cache.TryGetImageDataAsync(avatarUrl);
if (!_imageStreams.TryGetValue(avatarUrl, out s)) if (!succ)
{ {
using (var temp = await http.GetStreamAsync(avatarUrl)) using (var temp = await http.GetStreamAsync(avatarUrl))
using (var tempDraw = Image.Load(temp).Resize(69, 70))
{ {
var tempDraw = Image.Load(temp);
tempDraw = tempDraw.Resize(69, 70);
ApplyRoundedCorners(tempDraw, 35); ApplyRoundedCorners(tempDraw, 35);
s = tempDraw.ToStream().ToArray(); data = tempDraw.ToStream().ToArray();
} }
_imageStreams.AddOrUpdate(avatarUrl, s, (k, v) => s); await _cache.SetImageDataAsync(avatarUrl, data);
} }
var toDraw = Image.Load(s); var toDraw = Image.Load(data);
img.DrawImage(toDraw, img.DrawImage(toDraw,
@ -699,20 +688,19 @@ namespace NadekoBot.Modules.Xp.Services
var imgUrl = stats.User.Club.ImageUrl; var imgUrl = stats.User.Club.ImageUrl;
try try
{ {
byte[] s; var (succ, data) = await _cache.TryGetImageDataAsync(imgUrl);
if (!_imageStreams.TryGetValue(imgUrl, out s)) if (!succ)
{ {
using (var temp = await http.GetStreamAsync(imgUrl)) using (var temp = await http.GetStreamAsync(imgUrl))
using (var tempDraw = Image.Load(temp).Resize(45, 45))
{ {
var tempDraw = Image.Load(temp);
tempDraw = tempDraw.Resize(45, 45);
ApplyRoundedCorners(tempDraw, 22.5f); ApplyRoundedCorners(tempDraw, 22.5f);
s = tempDraw.ToStream().ToArray(); data = tempDraw.ToStream().ToArray();
} }
_imageStreams.AddOrUpdate(imgUrl, s, (k, v) => s); await _cache.SetImageDataAsync(imgUrl, data);
} }
var toDraw = Image.Load(s); var toDraw = Image.Load(data);
img.DrawImage(toDraw, img.DrawImage(toDraw,
1, 1,
@ -725,11 +713,8 @@ namespace NadekoBot.Modules.Xp.Services
} }
} }
var arr = img.ToStream().ToArray(); return img.Resize(432, 211).ToStream();
}
//_log.Info("{0:F2} KB", arr.Length * 1.0f / 1.KB());
return img;
}); });

View File

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

View File

@ -20,6 +20,8 @@ using NadekoBot.Common.ShardCom;
using NadekoBot.Common.TypeReaders; using NadekoBot.Common.TypeReaders;
using NadekoBot.Common.TypeReaders.Models; using NadekoBot.Common.TypeReaders.Models;
using NadekoBot.Services.Database; using NadekoBot.Services.Database;
using StackExchange.Redis;
using Newtonsoft.Json;
namespace NadekoBot namespace NadekoBot
{ {
@ -139,6 +141,7 @@ namespace NadekoBot
.AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this .AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this
.AddManual<NadekoBot>(this) .AddManual<NadekoBot>(this)
.AddManual<IUnitOfWork>(uow) .AddManual<IUnitOfWork>(uow)
.AddManual<IDataCache>(new RedisCache())
.LoadFrom(Assembly.GetEntryAssembly()) .LoadFrom(Assembly.GetEntryAssembly())
.Build(); .Build();
@ -239,13 +242,6 @@ namespace NadekoBot
#if GLOBAL_NADEKO #if GLOBAL_NADEKO
isPublicNadeko = true; isPublicNadeko = true;
#endif #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 //unload modules which are not available on the public bot
if(isPublicNadeko) if(isPublicNadeko)
@ -256,6 +252,7 @@ namespace NadekoBot
.ForEach(x => CommandService.RemoveModuleAsync(x)); .ForEach(x => CommandService.RemoveModuleAsync(x));
Ready.TrySetResult(true); Ready.TrySetResult(true);
HandleStatusChanges();
_log.Info($"Shard {Client.ShardId} ready."); _log.Info($"Shard {Client.ShardId} ready.");
//_log.Info(await stats.Print().ConfigureAwait(false)); //_log.Info(await stats.Print().ConfigureAwait(false));
} }
@ -318,5 +315,51 @@ namespace NadekoBot
} }
})).Start(); })).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,30 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>General purpose Discord bot written in C#.</Description> <TargetFramework>netcoreapp2.0</TargetFramework>
<Copyright>Kwoth</Copyright> <RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<Authors>Kwoth</Authors> <OutputType>exe</OutputType>
<PublisherName>Kwoth</PublisherName> <AssetTargetFallback>$(AssetTargetFallback);dnxcore50;portable-net45+win8+wpa81</AssetTargetFallback>
<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> <ApplicationIcon>nadeko_icon.ico</ApplicationIcon>
<RuntimeIdentifiers>win7-x64<!--;ubuntu.14.04-x64;osx.10.10-x64 --></RuntimeIdentifiers> <RuntimeIdentifiers>win7-x64<!--;ubuntu.14.04-x64;osx.10.10-x64 --></RuntimeIdentifiers>
<Configurations>Debug;Release;global_nadeko</Configurations>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Version)' == '' "> <PropertyGroup Condition=" '$(Version)' == '' ">
<VersionPrefix Condition=" '$(VersionPrefix)' == '' ">1.4.1</VersionPrefix> <VersionPrefix Condition=" '$(VersionPrefix)' == '' ">1.9.1</VersionPrefix>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version> <Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
<Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version> <Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version>
</PropertyGroup> </PropertyGroup>
@ -59,27 +47,24 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AngleSharp" Version="0.9.9" /> <PackageReference Include="AngleSharp" Version="0.9.9" />
<PackageReference Include="Discord.Net" Version="2.0.0-alpha-build-00828" /> <PackageReference Include="Discord.Net" Version="2.0.0-alpha-build-00828" />
<PackageReference Include="libvideo" Version="1.0.1" /> <PackageReference Include="CoreCLR-NCalc" Version="2.1.3" />
<PackageReference Include="CoreCLR-NCalc" Version="2.1.2" /> <PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.29.1.138" />
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.19.0.138" /> <PackageReference Include="Google.Apis.YouTube.v3" Version="1.29.1.976" />
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.20.0.701" /> <PackageReference Include="Google.Apis.Customsearch.v1" Version="1.29.1.896" />
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.20.0.466" />
<PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00194" /> <PackageReference Include="ImageSharp" Version="1.0.0-alpha9-00194" />
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha9-00189" /> <PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha9-00189" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="1.1.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="1.1.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="NLog" Version="5.0.0-beta10" />
<PackageReference Include="NLog" Version="5.0.0-beta03" /> <PackageReference Include="StackExchange.Redis" Version="1.2.6" />
<PackageReference Include="NYoutubeDL" Version="0.4.4" /> <PackageReference Include="System.ValueTuple" Version="4.4.0" />
<PackageReference Include="System.ValueTuple" Version="4.4.0-preview1-25305-02" />
<PackageReference Include="System.Xml.XPath" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'GlobalNadeko' "> <PropertyGroup Condition=" '$(Configuration)' == 'GlobalNadeko' ">
@ -87,22 +72,12 @@
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn> <NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<LangVersion>latest</LangVersion> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" /> <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" /> <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\CommandStrings.Designer.cs">
<SubType>Designer</SubType>
</Compile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,15 +0,0 @@
<?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

@ -1,7 +0,0 @@
<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

@ -1,19 +0,0 @@
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> <value>`{0}xpex Role Excluded-Role` `{0}xpex Server`</value>
</data> </data>
<data name="xpexclude_desc" xml:space="preserve"> <data name="xpexclude_desc" xml:space="preserve">
<value>Exclude a user or a role from the xp system, or whole current server.</value> <value>Exclude a channel, role or current server from the xp system.</value>
</data> </data>
<data name="xpnotify_cmd" xml:space="preserve"> <data name="xpnotify_cmd" xml:space="preserve">
<value>xpnotify xpn</value> <value>xpnotify xpn</value>
@ -3780,4 +3780,31 @@
<data name="nsfwclearcache_desc" xml:space="preserve"> <data name="nsfwclearcache_desc" xml:space="preserve">
<value>Clears nsfw cache.</value> <value>Clears nsfw cache.</value>
</data> </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> </root>

View File

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

View File

@ -10,6 +10,9 @@ namespace NadekoBot.Services.Database.Models
public string AvatarId { get; set; } public string AvatarId { get; set; }
public ClubInfo Club { 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 LastLevelUp { get; set; } = DateTime.UtcNow;
public DateTime LastXpGain { get; set; } = DateTime.MinValue; public DateTime LastXpGain { get; set; } = DateTime.MinValue;
public XpNotificationType NotifyOnLevelUp { get; set; } public XpNotificationType NotifyOnLevelUp { get; set; }

View File

@ -3,23 +3,21 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System; using System;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Data.Sqlite;
using System.IO;
namespace NadekoBot.Services.Database namespace NadekoBot.Services.Database
{ {
public class NadekoContextFactory : IDesignTimeDbContextFactory<NadekoContext>
public class NadekoContextFactory : IDbContextFactory<NadekoContext>
{ {
/// <summary> public NadekoContext CreateDbContext(string[] args)
/// :\ Used for migrations
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
public NadekoContext Create(DbContextFactoryOptions options)
{ {
var optionsBuilder = new DbContextOptionsBuilder(); var optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
optionsBuilder.UseSqlite("Filename=./data/NadekoBot.db"); var builder = new SqliteConnectionStringBuilder("Data Source=data/NadekoBot.db");
builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource);
optionsBuilder.UseSqlite(builder.ToString());
var ctx = new NadekoContext(optionsBuilder.Options); var ctx = new NadekoContext(optionsBuilder.Options);
ctx.Database.SetCommandTimeout(60); ctx.Database.SetCommandTimeout(60);
return ctx; return ctx;
@ -58,12 +56,7 @@ namespace NadekoBot.Services.Database
public DbSet<ModulePrefix> ModulePrefixes { get; set; } public DbSet<ModulePrefix> ModulePrefixes { get; set; }
public DbSet<RewardedUser> RewardedUsers { get; set; } public DbSet<RewardedUser> RewardedUsers { get; set; }
public NadekoContext() : base() public NadekoContext(DbContextOptions<NadekoContext> options) : base(options)
{
}
public NadekoContext(DbContextOptions options) : base(options)
{ {
} }
@ -231,7 +224,7 @@ namespace NadekoBot.Services.Database
musicPlaylistEntity musicPlaylistEntity
.HasMany(p => p.Songs) .HasMany(p => p.Songs)
.WithOne() .WithOne()
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Cascade); .OnDelete(DeleteBehavior.Cascade);
#endregion #endregion

View File

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

View File

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

View File

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

View File

@ -24,6 +24,30 @@ namespace NadekoBot.Services.Database.Repositories.Impl
return func(_set).FirstOrDefault(x => x.Owner.UserId == userId); 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) public ClubInfo GetByName(string name, int discrim, Func<DbSet<ClubInfo>, IQueryable<ClubInfo>> func = null)
{ {
if (func == null) if (func == null)

View File

@ -37,5 +37,23 @@ namespace NadekoBot.Services.Database.Repositories.Impl
return toReturn; 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,11 +29,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl
return usr; 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) public UserXpStats[] GetUsersFor(ulong guildId, int page)
{ {
return _set.Where(x => x.GuildId == guildId) return _set.Where(x => x.GuildId == guildId)
@ -43,15 +38,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.ToArray(); .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) public int GetUserGuildRanking(ulong userId, ulong guildId)
{ {
return _set return _set
@ -62,16 +48,5 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.DefaultIfEmpty() .DefaultIfEmpty()
.Sum())) + 1; .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,25 +1,28 @@
using Microsoft.EntityFrameworkCore; using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Services.Database; using NadekoBot.Services.Database;
using System;
using System.IO;
using System.Linq; using System.Linq;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class DbService public class DbService
{ {
private readonly DbContextOptions options; private readonly DbContextOptions<NadekoContext> options;
private readonly DbContextOptions migrateOptions; private readonly DbContextOptions<NadekoContext> migrateOptions;
private readonly string _connectionString;
public DbService(IBotCredentials creds) public DbService(IBotCredentials creds)
{ {
_connectionString = creds.Db.ConnectionString; var builder = new SqliteConnectionStringBuilder(creds.Db.ConnectionString);
var optionsBuilder = new DbContextOptionsBuilder(); builder.DataSource = Path.Combine(AppContext.BaseDirectory, builder.DataSource);
optionsBuilder.UseSqlite(creds.Db.ConnectionString);
var optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
optionsBuilder.UseSqlite(builder.ToString());
options = optionsBuilder.Options; options = optionsBuilder.Options;
optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder = new DbContextOptionsBuilder<NadekoContext>();
optionsBuilder.UseSqlite(creds.Db.ConnectionString, x => x.SuppressForeignKeyEnforcement()); optionsBuilder.UseSqlite(builder.ToString(), x => x.SuppressForeignKeyEnforcement());
migrateOptions = optionsBuilder.Options; migrateOptions = optionsBuilder.Options;
} }

View File

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

View File

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

View File

@ -0,0 +1,40 @@
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 IBotCredentials _creds;
private readonly DateTime _started; private readonly DateTime _started;
public const string BotVersion = "1.8.4"; public const string BotVersion = "1.9.0";
public string Author => "Kwoth#2560"; public string Author => "Kwoth#2560";
public string Library => "Discord.Net"; public string Library => "Discord.Net";

View File

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

View File

@ -13,7 +13,6 @@
"customreactions_stats_not_found": "No stats for that trigger found, no action taken.", "customreactions_stats_not_found": "No stats for that trigger found, no action taken.",
"customreactions_trigger": "Trigger", "customreactions_trigger": "Trigger",
"customreactions_redacted_too_long": "Redecated because it's too long.", "customreactions_redacted_too_long": "Redecated because it's too long.",
"nsfw_autohentai_stopped": "Autohentai stopped.",
"nsfw_not_found": "No results found.", "nsfw_not_found": "No results found.",
"nsfw_blacklisted_tag_list": "List of blacklisted tags:", "nsfw_blacklisted_tag_list": "List of blacklisted tags:",
"nsfw_blacklisted_tag": "One or more tags you've used are blacklisted", "nsfw_blacklisted_tag": "One or more tags you've used are blacklisted",
@ -868,5 +867,10 @@
"xp_club_icon_set": "New club icon set.", "xp_club_icon_set": "New club icon set.",
"xp_club_bans_for": "Bans for {0} club", "xp_club_bans_for": "Bans for {0} club",
"xp_club_apps_for": "Applicants for {0} club", "xp_club_apps_for": "Applicants for {0} club",
"xp_club_leaderboard": "Club leaderboard - page {0}" "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."
} }

View File

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

File diff suppressed because it is too large Load Diff