music and clash of clans readded
This commit is contained in:
parent
2df415341c
commit
c183e8ad58
@ -6,7 +6,7 @@ namespace NadekoBot.Attributes
|
|||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
sealed class NadekoModuleAttribute : GroupAttribute
|
sealed class NadekoModuleAttribute : GroupAttribute
|
||||||
{
|
{
|
||||||
public NadekoModuleAttribute(string moduleName) : base("")
|
public NadekoModuleAttribute(string moduleName) : base(moduleName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,11 @@ namespace NadekoBot.TypeReaders
|
|||||||
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
|
public override Task<TypeReaderResult> Read(ICommandContext context, string input)
|
||||||
{
|
{
|
||||||
input = input.ToUpperInvariant();
|
input = input.ToUpperInvariant();
|
||||||
|
if (!input.StartsWith(NadekoBot.Prefix.ToUpperInvariant()))
|
||||||
|
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "No such command found."));
|
||||||
|
|
||||||
|
input = input.Substring(NadekoBot.Prefix.Length);
|
||||||
|
|
||||||
var cmd = _cmds.Commands.FirstOrDefault(c =>
|
var cmd = _cmds.Commands.FirstOrDefault(c =>
|
||||||
c.Aliases.Select(a => a.ToUpperInvariant()).Contains(input));
|
c.Aliases.Select(a => a.ToUpperInvariant()).Contains(input));
|
||||||
if (cmd == null)
|
if (cmd == null)
|
||||||
|
@ -1,81 +1,23 @@
|
|||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Discord;
|
using Discord;
|
||||||
using NadekoBot.Services;
|
|
||||||
using NadekoBot.Attributes;
|
using NadekoBot.Attributes;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using System.Linq;
|
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using System.Threading;
|
using NadekoBot.Services.ClashOfClans;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.ClashOfClans
|
namespace NadekoBot.Modules.ClashOfClans
|
||||||
{
|
{
|
||||||
[NadekoModule("ClashOfClans", ",")]
|
|
||||||
public class ClashOfClans : NadekoTopLevelModule
|
public class ClashOfClans : NadekoTopLevelModule
|
||||||
{
|
{
|
||||||
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
|
private readonly ClashOfClansService _service;
|
||||||
|
|
||||||
private static Timer checkWarTimer { get; }
|
public ClashOfClans(ClashOfClansService service)
|
||||||
|
|
||||||
static ClashOfClans()
|
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
_service = service;
|
||||||
{
|
|
||||||
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
|
|
||||||
uow.ClashOfClans
|
|
||||||
.GetAllWars()
|
|
||||||
.Select(cw =>
|
|
||||||
{
|
|
||||||
cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId)?
|
|
||||||
.GetTextChannel(cw.ChannelId);
|
|
||||||
return cw;
|
|
||||||
})
|
|
||||||
.Where(cw => cw.Channel != null)
|
|
||||||
.GroupBy(cw => cw.GuildId)
|
|
||||||
.ToDictionary(g => g.Key, g => g.ToList()));
|
|
||||||
}
|
|
||||||
|
|
||||||
checkWarTimer = new Timer(async _ =>
|
|
||||||
{
|
|
||||||
foreach (var kvp in ClashWars)
|
|
||||||
{
|
|
||||||
foreach (var war in kvp.Value)
|
|
||||||
{
|
|
||||||
try { await CheckWar(TimeSpan.FromHours(2), war).ConfigureAwait(false); } catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
|
|
||||||
{
|
|
||||||
var Bases = war.Bases;
|
|
||||||
for (var i = 0; i < Bases.Count; i++)
|
|
||||||
{
|
|
||||||
var callUser = Bases[i].CallUser;
|
|
||||||
if (callUser == null) continue;
|
|
||||||
if ((!Bases[i].BaseDestroyed) && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
|
|
||||||
{
|
|
||||||
if (Bases[i].Stars != 3)
|
|
||||||
Bases[i].BaseDestroyed = true;
|
|
||||||
else
|
|
||||||
Bases[i] = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SaveWar(war);
|
|
||||||
await war.Channel.SendErrorAsync(GetTextStatic("claim_expired",
|
|
||||||
NadekoBot.Localization.GetCultureInfo(war.Channel.GuildId),
|
|
||||||
typeof(ClashOfClans).Name.ToLowerInvariant(),
|
|
||||||
Format.Bold(Bases[i].CallUser),
|
|
||||||
war.ShortPrint()));
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -92,18 +34,18 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<ClashWar> wars;
|
List<ClashWar> wars;
|
||||||
if (!ClashWars.TryGetValue(Context.Guild.Id, out wars))
|
if (!_service.ClashWars.TryGetValue(Context.Guild.Id, out wars))
|
||||||
{
|
{
|
||||||
wars = new List<ClashWar>();
|
wars = new List<ClashWar>();
|
||||||
if (!ClashWars.TryAdd(Context.Guild.Id, wars))
|
if (!_service.ClashWars.TryAdd(Context.Guild.Id, wars))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id);
|
var cw = await _service.CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id);
|
||||||
|
|
||||||
wars.Add(cw);
|
wars.Add(cw);
|
||||||
await ReplyErrorLocalized("war_created", cw.ShortPrint()).ConfigureAwait(false);
|
await ReplyErrorLocalized("war_created", _service.ShortPrint(cw)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -113,7 +55,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
int num = 0;
|
int num = 0;
|
||||||
int.TryParse(number, out num);
|
int.TryParse(number, out num);
|
||||||
|
|
||||||
var warsInfo = GetWarInfo(Context.Guild, num);
|
var warsInfo = _service.GetWarInfo(Context.Guild, num);
|
||||||
if (warsInfo == null)
|
if (warsInfo == null)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||||
@ -123,13 +65,13 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
war.Start();
|
war.Start();
|
||||||
await ReplyConfirmLocalized("war_started", war.ShortPrint()).ConfigureAwait(false);
|
await ReplyConfirmLocalized("war_started", _service.ShortPrint(war)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("war_already_started", war.ShortPrint()).ConfigureAwait(false);
|
await ReplyErrorLocalized("war_already_started", _service.ShortPrint(war)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
SaveWar(war);
|
_service.SaveWar(war);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -142,7 +84,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
{
|
{
|
||||||
//check if there are any wars
|
//check if there are any wars
|
||||||
List<ClashWar> wars = null;
|
List<ClashWar> wars = null;
|
||||||
ClashWars.TryGetValue(Context.Guild.Id, out wars);
|
_service.ClashWars.TryGetValue(Context.Guild.Id, out wars);
|
||||||
if (wars == null || wars.Count == 0)
|
if (wars == null || wars.Count == 0)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false);
|
await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false);
|
||||||
@ -163,21 +105,21 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
var num = 0;
|
var num = 0;
|
||||||
int.TryParse(number, out num);
|
int.TryParse(number, out num);
|
||||||
//if number is not null, print the war needed
|
//if number is not null, print the war needed
|
||||||
var warsInfo = GetWarInfo(Context.Guild, num);
|
var warsInfo = _service.GetWarInfo(Context.Guild, num);
|
||||||
if (warsInfo == null)
|
if (warsInfo == null)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var war = warsInfo.Item1[warsInfo.Item2];
|
var war = warsInfo.Item1[warsInfo.Item2];
|
||||||
await Context.Channel.SendConfirmAsync(war.Localize("info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), war.ToPrettyString()).ConfigureAwait(false);
|
await Context.Channel.SendConfirmAsync(_service.Localize(war, "info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), _service.ToPrettyString(war)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Claim(int number, int baseNumber, [Remainder] string other_name = null)
|
public async Task Claim(int number, int baseNumber, [Remainder] string other_name = null)
|
||||||
{
|
{
|
||||||
var warsInfo = GetWarInfo(Context.Guild, number);
|
var warsInfo = _service.GetWarInfo(Context.Guild, number);
|
||||||
if (warsInfo == null || warsInfo.Item1.Count == 0)
|
if (warsInfo == null || warsInfo.Item1.Count == 0)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||||
@ -190,9 +132,9 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var war = warsInfo.Item1[warsInfo.Item2];
|
var war = warsInfo.Item1[warsInfo.Item2];
|
||||||
war.Call(usr, baseNumber - 1);
|
_service.Call(war, usr, baseNumber - 1);
|
||||||
SaveWar(war);
|
_service.SaveWar(war);
|
||||||
await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, war.ShortPrint()).ConfigureAwait(false);
|
await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, _service.ShortPrint(war)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -225,7 +167,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task EndWar(int number)
|
public async Task EndWar(int number)
|
||||||
{
|
{
|
||||||
var warsInfo = GetWarInfo(Context.Guild, number);
|
var warsInfo = _service.GetWarInfo(Context.Guild, number);
|
||||||
if (warsInfo == null)
|
if (warsInfo == null)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||||
@ -233,8 +175,8 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
}
|
}
|
||||||
var war = warsInfo.Item1[warsInfo.Item2];
|
var war = warsInfo.Item1[warsInfo.Item2];
|
||||||
war.End();
|
war.End();
|
||||||
SaveWar(war);
|
_service.SaveWar(war);
|
||||||
await ReplyConfirmLocalized("war_ended", warsInfo.Item1[warsInfo.Item2].ShortPrint()).ConfigureAwait(false);
|
await ReplyConfirmLocalized("war_ended", _service.ShortPrint(warsInfo.Item1[warsInfo.Item2])).ConfigureAwait(false);
|
||||||
|
|
||||||
warsInfo.Item1.RemoveAt(warsInfo.Item2);
|
warsInfo.Item1.RemoveAt(warsInfo.Item2);
|
||||||
}
|
}
|
||||||
@ -243,7 +185,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Unclaim(int number, [Remainder] string otherName = null)
|
public async Task Unclaim(int number, [Remainder] string otherName = null)
|
||||||
{
|
{
|
||||||
var warsInfo = GetWarInfo(Context.Guild, number);
|
var warsInfo = _service.GetWarInfo(Context.Guild, number);
|
||||||
if (warsInfo == null || warsInfo.Item1.Count == 0)
|
if (warsInfo == null || warsInfo.Item1.Count == 0)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||||
@ -256,9 +198,9 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var war = warsInfo.Item1[warsInfo.Item2];
|
var war = warsInfo.Item1[warsInfo.Item2];
|
||||||
var baseNumber = war.Uncall(usr);
|
var baseNumber = _service.Uncall(war, usr);
|
||||||
SaveWar(war);
|
_service.SaveWar(war);
|
||||||
await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, war.ShortPrint()).ConfigureAwait(false);
|
await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, _service.ShortPrint(war)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -268,7 +210,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
|
|
||||||
private async Task FinishClaim(int number, int baseNumber, int stars = 3)
|
private async Task FinishClaim(int number, int baseNumber, int stars = 3)
|
||||||
{
|
{
|
||||||
var warInfo = GetWarInfo(Context.Guild, number);
|
var warInfo = _service.GetWarInfo(Context.Guild, number);
|
||||||
if (warInfo == null || warInfo.Item1.Count == 0)
|
if (warInfo == null || warInfo.Item1.Count == 0)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||||
@ -279,87 +221,19 @@ namespace NadekoBot.Modules.ClashOfClans
|
|||||||
{
|
{
|
||||||
if (baseNumber == -1)
|
if (baseNumber == -1)
|
||||||
{
|
{
|
||||||
baseNumber = war.FinishClaim(Context.User.Username, stars);
|
baseNumber = _service.FinishClaim(war, Context.User.Username, stars);
|
||||||
SaveWar(war);
|
_service.SaveWar(war);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
war.FinishClaim(baseNumber, stars);
|
_service.FinishClaim(war, baseNumber, stars);
|
||||||
}
|
}
|
||||||
await ReplyConfirmLocalized("base_destroyed", baseNumber +1, war.ShortPrint()).ConfigureAwait(false);
|
await ReplyConfirmLocalized("base_destroyed", baseNumber + 1, _service.ShortPrint(war)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
|
await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Tuple<List<ClashWar>, int> GetWarInfo(IGuild guild, int num)
|
|
||||||
{
|
|
||||||
List<ClashWar> wars = null;
|
|
||||||
ClashWars.TryGetValue(guild.Id, out wars);
|
|
||||||
if (wars == null || wars.Count == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// get the number of the war
|
|
||||||
else if (num < 1 || num > wars.Count)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
num -= 1;
|
|
||||||
//get the actual war
|
|
||||||
return new Tuple<List<ClashWar>, int>(wars, num);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId)
|
|
||||||
{
|
|
||||||
var channel = NadekoBot.Client.GetGuild(serverId)?.GetTextChannel(channelId);
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
|
||||||
{
|
|
||||||
var cw = new ClashWar
|
|
||||||
{
|
|
||||||
EnemyClan = enemyClan,
|
|
||||||
Size = size,
|
|
||||||
Bases = new List<ClashCaller>(size),
|
|
||||||
GuildId = serverId,
|
|
||||||
ChannelId = channelId,
|
|
||||||
Channel = channel,
|
|
||||||
};
|
|
||||||
cw.Bases.Capacity = size;
|
|
||||||
for (int i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
cw.Bases.Add(new ClashCaller()
|
|
||||||
{
|
|
||||||
CallUser = null,
|
|
||||||
SequenceNumber = i,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Console.WriteLine(cw.Bases.Capacity);
|
|
||||||
uow.ClashOfClans.Add(cw);
|
|
||||||
await uow.CompleteAsync();
|
|
||||||
return cw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SaveWar(ClashWar cw)
|
|
||||||
{
|
|
||||||
if (cw.WarState == ClashWar.StateOfWar.Ended)
|
|
||||||
{
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
|
||||||
{
|
|
||||||
uow.ClashOfClans.Remove(cw);
|
|
||||||
uow.CompleteAsync();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
|
||||||
{
|
|
||||||
uow.ClashOfClans.Update(cw);
|
|
||||||
uow.CompleteAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,148 +0,0 @@
|
|||||||
using NadekoBot.Services.Database.Models;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using static NadekoBot.Services.Database.Models.ClashWar;
|
|
||||||
|
|
||||||
namespace NadekoBot.Modules.ClashOfClans
|
|
||||||
{
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
public static void ResetTime(this ClashCaller c)
|
|
||||||
{
|
|
||||||
c.TimeAdded = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Destroy(this ClashCaller c)
|
|
||||||
{
|
|
||||||
c.BaseDestroyed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void End(this ClashWar cw)
|
|
||||||
{
|
|
||||||
//Ended = true;
|
|
||||||
cw.WarState = StateOfWar.Ended;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Call(this ClashWar cw, string u, int baseNumber)
|
|
||||||
{
|
|
||||||
if (baseNumber < 0 || baseNumber >= cw.Bases.Count)
|
|
||||||
throw new ArgumentException(cw.Localize("invalid_base_number"));
|
|
||||||
if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
|
|
||||||
throw new ArgumentException(cw.Localize("base_already_claimed"));
|
|
||||||
for (var i = 0; i < cw.Bases.Count; i++)
|
|
||||||
{
|
|
||||||
if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
|
|
||||||
throw new ArgumentException(cw.Localize("claimed_other", u, i + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
var cc = cw.Bases[baseNumber];
|
|
||||||
cc.CallUser = u.Trim();
|
|
||||||
cc.TimeAdded = DateTime.UtcNow;
|
|
||||||
cc.BaseDestroyed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Start(this ClashWar cw)
|
|
||||||
{
|
|
||||||
if (cw.WarState == StateOfWar.Started)
|
|
||||||
throw new InvalidOperationException("war_already_started");
|
|
||||||
//if (Started)
|
|
||||||
// throw new InvalidOperationException();
|
|
||||||
//Started = true;
|
|
||||||
cw.WarState = StateOfWar.Started;
|
|
||||||
cw.StartedAt = DateTime.UtcNow;
|
|
||||||
foreach (var b in cw.Bases.Where(b => b.CallUser != null))
|
|
||||||
{
|
|
||||||
b.ResetTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int Uncall(this ClashWar cw, string user)
|
|
||||||
{
|
|
||||||
user = user.Trim();
|
|
||||||
for (var i = 0; i < cw.Bases.Count; i++)
|
|
||||||
{
|
|
||||||
if (cw.Bases[i]?.CallUser != user) continue;
|
|
||||||
cw.Bases[i].CallUser = null;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
throw new InvalidOperationException(cw.Localize("not_partic"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ShortPrint(this ClashWar cw) =>
|
|
||||||
$"`{cw.EnemyClan}` ({cw.Size} v {cw.Size})";
|
|
||||||
|
|
||||||
public static string ToPrettyString(this ClashWar cw)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
if (cw.WarState == StateOfWar.Created)
|
|
||||||
sb.AppendLine("`not started`");
|
|
||||||
var twoHours = new TimeSpan(2, 0, 0);
|
|
||||||
for (var i = 0; i < cw.Bases.Count; i++)
|
|
||||||
{
|
|
||||||
if (cw.Bases[i].CallUser == null)
|
|
||||||
{
|
|
||||||
sb.AppendLine($"`{i + 1}.` ❌*{cw.Localize("not_claimed")}*");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (cw.Bases[i].BaseDestroyed)
|
|
||||||
{
|
|
||||||
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {new string('⭐', cw.Bases[i].Stars)}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var left = (cw.WarState == StateOfWar.Started) ? twoHours - (DateTime.UtcNow - cw.Bases[i].TimeAdded) : twoHours;
|
|
||||||
if (cw.Bases[i].Stars == 3)
|
|
||||||
{
|
|
||||||
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left {new string('⭐', cw.Bases[i].Stars)} {string.Concat(Enumerable.Repeat("🔸", 3 - cw.Bases[i].Stars))}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int FinishClaim(this ClashWar cw, string user, int stars = 3)
|
|
||||||
{
|
|
||||||
user = user.Trim();
|
|
||||||
for (var i = 0; i < cw.Bases.Count; i++)
|
|
||||||
{
|
|
||||||
if (cw.Bases[i]?.BaseDestroyed != false || cw.Bases[i]?.CallUser != user) continue;
|
|
||||||
cw.Bases[i].BaseDestroyed = true;
|
|
||||||
cw.Bases[i].Stars = stars;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
throw new InvalidOperationException(cw.Localize("not_partic_or_destroyed", user));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void FinishClaim(this ClashWar cw, int index, int stars = 3)
|
|
||||||
{
|
|
||||||
if (index < 0 || index > cw.Bases.Count)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
|
||||||
var toFinish = cw.Bases[index];
|
|
||||||
if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(cw.Localize("base_already_destroyed"));
|
|
||||||
if (toFinish.CallUser == null) throw new InvalidOperationException(cw.Localize("base_already_unclaimed"));
|
|
||||||
toFinish.BaseDestroyed = true;
|
|
||||||
toFinish.Stars = stars;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Localize(this ClashWar cw, string key)
|
|
||||||
{
|
|
||||||
return NadekoTopLevelModule.GetTextStatic(key,
|
|
||||||
NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId),
|
|
||||||
typeof(ClashOfClans).Name.ToLowerInvariant());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Localize(this ClashWar cw, string key, params object[] replacements)
|
|
||||||
{
|
|
||||||
return string.Format(cw.Localize(key), replacements);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,97 +17,12 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
{
|
{
|
||||||
public static class CustomReactionExtensions
|
public static class CustomReactionExtensions
|
||||||
{
|
{
|
||||||
public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage context)
|
|
||||||
{
|
|
||||||
var channel = cr.DmResponse ? await context.Author.CreateDMChannelAsync() : context.Channel;
|
|
||||||
|
|
||||||
CustomReactions.ReactionStats.AddOrUpdate(cr.Trigger, 1, (k, old) => ++old);
|
|
||||||
|
|
||||||
CREmbed crembed;
|
|
||||||
if (CREmbed.TryParse(cr.Response, out crembed))
|
|
||||||
{
|
|
||||||
return await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "");
|
|
||||||
}
|
|
||||||
return await channel.SendMessageAsync(cr.ResponseWithContext(context).SanitizeMentions());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoModule("CustomReactions", ".")]
|
[NadekoModule("CustomReactions", ".")]
|
||||||
public class CustomReactions : NadekoTopLevelModule
|
public class CustomReactions : NadekoTopLevelModule
|
||||||
{
|
{
|
||||||
private static CustomReaction[] _globalReactions = new CustomReaction[] { };
|
|
||||||
public static CustomReaction[] GlobalReactions => _globalReactions;
|
|
||||||
public static ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>();
|
|
||||||
|
|
||||||
public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
|
|
||||||
|
|
||||||
private new static readonly Logger _log;
|
|
||||||
|
|
||||||
static CustomReactions()
|
|
||||||
{
|
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
|
||||||
var sw = Stopwatch.StartNew();
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
|
||||||
{
|
|
||||||
var items = uow.CustomReactions.GetAll();
|
|
||||||
GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray()));
|
|
||||||
_globalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
|
|
||||||
}
|
|
||||||
sw.Stop();
|
|
||||||
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearStats() => ReactionStats.Clear();
|
|
||||||
|
|
||||||
public static CustomReaction TryGetCustomReaction(IUserMessage umsg)
|
|
||||||
{
|
|
||||||
var channel = umsg.Channel as SocketTextChannel;
|
|
||||||
if (channel == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var content = umsg.Content.Trim().ToLowerInvariant();
|
|
||||||
CustomReaction[] reactions;
|
|
||||||
|
|
||||||
GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
|
|
||||||
if (reactions != null && reactions.Any())
|
|
||||||
{
|
|
||||||
var rs = reactions.Where(cr =>
|
|
||||||
{
|
|
||||||
if (cr == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
|
|
||||||
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
|
|
||||||
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
if (rs.Length != 0)
|
|
||||||
{
|
|
||||||
var reaction = rs[new NadekoRandom().Next(0, rs.Length)];
|
|
||||||
if (reaction != null)
|
|
||||||
{
|
|
||||||
if (reaction.Response == "-")
|
|
||||||
return null;
|
|
||||||
return reaction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var grs = GlobalReactions.Where(cr =>
|
|
||||||
{
|
|
||||||
if (cr == null)
|
|
||||||
return false;
|
|
||||||
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
|
|
||||||
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
|
|
||||||
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
|
|
||||||
}).ToArray();
|
|
||||||
if (grs.Length == 0)
|
|
||||||
return null;
|
|
||||||
var greaction = grs[new NadekoRandom().Next(0, grs.Length)];
|
|
||||||
|
|
||||||
return greaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task AddCustReact(string key, [Remainder] string message)
|
public async Task AddCustReact(string key, [Remainder] string message)
|
||||||
{
|
{
|
||||||
@ -131,7 +46,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
Response = message,
|
Response = message,
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
uow.CustomReactions.Add(cr);
|
uow.CustomReactions.Add(cr);
|
||||||
|
|
||||||
@ -309,7 +224,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
|
|
||||||
var success = false;
|
var success = false;
|
||||||
CustomReaction toDelete;
|
CustomReaction toDelete;
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
toDelete = uow.CustomReactions.Get(id);
|
toDelete = uow.CustomReactions.Get(id);
|
||||||
if (toDelete == null) //not found
|
if (toDelete == null) //not found
|
||||||
@ -381,7 +296,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
|
|
||||||
var setValue = reaction.DmResponse = !reaction.DmResponse;
|
var setValue = reaction.DmResponse = !reaction.DmResponse;
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
uow.CustomReactions.Get(id).DmResponse = setValue;
|
uow.CustomReactions.Get(id).DmResponse = setValue;
|
||||||
uow.Complete();
|
uow.Complete();
|
||||||
@ -432,7 +347,7 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
|
|
||||||
var setValue = reaction.AutoDeleteTrigger = !reaction.AutoDeleteTrigger;
|
var setValue = reaction.AutoDeleteTrigger = !reaction.AutoDeleteTrigger;
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
uow.CustomReactions.Get(id).AutoDeleteTrigger = setValue;
|
uow.CustomReactions.Get(id).AutoDeleteTrigger = setValue;
|
||||||
uow.Complete();
|
uow.Complete();
|
||||||
|
@ -9,19 +9,27 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Help
|
namespace NadekoBot.Modules.Help
|
||||||
{
|
{
|
||||||
[NadekoModule("Help", "-")]
|
|
||||||
public class Help : NadekoTopLevelModule
|
public class Help : NadekoTopLevelModule
|
||||||
{
|
{
|
||||||
private static string helpString { get; } = NadekoBot.BotConfig.HelpString;
|
|
||||||
public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]);
|
|
||||||
|
|
||||||
public static string DMHelpString { get; } = NadekoBot.BotConfig.DMHelpString;
|
|
||||||
|
|
||||||
public const string PatreonUrl = "https://patreon.com/nadekobot";
|
public const string PatreonUrl = "https://patreon.com/nadekobot";
|
||||||
public const string PaypalUrl = "https://paypal.me/Kwoth";
|
public const string PaypalUrl = "https://paypal.me/Kwoth";
|
||||||
|
private readonly IBotCredentials _creds;
|
||||||
|
private readonly BotConfig _config;
|
||||||
|
private readonly CommandService _cmds;
|
||||||
|
|
||||||
|
public string HelpString => String.Format(_config.HelpString, _creds.ClientId, NadekoBot.Prefix);
|
||||||
|
public string DMHelpString => _config.DMHelpString;
|
||||||
|
|
||||||
|
public Help(IBotCredentials creds, BotConfig config, CommandService cmds)
|
||||||
|
{
|
||||||
|
_creds = creds;
|
||||||
|
_config = config;
|
||||||
|
_cmds = cmds;
|
||||||
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task Modules()
|
public async Task Modules()
|
||||||
@ -30,8 +38,9 @@ namespace NadekoBot.Modules.Help
|
|||||||
.WithFooter(efb => efb.WithText("ℹ️" + GetText("modules_footer", Prefix)))
|
.WithFooter(efb => efb.WithText("ℹ️" + GetText("modules_footer", Prefix)))
|
||||||
.WithTitle(GetText("list_of_modules"))
|
.WithTitle(GetText("list_of_modules"))
|
||||||
.WithDescription(string.Join("\n",
|
.WithDescription(string.Join("\n",
|
||||||
NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule())
|
_cmds.Modules.GroupBy(m => m.GetTopLevelModule())
|
||||||
.Where(m => !Permissions.Permissions.GlobalPermissionCommands.BlockedModules.Contains(m.Key.Name.ToLowerInvariant()))
|
//todo perms
|
||||||
|
//.Where(m => !Permissions.Permissions.GlobalPermissionCommands.BlockedModules.Contains(m.Key.Name.ToLowerInvariant()))
|
||||||
.Select(m => "• " + m.Key.Name)
|
.Select(m => "• " + m.Key.Name)
|
||||||
.OrderBy(s => s)));
|
.OrderBy(s => s)));
|
||||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
@ -45,8 +54,9 @@ namespace NadekoBot.Modules.Help
|
|||||||
module = module?.Trim().ToUpperInvariant();
|
module = module?.Trim().ToUpperInvariant();
|
||||||
if (string.IsNullOrWhiteSpace(module))
|
if (string.IsNullOrWhiteSpace(module))
|
||||||
return;
|
return;
|
||||||
var cmds = NadekoBot.CommandService.Commands.Where(c => c.Module.GetTopLevelModule().Name.ToUpperInvariant().StartsWith(module))
|
var cmds = _cmds.Commands.Where(c => c.Module.GetTopLevelModule().Name.ToUpperInvariant().StartsWith(module))
|
||||||
.Where(c => !Permissions.Permissions.GlobalPermissionCommands.BlockedCommands.Contains(c.Aliases.First().ToLowerInvariant()))
|
//todo perms
|
||||||
|
//.Where(c => !Permissions.Permissions.GlobalPermissionCommands.BlockedCommands.Contains(c.Aliases.First().ToLowerInvariant()))
|
||||||
.OrderBy(c => c.Aliases.First())
|
.OrderBy(c => c.Aliases.First())
|
||||||
.Distinct(new CommandTextEqualityComparer())
|
.Distinct(new CommandTextEqualityComparer())
|
||||||
.AsEnumerable();
|
.AsEnumerable();
|
||||||
@ -62,36 +72,33 @@ namespace NadekoBot.Modules.Help
|
|||||||
|
|
||||||
for (int i = 0; i < groups.Count(); i++)
|
for (int i = 0; i < groups.Count(); i++)
|
||||||
{
|
{
|
||||||
await channel.SendTableAsync(i == 0 ? $"📃 **{GetText("list_of_commands")}**\n" : "", groups.ElementAt(i), el => $"{el.Aliases.First(),-15} {"[" + el.Aliases.Skip(1).FirstOrDefault() + "]",-8}").ConfigureAwait(false);
|
await channel.SendTableAsync(i == 0 ? $"📃 **{GetText("list_of_commands")}**\n" : "", groups.ElementAt(i), el => $"{Prefix + el.Aliases.First(),-15} {"[" + el.Aliases.Skip(1).FirstOrDefault() + "]",-8}").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false);
|
await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
public async Task H([Remainder] string comToFind = null)
|
public async Task H([Remainder] CommandInfo com = null)
|
||||||
{
|
{
|
||||||
var channel = Context.Channel;
|
var channel = Context.Channel;
|
||||||
|
|
||||||
comToFind = comToFind?.ToLowerInvariant();
|
if (com == null)
|
||||||
if (string.IsNullOrWhiteSpace(comToFind))
|
|
||||||
{
|
{
|
||||||
IMessageChannel ch = channel is ITextChannel ? await ((IGuildUser)Context.User).CreateDMChannelAsync() : channel;
|
IMessageChannel ch = channel is ITextChannel ? await ((IGuildUser)Context.User).CreateDMChannelAsync() : channel;
|
||||||
await ch.SendMessageAsync(HelpString).ConfigureAwait(false);
|
await ch.SendMessageAsync(HelpString).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var com = NadekoBot.CommandService.Commands.FirstOrDefault(c => c.Aliases.Select(a=>a.ToLowerInvariant()).Contains(comToFind));
|
|
||||||
|
|
||||||
if (com == null)
|
if (com == null)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
|
await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var str = string.Format("**`{0}`**", com.Aliases.First());
|
var str = string.Format("**`{0}`**", Prefix + com.Aliases.First());
|
||||||
var alias = com.Aliases.Skip(1).FirstOrDefault();
|
var alias = com.Aliases.Skip(1).FirstOrDefault();
|
||||||
if (alias != null)
|
if (alias != null)
|
||||||
str += string.Format(" **/ `{0}`**", alias);
|
str += string.Format(" **/ `{0}`**", Prefix + alias);
|
||||||
var embed = new EmbedBuilder()
|
var embed = new EmbedBuilder()
|
||||||
.AddField(fb => fb.WithName(str).WithValue($"{com.RealSummary()} {GetCommandRequirements(com)}").WithIsInline(true))
|
.AddField(fb => fb.WithName(str).WithValue($"{com.RealSummary()} {GetCommandRequirements(com)}").WithIsInline(true))
|
||||||
.AddField(fb => fb.WithName(GetText("usage")).WithValue(com.RealRemarks()).WithIsInline(false))
|
.AddField(fb => fb.WithName(GetText("usage")).WithValue(com.RealRemarks()).WithIsInline(false))
|
||||||
@ -122,7 +129,7 @@ namespace NadekoBot.Modules.Help
|
|||||||
var helpstr = new StringBuilder();
|
var helpstr = new StringBuilder();
|
||||||
helpstr.AppendLine(GetText("cmdlist_donate", PatreonUrl, PaypalUrl) + "\n");
|
helpstr.AppendLine(GetText("cmdlist_donate", PatreonUrl, PaypalUrl) + "\n");
|
||||||
helpstr.AppendLine("##"+ GetText("table_of_contents"));
|
helpstr.AppendLine("##"+ GetText("table_of_contents"));
|
||||||
helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help")
|
helpstr.AppendLine(string.Join("\n", _cmds.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help")
|
||||||
.Select(m => m.GetTopLevelModule().Name)
|
.Select(m => m.GetTopLevelModule().Name)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.OrderBy(m => m)
|
.OrderBy(m => m)
|
||||||
@ -130,7 +137,7 @@ namespace NadekoBot.Modules.Help
|
|||||||
.Select(m => string.Format("- [{0}](#{1})", m, m.ToLowerInvariant()))));
|
.Select(m => string.Format("- [{0}](#{1})", m, m.ToLowerInvariant()))));
|
||||||
helpstr.AppendLine();
|
helpstr.AppendLine();
|
||||||
string lastModule = null;
|
string lastModule = null;
|
||||||
foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First()))
|
foreach (var com in _cmds.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First()))
|
||||||
{
|
{
|
||||||
var module = com.Module.GetTopLevelModule();
|
var module = com.Module.GetTopLevelModule();
|
||||||
if (module.Name != lastModule)
|
if (module.Name != lastModule)
|
||||||
@ -147,10 +154,9 @@ namespace NadekoBot.Modules.Help
|
|||||||
lastModule = module.Name;
|
lastModule = module.Name;
|
||||||
}
|
}
|
||||||
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" +
|
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" +
|
||||||
$" {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} |" +
|
$" {string.Format(com.Summary, NadekoBot.Prefix)} {GetCommandRequirements(com)} |" +
|
||||||
$" {string.Format(com.Remarks, com.Module.GetPrefix())}");
|
$" {string.Format(com.Remarks, NadekoBot.Prefix)}");
|
||||||
}
|
}
|
||||||
helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName");
|
|
||||||
File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
|
File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
|
||||||
await ReplyConfirmLocalized("commandlist_regen").ConfigureAwait(false);
|
await ReplyConfirmLocalized("commandlist_regen").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using NadekoBot.Modules.Music.Classes;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -19,26 +17,28 @@ using NadekoBot.Services.Music;
|
|||||||
|
|
||||||
namespace NadekoBot.Modules.Music
|
namespace NadekoBot.Modules.Music
|
||||||
{
|
{
|
||||||
[NadekoModule("Music", "!!")]
|
|
||||||
[DontAutoLoad]
|
|
||||||
public class Music : NadekoTopLevelModule
|
public class Music : NadekoTopLevelModule
|
||||||
{
|
{
|
||||||
private static MusicService music;
|
private static MusicService _music;
|
||||||
|
private readonly DiscordShardedClient _client;
|
||||||
|
private readonly IBotCredentials _creds;
|
||||||
|
private readonly IGoogleApiService _google;
|
||||||
|
private readonly DbHandler _db;
|
||||||
|
|
||||||
static Music()
|
public Music(DiscordShardedClient client, IBotCredentials creds, IGoogleApiService google,
|
||||||
|
DbHandler db, MusicService music)
|
||||||
{
|
{
|
||||||
|
_client = client;
|
||||||
|
_creds = creds;
|
||||||
|
_google = google;
|
||||||
|
_db = db;
|
||||||
|
_music = music;
|
||||||
//it can fail if its currenctly opened or doesn't exist. Either way i don't care
|
//it can fail if its currenctly opened or doesn't exist. Either way i don't care
|
||||||
try { Directory.Delete(MusicDataPath, true); } catch { }
|
|
||||||
|
|
||||||
NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||||
|
|
||||||
Directory.CreateDirectory(MusicDataPath);
|
|
||||||
|
|
||||||
//todo move to service
|
|
||||||
music = NadekoBot.MusicService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Task Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState)
|
private Task Client_UserVoiceStateUpdated(SocketUser iusr, SocketVoiceState oldState, SocketVoiceState newState)
|
||||||
{
|
{
|
||||||
var usr = iusr as SocketGuildUser;
|
var usr = iusr as SocketGuildUser;
|
||||||
if (usr == null ||
|
if (usr == null ||
|
||||||
@ -46,14 +46,14 @@ namespace NadekoBot.Modules.Music
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
MusicPlayer player;
|
MusicPlayer player;
|
||||||
if ((player = music.GetPlayer(usr.Guild.Id)) == null)
|
if ((player = _music.GetPlayer(usr.Guild.Id)) == null)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//if bot moved
|
//if bot moved
|
||||||
if ((player.PlaybackVoiceChannel == oldState.VoiceChannel) &&
|
if ((player.PlaybackVoiceChannel == oldState.VoiceChannel) &&
|
||||||
usr.Id == NadekoBot.Client.CurrentUser.Id)
|
usr.Id == _client.CurrentUser.Id)
|
||||||
{
|
{
|
||||||
if (player.Paused && newState.VoiceChannel.Users.Count > 1) //unpause if there are people in the new channel
|
if (player.Paused && newState.VoiceChannel.Users.Count > 1) //unpause if there are people in the new channel
|
||||||
player.TogglePause();
|
player.TogglePause();
|
||||||
@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
if (musicPlayer.PlaybackVoiceChannel == ((IGuildUser)Context.User).VoiceChannel)
|
if (musicPlayer.PlaybackVoiceChannel == ((IGuildUser)Context.User).VoiceChannel)
|
||||||
{
|
{
|
||||||
@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public Task Stop()
|
public Task Stop()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
||||||
{
|
{
|
||||||
@ -125,10 +125,10 @@ namespace NadekoBot.Modules.Music
|
|||||||
public Task Destroy()
|
public Task Destroy()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
||||||
music.DestroyPlayer(Context.Guild.Id);
|
_music.DestroyPlayer(Context.Guild.Id);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public Task Pause()
|
public Task Pause()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -153,7 +153,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
{
|
{
|
||||||
var channel = (ITextChannel)Context.Channel;
|
var channel = (ITextChannel)Context.Channel;
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||||
return;
|
return;
|
||||||
@ -173,7 +173,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task Queue([Remainder] string query)
|
public async Task Queue([Remainder] string query)
|
||||||
{
|
{
|
||||||
await music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query).ConfigureAwait(false);
|
await _music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query).ConfigureAwait(false);
|
||||||
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
||||||
{
|
{
|
||||||
Context.Message.DeleteAfter(10);
|
Context.Message.DeleteAfter(10);
|
||||||
@ -184,7 +184,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
[RequireContext(ContextType.Guild)]
|
[RequireContext(ContextType.Guild)]
|
||||||
public async Task SoundCloudQueue([Remainder] string query)
|
public async Task SoundCloudQueue([Remainder] string query)
|
||||||
{
|
{
|
||||||
await music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false);
|
await _music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, query, musicType: MusicType.Soundcloud).ConfigureAwait(false);
|
||||||
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
||||||
{
|
{
|
||||||
Context.Message.DeleteAfter(10);
|
Context.Message.DeleteAfter(10);
|
||||||
@ -197,7 +197,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
{
|
{
|
||||||
Song currentSong;
|
Song currentSong;
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
if ((currentSong = musicPlayer?.CurrentSong) == null)
|
if ((currentSong = musicPlayer?.CurrentSong) == null)
|
||||||
{
|
{
|
||||||
@ -250,7 +250,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
return embed;
|
return embed;
|
||||||
};
|
};
|
||||||
await Context.Channel.SendPaginatedConfirmAsync(page, printAction, lastPage, false).ConfigureAwait(false);
|
await Context.Channel.SendPaginatedConfirmAsync(_client, page, printAction, lastPage, false).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[NadekoCommand, Usage, Description, Aliases]
|
[NadekoCommand, Usage, Description, Aliases]
|
||||||
@ -258,7 +258,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task NowPlaying()
|
public async Task NowPlaying()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
var currentSong = musicPlayer.CurrentSong;
|
var currentSong = musicPlayer.CurrentSong;
|
||||||
if (currentSong == null)
|
if (currentSong == null)
|
||||||
@ -279,7 +279,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task Volume(int val)
|
public async Task Volume(int val)
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||||
return;
|
return;
|
||||||
@ -301,7 +301,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
|
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
|
uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
|
||||||
uow.Complete();
|
uow.Complete();
|
||||||
@ -314,7 +314,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task ShufflePlaylist()
|
public async Task ShufflePlaylist()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||||
return;
|
return;
|
||||||
@ -338,13 +338,13 @@ namespace NadekoBot.Modules.Music
|
|||||||
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
|
var plId = (await _google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
|
||||||
if (plId == null)
|
if (plId == null)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
|
var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
|
||||||
if (!ids.Any())
|
if (!ids.Any())
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||||
@ -366,7 +366,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
return;
|
return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await music.QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, id, true).ConfigureAwait(false);
|
await _music.QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, id, true).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (SongNotFoundException) { }
|
catch (SongNotFoundException) { }
|
||||||
catch { try { cancelSource.Cancel(); } catch { } }
|
catch { try { cancelSource.Cancel(); } catch { } }
|
||||||
@ -391,11 +391,11 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
using (var http = new HttpClient())
|
using (var http = new HttpClient())
|
||||||
{
|
{
|
||||||
var scvids = JObject.Parse(await http.GetStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Credentials.SoundCloudClientId}").ConfigureAwait(false))["tracks"].ToObject<SoundCloudVideo[]>();
|
var scvids = JObject.Parse(await http.GetStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={_creds.SoundCloudClientId}").ConfigureAwait(false))["tracks"].ToObject<SoundCloudVideo[]>();
|
||||||
await music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, scvids[0].TrackLink).ConfigureAwait(false);
|
await _music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, scvids[0].TrackLink).ConfigureAwait(false);
|
||||||
|
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var svideo in scvids.Skip(1))
|
foreach (var svideo in scvids.Skip(1))
|
||||||
@ -406,7 +406,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
{
|
{
|
||||||
Title = svideo.FullName,
|
Title = svideo.FullName,
|
||||||
Provider = "SoundCloud",
|
Provider = "SoundCloud",
|
||||||
Uri = svideo.StreamLink,
|
Uri = svideo.GetStreamLink(_creds),
|
||||||
ProviderType = MusicType.Normal,
|
ProviderType = MusicType.Normal,
|
||||||
Query = svideo.TrackLink,
|
Query = svideo.TrackLink,
|
||||||
}), ((IGuildUser)Context.User).Username);
|
}), ((IGuildUser)Context.User).Username);
|
||||||
@ -433,7 +433,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await music.QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
|
await _music.QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (PlaylistFullException)
|
catch (PlaylistFullException)
|
||||||
{
|
{
|
||||||
@ -457,7 +457,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radioLink, musicType: MusicType.Radio).ConfigureAwait(false);
|
await _music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radioLink, musicType: MusicType.Radio).ConfigureAwait(false);
|
||||||
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
|
||||||
{
|
{
|
||||||
Context.Message.DeleteAfter(10);
|
Context.Message.DeleteAfter(10);
|
||||||
@ -473,7 +473,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
var arg = path;
|
var arg = path;
|
||||||
if (string.IsNullOrWhiteSpace(arg))
|
if (string.IsNullOrWhiteSpace(arg))
|
||||||
return;
|
return;
|
||||||
await music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, path, musicType: MusicType.Local).ConfigureAwait(false);
|
await _music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, path, musicType: MusicType.Local).ConfigureAwait(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,7 +495,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public Task Remove(int num)
|
public Task Remove(int num)
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -512,7 +512,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
if (all.Trim().ToUpperInvariant() != "ALL")
|
if (all.Trim().ToUpperInvariant() != "ALL")
|
||||||
return;
|
return;
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
musicPlayer.ClearQueue();
|
musicPlayer.ClearQueue();
|
||||||
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
|
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
|
||||||
@ -526,7 +526,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fromto = fromto?.Trim();
|
fromto = fromto?.Trim();
|
||||||
@ -569,7 +569,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task SetMaxQueue(uint size = 0)
|
public async Task SetMaxQueue(uint size = 0)
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
musicPlayer.MaxQueueSize = size;
|
musicPlayer.MaxQueueSize = size;
|
||||||
@ -589,7 +589,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
var channel = (ITextChannel)Context.Channel;
|
var channel = (ITextChannel)Context.Channel;
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
musicPlayer.MaxPlaytimeSeconds = seconds;
|
musicPlayer.MaxPlaytimeSeconds = seconds;
|
||||||
if (seconds == 0)
|
if (seconds == 0)
|
||||||
@ -603,7 +603,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task ReptCurSong()
|
public async Task ReptCurSong()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
var currentSong = musicPlayer.CurrentSong;
|
var currentSong = musicPlayer.CurrentSong;
|
||||||
if (currentSong == null)
|
if (currentSong == null)
|
||||||
@ -626,7 +626,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task RepeatPl()
|
public async Task RepeatPl()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
var currentValue = musicPlayer.ToggleRepeatPlaylist();
|
var currentValue = musicPlayer.ToggleRepeatPlaylist();
|
||||||
if(currentValue)
|
if(currentValue)
|
||||||
@ -640,7 +640,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task Save([Remainder] string name)
|
public async Task Save([Remainder] string name)
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var curSong = musicPlayer.CurrentSong;
|
var curSong = musicPlayer.CurrentSong;
|
||||||
@ -655,7 +655,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
MusicPlaylist playlist;
|
MusicPlaylist playlist;
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
playlist = new MusicPlaylist
|
playlist = new MusicPlaylist
|
||||||
{
|
{
|
||||||
@ -679,7 +679,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task Load([Remainder] int id)
|
public async Task Load([Remainder] int id)
|
||||||
{
|
{
|
||||||
MusicPlaylist mpl;
|
MusicPlaylist mpl;
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
mpl = uow.MusicPlaylists.GetWithSongs(id);
|
mpl = uow.MusicPlaylists.GetWithSongs(id);
|
||||||
}
|
}
|
||||||
@ -696,7 +696,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
var usr = (IGuildUser)Context.User;
|
var usr = (IGuildUser)Context.User;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await music.QueueSong(usr, (ITextChannel)Context.Channel, usr.VoiceChannel, item.Query, true, item.ProviderType).ConfigureAwait(false);
|
await _music.QueueSong(usr, (ITextChannel)Context.Channel, usr.VoiceChannel, item.Query, true, item.ProviderType).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (SongNotFoundException) { }
|
catch (SongNotFoundException) { }
|
||||||
catch { break; }
|
catch { break; }
|
||||||
@ -714,7 +714,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
|
|
||||||
List<MusicPlaylist> playlists;
|
List<MusicPlaylist> playlists;
|
||||||
|
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num);
|
playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num);
|
||||||
}
|
}
|
||||||
@ -735,13 +735,13 @@ namespace NadekoBot.Modules.Music
|
|||||||
var success = false;
|
var success = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
var pl = uow.MusicPlaylists.Get(id);
|
var pl = uow.MusicPlaylists.Get(id);
|
||||||
|
|
||||||
if (pl != null)
|
if (pl != null)
|
||||||
{
|
{
|
||||||
if (NadekoBot.Credentials.IsOwner(Context.User) || pl.AuthorId == Context.User.Id)
|
if (_creds.IsOwner(Context.User) || pl.AuthorId == Context.User.Id)
|
||||||
{
|
{
|
||||||
uow.MusicPlaylists.Remove(pl);
|
uow.MusicPlaylists.Remove(pl);
|
||||||
await uow.CompleteAsync().ConfigureAwait(false);
|
await uow.CompleteAsync().ConfigureAwait(false);
|
||||||
@ -766,7 +766,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task Goto(int time)
|
public async Task Goto(int time)
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||||
return;
|
return;
|
||||||
@ -801,7 +801,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task Autoplay()
|
public async Task Autoplay()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!musicPlayer.ToggleAutoplay())
|
if (!musicPlayer.ToggleAutoplay())
|
||||||
@ -816,7 +816,7 @@ namespace NadekoBot.Modules.Music
|
|||||||
public async Task SetMusicChannel()
|
public async Task SetMusicChannel()
|
||||||
{
|
{
|
||||||
MusicPlayer musicPlayer;
|
MusicPlayer musicPlayer;
|
||||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||||
{
|
{
|
||||||
await ReplyErrorLocalized("no_player").ConfigureAwait(false);
|
await ReplyErrorLocalized("no_player").ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
|
@ -15,7 +15,6 @@ using NadekoBot.Services.Searches;
|
|||||||
|
|
||||||
namespace NadekoBot.Modules.NSFW
|
namespace NadekoBot.Modules.NSFW
|
||||||
{
|
{
|
||||||
[NadekoModule("NSFW")]
|
|
||||||
public class NSFW : NadekoTopLevelModule
|
public class NSFW : NadekoTopLevelModule
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<ulong, Timer> _autoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
|
private static readonly ConcurrentDictionary<ulong, Timer> _autoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
|
||||||
|
@ -17,7 +17,6 @@ namespace NadekoBot.Modules
|
|||||||
public readonly string ModuleTypeName;
|
public readonly string ModuleTypeName;
|
||||||
public readonly string LowerModuleTypeName;
|
public readonly string LowerModuleTypeName;
|
||||||
|
|
||||||
|
|
||||||
//todo :thinking:
|
//todo :thinking:
|
||||||
public NadekoStrings _strings { get; set; }
|
public NadekoStrings _strings { get; set; }
|
||||||
public ILocalization _localization { get; set; }
|
public ILocalization _localization { get; set; }
|
||||||
|
@ -20,7 +20,6 @@ using System.Diagnostics;
|
|||||||
|
|
||||||
namespace NadekoBot.Modules.Utility
|
namespace NadekoBot.Modules.Utility
|
||||||
{
|
{
|
||||||
[NadekoModule("Utility")]
|
|
||||||
public partial class Utility : NadekoTopLevelModule
|
public partial class Utility : NadekoTopLevelModule
|
||||||
{
|
{
|
||||||
private static ConcurrentDictionary<ulong, Timer> _rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
|
private static ConcurrentDictionary<ulong, Timer> _rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
|
||||||
|
@ -18,6 +18,8 @@ using NadekoBot.Services.Database.Models;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using NadekoBot.Modules.Utility;
|
using NadekoBot.Modules.Utility;
|
||||||
using NadekoBot.Services.Searches;
|
using NadekoBot.Services.Searches;
|
||||||
|
using NadekoBot.Services.ClashOfClans;
|
||||||
|
using NadekoBot.Services.Music;
|
||||||
|
|
||||||
namespace NadekoBot
|
namespace NadekoBot
|
||||||
{
|
{
|
||||||
@ -75,7 +77,6 @@ namespace NadekoBot
|
|||||||
var greetSettingsService = new GreetSettingsService(AllGuildConfigs, db);
|
var greetSettingsService = new GreetSettingsService(AllGuildConfigs, db);
|
||||||
|
|
||||||
var localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), db);
|
var localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), db);
|
||||||
//var musicService = new MusicService(google, strings, localization);
|
|
||||||
|
|
||||||
var commandService = new CommandService(new CommandServiceConfig()
|
var commandService = new CommandService(new CommandServiceConfig()
|
||||||
{
|
{
|
||||||
@ -89,9 +90,15 @@ namespace NadekoBot
|
|||||||
|
|
||||||
var images = new ImagesService();
|
var images = new ImagesService();
|
||||||
|
|
||||||
|
var currencyHandler = new CurrencyHandler(BotConfig, db);
|
||||||
|
|
||||||
|
var soundcloud = new SoundCloudApiService(credentials);
|
||||||
|
|
||||||
//module services
|
//module services
|
||||||
var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db);
|
var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db);
|
||||||
var searchesService = new SearchesService();
|
var searchesService = new SearchesService();
|
||||||
|
var clashService = new ClashOfClansService(Client, db, localization, strings);
|
||||||
|
var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials);
|
||||||
|
|
||||||
//initialize Services
|
//initialize Services
|
||||||
Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces
|
Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces
|
||||||
@ -105,12 +112,15 @@ namespace NadekoBot
|
|||||||
.Add<NadekoStrings>(strings)
|
.Add<NadekoStrings>(strings)
|
||||||
.Add<DiscordShardedClient>(Client)
|
.Add<DiscordShardedClient>(Client)
|
||||||
.Add<GreetSettingsService>(greetSettingsService)
|
.Add<GreetSettingsService>(greetSettingsService)
|
||||||
//.Add(musicService)
|
.Add<BotConfig>(BotConfig)
|
||||||
|
.Add<CurrencyHandler>(currencyHandler)
|
||||||
|
.Add(musicService)
|
||||||
.Add<CommandHandler>(commandHandler)
|
.Add<CommandHandler>(commandHandler)
|
||||||
.Add<DbHandler>(db)
|
.Add<DbHandler>(db)
|
||||||
//modules
|
//modules
|
||||||
.Add<UtilityService>(utilityService)
|
.Add<UtilityService>(utilityService)
|
||||||
.Add<SearchesService>(searchesService)
|
.Add<SearchesService>(searchesService)
|
||||||
|
.Add<ClashOfClansService>(clashService)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
commandHandler.AddServices(Services);
|
commandHandler.AddServices(Services);
|
||||||
|
@ -29,35 +29,26 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="data\**\*;credentials.json;credentials_example.json" />
|
<Compile Remove="data\**\*;credentials.json;credentials_example.json" />
|
||||||
<Compile Remove="Modules\Administration\**" />
|
<Compile Remove="Modules\Administration\**" />
|
||||||
<Compile Remove="Modules\ClashOfClans\**" />
|
|
||||||
<Compile Remove="Modules\CustomReactions\**" />
|
<Compile Remove="Modules\CustomReactions\**" />
|
||||||
<Compile Remove="Modules\Gambling\**" />
|
<Compile Remove="Modules\Gambling\**" />
|
||||||
<Compile Remove="Modules\Games\**" />
|
<Compile Remove="Modules\Games\**" />
|
||||||
<Compile Remove="Modules\Help\**" />
|
<Compile Remove="Modules\NSFW\**" />
|
||||||
<Compile Remove="Modules\Music\**" />
|
|
||||||
<Compile Remove="Modules\Permissions\**" />
|
<Compile Remove="Modules\Permissions\**" />
|
||||||
<Compile Remove="Modules\Searches\**" />
|
<Compile Remove="Modules\Searches\**" />
|
||||||
<Compile Remove="Services\Music\**" />
|
|
||||||
<EmbeddedResource Remove="Modules\Administration\**" />
|
<EmbeddedResource Remove="Modules\Administration\**" />
|
||||||
<EmbeddedResource Remove="Modules\ClashOfClans\**" />
|
|
||||||
<EmbeddedResource Remove="Modules\CustomReactions\**" />
|
<EmbeddedResource Remove="Modules\CustomReactions\**" />
|
||||||
<EmbeddedResource Remove="Modules\Gambling\**" />
|
<EmbeddedResource Remove="Modules\Gambling\**" />
|
||||||
<EmbeddedResource Remove="Modules\Games\**" />
|
<EmbeddedResource Remove="Modules\Games\**" />
|
||||||
<EmbeddedResource Remove="Modules\Help\**" />
|
<EmbeddedResource Remove="Modules\NSFW\**" />
|
||||||
<EmbeddedResource Remove="Modules\Music\**" />
|
|
||||||
<EmbeddedResource Remove="Modules\Permissions\**" />
|
<EmbeddedResource Remove="Modules\Permissions\**" />
|
||||||
<EmbeddedResource Remove="Modules\Searches\**" />
|
<EmbeddedResource Remove="Modules\Searches\**" />
|
||||||
<EmbeddedResource Remove="Services\Music\**" />
|
|
||||||
<None Remove="Modules\Administration\**" />
|
<None Remove="Modules\Administration\**" />
|
||||||
<None Remove="Modules\ClashOfClans\**" />
|
|
||||||
<None Remove="Modules\CustomReactions\**" />
|
<None Remove="Modules\CustomReactions\**" />
|
||||||
<None Remove="Modules\Gambling\**" />
|
<None Remove="Modules\Gambling\**" />
|
||||||
<None Remove="Modules\Games\**" />
|
<None Remove="Modules\Games\**" />
|
||||||
<None Remove="Modules\Help\**" />
|
<None Remove="Modules\NSFW\**" />
|
||||||
<None Remove="Modules\Music\**" />
|
|
||||||
<None Remove="Modules\Permissions\**" />
|
<None Remove="Modules\Permissions\**" />
|
||||||
<None Remove="Modules\Searches\**" />
|
<None Remove="Modules\Searches\**" />
|
||||||
<None Remove="Services\Music\**" />
|
|
||||||
<None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so">
|
<None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
@ -105,4 +96,8 @@
|
|||||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
||||||
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
|
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Modules\Music\Classes\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
263
src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs
Normal file
263
src/NadekoBot/Services/ClashOfClans/ClashOfClansService.cs
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using NadekoBot.Extensions;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.ClashOfClans
|
||||||
|
{
|
||||||
|
// todo, just made this compile, it's a complete mess. A lot of the things here should actually be in the actual module.
|
||||||
|
// service should just handle the state, module should print out what happened, so everything that has to do with strings
|
||||||
|
// shouldn't be here
|
||||||
|
public class ClashOfClansService
|
||||||
|
{
|
||||||
|
private readonly DiscordShardedClient _client;
|
||||||
|
private readonly DbHandler _db;
|
||||||
|
private readonly ILocalization _localization;
|
||||||
|
private readonly NadekoStrings _strings;
|
||||||
|
private readonly Timer checkWarTimer;
|
||||||
|
|
||||||
|
public ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
|
||||||
|
|
||||||
|
public ClashOfClansService(DiscordShardedClient client, DbHandler db, ILocalization localization, NadekoStrings strings)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
_db = db;
|
||||||
|
_localization = localization;
|
||||||
|
_strings = strings;
|
||||||
|
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
|
||||||
|
uow.ClashOfClans
|
||||||
|
.GetAllWars()
|
||||||
|
.Select(cw =>
|
||||||
|
{
|
||||||
|
cw.Channel = _client.GetGuild(cw.GuildId)?
|
||||||
|
.GetTextChannel(cw.ChannelId);
|
||||||
|
return cw;
|
||||||
|
})
|
||||||
|
.Where(cw => cw.Channel != null)
|
||||||
|
.GroupBy(cw => cw.GuildId)
|
||||||
|
.ToDictionary(g => g.Key, g => g.ToList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
checkWarTimer = new Timer(async _ =>
|
||||||
|
{
|
||||||
|
foreach (var kvp in ClashWars)
|
||||||
|
{
|
||||||
|
foreach (var war in kvp.Value)
|
||||||
|
{
|
||||||
|
try { await CheckWar(TimeSpan.FromHours(2), war).ConfigureAwait(false); } catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckWar(TimeSpan callExpire, ClashWar war)
|
||||||
|
{
|
||||||
|
var Bases = war.Bases;
|
||||||
|
for (var i = 0; i < Bases.Count; i++)
|
||||||
|
{
|
||||||
|
var callUser = Bases[i].CallUser;
|
||||||
|
if (callUser == null) continue;
|
||||||
|
if ((!Bases[i].BaseDestroyed) && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
|
||||||
|
{
|
||||||
|
if (Bases[i].Stars != 3)
|
||||||
|
Bases[i].BaseDestroyed = true;
|
||||||
|
else
|
||||||
|
Bases[i] = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SaveWar(war);
|
||||||
|
await war.Channel.SendErrorAsync(_strings.GetText("claim_expired",
|
||||||
|
_localization.GetCultureInfo(war.Channel.GuildId),
|
||||||
|
typeof(ClashOfClansService).Name.ToLowerInvariant(),
|
||||||
|
Format.Bold(Bases[i].CallUser),
|
||||||
|
ShortPrint(war)));
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tuple<List<ClashWar>, int> GetWarInfo(IGuild guild, int num)
|
||||||
|
{
|
||||||
|
List<ClashWar> wars = null;
|
||||||
|
ClashWars.TryGetValue(guild.Id, out wars);
|
||||||
|
if (wars == null || wars.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// get the number of the war
|
||||||
|
else if (num < 1 || num > wars.Count)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
num -= 1;
|
||||||
|
//get the actual war
|
||||||
|
return new Tuple<List<ClashWar>, int>(wars, num);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId)
|
||||||
|
{
|
||||||
|
var channel = _client.GetGuild(serverId)?.GetTextChannel(channelId);
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var cw = new ClashWar
|
||||||
|
{
|
||||||
|
EnemyClan = enemyClan,
|
||||||
|
Size = size,
|
||||||
|
Bases = new List<ClashCaller>(size),
|
||||||
|
GuildId = serverId,
|
||||||
|
ChannelId = channelId,
|
||||||
|
Channel = channel,
|
||||||
|
};
|
||||||
|
cw.Bases.Capacity = size;
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
cw.Bases.Add(new ClashCaller()
|
||||||
|
{
|
||||||
|
CallUser = null,
|
||||||
|
SequenceNumber = i,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Console.WriteLine(cw.Bases.Capacity);
|
||||||
|
uow.ClashOfClans.Add(cw);
|
||||||
|
await uow.CompleteAsync();
|
||||||
|
return cw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveWar(ClashWar cw)
|
||||||
|
{
|
||||||
|
if (cw.WarState == StateOfWar.Ended)
|
||||||
|
{
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
uow.ClashOfClans.Remove(cw);
|
||||||
|
uow.CompleteAsync();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
uow.ClashOfClans.Update(cw);
|
||||||
|
uow.CompleteAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Call(ClashWar cw, string u, int baseNumber)
|
||||||
|
{
|
||||||
|
if (baseNumber < 0 || baseNumber >= cw.Bases.Count)
|
||||||
|
throw new ArgumentException(Localize(cw, "invalid_base_number"));
|
||||||
|
if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
|
||||||
|
throw new ArgumentException(Localize(cw, "base_already_claimed"));
|
||||||
|
for (var i = 0; i < cw.Bases.Count; i++)
|
||||||
|
{
|
||||||
|
if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
|
||||||
|
throw new ArgumentException(Localize(cw, "claimed_other", u, i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
var cc = cw.Bases[baseNumber];
|
||||||
|
cc.CallUser = u.Trim();
|
||||||
|
cc.TimeAdded = DateTime.UtcNow;
|
||||||
|
cc.BaseDestroyed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FinishClaim(ClashWar cw, string user, int stars = 3)
|
||||||
|
{
|
||||||
|
user = user.Trim();
|
||||||
|
for (var i = 0; i < cw.Bases.Count; i++)
|
||||||
|
{
|
||||||
|
if (cw.Bases[i]?.BaseDestroyed != false || cw.Bases[i]?.CallUser != user) continue;
|
||||||
|
cw.Bases[i].BaseDestroyed = true;
|
||||||
|
cw.Bases[i].Stars = stars;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException(Localize(cw, "not_partic_or_destroyed", user));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinishClaim(ClashWar cw, int index, int stars = 3)
|
||||||
|
{
|
||||||
|
if (index < 0 || index > cw.Bases.Count)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
|
var toFinish = cw.Bases[index];
|
||||||
|
if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(Localize(cw, "base_already_destroyed"));
|
||||||
|
if (toFinish.CallUser == null) throw new InvalidOperationException(Localize(cw, "base_already_unclaimed"));
|
||||||
|
toFinish.BaseDestroyed = true;
|
||||||
|
toFinish.Stars = stars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Uncall(ClashWar cw, string user)
|
||||||
|
{
|
||||||
|
user = user.Trim();
|
||||||
|
for (var i = 0; i < cw.Bases.Count; i++)
|
||||||
|
{
|
||||||
|
if (cw.Bases[i]?.CallUser != user) continue;
|
||||||
|
cw.Bases[i].CallUser = null;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException(Localize(cw, "not_partic"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ShortPrint(ClashWar cw) =>
|
||||||
|
$"`{cw.EnemyClan}` ({cw.Size} v {cw.Size})";
|
||||||
|
|
||||||
|
public string ToPrettyString(ClashWar cw)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
if (cw.WarState == StateOfWar.Created)
|
||||||
|
sb.AppendLine("`not started`");
|
||||||
|
var twoHours = new TimeSpan(2, 0, 0);
|
||||||
|
for (var i = 0; i < cw.Bases.Count; i++)
|
||||||
|
{
|
||||||
|
if (cw.Bases[i].CallUser == null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"`{i + 1}.` ❌*{Localize(cw, "not_claimed")}*");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cw.Bases[i].BaseDestroyed)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {new string('⭐', cw.Bases[i].Stars)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var left = (cw.WarState == StateOfWar.Started) ? twoHours - (DateTime.UtcNow - cw.Bases[i].TimeAdded) : twoHours;
|
||||||
|
if (cw.Bases[i].Stars == 3)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"`{i + 1}.` ✅ `{cw.Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left {new string('⭐', cw.Bases[i].Stars)} {string.Concat(Enumerable.Repeat("🔸", 3 - cw.Bases[i].Stars))}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Localize(ClashWar cw, string key, params object[] replacements)
|
||||||
|
{
|
||||||
|
return string.Format(Localize(cw, key), replacements);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Localize(ClashWar cw, string key)
|
||||||
|
{
|
||||||
|
return _strings.GetText(key,
|
||||||
|
_localization.GetCultureInfo(cw.Channel?.GuildId),
|
||||||
|
"ClashOfClans".ToLowerInvariant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
src/NadekoBot/Services/ClashOfClans/Extensions.cs
Normal file
40
src/NadekoBot/Services/ClashOfClans/Extensions.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services.ClashOfClans
|
||||||
|
{
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static void ResetTime(this ClashCaller c)
|
||||||
|
{
|
||||||
|
c.TimeAdded = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Destroy(this ClashCaller c)
|
||||||
|
{
|
||||||
|
c.BaseDestroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void End(this ClashWar cw)
|
||||||
|
{
|
||||||
|
//Ended = true;
|
||||||
|
cw.WarState = StateOfWar.Ended;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Start(this ClashWar cw)
|
||||||
|
{
|
||||||
|
if (cw.WarState == StateOfWar.Started)
|
||||||
|
throw new InvalidOperationException("war_already_started");
|
||||||
|
//if (Started)
|
||||||
|
// throw new InvalidOperationException();
|
||||||
|
//Started = true;
|
||||||
|
cw.WarState = StateOfWar.Started;
|
||||||
|
cw.StartedAt = DateTime.UtcNow;
|
||||||
|
foreach (var b in cw.Bases.Where(b => b.CallUser != null))
|
||||||
|
{
|
||||||
|
b.ResetTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
src/NadekoBot/Services/CustomReactions/CustomReactions.cs
Normal file
88
src/NadekoBot/Services/CustomReactions/CustomReactions.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
using Discord;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using NLog;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NadekoBot.Services
|
||||||
|
{
|
||||||
|
public class CustomReactions
|
||||||
|
{
|
||||||
|
private CustomReaction[] _globalReactions = new CustomReaction[] { };
|
||||||
|
public CustomReaction[] GlobalReactions => _globalReactions;
|
||||||
|
public ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>();
|
||||||
|
|
||||||
|
public ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
|
||||||
|
|
||||||
|
private readonly Logger _log;
|
||||||
|
private readonly DbHandler _db;
|
||||||
|
|
||||||
|
public CustomReactions(DbHandler db)
|
||||||
|
{
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
_db = db;
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
using (var uow = _db.UnitOfWork)
|
||||||
|
{
|
||||||
|
var items = uow.CustomReactions.GetAll();
|
||||||
|
GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray()));
|
||||||
|
_globalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
|
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearStats() => ReactionStats.Clear();
|
||||||
|
|
||||||
|
public CustomReaction TryGetCustomReaction(IUserMessage umsg)
|
||||||
|
{
|
||||||
|
var channel = umsg.Channel as SocketTextChannel;
|
||||||
|
if (channel == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var content = umsg.Content.Trim().ToLowerInvariant();
|
||||||
|
CustomReaction[] reactions;
|
||||||
|
|
||||||
|
GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
|
||||||
|
if (reactions != null && reactions.Any())
|
||||||
|
{
|
||||||
|
var rs = reactions.Where(cr =>
|
||||||
|
{
|
||||||
|
if (cr == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
|
||||||
|
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
|
||||||
|
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
if (rs.Length != 0)
|
||||||
|
{
|
||||||
|
var reaction = rs[new NadekoRandom().Next(0, rs.Length)];
|
||||||
|
if (reaction != null)
|
||||||
|
{
|
||||||
|
if (reaction.Response == "-")
|
||||||
|
return null;
|
||||||
|
return reaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var grs = GlobalReactions.Where(cr =>
|
||||||
|
{
|
||||||
|
if (cr == null)
|
||||||
|
return false;
|
||||||
|
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
|
||||||
|
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
|
||||||
|
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
|
||||||
|
}).ToArray();
|
||||||
|
if (grs.Length == 0)
|
||||||
|
return null;
|
||||||
|
var greaction = grs[new NadekoRandom().Next(0, grs.Length)];
|
||||||
|
|
||||||
|
return greaction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using NadekoBot.DataStructures;
|
||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Services;
|
using NadekoBot.Services;
|
||||||
using NadekoBot.Services.Database.Models;
|
using NadekoBot.Services.Database.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.CustomReactions
|
namespace NadekoBot.Modules.CustomReactions
|
||||||
{
|
{
|
||||||
@ -103,5 +102,19 @@ namespace NadekoBot.Modules.CustomReactions
|
|||||||
|
|
||||||
public static string ResponseWithContext(this CustomReaction cr, IUserMessage ctx)
|
public static string ResponseWithContext(this CustomReaction cr, IUserMessage ctx)
|
||||||
=> cr.Response.ResolveResponseString(ctx, cr.Trigger.ResolveTriggerString(ctx));
|
=> cr.Response.ResolveResponseString(ctx, cr.Trigger.ResolveTriggerString(ctx));
|
||||||
|
|
||||||
|
public static async Task<IUserMessage> Send(this CustomReaction cr, IUserMessage context)
|
||||||
|
{
|
||||||
|
var channel = cr.DmResponse ? await context.Author.CreateDMChannelAsync() : context.Channel;
|
||||||
|
|
||||||
|
CustomReactions.ReactionStats.AddOrUpdate(cr.Trigger, 1, (k, old) => ++old);
|
||||||
|
|
||||||
|
CREmbed crembed;
|
||||||
|
if (CREmbed.TryParse(cr.Response, out crembed))
|
||||||
|
{
|
||||||
|
return await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "");
|
||||||
|
}
|
||||||
|
return await channel.SendMessageAsync(cr.ResponseWithContext(context).SanitizeMentions());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,15 +7,6 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
{
|
{
|
||||||
public class ClashWar : DbEntity
|
public class ClashWar : DbEntity
|
||||||
{
|
{
|
||||||
public enum DestroyStars
|
|
||||||
{
|
|
||||||
One, Two, Three
|
|
||||||
}
|
|
||||||
public enum StateOfWar
|
|
||||||
{
|
|
||||||
Started, Ended, Created
|
|
||||||
}
|
|
||||||
|
|
||||||
public string EnemyClan { get; set; }
|
public string EnemyClan { get; set; }
|
||||||
public int Size { get; set; }
|
public int Size { get; set; }
|
||||||
public StateOfWar WarState { get; set; } = StateOfWar.Created;
|
public StateOfWar WarState { get; set; } = StateOfWar.Created;
|
||||||
@ -29,4 +20,13 @@ namespace NadekoBot.Services.Database.Models
|
|||||||
|
|
||||||
public List<ClashCaller> Bases { get; set; } = new List<ClashCaller>();
|
public List<ClashCaller> Bases { get; set; } = new List<ClashCaller>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum DestroyStars
|
||||||
|
{
|
||||||
|
One, Two, Three
|
||||||
|
}
|
||||||
|
public enum StateOfWar
|
||||||
|
{
|
||||||
|
Started, Ended, Created
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,10 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NadekoBot.Services.Music;
|
using NadekoBot.Services.Database.Models;
|
||||||
using NadekoBot.Services;
|
|
||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Music
|
||||||
{
|
{
|
||||||
|
|
||||||
public enum StreamState
|
public enum StreamState
|
||||||
{
|
{
|
||||||
Resolving,
|
Resolving,
|
||||||
@ -24,7 +22,6 @@ namespace NadekoBot.Services.Music
|
|||||||
|
|
||||||
public class MusicPlayer
|
public class MusicPlayer
|
||||||
{
|
{
|
||||||
public const string MusicDataPath = "data/musicdata";
|
|
||||||
private IAudioClient AudioClient { get; set; }
|
private IAudioClient AudioClient { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -6,23 +6,45 @@ using Discord;
|
|||||||
using NadekoBot.Extensions;
|
using NadekoBot.Extensions;
|
||||||
using NadekoBot.Modules;
|
using NadekoBot.Modules;
|
||||||
using NadekoBot.Services.Impl;
|
using NadekoBot.Services.Impl;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using NLog;
|
||||||
|
using System.IO;
|
||||||
|
using VideoLibrary;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Music
|
||||||
{
|
{
|
||||||
public class MusicService
|
public class MusicService
|
||||||
{
|
{
|
||||||
|
public const string MusicDataPath = "data/musicdata";
|
||||||
|
|
||||||
private readonly IGoogleApiService _google;
|
private readonly IGoogleApiService _google;
|
||||||
private readonly NadekoStrings _strings;
|
private readonly NadekoStrings _strings;
|
||||||
private readonly ILocalization _localization;
|
private readonly ILocalization _localization;
|
||||||
private GoogleApiService google;
|
private GoogleApiService google;
|
||||||
|
private readonly DbHandler _db;
|
||||||
|
private readonly Logger _log;
|
||||||
|
private readonly SoundCloudApiService _sc;
|
||||||
|
private readonly IBotCredentials _creds;
|
||||||
|
|
||||||
public ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
|
public ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
|
||||||
|
|
||||||
public MusicService(IGoogleApiService google, NadekoStrings strings, ILocalization localization)
|
public MusicService(IGoogleApiService google,
|
||||||
|
NadekoStrings strings, ILocalization localization, DbHandler db,
|
||||||
|
SoundCloudApiService sc, IBotCredentials creds)
|
||||||
{
|
{
|
||||||
_google = google;
|
_google = google;
|
||||||
_strings = strings;
|
_strings = strings;
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
|
_db = db;
|
||||||
|
_sc = sc;
|
||||||
|
_creds = creds;
|
||||||
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
try { Directory.Delete(MusicDataPath, true); } catch { }
|
||||||
|
|
||||||
|
Directory.CreateDirectory(MusicDataPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MusicPlayer GetPlayer(ulong guildId)
|
public MusicPlayer GetPlayer(ulong guildId)
|
||||||
@ -39,7 +61,7 @@ namespace NadekoBot.Services.Music
|
|||||||
return MusicPlayers.GetOrAdd(guildId, server =>
|
return MusicPlayers.GetOrAdd(guildId, server =>
|
||||||
{
|
{
|
||||||
float vol;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
|
float vol;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
|
||||||
using (var uow = DbHandler.UnitOfWork())
|
using (var uow = _db.UnitOfWork)
|
||||||
{
|
{
|
||||||
//todo move to cached variable
|
//todo move to cached variable
|
||||||
vol = uow.GuildConfigs.For(guildId, set => set).DefaultMusicVolume;
|
vol = uow.GuildConfigs.For(guildId, set => set).DefaultMusicVolume;
|
||||||
@ -168,7 +190,7 @@ namespace NadekoBot.Services.Music
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
musicPlayer.ThrowIfQueueFull();
|
musicPlayer.ThrowIfQueueFull();
|
||||||
resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false);
|
resolvedSong = await ResolveSong(query, musicType).ConfigureAwait(false);
|
||||||
|
|
||||||
if (resolvedSong == null)
|
if (resolvedSong == null)
|
||||||
throw new SongNotFoundException();
|
throw new SongNotFoundException();
|
||||||
@ -213,5 +235,202 @@ namespace NadekoBot.Services.Music
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
|
if (musicType != MusicType.Local && IsRadioLink(query))
|
||||||
|
{
|
||||||
|
musicType = MusicType.Radio;
|
||||||
|
query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (musicType)
|
||||||
|
{
|
||||||
|
case MusicType.Local:
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Uri = "\"" + Path.GetFullPath(query) + "\"",
|
||||||
|
Title = Path.GetFileNameWithoutExtension(query),
|
||||||
|
Provider = "Local File",
|
||||||
|
ProviderType = musicType,
|
||||||
|
Query = query,
|
||||||
|
});
|
||||||
|
case MusicType.Radio:
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Uri = query,
|
||||||
|
Title = $"{query}",
|
||||||
|
Provider = "Radio Stream",
|
||||||
|
ProviderType = musicType,
|
||||||
|
Query = query
|
||||||
|
})
|
||||||
|
{ TotalTime = TimeSpan.MaxValue };
|
||||||
|
}
|
||||||
|
if (_sc.IsSoundCloudLink(query))
|
||||||
|
{
|
||||||
|
var svideo = await _sc.ResolveVideoAsync(query).ConfigureAwait(false);
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Title = svideo.FullName,
|
||||||
|
Provider = "SoundCloud",
|
||||||
|
Uri = svideo.GetStreamLink(_creds),
|
||||||
|
ProviderType = musicType,
|
||||||
|
Query = svideo.TrackLink,
|
||||||
|
AlbumArt = svideo.artwork_url,
|
||||||
|
})
|
||||||
|
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (musicType == MusicType.Soundcloud)
|
||||||
|
{
|
||||||
|
var svideo = await _sc.GetVideoByQueryAsync(query).ConfigureAwait(false);
|
||||||
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Title = svideo.FullName,
|
||||||
|
Provider = "SoundCloud",
|
||||||
|
Uri = svideo.GetStreamLink(_creds),
|
||||||
|
ProviderType = MusicType.Soundcloud,
|
||||||
|
Query = svideo.TrackLink,
|
||||||
|
AlbumArt = svideo.artwork_url,
|
||||||
|
})
|
||||||
|
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
|
||||||
|
}
|
||||||
|
|
||||||
|
var link = (await _google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
|
||||||
|
if (string.IsNullOrWhiteSpace(link))
|
||||||
|
throw new OperationCanceledException("Not a valid youtube query.");
|
||||||
|
var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
|
||||||
|
var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
|
||||||
|
var video = videos
|
||||||
|
.Where(v => v.AudioBitrate < 256)
|
||||||
|
.OrderByDescending(v => v.AudioBitrate)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (video == null) // do something with this error
|
||||||
|
throw new Exception("Could not load any video elements based on the query.");
|
||||||
|
var m = Regex.Match(query, @"\?t=(?<t>\d*)");
|
||||||
|
int gotoTime = 0;
|
||||||
|
if (m.Captures.Count > 0)
|
||||||
|
int.TryParse(m.Groups["t"].ToString(), out gotoTime);
|
||||||
|
var song = new Song(new SongInfo
|
||||||
|
{
|
||||||
|
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
||||||
|
Provider = "YouTube",
|
||||||
|
Uri = await video.GetUriAsync().ConfigureAwait(false),
|
||||||
|
Query = link,
|
||||||
|
ProviderType = musicType,
|
||||||
|
});
|
||||||
|
song.SkipTo = gotoTime;
|
||||||
|
return song;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Warn($"Failed resolving the link.{ex.Message}");
|
||||||
|
_log.Warn(ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> HandleStreamContainers(string query)
|
||||||
|
{
|
||||||
|
string file = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var http = new HttpClient())
|
||||||
|
{
|
||||||
|
file = await http.GetStringAsync(query).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
if (query.Contains(".pls"))
|
||||||
|
{
|
||||||
|
//File1=http://armitunes.com:8000/
|
||||||
|
//Regex.Match(query)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_log.Warn($"Failed reading .pls:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (query.Contains(".m3u"))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
# This is a comment
|
||||||
|
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
|
||||||
|
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
|
||||||
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_log.Warn($"Failed reading .m3u:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (query.Contains(".asx"))
|
||||||
|
{
|
||||||
|
//<ref href="http://armitunes.com:8000"/>
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_log.Warn($"Failed reading .asx:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (query.Contains(".xspf"))
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<playlist version="1" xmlns="http://xspf.org/ns/0/">
|
||||||
|
<trackList>
|
||||||
|
<track><location>file:///mp3s/song_1.mp3</location></track>
|
||||||
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
|
||||||
|
var res = m.Groups["url"]?.ToString();
|
||||||
|
return res?.Trim();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_log.Warn($"Failed reading .xspf:\n{file}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsRadioLink(string query) =>
|
||||||
|
(query.StartsWith("http") ||
|
||||||
|
query.StartsWith("ww"))
|
||||||
|
&&
|
||||||
|
(query.Contains(".pls") ||
|
||||||
|
query.Contains(".m3u") ||
|
||||||
|
query.Contains(".asx") ||
|
||||||
|
query.Contains(".xspf"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Discord;
|
using Discord;
|
||||||
|
using NadekoBot.Services.Database.Models;
|
||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Music
|
||||||
{
|
{
|
||||||
@ -144,7 +145,7 @@ namespace NadekoBot.Services.Music
|
|||||||
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
BytesSent = (ulong) SkipTo * 3840 * 50;
|
BytesSent = (ulong) SkipTo * 3840 * 50;
|
||||||
var filename = Path.Combine(MusicPlayer.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
var filename = Path.Combine(MusicService.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
||||||
|
|
||||||
var inStream = new SongBuffer(MusicPlayer, filename, SongInfo, SkipTo, _frameBytes * 100);
|
var inStream = new SongBuffer(MusicPlayer, filename, SongInfo, SkipTo, _frameBytes * 100);
|
||||||
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
|
var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
|
||||||
|
@ -1,214 +1,9 @@
|
|||||||
using System;
|
using NLog;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using NLog;
|
|
||||||
using VideoLibrary;
|
|
||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Music
|
||||||
{
|
{
|
||||||
public static class SongHandler
|
public static class SongHandler
|
||||||
{
|
{
|
||||||
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(query))
|
|
||||||
throw new ArgumentNullException(nameof(query));
|
|
||||||
|
|
||||||
if (musicType != MusicType.Local && IsRadioLink(query))
|
|
||||||
{
|
|
||||||
musicType = MusicType.Radio;
|
|
||||||
query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
switch (musicType)
|
|
||||||
{
|
|
||||||
case MusicType.Local:
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Uri = "\"" + Path.GetFullPath(query) + "\"",
|
|
||||||
Title = Path.GetFileNameWithoutExtension(query),
|
|
||||||
Provider = "Local File",
|
|
||||||
ProviderType = musicType,
|
|
||||||
Query = query,
|
|
||||||
});
|
|
||||||
case MusicType.Radio:
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Uri = query,
|
|
||||||
Title = $"{query}",
|
|
||||||
Provider = "Radio Stream",
|
|
||||||
ProviderType = musicType,
|
|
||||||
Query = query
|
|
||||||
})
|
|
||||||
{ TotalTime = TimeSpan.MaxValue };
|
|
||||||
}
|
|
||||||
var sc = SoundCloud.GetInstance(_creds);
|
|
||||||
if (SoundCloud.Default.IsSoundCloudLink(query))
|
|
||||||
{
|
|
||||||
var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false);
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Title = svideo.FullName,
|
|
||||||
Provider = "SoundCloud",
|
|
||||||
Uri = svideo.StreamLink,
|
|
||||||
ProviderType = musicType,
|
|
||||||
Query = svideo.TrackLink,
|
|
||||||
AlbumArt = svideo.artwork_url,
|
|
||||||
})
|
|
||||||
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (musicType == MusicType.Soundcloud)
|
|
||||||
{
|
|
||||||
var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false);
|
|
||||||
return new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Title = svideo.FullName,
|
|
||||||
Provider = "SoundCloud",
|
|
||||||
Uri = svideo.StreamLink,
|
|
||||||
ProviderType = MusicType.Soundcloud,
|
|
||||||
Query = svideo.TrackLink,
|
|
||||||
AlbumArt = svideo.artwork_url,
|
|
||||||
})
|
|
||||||
{ TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
|
|
||||||
}
|
|
||||||
|
|
||||||
var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault();
|
|
||||||
if (string.IsNullOrWhiteSpace(link))
|
|
||||||
throw new OperationCanceledException("Not a valid youtube query.");
|
|
||||||
var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
|
|
||||||
var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
|
|
||||||
var video = videos
|
|
||||||
.Where(v => v.AudioBitrate < 256)
|
|
||||||
.OrderByDescending(v => v.AudioBitrate)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (video == null) // do something with this error
|
|
||||||
throw new Exception("Could not load any video elements based on the query.");
|
|
||||||
var m = Regex.Match(query, @"\?t=(?<t>\d*)");
|
|
||||||
int gotoTime = 0;
|
|
||||||
if (m.Captures.Count > 0)
|
|
||||||
int.TryParse(m.Groups["t"].ToString(), out gotoTime);
|
|
||||||
var song = new Song(new SongInfo
|
|
||||||
{
|
|
||||||
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
|
||||||
Provider = "YouTube",
|
|
||||||
Uri = await video.GetUriAsync().ConfigureAwait(false),
|
|
||||||
Query = link,
|
|
||||||
ProviderType = musicType,
|
|
||||||
});
|
|
||||||
song.SkipTo = gotoTime;
|
|
||||||
return song;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_log.Warn($"Failed resolving the link.{ex.Message}");
|
|
||||||
_log.Warn(ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<string> HandleStreamContainers(string query)
|
|
||||||
{
|
|
||||||
string file = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var http = new HttpClient())
|
|
||||||
{
|
|
||||||
file = await http.GetStringAsync(query).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
if (query.Contains(".pls"))
|
|
||||||
{
|
|
||||||
//File1=http://armitunes.com:8000/
|
|
||||||
//Regex.Match(query)
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_log.Warn($"Failed reading .pls:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (query.Contains(".m3u"))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
# This is a comment
|
|
||||||
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
|
|
||||||
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
|
|
||||||
*/
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_log.Warn($"Failed reading .m3u:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (query.Contains(".asx"))
|
|
||||||
{
|
|
||||||
//<ref href="http://armitunes.com:8000"/>
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_log.Warn($"Failed reading .asx:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (query.Contains(".xspf"))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<playlist version="1" xmlns="http://xspf.org/ns/0/">
|
|
||||||
<trackList>
|
|
||||||
<track><location>file:///mp3s/song_1.mp3</location></track>
|
|
||||||
*/
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
|
|
||||||
var res = m.Groups["url"]?.ToString();
|
|
||||||
return res?.Trim();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_log.Warn($"Failed reading .xspf:\n{file}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsRadioLink(string query) =>
|
|
||||||
(query.StartsWith("http") ||
|
|
||||||
query.StartsWith("ww"))
|
|
||||||
&&
|
|
||||||
(query.Contains(".pls") ||
|
|
||||||
query.Contains(".m3u") ||
|
|
||||||
query.Contains(".asx") ||
|
|
||||||
query.Contains(".xspf"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,16 +6,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Music
|
||||||
{
|
{
|
||||||
public class SoundCloud
|
public class SoundCloudApiService
|
||||||
{
|
{
|
||||||
private readonly IBotCredentials _creds;
|
private readonly IBotCredentials _creds;
|
||||||
|
|
||||||
//todo make a service
|
public SoundCloudApiService(IBotCredentials creds)
|
||||||
private static SoundCloud _instance = null;
|
|
||||||
public static SoundCloud GetInstance(IBotCredentials creds) => _instance ?? (_instance = new SoundCloud(creds));
|
|
||||||
|
|
||||||
static SoundCloud() { }
|
|
||||||
public SoundCloud(IBotCredentials creds)
|
|
||||||
{
|
{
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
}
|
}
|
||||||
@ -36,7 +31,7 @@ namespace NadekoBot.Services.Music
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var responseObj = Newtonsoft.Json.JsonConvert.DeserializeObject<SoundCloudVideo>(response);
|
var responseObj = JsonConvert.DeserializeObject<SoundCloudVideo>(response);
|
||||||
if (responseObj?.Kind != "track")
|
if (responseObj?.Kind != "track")
|
||||||
throw new InvalidOperationException("Url is either not a track, or it doesn't exist.");
|
throw new InvalidOperationException("Url is either not a track, or it doesn't exist.");
|
||||||
|
|
||||||
@ -84,7 +79,7 @@ namespace NadekoBot.Services.Music
|
|||||||
}
|
}
|
||||||
public class SoundCloudUser
|
public class SoundCloudUser
|
||||||
{
|
{
|
||||||
[Newtonsoft.Json.JsonProperty("username")]
|
[JsonProperty("username")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
}
|
}
|
||||||
/*
|
/*
|
Loading…
Reference in New Issue
Block a user