Clash of clans organizer. M3u support fix. Better strings on a few places.

This commit is contained in:
Master Kwoth 2016-02-16 21:11:27 +01:00
parent 643f8590a7
commit 5fa81c1810
6 changed files with 346 additions and 6 deletions

View File

@ -0,0 +1,338 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using System.Collections.Concurrent;
using Discord;
using System.Threading;
namespace NadekoBot.Commands {
class ClashOfClans : DiscordCommand {
private static string prefix = ",";
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
private object writeLock { get; } = new object();
public ClashOfClans() : base() {
}
public override Func<CommandEventArgs, Task> DoFunc() => async e => {
if (!e.User.ServerPermissions.ManageChannels)
return;
List<ClashWar> wars;
if (!ClashWars.TryGetValue(e.Server.Id, out wars)) {
wars = new List<ClashWar>();
if (!ClashWars.TryAdd(e.Server.Id, wars))
return;
}
string enemyClan = e.GetArg("enemy_clan");
if (string.IsNullOrWhiteSpace(enemyClan)) {
return;
}
int size;
if (!int.TryParse(e.GetArg("size"), out size) || size < 10 || size > 50 || size % 5 != 0) {
await e.Channel.SendMessage("💢🔰 Not a Valid war size");
return;
}
var cw = new ClashWar(enemyClan, size, e);
cw.Start();
wars.Add(cw);
cw.OnUserTimeExpired += async (u) => {
await e.Channel.SendMessage($"❗🔰**Claim from {u.Mention} for a war against {cw.ShortPrint()} has expired.**");
};
cw.OnWarEnded += async () => {
await e.Channel.SendMessage($"❗🔰**War against {cw.ShortPrint()} ended.**");
};
await e.Channel.SendMessage($"❗🔰**STARTED** `{size} v {size}` **CLAN WAR AGAINST** `{enemyClan}`");
//war with the index X started.
};
public override void Init(CommandGroupBuilder cgb) {
cgb.CreateCommand(prefix + "startwar")
.Alias(prefix + "sw")
.Description($"Starts a new war by specifying a size (>10 and multiple of 5) and enemy clan name. War ends in 23 hours. You need manage channels permission to use this.\n**Usage**:{prefix}sw 15 The Enemy Clan")
.Parameter("size")
.Parameter("enemy_clan", ParameterType.Unparsed)
.Do(DoFunc());
cgb.CreateCommand(prefix + "listwar")
.Alias(prefix + "lw")
.Description($"Shows the active war claims by a number. Shows all wars in a short way if no number is specified.\n**Usage**: {prefix}lw [war_number] or {prefix}lw")
.Parameter("number", ParameterType.Optional)
.Do(async e => {
// if number is null, print all wars in a short way
if (string.IsNullOrWhiteSpace(e.GetArg("number"))) {
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(e.Server.Id, out wars);
if (wars == null || wars.Count == 0) {
await e.Channel.SendMessage("🔰 **No active wars.**");
return;
}
var sb = new StringBuilder();
sb.AppendLine("🔰 **LIST OF ACTIVE WARS**");
sb.AppendLine("**-------------------------**");
for (int i = 0; i < wars.Count; i++) {
sb.AppendLine($"**#{i + 1}.** `Enemy:` **{wars[i].EnemyClan}**");
sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**");
sb.AppendLine("**-------------------------**");
}
await e.Channel.SendMessage(sb.ToString());
return;
}
//if number is not null, print the war needed
var warsInfo = GetInfo(e);
if (warsInfo == null) {
await e.Channel.SendMessage("💢🔰 **That war does not exist.**");
return;
}
await e.Channel.SendMessage(warsInfo.Item1[warsInfo.Item2].ToString());
});
cgb.CreateCommand(prefix + "claim")
.Alias(prefix + "call")
.Alias(prefix + "c")
.Description($"Claims a certain base from a certain war.\n**Usage**: {prefix}call [war_number] [base_number]")
.Parameter("number")
.Parameter("baseNumber")
.Do(async e => {
var warsInfo = GetInfo(e);
if (warsInfo == null || warsInfo.Item1.Count == 0) {
await e.Channel.SendMessage("💢🔰 **That war does not exist.**");
return;
}
int baseNum;
if (!int.TryParse(e.GetArg("baseNumber"), out baseNum)) {
await e.Channel.SendMessage("💢🔰 **Invalid base number.**");
return;
}
try {
var war = warsInfo.Item1[warsInfo.Item2];
await war.Call(e.User, baseNum - 1);
await e.Channel.SendMessage($"🔰{e.User.Mention} claimed a base #{baseNum} for a war against {war.ShortPrint()}");
}
catch (Exception ex) {
await e.Channel.SendMessage($"💢🔰 {ex.Message}");
}
});
cgb.CreateCommand(prefix + "cf")
.Alias(prefix + "claimfinish")
.Description($"Finish your claim if you destroyed a base.\n**Usage**: {prefix}cf [war_number]")
.Parameter("number", ParameterType.Required)
.Do(async e => {
var warInfo = GetInfo(e);
if (warInfo == null || warInfo.Item1.Count == 0) {
await e.Channel.SendMessage("💢🔰 **That war does not exist.**");
return;
}
var war = warInfo.Item1[warInfo.Item2];
try {
var baseNum = war.FinishClaim(e.User);
await e.Channel.SendMessage($"❗🔰{e.User.Mention} **DESTROYED** a base #{baseNum} in a war against {war.ShortPrint()}");
}
catch (Exception ex) {
await e.Channel.SendMessage($"💢🔰 {ex.Message}");
}
});
cgb.CreateCommand(prefix + "unclaim")
.Alias(prefix + "uncall")
.Alias(prefix + "uc")
.Description($"Removes your claim from a certain war.\n**Usage**: {prefix}uc [war_number] [base_number]")
.Parameter("number", ParameterType.Required)
.Do(async e => {
var warsInfo = GetInfo(e);
if (warsInfo == null || warsInfo.Item1.Count == 0) {
await e.Channel.SendMessage("💢🔰 **That war does not exist.**");
return;
}
try {
var war = warsInfo.Item1[warsInfo.Item2];
int baseNumber = war.Uncall(e.User);
await e.Channel.SendMessage($"🔰 {e.User.Mention} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}");
}
catch (Exception ex) {
await e.Channel.SendMessage($"💢🔰 {ex.Message}");
}
});
cgb.CreateCommand(prefix + "endwar")
.Alias(prefix + "ew")
.Description($"Ends the war with a given index.\n**Usage**:{prefix}ew [war_number]")
.Parameter("number")
.Do(async e => {
if (!e.User.ServerPermissions.ManageChannels)
return;
var warsInfo = GetInfo(e);
if (warsInfo == null) {
await e.Channel.SendMessage("💢🔰 That war does not exist.");
return;
}
warsInfo.Item1[warsInfo.Item2].End();
int size = warsInfo.Item1[warsInfo.Item2].Size;
warsInfo.Item1.RemoveAt(warsInfo.Item2);
});
}
private Tuple<List<ClashWar>, int> GetInfo(CommandEventArgs e) {
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(e.Server.Id, out wars);
if (wars == null || wars.Count == 0) {
return null;
}
// get the number of the war
int num;
if (string.IsNullOrWhiteSpace(e.GetArg("number")))
num = 0;
else if (!int.TryParse(e.GetArg("number"), out num) || num > wars.Count) {
return null;
}
num -= 1;
//get the actual war
return new Tuple<List<ClashWar>, int>(wars, num);
}
}
internal class Caller {
private User _user;
public User CallUser
{
get { return _user; }
set { _user = value; }
}
private DateTime timeAdded;
public DateTime TimeAdded
{
get { return timeAdded; }
set { timeAdded = value; }
}
public bool BaseDestroyed { get; internal set; }
}
internal class ClashWar {
public static TimeSpan callExpire => new TimeSpan(2, 0, 0);
private CommandEventArgs e;
private string enemyClan;
public string EnemyClan => enemyClan;
private int size;
public int Size => size;
private Caller[] bases;
private CancellationTokenSource[] baseCancelTokens;
private CancellationTokenSource endTokenSource = new CancellationTokenSource();
public Action<User> OnUserTimeExpired { get; set; } = null;
public Action OnWarEnded { get; set; } = null;
public ClashWar(string enemyClan, int size, CommandEventArgs e) {
this.enemyClan = enemyClan;
this.size = size;
this.bases = new Caller[size];
this.baseCancelTokens = new CancellationTokenSource[size];
}
internal void End() {
if (!endTokenSource.Token.IsCancellationRequested) {
endTokenSource.Cancel();
if (OnWarEnded != null)
OnWarEnded();
}
}
internal async Task Call(User u, int baseNumber) {
if (baseNumber < 0 || baseNumber >= bases.Length)
throw new ArgumentException("Invalid base number");
if (bases[baseNumber] != null)
throw new ArgumentException("That base is already claimed.");
for (int i = 0; i < bases.Length; i++) {
if (bases[i]?.BaseDestroyed == false && bases[i]?.CallUser == u)
throw new ArgumentException($"💢 {u.Mention} You already claimed a base #{i + 1}. You can't claim a new one.");
}
bases[baseNumber] = new Caller { CallUser = u, TimeAdded = DateTime.Now, BaseDestroyed = false };
}
internal async void Start() {
try {
Task.Run(async () => await ClearArray());
await Task.Delay(new TimeSpan(23, 0, 0), endTokenSource.Token);
}
catch (Exception) { }
finally {
End();
}
}
internal int Uncall(User user) {
for (int i = 0; i < bases.Length; i++) {
if (bases[i]?.CallUser == user) {
bases[i] = null;
return i;
}
}
throw new InvalidOperationException("You are not participating in that war.");
}
private async Task ClearArray() {
while (!endTokenSource.IsCancellationRequested) {
await Task.Delay(5000);
for (int i = 0; i < bases.Length; i++) {
if (bases[i] == null) continue;
if (!bases[i].BaseDestroyed && DateTime.Now - bases[i].TimeAdded >= callExpire) {
Console.WriteLine($"Removing user {bases[i].CallUser.Name}");
if (OnUserTimeExpired != null)
OnUserTimeExpired(bases[i].CallUser);
bases[i] = null;
}
}
}
Console.WriteLine("Out of clear array");
}
public string ShortPrint() =>
$"`{enemyClan}` ({size} v {size})";
public override string ToString() {
var sb = new StringBuilder();
sb.AppendLine($"🔰**WAR AGAINST `{enemyClan}` ({size} v {size}) INFO:**");
for (int i = 0; i < bases.Length; i++) {
if (bases[i] == null) {
sb.AppendLine($"`{i + 1}.` ❌*unclaimed*");
}
else {
if (bases[i].BaseDestroyed) {
sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser.Name}` ⭐ ⭐ ⭐");
}
else {
var left = callExpire - (DateTime.Now - bases[i].TimeAdded);
sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser.Name}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
}
}
}
return sb.ToString();
}
internal int FinishClaim(User user) {
for (int i = 0; i < bases.Length; i++) {
if (bases[i]?.BaseDestroyed == false && bases[i]?.CallUser == user) {
bases[i].BaseDestroyed = true;
return i;
}
}
throw new InvalidOperationException($"{user.Mention} You are either not participating in that war, or you already destroyed a base.");
}
}
}

View File

@ -74,7 +74,7 @@ namespace NadekoBot.Modules {
foreach (var answ in answers) {
msgToSend += $"`{num++}.` **{answ}**\n";
}
msgToSend += "\n**Private Message me with the corresponding number of the answer.\n @everyone**";
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
await e.Channel.SendMessage(msgToSend);
}

View File

@ -600,9 +600,9 @@ namespace NadekoBot.Modules {
await Task.Run(async () => {
var rows = Classes.DBHandler.Instance.GetAllRows<Donator>();
var donatorsOrdered = rows.OrderByDescending(d => d.Amount);
string str = $"`Total number of people who donated is {donatorsOrdered.Count()}`\n";
string str = $"**Thanks to the people listed below for making this project happen!**\n";
await e.Channel.SendMessage(str + string.Join(", ", donatorsOrdered.Select(d => d.UserName)));
await e.Channel.SendMessage(str + string.Join("", donatorsOrdered.Select(d => d.UserName)));
});
});

View File

@ -18,6 +18,7 @@ namespace NadekoBot.Modules
commands.Add(new Trivia());
commands.Add(new SpeedTyping());
commands.Add(new PollCommand());
commands.Add(new ClashOfClans());
_8BallAnswers = JArray.Parse(File.ReadAllText("data/8ball.json")).Select(t => t.ToString()).ToArray();
}

View File

@ -195,7 +195,7 @@ namespace NadekoBot.Modules {
}
var ids = await SearchHelper.GetVideoIDs(await SearchHelper.GetPlaylistIdByKeyword(e.GetArg("playlist")));
//todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE
var msg = await e.Send($"🎵 Attempting to queue **{ids.Count}** songs".SnPl(ids.Count));
var msg = await e.Send($"🎵 `Attempting to queue **{ids.Count}** songs".SnPl(ids.Count)+"...`");
foreach (var id in ids) {
Task.Run(async () => await QueueSong(e, id, true)).ConfigureAwait(false);
await Task.Delay(150);
@ -321,7 +321,7 @@ namespace NadekoBot.Modules {
sr.OnBuffering += async () => {
msg = await e.Send($"🎵`Buffering...`{sr.FullPrettyName}");
};
sr.Resolve();
await sr.Resolve();
} catch (Exception ex) {
Console.WriteLine();
await e.Send($"💢 {ex.Message}");
@ -365,7 +365,7 @@ namespace NadekoBot.Modules {
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/
try {
var m = Regex.Match(file, "(?<url>^[^#].*)");
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}

View File

@ -145,6 +145,7 @@
<Compile Include="Classes\_DataModels\RequestModel.cs" />
<Compile Include="Classes\_DataModels\StatsModel.cs" />
<Compile Include="Classes\_DataModels\TypingArticleModel.cs" />
<Compile Include="Commands\ClashOfClans.cs" />
<Compile Include="Commands\TriviaCommand.cs" />
<Compile Include="Classes\Trivia\TriviaGame.cs" />
<Compile Include="Classes\Trivia\TriviaQuestion.cs" />