Fixes, more stuff, .stats added?

This commit is contained in:
Kwoth
2016-08-18 23:00:54 +02:00
parent 64f7680fe7
commit 9d128f1ff3
21 changed files with 1086 additions and 506 deletions

View File

@@ -16,10 +16,11 @@ using System.Text.RegularExpressions;
namespace NadekoBot.Modules.Administration
{
[Module(".", AppendSpace = false)]
public partial class AdministrationModule : DiscordModule
public partial class Administration : DiscordModule
{
public AdministrationModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
public Administration(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}
////todo owner only
@@ -188,10 +189,12 @@ namespace NadekoBot.Modules.Administration
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
[RequirePermission(GuildPermission.BanMembers)]
public async Task Ban(IMessage imsg, IGuildUser user, [Remainder] string msg = null)
public async Task Ban(IMessage imsg, IGuildUser user)
{
var channel = imsg.Channel as ITextChannel;
var msg = "";
if (!string.IsNullOrWhiteSpace(msg))
{
await (await user.CreateDMChannelAsync()).SendMessageAsync($"**You have been BANNED from `{channel.Guild.Name}` server.**\n" +
@@ -217,15 +220,10 @@ namespace NadekoBot.Modules.Administration
{
var channel = imsg.Channel as ITextChannel;
if (user == null)
{
await imsg.Channel.SendMessageAsync("User not found.").ConfigureAwait(false);
return;
}
if (!string.IsNullOrWhiteSpace(msg))
{
await user.SendMessageAsync($"**You have been SOFT-BANNED from `{channel.Guild.Name}` server.**\n" +
$"Reason: {msg}").ConfigureAwait(false);
$"Reason: {msg}").ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false); // temp solution; give time for a message to be send, fu volt
}
try
@@ -492,7 +490,7 @@ namespace NadekoBot.Modules.Administration
}
}
//todo owner only
////todo owner only
//[LocalizedCommand, LocalizedDescription, LocalizedSummary]
//[RequireContext(ContextType.Guild)]
//public async Task Die(IMessage imsg)

View File

@@ -1,184 +1,285 @@
using Discord;
using Newtonsoft.Json;
using Discord.Commands;
using NadekoBot.Classes.ClashOfClans;
using System;
using System.Linq;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Discord;
using NadekoBot.Services;
using NadekoBot.Attributes;
namespace NadekoBot.Classes.ClashOfClans
//todo DB
namespace NadekoBot.Modules.ClashOfClans
{
public enum DestroyStars
[Module(",",AppendSpace = false)]
internal class ClashOfClans : DiscordModule
{
One, Two, Three
}
public enum WarState
{
Started, Ended, Created
}
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
internal class Caller
{
public string CallUser { get; set; }
public DateTime TimeAdded { get; set; }
public bool BaseDestroyed { get; set; }
public int Stars { get; set; } = 3;
public Caller() { }
public Caller(string callUser, DateTime timeAdded, bool baseDestroyed)
public ClashOfClans(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
CallUser = callUser;
TimeAdded = timeAdded;
BaseDestroyed = baseDestroyed;
}
public void ResetTime()
private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
{
TimeAdded = DateTime.UtcNow;
}
public void Destroy()
{
BaseDestroyed = true;
}
}
internal class ClashWar
{
private static TimeSpan callExpire => new TimeSpan(2, 0, 0);
public string EnemyClan { get; set; }
public int Size { get; set; }
public Caller[] Bases { get; set; }
public WarState WarState { get; set; } = WarState.Created;
//public bool Started { get; set; } = false;
public DateTime StartedAt { get; set; }
//public bool Ended { get; private set; } = false;
public ulong ServerId { get; set; }
public ulong ChannelId { get; set; }
[JsonIgnore]
public ITextChannel Channel { get; internal set; }
/// <summary>
/// This init is purely for the deserialization
/// </summary>
public ClashWar() { }
public ClashWar(string enemyClan, int size, ulong serverId, ulong channelId)
{
this.EnemyClan = enemyClan;
this.Size = size;
this.Bases = new Caller[size];
this.ServerId = serverId;
this.ChannelId = channelId;
this.Channel = NadekoBot.Client.GetGuildsAsync() //nice api you got here volt,
.GetAwaiter() //especially like how getguildsasync isn't async at all internally.
.GetResult() //But hey, lib has to be async kek
.FirstOrDefault(s => s.Id == serverId)? // srsly
.GetChannelsAsync() //wtf is this
.GetAwaiter() // oh i know, its the implementation detail
.GetResult() // and makes library look consistent
.FirstOrDefault(c => c.Id == channelId) // its not common sense to make library work like this.
as ITextChannel; // oh and don't forget to cast it to this arbitrary bullshit
}
internal void End()
{
//Ended = true;
WarState = WarState.Ended;
}
internal void Call(string 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.");
var Bases = war.Bases;
for (var i = 0; i < Bases.Length; i++)
{
if (Bases[i]?.BaseDestroyed == false && Bases[i]?.CallUser == u)
throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one.");
}
Bases[baseNumber] = new Caller(u.Trim(), DateTime.UtcNow, false);
}
internal void Start()
{
if (WarState == WarState.Started)
throw new InvalidOperationException("War already started");
//if (Started)
// throw new InvalidOperationException();
//Started = true;
WarState = WarState.Started;
StartedAt = DateTime.UtcNow;
foreach (var b in Bases.Where(b => b != null))
{
b.ResetTime();
}
}
internal int Uncall(string user)
{
user = user.Trim();
for (var i = 0; i < Bases.Length; i++)
{
if (Bases[i]?.CallUser != user) continue;
Bases[i] = null;
return i;
}
throw new InvalidOperationException("You are not participating in that war.");
}
public string ShortPrint() =>
$"`{EnemyClan}` ({Size} v {Size})";
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"🔰**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**");
if (WarState == WarState.Created)
sb.AppendLine("`not started`");
for (var i = 0; i < Bases.Length; i++)
{
if (Bases[i] == null)
if (Bases[i] == null) continue;
if (!Bases[i].BaseDestroyed && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
{
sb.AppendLine($"`{i + 1}.` ❌*unclaimed*");
await war.Channel.SendMessageAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
Bases[i] = null;
}
else
}
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task CreateWar(IMessage imsg, int size, [Remainder] string enemyClan = null)
{
var channel = imsg.Channel as ITextChannel;
if (!(imsg.Author as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(enemyClan))
return;
if (size < 10 || size > 50 || size % 5 != 0)
{
await imsg.Channel.SendMessageAsync("💢🔰 Not a Valid war size").ConfigureAwait(false);
return;
}
List<ClashWar> wars;
if (!ClashWars.TryGetValue(channel.Guild.Id, out wars))
{
wars = new List<ClashWar>();
if (!ClashWars.TryAdd(channel.Guild.Id, wars))
return;
}
var cw = new ClashWar(enemyClan, size, channel.Guild.Id, imsg.Channel.Id);
//cw.Start();
wars.Add(cw);
await imsg.Channel.SendMessageAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task StartWar(IMessage imsg, [Remainder] string number = null)
{
var channel = imsg.Channel as ITextChannel;
int num = 0;
int.TryParse(number, out num);
var warsInfo = GetWarInfo(imsg, num);
if (warsInfo == null)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var war = warsInfo.Item1[warsInfo.Item2];
try
{
war.Start();
await imsg.Channel.SendMessageAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
}
catch
{
await imsg.Channel.SendMessageAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
}
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ListWar(IMessage imsg, [Remainder] string number = null)
{
var channel = imsg.Channel as ITextChannel;
// if number is null, print all wars in a short way
if (string.IsNullOrWhiteSpace(number))
{
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(channel.Guild.Id, out wars);
if (wars == null || wars.Count == 0)
{
if (Bases[i].BaseDestroyed)
{
sb.AppendLine($"`{i + 1}.` ✅ `{Bases[i].CallUser}` {new string('⭐', Bases[i].Stars)}");
}
else
{
var left = (WarState == WarState.Started) ? callExpire - (DateTime.UtcNow - Bases[i].TimeAdded) : callExpire;
sb.AppendLine($"`{i + 1}.` ✅ `{Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
}
await imsg.Channel.SendMessageAsync("🔰 **No active wars.**").ConfigureAwait(false);
return;
}
var sb = new StringBuilder();
sb.AppendLine("🔰 **LIST OF ACTIVE WARS**");
sb.AppendLine("**-------------------------**");
for (var 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 imsg.Channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
return;
}
return sb.ToString();
var num = 0;
int.TryParse(number, out num);
//if number is not null, print the war needed
var warsInfo = GetWarInfo(imsg, num);
if (warsInfo == null)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
await imsg.Channel.SendMessageAsync(warsInfo.Item1[warsInfo.Item2].ToString()).ConfigureAwait(false);
}
internal int FinishClaim(string user, int stars = 3)
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task Claim(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
user = user.Trim();
for (var i = 0; i < Bases.Length; i++)
var channel = imsg.Channel as ITextChannel;
var warsInfo = GetWarInfo(imsg, number);
if (warsInfo == null || warsInfo.Item1.Count == 0)
{
if (Bases[i]?.BaseDestroyed != false || Bases[i]?.CallUser != user) continue;
Bases[i].BaseDestroyed = true;
Bases[i].Stars = stars;
return i;
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base.");
var usr =
string.IsNullOrWhiteSpace(other_name) ?
imsg.Author.Username :
other_name;
try
{
var war = warsInfo.Item1[warsInfo.Item2];
war.Call(usr, baseNumber - 1);
await imsg.Channel.SendMessageAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false);
}
catch (Exception ex)
{
await imsg.Channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false);
}
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish1(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = imsg.Channel as ITextChannel;
await FinishClaim(imsg, number, baseNumber, other_name, 1);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish2(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = imsg.Channel as ITextChannel;
await FinishClaim(imsg, number, baseNumber, other_name, 2);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = imsg.Channel as ITextChannel;
await FinishClaim(imsg, number, baseNumber, other_name);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task EndWar(IMessage imsg, int number)
{
var channel = imsg.Channel as ITextChannel;
var warsInfo = GetWarInfo(imsg,number);
if (warsInfo == null)
{
await imsg.Channel.SendMessageAsync("💢🔰 That war does not exist.").ConfigureAwait(false);
return;
}
warsInfo.Item1[warsInfo.Item2].End();
await imsg.Channel.SendMessageAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false);
var size = warsInfo.Item1[warsInfo.Item2].Size;
warsInfo.Item1.RemoveAt(warsInfo.Item2);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task Unclaim(IMessage imsg, int number, [Remainder] string otherName = null)
{
var channel = imsg.Channel as ITextChannel;
var warsInfo = GetWarInfo(imsg, number);
if (warsInfo == null || warsInfo.Item1.Count == 0)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var usr =
string.IsNullOrWhiteSpace(otherName) ?
imsg.Author.Username :
otherName;
try
{
var war = warsInfo.Item1[warsInfo.Item2];
var baseNumber = war.Uncall(usr);
await imsg.Channel.SendMessageAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false);
}
catch (Exception ex)
{
await imsg.Channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false);
}
}
private async Task FinishClaim(IMessage imsg, int number, int baseNumber, [Remainder] string other_name, int stars = 3)
{
var channel = imsg.Channel as ITextChannel;
var warInfo = GetWarInfo(imsg, number);
if (warInfo == null || warInfo.Item1.Count == 0)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var usr =
string.IsNullOrWhiteSpace(other_name) ?
imsg.Author.Username :
other_name;
var war = warInfo.Item1[warInfo.Item2];
try
{
var baseNum = war.FinishClaim(usr, stars);
await imsg.Channel.SendMessageAsync($"❗🔰{imsg.Author.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false);
}
catch (Exception ex)
{
await imsg.Channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false);
}
}
private static Tuple<List<ClashWar>, int> GetWarInfo(IMessage imsg, int num)
{
var channel = imsg.Channel as ITextChannel;
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(channel.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);
}
}
}

View File

@@ -1,285 +0,0 @@
using Discord.Commands;
using NadekoBot.Classes.ClashOfClans;
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;
//todo DB
namespace NadekoBot.Modules.ClashOfClans
{
[Module(",",AppendSpace = false)]
internal class ClashOfClansModule : DiscordModule
{
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
public ClashOfClansModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}
private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
{
var Bases = war.Bases;
for (var i = 0; i < Bases.Length; i++)
{
if (Bases[i] == null) continue;
if (!Bases[i].BaseDestroyed && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
{
await war.Channel.SendMessageAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
Bases[i] = null;
}
}
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task CreateWar(IMessage imsg, int size, [Remainder] string enemyClan = null)
{
var channel = imsg.Channel as ITextChannel;
if (!(imsg.Author as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(enemyClan))
return;
if (size < 10 || size > 50 || size % 5 != 0)
{
await imsg.Channel.SendMessageAsync("💢🔰 Not a Valid war size").ConfigureAwait(false);
return;
}
List<ClashWar> wars;
if (!ClashWars.TryGetValue(channel.Guild.Id, out wars))
{
wars = new List<ClashWar>();
if (!ClashWars.TryAdd(channel.Guild.Id, wars))
return;
}
var cw = new ClashWar(enemyClan, size, channel.Guild.Id, imsg.Channel.Id);
//cw.Start();
wars.Add(cw);
await imsg.Channel.SendMessageAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task StartWar(IMessage imsg, [Remainder] string number = null)
{
var channel = imsg.Channel as ITextChannel;
int num = 0;
int.TryParse(number, out num);
var warsInfo = GetWarInfo(imsg, num);
if (warsInfo == null)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var war = warsInfo.Item1[warsInfo.Item2];
try
{
war.Start();
await imsg.Channel.SendMessageAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
}
catch
{
await imsg.Channel.SendMessageAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
}
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ListWar(IMessage imsg, [Remainder] string number = null)
{
var channel = imsg.Channel as ITextChannel;
// if number is null, print all wars in a short way
if (string.IsNullOrWhiteSpace(number))
{
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(channel.Guild.Id, out wars);
if (wars == null || wars.Count == 0)
{
await imsg.Channel.SendMessageAsync("🔰 **No active wars.**").ConfigureAwait(false);
return;
}
var sb = new StringBuilder();
sb.AppendLine("🔰 **LIST OF ACTIVE WARS**");
sb.AppendLine("**-------------------------**");
for (var 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 imsg.Channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
return;
}
var num = 0;
int.TryParse(number, out num);
//if number is not null, print the war needed
var warsInfo = GetWarInfo(imsg, num);
if (warsInfo == null)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
await imsg.Channel.SendMessageAsync(warsInfo.Item1[warsInfo.Item2].ToString()).ConfigureAwait(false);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task Claim(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = imsg.Channel as ITextChannel;
var warsInfo = GetWarInfo(imsg, number);
if (warsInfo == null || warsInfo.Item1.Count == 0)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var usr =
string.IsNullOrWhiteSpace(other_name) ?
imsg.Author.Username :
other_name;
try
{
var war = warsInfo.Item1[warsInfo.Item2];
war.Call(usr, baseNumber - 1);
await imsg.Channel.SendMessageAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false);
}
catch (Exception ex)
{
await imsg.Channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false);
}
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish1(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = imsg.Channel as ITextChannel;
await FinishClaim(imsg, number, baseNumber, other_name, 1);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish2(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = imsg.Channel as ITextChannel;
await FinishClaim(imsg, number, baseNumber, other_name, 2);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task ClaimFinish(IMessage imsg, int number, int baseNumber, [Remainder] string other_name = null)
{
var channel = imsg.Channel as ITextChannel;
await FinishClaim(imsg, number, baseNumber, other_name);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task EndWar(IMessage imsg, int number)
{
var channel = imsg.Channel as ITextChannel;
var warsInfo = GetWarInfo(imsg,number);
if (warsInfo == null)
{
await imsg.Channel.SendMessageAsync("💢🔰 That war does not exist.").ConfigureAwait(false);
return;
}
warsInfo.Item1[warsInfo.Item2].End();
await imsg.Channel.SendMessageAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false);
var size = warsInfo.Item1[warsInfo.Item2].Size;
warsInfo.Item1.RemoveAt(warsInfo.Item2);
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task Unclaim(IMessage imsg, int number, [Remainder] string otherName = null)
{
var channel = imsg.Channel as ITextChannel;
var warsInfo = GetWarInfo(imsg, number);
if (warsInfo == null || warsInfo.Item1.Count == 0)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var usr =
string.IsNullOrWhiteSpace(otherName) ?
imsg.Author.Username :
otherName;
try
{
var war = warsInfo.Item1[warsInfo.Item2];
var baseNumber = war.Uncall(usr);
await imsg.Channel.SendMessageAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false);
}
catch (Exception ex)
{
await imsg.Channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false);
}
}
private async Task FinishClaim(IMessage imsg, int number, int baseNumber, [Remainder] string other_name, int stars = 3)
{
var channel = imsg.Channel as ITextChannel;
var warInfo = GetWarInfo(imsg, number);
if (warInfo == null || warInfo.Item1.Count == 0)
{
await imsg.Channel.SendMessageAsync("💢🔰 **That war does not exist.**").ConfigureAwait(false);
return;
}
var usr =
string.IsNullOrWhiteSpace(other_name) ?
imsg.Author.Username :
other_name;
var war = warInfo.Item1[warInfo.Item2];
try
{
var baseNum = war.FinishClaim(usr, stars);
await imsg.Channel.SendMessageAsync($"❗🔰{imsg.Author.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false);
}
catch (Exception ex)
{
await imsg.Channel.SendMessageAsync($"💢🔰 {ex.Message}").ConfigureAwait(false);
}
}
private static Tuple<List<ClashWar>, int> GetWarInfo(IMessage imsg, int num)
{
var channel = imsg.Channel as ITextChannel;
//check if there are any wars
List<ClashWar> wars = null;
ClashWars.TryGetValue(channel.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);
}
}
}

View File

@@ -0,0 +1,183 @@
using Discord;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Text;
namespace NadekoBot.Classes.ClashOfClans
{
internal class ClashWar
{
public enum DestroyStars
{
One, Two, Three
}
public enum StateOfWar
{
Started, Ended, Created
}
internal class Caller
{
public string CallUser { get; set; }
public DateTime TimeAdded { get; set; }
public bool BaseDestroyed { get; set; }
public int Stars { get; set; } = 3;
public Caller() { }
public Caller(string callUser, DateTime timeAdded, bool baseDestroyed)
{
CallUser = callUser;
TimeAdded = timeAdded;
BaseDestroyed = baseDestroyed;
}
public void ResetTime()
{
TimeAdded = DateTime.UtcNow;
}
public void Destroy()
{
BaseDestroyed = true;
}
}
private static TimeSpan callExpire => new TimeSpan(2, 0, 0);
public string EnemyClan { get; set; }
public int Size { get; set; }
public Caller[] Bases { get; set; }
public StateOfWar WarState { get; set; } = StateOfWar.Created;
//public bool Started { get; set; } = false;
public DateTime StartedAt { get; set; }
//public bool Ended { get; private set; } = false;
public ulong ServerId { get; set; }
public ulong ChannelId { get; set; }
[JsonIgnore]
public ITextChannel Channel { get; internal set; }
/// <summary>
/// This init is purely for the deserialization
/// </summary>
public ClashWar() { }
public ClashWar(string enemyClan, int size, ulong serverId, ulong channelId)
{
this.EnemyClan = enemyClan;
this.Size = size;
this.Bases = new Caller[size];
this.ServerId = serverId;
this.ChannelId = channelId;
this.Channel = NadekoBot.Client.GetGuildsAsync() //nice api you got here volt,
.GetAwaiter() //especially like how getguildsasync isn't async at all internally.
.GetResult() //But hey, lib has to be async kek
.FirstOrDefault(s => s.Id == serverId)? // srsly
.GetChannelsAsync() //wtf is this
.GetAwaiter() // oh i know, its the implementation detail
.GetResult() // and makes library look consistent
.FirstOrDefault(c => c.Id == channelId) // its not common sense to make library work like this.
as ITextChannel; // oh and don't forget to cast it to this arbitrary bullshit
}
internal void End()
{
//Ended = true;
WarState = StateOfWar.Ended;
}
internal void Call(string 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 (var i = 0; i < Bases.Length; i++)
{
if (Bases[i]?.BaseDestroyed == false && Bases[i]?.CallUser == u)
throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one.");
}
Bases[baseNumber] = new Caller(u.Trim(), DateTime.UtcNow, false);
}
internal void Start()
{
if (WarState == StateOfWar.Started)
throw new InvalidOperationException("War already started");
//if (Started)
// throw new InvalidOperationException();
//Started = true;
WarState = StateOfWar.Started;
StartedAt = DateTime.UtcNow;
foreach (var b in Bases.Where(b => b != null))
{
b.ResetTime();
}
}
internal int Uncall(string user)
{
user = user.Trim();
for (var i = 0; i < Bases.Length; i++)
{
if (Bases[i]?.CallUser != user) continue;
Bases[i] = null;
return i;
}
throw new InvalidOperationException("You are not participating in that war.");
}
public string ShortPrint() =>
$"`{EnemyClan}` ({Size} v {Size})";
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"🔰**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**");
if (WarState == StateOfWar.Created)
sb.AppendLine("`not started`");
for (var 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}` {new string('⭐', Bases[i].Stars)}");
}
else
{
var left = (WarState == StateOfWar.Started) ? callExpire - (DateTime.UtcNow - Bases[i].TimeAdded) : callExpire;
sb.AppendLine($"`{i + 1}.` ✅ `{Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
}
}
}
return sb.ToString();
}
internal int FinishClaim(string user, int stars = 3)
{
user = user.Trim();
for (var i = 0; i < Bases.Length; i++)
{
if (Bases[i]?.BaseDestroyed != false || Bases[i]?.CallUser != user) continue;
Bases[i].BaseDestroyed = true;
Bases[i].Stars = stars;
return i;
}
throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base.");
}
}
}

View File

@@ -1,6 +1,7 @@
using Discord;
using Discord.Commands;
using NadekoBot.Services;
using NLog;
namespace NadekoBot.Modules
{
@@ -10,6 +11,7 @@ namespace NadekoBot.Modules
protected CommandService _commands;
protected IBotConfiguration _config;
protected IDiscordClient _client;
protected Logger _log;
public DiscordModule(ILocalization loc, CommandService cmds, IBotConfiguration config,IDiscordClient client)
{
@@ -17,6 +19,7 @@ namespace NadekoBot.Modules
_commands = cmds;
_config = config;
_client = client;
_log = LogManager.GetCurrentClassLogger();
}
}
}

View File

@@ -11,11 +11,11 @@ using NadekoBot.Extensions;
namespace NadekoBot.Modules.Games
{
[Module(">", AppendSpace = false)]
public partial class GamesModule : DiscordModule
public partial class Games : DiscordModule
{
//todo DB
private IEnumerable<string> _8BallResponses;
public GamesModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
public Games(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}

View File

@@ -12,7 +12,7 @@ using System.Text;
namespace NadekoBot.Modules.Help
{
[Module("-", AppendSpace = false)]
public partial class HelpModule : DiscordModule
public partial class Help : DiscordModule
{
public string HelpString {
get {
@@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Help
return str + String.Format(str, NadekoBot.Credentials.ClientId);
}
}
public HelpModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
public Help(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}
@@ -66,7 +66,7 @@ namespace NadekoBot.Modules.Help
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task Help(IMessage imsg, [Remainder] string comToFind = null)
public async Task H(IMessage imsg, [Remainder] string comToFind = null)
{
var channel = imsg.Channel as ITextChannel;
@@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Help
}
helpstr = helpstr.Replace((await NadekoBot.Client.GetCurrentUserAsync()).Username , "@BotName");
#if DEBUG
File.WriteAllText("../../../docs/Commands List.md", helpstr.ToString());
File.WriteAllText("../../../../../docs/Commands List.md", helpstr.ToString());
#else
File.WriteAllText("commandlist.md", helpstr.ToString());
#endif

View File

@@ -9,13 +9,14 @@ using NadekoBot.Services;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Net;
namespace NadekoBot.Modules.NSFW
{
[Module("~", AppendSpace = false)]
public class NSFWModule : DiscordModule
public class NSFW : DiscordModule
{
public NSFWModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
public NSFW(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}
@@ -212,7 +213,7 @@ namespace NadekoBot.Modules.NSFW
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.202 Safari/535.1");
http.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
var data = await http.GetStringAsync("http://e621.net/post/index.xml?tags=" + Uri.EscapeUriString(tags) + "%20order:random&limit=1");
var data = await http.GetStreamAsync("http://e621.net/post/index.xml?tags=" + Uri.EscapeUriString(tags) + "%20order:random&limit=1");
var doc = XDocument.Load(data);
return doc.Descendants("file_url").FirstOrDefault().Value;
}

View File

@@ -7,9 +7,9 @@ using NadekoBot.Services;
namespace NadekoBot.Modules.Games.Commands
{
[Module(">", AppendSpace = false)]
public partial class PokemonModule : DiscordModule
public partial class Pokemon : DiscordModule
{
public PokemonModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
public Pokemon(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}

View File

@@ -76,7 +76,7 @@
// var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Creds.OsuAPIKey}&{mapId}";
// var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0];
// var sb = new System.Text.StringBuilder();
// var starRating = Math.Round(Double.Parse($"{obj["difficultyrating"]}"), 2);
// var starRating = Math.Round(Double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2);
// var time = TimeSpan.FromSeconds(Double.Parse($"{obj["total_length"]}")).ToString(@"mm\:ss");
// sb.AppendLine($"{obj["artist"]} - {obj["title"]}, mapped by {obj["creator"]}. https://osu.ppy.sh/s/{obj["beatmapset_id"]}");
// sb.AppendLine($"{starRating} stars, {obj["bpm"]} BPM | AR{obj["diff_approach"]}, CS{obj["diff_size"]}, OD{obj["diff_overall"]} | Length: {time}");
@@ -121,7 +121,7 @@
// {
// var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Creds.OsuAPIKey}&b={item["beatmap_id"]}";
// var map = JArray.Parse(await http.GetStringAsync(mapReqString).ConfigureAwait(false))[0];
// var pp = Math.Round(Double.Parse($"{item["pp"]}"), 2);
// var pp = Math.Round(Double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2);
// var acc = CalculateAcc(item, m);
// var mods = ResolveMods(Int32.Parse($"{item["enabled_mods"]}"));
// if (mods != "+")

View File

@@ -17,13 +17,13 @@ using NadekoBot.Modules.Searches.Commands.Models;
namespace NadekoBot.Modules.Searches
{
[Module("~", AppendSpace = false)]
public class SearchesModule : DiscordModule
public class Searches : DiscordModule
{
private readonly Random rng;
private IYoutubeService _yt { get; }
public SearchesModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client, IYoutubeService youtube) : base(loc, cmds, config, client)
public Searches(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client, IYoutubeService youtube) : base(loc, cmds, config, client)
{
rng = new Random();
_yt = youtube;

View File

@@ -94,6 +94,7 @@ namespace NadekoBot.Modules.Translator
{ "bn", "bn"},
{ "bg", "bg"},
{ "ca", "ca"},
{ "zh-TW", "zh-TW"},
{ "zh-CN", "zh-CN"},
{ "hr", "hr"},
{ "cs", "cs"},

View File

@@ -9,9 +9,9 @@ using NadekoBot.Services;
namespace NadekoBot.Modules.Translator
{
[Module("~", AppendSpace = false)]
public class TranslatorModule : DiscordModule
public class Translator : DiscordModule
{
public TranslatorModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
public Translator(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}

View File

@@ -11,7 +11,7 @@
////todo rewrite
//namespace NadekoBot.Modules.Trello
//{
// internal class TrelloModule : DiscordModule
// internal class Trello : DiscordModule
// {
// private readonly Timer t = new Timer { Interval = 2000 };
// public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Trello;

View File

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.Utility
{
partial class UtilityModule : DiscordModule
partial class Utility : DiscordModule
{
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]

View File

@@ -15,9 +15,9 @@ namespace NadekoBot.Modules.Utility
{
[Module(".", AppendSpace = false)]
public partial class UtilityModule : DiscordModule
public partial class Utility : DiscordModule
{
public UtilityModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
public Utility(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
{
}
@@ -127,6 +127,15 @@ namespace NadekoBot.Modules.Utility
await msg.Reply("`List of roles:` \n• " + string.Join("\n• ", (msg.Channel as ITextChannel).Guild.Roles.Except(new[] { guild.EveryoneRole })));
}
}
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
[RequireContext(ContextType.Guild)]
public async Task Stats(IMessage imsg)
{
var channel = imsg.Channel as ITextChannel;
await channel.SendMessageAsync(await NadekoBot.Stats.Print());
}
}
}