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)]
|
||||
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)
|
||||
{
|
||||
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 =>
|
||||
c.Aliases.Select(a => a.ToUpperInvariant()).Contains(input));
|
||||
if (cmd == null)
|
||||
|
@ -1,81 +1,23 @@
|
||||
using Discord.Commands;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System.Linq;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Threading;
|
||||
using NadekoBot.Services.ClashOfClans;
|
||||
|
||||
namespace NadekoBot.Modules.ClashOfClans
|
||||
{
|
||||
[NadekoModule("ClashOfClans", ",")]
|
||||
public class ClashOfClans : NadekoTopLevelModule
|
||||
{
|
||||
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
|
||||
private readonly ClashOfClansService _service;
|
||||
|
||||
private static Timer checkWarTimer { get; }
|
||||
|
||||
static ClashOfClans()
|
||||
public ClashOfClans(ClashOfClansService service)
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
{
|
||||
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 { }
|
||||
}
|
||||
}
|
||||
_service = service;
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -92,18 +34,18 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
return;
|
||||
}
|
||||
List<ClashWar> wars;
|
||||
if (!ClashWars.TryGetValue(Context.Guild.Id, out wars))
|
||||
if (!_service.ClashWars.TryGetValue(Context.Guild.Id, out wars))
|
||||
{
|
||||
wars = new List<ClashWar>();
|
||||
if (!ClashWars.TryAdd(Context.Guild.Id, wars))
|
||||
if (!_service.ClashWars.TryAdd(Context.Guild.Id, wars))
|
||||
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);
|
||||
await ReplyErrorLocalized("war_created", cw.ShortPrint()).ConfigureAwait(false);
|
||||
await ReplyErrorLocalized("war_created", _service.ShortPrint(cw)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
@ -113,7 +55,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
int num = 0;
|
||||
int.TryParse(number, out num);
|
||||
|
||||
var warsInfo = GetWarInfo(Context.Guild, num);
|
||||
var warsInfo = _service.GetWarInfo(Context.Guild, num);
|
||||
if (warsInfo == null)
|
||||
{
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
@ -123,13 +65,13 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
try
|
||||
{
|
||||
war.Start();
|
||||
await ReplyConfirmLocalized("war_started", war.ShortPrint()).ConfigureAwait(false);
|
||||
await ReplyConfirmLocalized("war_started", _service.ShortPrint(war)).ConfigureAwait(false);
|
||||
}
|
||||
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]
|
||||
@ -142,7 +84,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
{
|
||||
//check if there are any wars
|
||||
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)
|
||||
{
|
||||
await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false);
|
||||
@ -163,21 +105,21 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
var num = 0;
|
||||
int.TryParse(number, out num);
|
||||
//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)
|
||||
{
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
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]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
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)
|
||||
{
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
@ -190,9 +132,9 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
try
|
||||
{
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
war.Call(usr, baseNumber - 1);
|
||||
SaveWar(war);
|
||||
await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, war.ShortPrint()).ConfigureAwait(false);
|
||||
_service.Call(war, usr, baseNumber - 1);
|
||||
_service.SaveWar(war);
|
||||
await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, _service.ShortPrint(war)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -225,7 +167,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task EndWar(int number)
|
||||
{
|
||||
var warsInfo = GetWarInfo(Context.Guild, number);
|
||||
var warsInfo = _service.GetWarInfo(Context.Guild, number);
|
||||
if (warsInfo == null)
|
||||
{
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
@ -233,8 +175,8 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
}
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
war.End();
|
||||
SaveWar(war);
|
||||
await ReplyConfirmLocalized("war_ended", warsInfo.Item1[warsInfo.Item2].ShortPrint()).ConfigureAwait(false);
|
||||
_service.SaveWar(war);
|
||||
await ReplyConfirmLocalized("war_ended", _service.ShortPrint(warsInfo.Item1[warsInfo.Item2])).ConfigureAwait(false);
|
||||
|
||||
warsInfo.Item1.RemoveAt(warsInfo.Item2);
|
||||
}
|
||||
@ -243,7 +185,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
[RequireContext(ContextType.Guild)]
|
||||
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)
|
||||
{
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
@ -256,9 +198,9 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
try
|
||||
{
|
||||
var war = warsInfo.Item1[warsInfo.Item2];
|
||||
var baseNumber = war.Uncall(usr);
|
||||
SaveWar(war);
|
||||
await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, war.ShortPrint()).ConfigureAwait(false);
|
||||
var baseNumber = _service.Uncall(war, usr);
|
||||
_service.SaveWar(war);
|
||||
await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, _service.ShortPrint(war)).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -268,7 +210,7 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
|
||||
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)
|
||||
{
|
||||
await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
|
||||
@ -279,87 +221,19 @@ namespace NadekoBot.Modules.ClashOfClans
|
||||
{
|
||||
if (baseNumber == -1)
|
||||
{
|
||||
baseNumber = war.FinishClaim(Context.User.Username, stars);
|
||||
SaveWar(war);
|
||||
baseNumber = _service.FinishClaim(war, Context.User.Username, stars);
|
||||
_service.SaveWar(war);
|
||||
}
|
||||
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)
|
||||
{
|
||||
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 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", ".")]
|
||||
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]
|
||||
public async Task AddCustReact(string key, [Remainder] string message)
|
||||
{
|
||||
@ -131,7 +46,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
Response = message,
|
||||
};
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.CustomReactions.Add(cr);
|
||||
|
||||
@ -309,7 +224,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
|
||||
var success = false;
|
||||
CustomReaction toDelete;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
toDelete = uow.CustomReactions.Get(id);
|
||||
if (toDelete == null) //not found
|
||||
@ -381,7 +296,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
|
||||
var setValue = reaction.DmResponse = !reaction.DmResponse;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.CustomReactions.Get(id).DmResponse = setValue;
|
||||
uow.Complete();
|
||||
@ -432,7 +347,7 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
|
||||
var setValue = reaction.AutoDeleteTrigger = !reaction.AutoDeleteTrigger;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.CustomReactions.Get(id).AutoDeleteTrigger = setValue;
|
||||
uow.Complete();
|
||||
|
@ -9,19 +9,27 @@ using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Modules.Help
|
||||
{
|
||||
[NadekoModule("Help", "-")]
|
||||
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 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]
|
||||
public async Task Modules()
|
||||
@ -30,8 +38,9 @@ namespace NadekoBot.Modules.Help
|
||||
.WithFooter(efb => efb.WithText("ℹ️" + GetText("modules_footer", Prefix)))
|
||||
.WithTitle(GetText("list_of_modules"))
|
||||
.WithDescription(string.Join("\n",
|
||||
NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule())
|
||||
.Where(m => !Permissions.Permissions.GlobalPermissionCommands.BlockedModules.Contains(m.Key.Name.ToLowerInvariant()))
|
||||
_cmds.Modules.GroupBy(m => m.GetTopLevelModule())
|
||||
//todo perms
|
||||
//.Where(m => !Permissions.Permissions.GlobalPermissionCommands.BlockedModules.Contains(m.Key.Name.ToLowerInvariant()))
|
||||
.Select(m => "• " + m.Key.Name)
|
||||
.OrderBy(s => s)));
|
||||
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
|
||||
@ -45,8 +54,9 @@ namespace NadekoBot.Modules.Help
|
||||
module = module?.Trim().ToUpperInvariant();
|
||||
if (string.IsNullOrWhiteSpace(module))
|
||||
return;
|
||||
var cmds = NadekoBot.CommandService.Commands.Where(c => c.Module.GetTopLevelModule().Name.ToUpperInvariant().StartsWith(module))
|
||||
.Where(c => !Permissions.Permissions.GlobalPermissionCommands.BlockedCommands.Contains(c.Aliases.First().ToLowerInvariant()))
|
||||
var cmds = _cmds.Commands.Where(c => c.Module.GetTopLevelModule().Name.ToUpperInvariant().StartsWith(module))
|
||||
//todo perms
|
||||
//.Where(c => !Permissions.Permissions.GlobalPermissionCommands.BlockedCommands.Contains(c.Aliases.First().ToLowerInvariant()))
|
||||
.OrderBy(c => c.Aliases.First())
|
||||
.Distinct(new CommandTextEqualityComparer())
|
||||
.AsEnumerable();
|
||||
@ -62,36 +72,33 @@ namespace NadekoBot.Modules.Help
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[NadekoCommand, Usage, Description, Aliases]
|
||||
public async Task H([Remainder] string comToFind = null)
|
||||
public async Task H([Remainder] CommandInfo com = null)
|
||||
{
|
||||
var channel = Context.Channel;
|
||||
|
||||
comToFind = comToFind?.ToLowerInvariant();
|
||||
if (string.IsNullOrWhiteSpace(comToFind))
|
||||
if (com == null)
|
||||
{
|
||||
IMessageChannel ch = channel is ITextChannel ? await ((IGuildUser)Context.User).CreateDMChannelAsync() : channel;
|
||||
await ch.SendMessageAsync(HelpString).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var com = NadekoBot.CommandService.Commands.FirstOrDefault(c => c.Aliases.Select(a=>a.ToLowerInvariant()).Contains(comToFind));
|
||||
|
||||
if (com == null)
|
||||
{
|
||||
await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
|
||||
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();
|
||||
if (alias != null)
|
||||
str += string.Format(" **/ `{0}`**", alias);
|
||||
str += string.Format(" **/ `{0}`**", Prefix + alias);
|
||||
var embed = new EmbedBuilder()
|
||||
.AddField(fb => fb.WithName(str).WithValue($"{com.RealSummary()} {GetCommandRequirements(com)}").WithIsInline(true))
|
||||
.AddField(fb => fb.WithName(GetText("usage")).WithValue(com.RealRemarks()).WithIsInline(false))
|
||||
@ -122,7 +129,7 @@ namespace NadekoBot.Modules.Help
|
||||
var helpstr = new StringBuilder();
|
||||
helpstr.AppendLine(GetText("cmdlist_donate", PatreonUrl, PaypalUrl) + "\n");
|
||||
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)
|
||||
.Distinct()
|
||||
.OrderBy(m => m)
|
||||
@ -130,7 +137,7 @@ namespace NadekoBot.Modules.Help
|
||||
.Select(m => string.Format("- [{0}](#{1})", m, m.ToLowerInvariant()))));
|
||||
helpstr.AppendLine();
|
||||
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();
|
||||
if (module.Name != lastModule)
|
||||
@ -147,10 +154,9 @@ namespace NadekoBot.Modules.Help
|
||||
lastModule = module.Name;
|
||||
}
|
||||
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" +
|
||||
$" {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} |" +
|
||||
$" {string.Format(com.Remarks, com.Module.GetPrefix())}");
|
||||
$" {string.Format(com.Summary, NadekoBot.Prefix)} {GetCommandRequirements(com)} |" +
|
||||
$" {string.Format(com.Remarks, NadekoBot.Prefix)}");
|
||||
}
|
||||
helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName");
|
||||
File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
|
||||
await ReplyConfirmLocalized("commandlist_regen").ConfigureAwait(false);
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Modules.Music.Classes;
|
||||
using System.Collections.Concurrent;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Services;
|
||||
using System.IO;
|
||||
@ -19,26 +17,28 @@ using NadekoBot.Services.Music;
|
||||
|
||||
namespace NadekoBot.Modules.Music
|
||||
{
|
||||
[NadekoModule("Music", "!!")]
|
||||
[DontAutoLoad]
|
||||
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
|
||||
try { Directory.Delete(MusicDataPath, true); } catch { }
|
||||
|
||||
NadekoBot.Client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||
|
||||
Directory.CreateDirectory(MusicDataPath);
|
||||
|
||||
//todo move to service
|
||||
music = NadekoBot.MusicService;
|
||||
_client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||
}
|
||||
|
||||
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;
|
||||
if (usr == null ||
|
||||
@ -46,14 +46,14 @@ namespace NadekoBot.Modules.Music
|
||||
return Task.CompletedTask;
|
||||
|
||||
MusicPlayer player;
|
||||
if ((player = music.GetPlayer(usr.Guild.Id)) == null)
|
||||
if ((player = _music.GetPlayer(usr.Guild.Id)) == null)
|
||||
return Task.CompletedTask;
|
||||
|
||||
try
|
||||
{
|
||||
//if bot moved
|
||||
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
|
||||
player.TogglePause();
|
||||
@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Music
|
||||
return Task.CompletedTask;
|
||||
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return Task.CompletedTask;
|
||||
if (musicPlayer.PlaybackVoiceChannel == ((IGuildUser)Context.User).VoiceChannel)
|
||||
{
|
||||
@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Music
|
||||
public Task Stop()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return Task.CompletedTask;
|
||||
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
||||
{
|
||||
@ -125,10 +125,10 @@ namespace NadekoBot.Modules.Music
|
||||
public Task Destroy()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return Task.CompletedTask;
|
||||
if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel)
|
||||
music.DestroyPlayer(Context.Guild.Id);
|
||||
_music.DestroyPlayer(Context.Guild.Id);
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
||||
@ -139,7 +139,7 @@ namespace NadekoBot.Modules.Music
|
||||
public Task Pause()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return Task.CompletedTask;
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return Task.CompletedTask;
|
||||
@ -153,7 +153,7 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return;
|
||||
@ -173,7 +173,7 @@ namespace NadekoBot.Modules.Music
|
||||
[RequireContext(ContextType.Guild)]
|
||||
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)
|
||||
{
|
||||
Context.Message.DeleteAfter(10);
|
||||
@ -184,7 +184,7 @@ namespace NadekoBot.Modules.Music
|
||||
[RequireContext(ContextType.Guild)]
|
||||
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)
|
||||
{
|
||||
Context.Message.DeleteAfter(10);
|
||||
@ -197,7 +197,7 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
Song currentSong;
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
if ((currentSong = musicPlayer?.CurrentSong) == null)
|
||||
{
|
||||
@ -250,7 +250,7 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
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]
|
||||
@ -258,7 +258,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task NowPlaying()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
var currentSong = musicPlayer.CurrentSong;
|
||||
if (currentSong == null)
|
||||
@ -279,7 +279,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task Volume(int val)
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return;
|
||||
@ -301,7 +301,7 @@ namespace NadekoBot.Modules.Music
|
||||
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
|
||||
uow.Complete();
|
||||
@ -314,7 +314,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task ShufflePlaylist()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return;
|
||||
@ -338,13 +338,13 @@ namespace NadekoBot.Modules.Music
|
||||
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
|
||||
var plId = (await _google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
|
||||
if (plId == null)
|
||||
{
|
||||
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
|
||||
var ids = await _google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
|
||||
if (!ids.Any())
|
||||
{
|
||||
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
|
||||
@ -366,7 +366,7 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
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 { try { cancelSource.Cancel(); } catch { } }
|
||||
@ -391,11 +391,11 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
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[]>();
|
||||
await music.QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, scvids[0].TrackLink).ConfigureAwait(false);
|
||||
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);
|
||||
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
|
||||
foreach (var svideo in scvids.Skip(1))
|
||||
@ -406,7 +406,7 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
Title = svideo.FullName,
|
||||
Provider = "SoundCloud",
|
||||
Uri = svideo.StreamLink,
|
||||
Uri = svideo.GetStreamLink(_creds),
|
||||
ProviderType = MusicType.Normal,
|
||||
Query = svideo.TrackLink,
|
||||
}), ((IGuildUser)Context.User).Username);
|
||||
@ -433,7 +433,7 @@ namespace NadekoBot.Modules.Music
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -457,7 +457,7 @@ namespace NadekoBot.Modules.Music
|
||||
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
|
||||
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)
|
||||
{
|
||||
Context.Message.DeleteAfter(10);
|
||||
@ -473,7 +473,7 @@ namespace NadekoBot.Modules.Music
|
||||
var arg = path;
|
||||
if (string.IsNullOrWhiteSpace(arg))
|
||||
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)
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return Task.CompletedTask;
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return Task.CompletedTask;
|
||||
@ -512,7 +512,7 @@ namespace NadekoBot.Modules.Music
|
||||
if (all.Trim().ToUpperInvariant() != "ALL")
|
||||
return;
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
musicPlayer.ClearQueue();
|
||||
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
|
||||
@ -526,7 +526,7 @@ namespace NadekoBot.Modules.Music
|
||||
return;
|
||||
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
|
||||
fromto = fromto?.Trim();
|
||||
@ -569,7 +569,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task SetMaxQueue(uint size = 0)
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
|
||||
musicPlayer.MaxQueueSize = size;
|
||||
@ -589,7 +589,7 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
var channel = (ITextChannel)Context.Channel;
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
musicPlayer.MaxPlaytimeSeconds = seconds;
|
||||
if (seconds == 0)
|
||||
@ -603,7 +603,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task ReptCurSong()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
var currentSong = musicPlayer.CurrentSong;
|
||||
if (currentSong == null)
|
||||
@ -626,7 +626,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task RepeatPl()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
var currentValue = musicPlayer.ToggleRepeatPlaylist();
|
||||
if(currentValue)
|
||||
@ -640,7 +640,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task Save([Remainder] string name)
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
|
||||
var curSong = musicPlayer.CurrentSong;
|
||||
@ -655,7 +655,7 @@ namespace NadekoBot.Modules.Music
|
||||
}).ToList();
|
||||
|
||||
MusicPlaylist playlist;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
playlist = new MusicPlaylist
|
||||
{
|
||||
@ -679,7 +679,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task Load([Remainder] int id)
|
||||
{
|
||||
MusicPlaylist mpl;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
mpl = uow.MusicPlaylists.GetWithSongs(id);
|
||||
}
|
||||
@ -696,7 +696,7 @@ namespace NadekoBot.Modules.Music
|
||||
var usr = (IGuildUser)Context.User;
|
||||
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 { break; }
|
||||
@ -714,7 +714,7 @@ namespace NadekoBot.Modules.Music
|
||||
|
||||
List<MusicPlaylist> playlists;
|
||||
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
playlists = uow.MusicPlaylists.GetPlaylistsOnPage(num);
|
||||
}
|
||||
@ -735,13 +735,13 @@ namespace NadekoBot.Modules.Music
|
||||
var success = false;
|
||||
try
|
||||
{
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var pl = uow.MusicPlaylists.Get(id);
|
||||
|
||||
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);
|
||||
await uow.CompleteAsync().ConfigureAwait(false);
|
||||
@ -766,7 +766,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task Goto(int time)
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
|
||||
return;
|
||||
@ -801,7 +801,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task Autoplay()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
return;
|
||||
|
||||
if (!musicPlayer.ToggleAutoplay())
|
||||
@ -816,7 +816,7 @@ namespace NadekoBot.Modules.Music
|
||||
public async Task SetMusicChannel()
|
||||
{
|
||||
MusicPlayer musicPlayer;
|
||||
if ((musicPlayer = music.GetPlayer(Context.Guild.Id)) == null)
|
||||
if ((musicPlayer = _music.GetPlayer(Context.Guild.Id)) == null)
|
||||
{
|
||||
await ReplyErrorLocalized("no_player").ConfigureAwait(false);
|
||||
return;
|
||||
|
@ -15,7 +15,6 @@ using NadekoBot.Services.Searches;
|
||||
|
||||
namespace NadekoBot.Modules.NSFW
|
||||
{
|
||||
[NadekoModule("NSFW")]
|
||||
public class NSFW : NadekoTopLevelModule
|
||||
{
|
||||
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 LowerModuleTypeName;
|
||||
|
||||
|
||||
//todo :thinking:
|
||||
public NadekoStrings _strings { get; set; }
|
||||
public ILocalization _localization { get; set; }
|
||||
|
@ -20,7 +20,6 @@ using System.Diagnostics;
|
||||
|
||||
namespace NadekoBot.Modules.Utility
|
||||
{
|
||||
[NadekoModule("Utility")]
|
||||
public partial class Utility : NadekoTopLevelModule
|
||||
{
|
||||
private static ConcurrentDictionary<ulong, Timer> _rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
|
||||
|
@ -18,6 +18,8 @@ using NadekoBot.Services.Database.Models;
|
||||
using System.Threading;
|
||||
using NadekoBot.Modules.Utility;
|
||||
using NadekoBot.Services.Searches;
|
||||
using NadekoBot.Services.ClashOfClans;
|
||||
using NadekoBot.Services.Music;
|
||||
|
||||
namespace NadekoBot
|
||||
{
|
||||
@ -75,7 +77,6 @@ namespace NadekoBot
|
||||
var greetSettingsService = new GreetSettingsService(AllGuildConfigs, 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()
|
||||
{
|
||||
@ -89,9 +90,15 @@ namespace NadekoBot
|
||||
|
||||
var images = new ImagesService();
|
||||
|
||||
var currencyHandler = new CurrencyHandler(BotConfig, db);
|
||||
|
||||
var soundcloud = new SoundCloudApiService(credentials);
|
||||
|
||||
//module services
|
||||
var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db);
|
||||
var searchesService = new SearchesService();
|
||||
var clashService = new ClashOfClansService(Client, db, localization, strings);
|
||||
var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials);
|
||||
|
||||
//initialize Services
|
||||
Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces
|
||||
@ -105,12 +112,15 @@ namespace NadekoBot
|
||||
.Add<NadekoStrings>(strings)
|
||||
.Add<DiscordShardedClient>(Client)
|
||||
.Add<GreetSettingsService>(greetSettingsService)
|
||||
//.Add(musicService)
|
||||
.Add<BotConfig>(BotConfig)
|
||||
.Add<CurrencyHandler>(currencyHandler)
|
||||
.Add(musicService)
|
||||
.Add<CommandHandler>(commandHandler)
|
||||
.Add<DbHandler>(db)
|
||||
//modules
|
||||
.Add<UtilityService>(utilityService)
|
||||
.Add<SearchesService>(searchesService)
|
||||
.Add<ClashOfClansService>(clashService)
|
||||
.Build();
|
||||
|
||||
commandHandler.AddServices(Services);
|
||||
|
@ -29,35 +29,26 @@
|
||||
<ItemGroup>
|
||||
<Compile Remove="data\**\*;credentials.json;credentials_example.json" />
|
||||
<Compile Remove="Modules\Administration\**" />
|
||||
<Compile Remove="Modules\ClashOfClans\**" />
|
||||
<Compile Remove="Modules\CustomReactions\**" />
|
||||
<Compile Remove="Modules\Gambling\**" />
|
||||
<Compile Remove="Modules\Games\**" />
|
||||
<Compile Remove="Modules\Help\**" />
|
||||
<Compile Remove="Modules\Music\**" />
|
||||
<Compile Remove="Modules\NSFW\**" />
|
||||
<Compile Remove="Modules\Permissions\**" />
|
||||
<Compile Remove="Modules\Searches\**" />
|
||||
<Compile Remove="Services\Music\**" />
|
||||
<EmbeddedResource Remove="Modules\Administration\**" />
|
||||
<EmbeddedResource Remove="Modules\ClashOfClans\**" />
|
||||
<EmbeddedResource Remove="Modules\CustomReactions\**" />
|
||||
<EmbeddedResource Remove="Modules\Gambling\**" />
|
||||
<EmbeddedResource Remove="Modules\Games\**" />
|
||||
<EmbeddedResource Remove="Modules\Help\**" />
|
||||
<EmbeddedResource Remove="Modules\Music\**" />
|
||||
<EmbeddedResource Remove="Modules\NSFW\**" />
|
||||
<EmbeddedResource Remove="Modules\Permissions\**" />
|
||||
<EmbeddedResource Remove="Modules\Searches\**" />
|
||||
<EmbeddedResource Remove="Services\Music\**" />
|
||||
<None Remove="Modules\Administration\**" />
|
||||
<None Remove="Modules\ClashOfClans\**" />
|
||||
<None Remove="Modules\CustomReactions\**" />
|
||||
<None Remove="Modules\Gambling\**" />
|
||||
<None Remove="Modules\Games\**" />
|
||||
<None Remove="Modules\Help\**" />
|
||||
<None Remove="Modules\Music\**" />
|
||||
<None Remove="Modules\NSFW\**" />
|
||||
<None Remove="Modules\Permissions\**" />
|
||||
<None Remove="Modules\Searches\**" />
|
||||
<None Remove="Services\Music\**" />
|
||||
<None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
@ -105,4 +96,8 @@
|
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
|
||||
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Modules\Music\Classes\" />
|
||||
</ItemGroup>
|
||||
</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.WebSocket;
|
||||
using NadekoBot.DataStructures;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.CustomReactions
|
||||
{
|
||||
@ -103,5 +102,19 @@ namespace NadekoBot.Modules.CustomReactions
|
||||
|
||||
public static string ResponseWithContext(this CustomReaction cr, IUserMessage 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 enum DestroyStars
|
||||
{
|
||||
One, Two, Three
|
||||
}
|
||||
public enum StateOfWar
|
||||
{
|
||||
Started, Ended, Created
|
||||
}
|
||||
|
||||
public string EnemyClan { get; set; }
|
||||
public int Size { get; set; }
|
||||
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 enum DestroyStars
|
||||
{
|
||||
One, Two, Three
|
||||
}
|
||||
public enum StateOfWar
|
||||
{
|
||||
Started, Ended, Created
|
||||
}
|
||||
}
|
||||
|
@ -8,12 +8,10 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NadekoBot.Services.Music;
|
||||
using NadekoBot.Services;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Services.Music
|
||||
{
|
||||
|
||||
public enum StreamState
|
||||
{
|
||||
Resolving,
|
||||
@ -24,7 +22,6 @@ namespace NadekoBot.Services.Music
|
||||
|
||||
public class MusicPlayer
|
||||
{
|
||||
public const string MusicDataPath = "data/musicdata";
|
||||
private IAudioClient AudioClient { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -6,23 +6,45 @@ using Discord;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules;
|
||||
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
|
||||
{
|
||||
public class MusicService
|
||||
{
|
||||
public const string MusicDataPath = "data/musicdata";
|
||||
|
||||
private readonly IGoogleApiService _google;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly ILocalization _localization;
|
||||
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 MusicService(IGoogleApiService google, NadekoStrings strings, ILocalization localization)
|
||||
public MusicService(IGoogleApiService google,
|
||||
NadekoStrings strings, ILocalization localization, DbHandler db,
|
||||
SoundCloudApiService sc, IBotCredentials creds)
|
||||
{
|
||||
_google = google;
|
||||
_strings = strings;
|
||||
_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)
|
||||
@ -39,7 +61,7 @@ namespace NadekoBot.Services.Music
|
||||
return MusicPlayers.GetOrAdd(guildId, server =>
|
||||
{
|
||||
float vol;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
|
||||
using (var uow = DbHandler.UnitOfWork())
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
//todo move to cached variable
|
||||
vol = uow.GuildConfigs.For(guildId, set => set).DefaultMusicVolume;
|
||||
@ -168,7 +190,7 @@ namespace NadekoBot.Services.Music
|
||||
try
|
||||
{
|
||||
musicPlayer.ThrowIfQueueFull();
|
||||
resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false);
|
||||
resolvedSong = await ResolveSong(query, musicType).ConfigureAwait(false);
|
||||
|
||||
if (resolvedSong == null)
|
||||
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.Net;
|
||||
using Discord;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
|
||||
namespace NadekoBot.Services.Music
|
||||
{
|
||||
@ -144,7 +145,7 @@ namespace NadekoBot.Services.Music
|
||||
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
||||
{
|
||||
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 bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
|
||||
|
@ -1,214 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using VideoLibrary;
|
||||
using NLog;
|
||||
|
||||
namespace NadekoBot.Services.Music
|
||||
{
|
||||
public static class SongHandler
|
||||
{
|
||||
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
|
||||
{
|
||||
public class SoundCloud
|
||||
public class SoundCloudApiService
|
||||
{
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
//todo make a service
|
||||
private static SoundCloud _instance = null;
|
||||
public static SoundCloud GetInstance(IBotCredentials creds) => _instance ?? (_instance = new SoundCloud(creds));
|
||||
|
||||
static SoundCloud() { }
|
||||
public SoundCloud(IBotCredentials creds)
|
||||
public SoundCloudApiService(IBotCredentials 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")
|
||||
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
|
||||
{
|
||||
[Newtonsoft.Json.JsonProperty("username")]
|
||||
[JsonProperty("username")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
/*
|
Loading…
Reference in New Issue
Block a user