Added .clubadmin, .autoboobs and .autobutts, cleaned up SongBuffer.

This commit is contained in:
Master Kwoth 2017-09-15 22:17:31 +02:00
parent 16fd835d4b
commit 4841418cff
16 changed files with 2225 additions and 379 deletions

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

@ -462,9 +462,11 @@ namespace NadekoBot.Migrations
b.Property<string>("Discriminator");
b.Property<bool>("IsClubAdmin");
b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 13, 4, 26, 53, 906, DateTimeKind.Local));
.HasDefaultValue(new DateTime(2017, 9, 15, 5, 48, 8, 660, DateTimeKind.Local));
b.Property<DateTime>("LastXpGain");
@ -1364,7 +1366,7 @@ namespace NadekoBot.Migrations
b.Property<DateTime>("LastLevelUp")
.ValueGeneratedOnAdd()
.HasDefaultValue(new DateTime(2017, 9, 13, 4, 26, 53, 910, DateTimeKind.Local));
.HasDefaultValue(new DateTime(2017, 9, 15, 5, 48, 8, 665, DateTimeKind.Local));
b.Property<int>("NotifyOnLevelUp");

View File

@ -3,7 +3,6 @@ using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Common
{
@ -18,27 +17,16 @@ 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)
{
@ -68,113 +56,14 @@ Check the guides for your platform on how to setup ffmpeg correctly:
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardError = false,
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)
@ -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
{
// 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)
@ -49,10 +53,34 @@ 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)]
@ -65,7 +93,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("autohentai_stopped").ConfigureAwait(false);
await ReplyConfirmLocalized("stopped").ConfigureAwait(false);
return;
}
@ -99,8 +127,90 @@ 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)
{
@ -199,7 +309,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

@ -26,6 +26,27 @@ 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)
{
@ -97,7 +118,14 @@ namespace NadekoBot.Modules.Xp
.AddField("Level Req.", club.MinimumLevelReq.ToString(), true)
.AddField("Members", string.Join("\n", club.Users
.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))
return embed.WithThumbnailUrl(club.ImageUrl);
@ -112,7 +140,7 @@ namespace NadekoBot.Modules.Xp
if (--page < 0)
return Task.CompletedTask;
var club = _service.GetBansAndApplications(Context.User.Id);
var club = _service.GetClubWithBansAndApplications(Context.User.Id);
if (club == null)
return ReplyErrorLocalized("club_not_exists");
@ -143,11 +171,11 @@ namespace NadekoBot.Modules.Xp
if (--page < 0)
return Task.CompletedTask;
var club = _service.GetBansAndApplications(Context.User.Id);
var club = _service.GetClubWithBansAndApplications(Context.User.Id);
if (club == null)
return ReplyErrorLocalized("club_not_exists");
var bans = club
var apps = club
.Applicants
.Select(x => x.User)
.ToArray();
@ -155,7 +183,7 @@ namespace NadekoBot.Modules.Xp
return Context.Channel.SendPaginatedConfirmAsync(_client, page,
curPage =>
{
var toShow = string.Join("\n", bans
var toShow = string.Join("\n", apps
.Skip(page * 10)
.Take(10)
.Select(x => x.ToString()));
@ -164,7 +192,7 @@ namespace NadekoBot.Modules.Xp
.WithTitle(GetText("club_apps_for", club.ToString()))
.WithDescription(toShow);
}, bans.Length / 10);
}, apps.Length / 10);
}
[NadekoCommand, Usage, Description, Aliases]

View File

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

View File

@ -144,7 +144,7 @@ namespace NadekoBot.Modules.Xp.Services
du.LastXpGain = DateTime.UtcNow;
var globalXp = uow.Xp.GetTotalUserXp(item.Key.User.Id);
var globalXp = du.TotalXp;
var oldGlobalLevelData = new LevelStats(globalXp);
var newGlobalLevelData = new LevelStats(globalXp + xp);
@ -403,17 +403,6 @@ 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;

View File

@ -3780,4 +3780,31 @@
<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

@ -10,6 +10,7 @@ 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;

View File

@ -329,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)
@ -340,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,6 +10,7 @@ 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

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

@ -29,11 +29,6 @@ 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)

View File

@ -13,7 +13,6 @@
"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",
@ -868,5 +867,10 @@
"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_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."
}