Merge pull request #57 from Kwoth/dev

ups
This commit is contained in:
samvaio 2017-01-08 23:20:28 +05:30 committed by GitHub
commit 024ecf521a
26 changed files with 643 additions and 279 deletions

@ -1 +1 @@
Subproject commit fa2568bc312ba35f1518e47601c62fccdb949731
Subproject commit b9f767337d2b7c07ed76eb83c3bc5030109d5238

View File

@ -0,0 +1,24 @@
using Discord.Commands;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static NadekoBot.Modules.Permissions.Permissions;
namespace NadekoBot.DataStructures
{
public struct ExecuteCommandResult
{
public readonly CommandInfo CommandInfo;
public readonly PermissionCache PermissionCache;
public readonly IResult Result;
public ExecuteCommandResult(CommandInfo commandInfo, PermissionCache cache, IResult result)
{
this.CommandInfo = commandInfo;
this.PermissionCache = cache;
this.Result = result;
}
}
}

View File

@ -78,7 +78,10 @@ namespace NadekoBot.Modules.Administration
_client.UserPresenceUpdated += _client_UserPresenceUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
_client.GuildUserUpdated += _client_GuildUserUpdated;
#if !GLOBAL_NADEKO
_client.UserUpdated += _client_UserUpdated;
#endif
_client.ChannelCreated += _client_ChannelCreated;
_client.ChannelDestroyed += _client_ChannelDestroyed;
@ -88,6 +91,38 @@ namespace NadekoBot.Modules.Administration
MuteCommands.UserUnmuted += MuteCommands_UserUnmuted;
}
private static async void _client_UserUpdated(SocketUser before, SocketUser after)
{
try
{
var str = "";
if (before.Username != after.Username)
str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Name Changed |** 🆔 `{before.Id}`\n\t\t`New:` **{after.ToString()}**";
else if (before.AvatarUrl != after.AvatarUrl)
str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await NadekoBot.Google.ShortenUrl(before.AvatarUrl)} `=>` {await NadekoBot.Google.ShortenUrl(after.AvatarUrl)}";
if (string.IsNullOrWhiteSpace(str))
return;
var guildsMemberOf = NadekoBot.Client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList();
foreach (var g in guildsMemberOf)
{
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(g.Id, out logSetting)
|| (logSetting.UserUpdatedId == null))
return;
ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null)
return;
try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch { }
}
}
catch
{ }
}
private static async void _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after)
{
try
@ -228,17 +263,10 @@ namespace NadekoBot.Modules.Administration
catch (Exception ex) { _log.Warn(ex); }
}
private static async void _client_UserUpdated(SocketUser uBefore, SocketUser uAfter)
private static async void _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after)
{
try
{
var before = uBefore as SocketGuildUser;
if (before == null)
return;
var after = uAfter as SocketGuildUser;
if (after == null)
return;
LogSetting logSetting;
if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting)
|| (logSetting.UserUpdatedId == null))
@ -247,30 +275,25 @@ namespace NadekoBot.Modules.Administration
ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) == null)
return;
string str = $"🕔`{prettyCurrentTime}`";
if (before.Username != after.Username)
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Name Changed |** 🆔 `{before.Id}`\n\t\t`New:` **{after.ToString()}**";
else if (before.Nickname != after.Nickname)
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Nickname Changed |** 🆔 `{before.Id}`\n\t\t`Old:` **{before.Nickname}#{before.Discriminator}**\n\t\t`New:` **{after.Nickname}#{after.Discriminator}**";
else if (before.AvatarUrl != after.AvatarUrl)
str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await NadekoBot.Google.ShortenUrl(before.AvatarUrl)} `=>` {await NadekoBot.Google.ShortenUrl(after.AvatarUrl)}";
var str = "";
if (before.Nickname != after.Nickname)
str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Nickname Changed |** 🆔 `{before.Id}`\n\t\t`Old:` **{before.Nickname}**\n\t\t`New:` **{after.Nickname}**";
else if (!before.RoleIds.SequenceEqual(after.RoleIds))
{
if (before.RoleIds.Count < after.RoleIds.Count)
{
var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => "**" + before.Guild.GetRole(r).Name + "**");
str += $"👤__**{before.ToString()}**__ **| User's Role Added |** 🆔 `{before.Id}`\n\t✅ {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔";
str = $"👤__**{before.ToString()}**__ **| User's Role Added |** 🆔 `{before.Id}`\n\t✅ {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔";
}
else if (before.RoleIds.Count > after.RoleIds.Count)
{
var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => "**" + before.Guild.GetRole(r).Name + "**");
str += $"👤__**{before.ToString()}**__ **| User's Role Removed |** 🆔 `{before.Id}`\n\t🚮 {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔";
str = $"👤__**{before.ToString()}**__ **| User's Role Removed |** 🆔 `{before.Id}`\n\t🚮 {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔";
}
}
else
return;
try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
try { await logChannel.SendMessageAsync($"🕔`{prettyCurrentTime}` " + str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
}
catch (Exception ex) { _log.Warn(ex); }
}
@ -745,9 +768,9 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync().ConfigureAwait(false);
}
if (action.Value)
await channel.SendMessageAsync("✅ Logging all events on this channel.").ConfigureAwait(false);
await channel.SendConfirmAsync("Logging all events in this channel.").ConfigureAwait(false);
else
await channel.SendMessageAsync(" Logging disabled.").ConfigureAwait(false);
await channel.SendConfirmAsync("Logging disabled.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -774,9 +797,9 @@ namespace NadekoBot.Modules.Administration
}
if (removed == 0)
await channel.SendMessageAsync($"🆗 Logging will **now ignore** #⃣ `{channel.Name} ({channel.Id})`").ConfigureAwait(false);
await channel.SendConfirmAsync($"Logging will IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false);
else
await channel.SendMessageAsync($" Logging will **no longer ignore** #⃣ `{channel.Name} ({channel.Id})`").ConfigureAwait(false);
await channel.SendConfirmAsync($"Logging will NOT IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -853,9 +876,9 @@ namespace NadekoBot.Modules.Administration
}
if (channelId != null)
await channel.SendMessageAsync($"✅ Logging `{type}` event in #⃣ `{channel.Name} ({channel.Id})`").ConfigureAwait(false);
await channel.SendConfirmAsync($"Logging **{type}** event in this channel.").ConfigureAwait(false);
else
await channel.SendMessageAsync($" Stopped logging `{type}` event.").ConfigureAwait(false);
await channel.SendConfirmAsync($"Stopped logging **{type}** event.").ConfigureAwait(false);
}
}
}

View File

@ -71,8 +71,8 @@ namespace NadekoBot.Modules.Administration
public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
new Dictionary<string, Func<string>> {
{"%servers%", () => NadekoBot.Client.GetGuilds().Count().ToString()},
{"%users%", () => NadekoBot.Client.GetGuilds().Select(s => s.Users.Count).Sum().ToString()},
{"%servers%", () => NadekoBot.Client.GetGuildsCount().ToString()},
{"%users%", () => NadekoBot.Client.GetGuilds().Sum(s => s.Users.Count).ToString()},
{"%playing%", () => {
var cnt = Music.Music.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null);
if (cnt != 1) return cnt.ToString();

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Administration
public async Task Leave([Remainder] string guildStr)
{
guildStr = guildStr.Trim().ToUpperInvariant();
var server = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Id.ToString().Trim().ToUpperInvariant() == guildStr) ??
var server = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Id.ToString() == guildStr) ??
NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr);
if (server == null)

View File

@ -26,9 +26,9 @@ namespace NadekoBot.Modules.Gambling
if (count == 1)
{
if (rng.Next(0, 2) == 1)
await Context.Channel.SendFileAsync(headsPath, $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false);
await Context.Channel.SendFileAsync(File.Open(headsPath, FileMode.OpenOrCreate), "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false);
else
await Context.Channel.SendFileAsync(tailsPath, $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false);
await Context.Channel.SendFileAsync(File.Open(tailsPath, FileMode.OpenOrCreate), "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false);
return;
}
if (count > 10 || count < 1)
@ -93,7 +93,7 @@ namespace NadekoBot.Modules.Gambling
str = $"{Context.User.Mention}`Better luck next time.`";
}
await Context.Channel.SendFileAsync(imgPathToSend, str).ConfigureAwait(false);
await Context.Channel.SendFileAsync(File.Open(imgPathToSend, FileMode.OpenOrCreate), "coin.jpg", str).ConfigureAwait(false);
}
}
}

View File

@ -88,10 +88,10 @@ namespace NadekoBot.Modules.Games
}
return true;
}
#if !GLOBAL_NADEKO
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(ChannelPermission.ManageMessages)]
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task Cleverbot()
{
var channel = (ITextChannel)Context.Channel;
@ -120,7 +120,6 @@ namespace NadekoBot.Modules.Games
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Enabled cleverbot on this server.").ConfigureAwait(false);
}
#endif
}
}
}

View File

@ -12,37 +12,15 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands.Hangman
{
public class HangmanModel
{
public List<HangmanObject> All { get; set; }
public List<HangmanObject> Animals { get; set; }
public List<HangmanObject> Countries { get; set; }
public List<HangmanObject> Movies { get; set; }
public List<HangmanObject> Things { get; set; }
}
public class HangmanTermPool
{
public enum HangmanTermType
{
All,
Animals,
Countries,
Movies,
Things
}
const string termsPath = "data/hangman.json";
public static HangmanModel data { get; }
public static IReadOnlyDictionary<string, HangmanObject[]> data { get; }
static HangmanTermPool()
{
try
{
data = JsonConvert.DeserializeObject<HangmanModel>(File.ReadAllText(termsPath));
data.All = data.Animals.Concat(data.Countries)
.Concat(data.Movies)
.Concat(data.Things)
.ToList();
data = JsonConvert.DeserializeObject<Dictionary<string, HangmanObject[]>>(File.ReadAllText(termsPath));
}
catch (Exception ex)
{
@ -50,23 +28,27 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
}
}
public static HangmanObject GetTerm(HangmanTermType type)
public static HangmanObject GetTerm(string type)
{
if (string.IsNullOrWhiteSpace(type))
throw new ArgumentNullException(nameof(type));
type = type.Trim();
var rng = new NadekoRandom();
switch (type)
{
case HangmanTermType.Animals:
return data.Animals[rng.Next(0, data.Animals.Count)];
case HangmanTermType.Countries:
return data.Countries[rng.Next(0, data.Countries.Count)];
case HangmanTermType.Movies:
return data.Movies[rng.Next(0, data.Movies.Count)];
case HangmanTermType.Things:
return data.Things[rng.Next(0, data.Things.Count)];
default:
return data.All[rng.Next(0, data.All.Count)];
if (type == "All") {
var keys = data.Keys.ToArray();
type = keys[rng.Next(0, keys.Length)];
}
HangmanObject[] termTypes;
data.TryGetValue(type, out termTypes);
if (termTypes.Length == 0)
return null;
return termTypes[rng.Next(0, termTypes.Length)];
}
}
@ -95,20 +77,23 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant()
.Where(c => char.IsLetter(c) || char.IsDigit(c)));
public HangmanTermPool.HangmanTermType TermType { get; }
public string TermType { get; }
public event Action<HangmanGame> OnEnded;
public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type)
public HangmanGame(IMessageChannel channel, string type)
{
_log = LogManager.GetCurrentClassLogger();
this.GameChannel = channel;
this.TermType = type;
this.TermType = type.ToTitleCase();
}
public void Start()
{
this.Term = HangmanTermPool.GetTerm(TermType);
if (this.Term == null)
throw new KeyNotFoundException("Can't find a term with that type. Use hangmanlist command.");
// start listening for answers when game starts
NadekoBot.Client.MessageReceived += PotentialGuess;
}

View File

@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Games
static HangmanCommands()
{
_log = LogManager.GetCurrentClassLogger();
typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", Enum.GetNames(typeof(HangmanTermPool.HangmanTermType)));
typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", HangmanTermPool.data.Keys);
}
[NadekoCommand, Usage, Description, Aliases]
@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Games
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Hangman(HangmanTermPool.HangmanTermType type = HangmanTermPool.HangmanTermType.All)
public async Task Hangman([Remainder]string type = "All")
{
var hm = new HangmanGame(Context.Channel, type);
@ -48,7 +48,14 @@ namespace NadekoBot.Modules.Games
HangmanGame throwaway;
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
};
hm.Start();
try
{
hm.Start();
}
catch (Exception ex) {
try { await Context.Channel.SendErrorAsync($"Starting errored: {ex.Message}").ConfigureAwait(false); } catch { }
return;
}
await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord);
}

View File

@ -94,7 +94,8 @@ namespace NadekoBot.Modules.Games
lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now);
var sent = await channel.SendFileAsync(
GetRandomCurrencyImagePath(),
File.Open(GetRandomCurrencyImagePath(), FileMode.OpenOrCreate),
"RandomFlower.jpg",
$"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`")
.ConfigureAwait(false);
plantedFlowers.AddOrUpdate(channel.Id, new List<IUserMessage>() { sent }, (id, old) => { old.Add(sent); return old; });
@ -104,7 +105,7 @@ namespace NadekoBot.Modules.Games
}
catch { }
}
#if !GLOBAL_NADEKO
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Pick()
@ -163,11 +164,10 @@ namespace NadekoBot.Modules.Games
}
else
{
msg = await Context.Channel.SendFileAsync(file, msgToSend).ConfigureAwait(false);
msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), "plant.jpg", msgToSend).ConfigureAwait(false);
}
plantedFlowers.AddOrUpdate(Context.Channel.Id, new List<IUserMessage>() { msg }, (id, old) => { old.Add(msg); return old; });
}
#endif
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -17,7 +18,7 @@ namespace NadekoBot.Modules.Games
[Group]
public class PollCommands : ModuleBase
{
public static ConcurrentDictionary<IGuild, Poll> ActivePolls = new ConcurrentDictionary<IGuild, Poll>();
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
@ -31,6 +32,18 @@ namespace NadekoBot.Modules.Games
public Task PublicPoll([Remainder] string arg = null)
=> InternalStartPoll(arg, isPublic: true);
[NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public async Task PollStats()
{
Games.Poll poll;
if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll))
return;
await Context.Channel.EmbedAsync(poll.GetStats("Current Poll Results"));
}
private async Task InternalStartPoll(string arg, bool isPublic = false)
{
var channel = (ITextChannel)Context.Channel;
@ -44,7 +57,7 @@ namespace NadekoBot.Modules.Games
return;
var poll = new Poll(Context.Message, data[0], data.Skip(1), isPublic: isPublic);
if (ActivePolls.TryAdd(channel.Guild, poll))
if (ActivePolls.TryAdd(channel.Guild.Id, poll))
{
await poll.StartPoll().ConfigureAwait(false);
}
@ -60,7 +73,7 @@ namespace NadekoBot.Modules.Games
var channel = (ITextChannel)Context.Channel;
Poll poll;
ActivePolls.TryRemove(channel.Guild, out poll);
ActivePolls.TryRemove(channel.Guild.Id, out poll);
await poll.StopPoll().ConfigureAwait(false);
}
}
@ -69,20 +82,55 @@ namespace NadekoBot.Modules.Games
{
private readonly IUserMessage originalMessage;
private readonly IGuild guild;
private readonly string[] answers;
private string[] Answers { get; }
private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>();
private readonly string question;
private DateTime started;
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
private readonly bool isPublic;
public bool IsPublic { get; }
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{
this.originalMessage = umsg;
this.guild = ((ITextChannel)umsg.Channel).Guild;
this.question = question;
this.answers = enumerable as string[] ?? enumerable.ToArray();
this.isPublic = isPublic;
this.Answers = enumerable as string[] ?? enumerable.ToArray();
this.IsPublic = isPublic;
}
public EmbedBuilder GetStats(string title)
{
var results = participants.GroupBy(kvp => kvp.Value)
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value)
.ToArray();
var eb = new EmbedBuilder().WithTitle(title);
var sb = new StringBuilder()
.AppendLine(Format.Bold(question))
.AppendLine();
var totalVotesCast = 0;
if (results.Length == 0)
{
sb.AppendLine("No votes cast.");
}
else
{
for (int i = 0; i < results.Length; i++)
{
var result = results[i];
sb.AppendLine($"`{i + 1}.` {Format.Bold(Answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes.");
totalVotesCast += result.Value;
}
}
eb.WithDescription(sb.ToString())
.WithFooter(efb => efb.WithText(totalVotesCast + " total votes cast."));
return eb;
}
public async Task StartPoll()
@ -91,8 +139,8 @@ namespace NadekoBot.Modules.Games
NadekoBot.Client.MessageReceived += Vote;
var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n";
var num = 1;
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!isPublic)
msgToSend = Answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!IsPublic)
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
@ -102,30 +150,7 @@ namespace NadekoBot.Modules.Games
public async Task StopPoll()
{
NadekoBot.Client.MessageReceived -= Vote;
try
{
var results = participants.GroupBy(kvp => kvp.Value)
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value);
var totalVotesCast = results.Sum(kvp => kvp.Value);
if (totalVotesCast == 0)
{
await originalMessage.Channel.SendMessageAsync("📄 **No votes have been cast.**").ConfigureAwait(false);
return;
}
var closeMessage = $"--------------**POLL CLOSED**--------------\n" +
$"📄 , here are the results:\n";
closeMessage = results.Aggregate(closeMessage, (current, kvp) => current + $"`{kvp.Key}.` **[{answers[kvp.Key - 1]}]**" +
$" has {kvp.Value} votes." +
$"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n");
await originalMessage.Channel.SendConfirmAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine($"Error in poll game {ex}");
}
await originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false);
}
private async void Vote(SocketMessage imsg)
@ -141,11 +166,11 @@ namespace NadekoBot.Modules.Games
int vote;
if (!int.TryParse(imsg.Content, out vote))
return;
if (vote < 1 || vote > answers.Length)
if (vote < 1 || vote > Answers.Length)
return;
IMessageChannel ch;
if (isPublic)
if (IsPublic)
{
//if public, channel must be the same the poll started in
if (originalMessage.Channel.Id != imsg.Channel.Id)
@ -167,7 +192,7 @@ namespace NadekoBot.Modules.Games
//user can vote only once
if (participants.TryAdd(msg.Author.Id, vote))
{
if (!isPublic)
if (!IsPublic)
{
await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
}

View File

@ -49,7 +49,7 @@ namespace NadekoBot.Modules.NSFW
var link = await provider.ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link))
{
if (noError)
if (!noError)
await channel.SendErrorAsync("No results found.").ConfigureAwait(false);
return;
}

View File

@ -23,13 +23,18 @@ namespace NadekoBot.Modules.Permissions
[Group]
public class BlacklistCommands : ModuleBase
{
public static ConcurrentHashSet<BlacklistItem> BlacklistedItems { get; set; } = new ConcurrentHashSet<BlacklistItem>();
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; } = new ConcurrentHashSet<ulong>();
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; } = new ConcurrentHashSet<ulong>();
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; } = new ConcurrentHashSet<ulong>();
static BlacklistCommands()
{
using (var uow = DbHandler.UnitOfWork())
{
BlacklistedItems = new ConcurrentHashSet<BlacklistItem>(uow.BotConfig.GetOrCreate().Blacklist);
var blacklist = uow.BotConfig.GetOrCreate().Blacklist;
BlacklistedUsers = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.User).Select(c => c.ItemId));
BlacklistedGuilds = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Server).Select(c => c.ItemId));
BlacklistedChannels = new ConcurrentHashSet<ulong>(blacklist.Where(bi => bi.Type == BlacklistType.Channel).Select(c => c.ItemId));
}
}
@ -66,12 +71,34 @@ namespace NadekoBot.Modules.Permissions
{
var item = new BlacklistItem { ItemId = id, Type = type };
uow.BotConfig.GetOrCreate().Blacklist.Add(item);
BlacklistedItems.Add(item);
if (type == BlacklistType.Server)
{
BlacklistedGuilds.Add(id);
}
else if (type == BlacklistType.Channel)
{
BlacklistedChannels.Add(id);
}
else if (type == BlacklistType.User)
{
BlacklistedUsers.Add(id);
}
}
else
{
uow.BotConfig.GetOrCreate().Blacklist.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
BlacklistedItems.RemoveWhere(bi => bi.ItemId == id && bi.Type == type);
if (type == BlacklistType.Server)
{
BlacklistedGuilds.TryRemove(id);
}
else if (type == BlacklistType.Channel)
{
BlacklistedChannels.TryRemove(id);
}
else if (type == BlacklistType.User)
{
BlacklistedUsers.TryRemove(id);
}
}
await uow.CompleteAsync().ConfigureAwait(false);
}

View File

@ -23,8 +23,8 @@ namespace NadekoBot.Modules.Searches.Commands.Models
public class Main
{
public double temp { get; set; }
public int pressure { get; set; }
public int humidity { get; set; }
public float pressure { get; set; }
public float humidity { get; set; }
public double temp_min { get; set; }
public double temp_max { get; set; }
}

View File

@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Utility
.AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue(guild.Roles.Count().ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
.WithImageUrl(guild.IconUrl)
.WithColor(NadekoBot.OkColor);
if (guild.Emojis.Count() > 0)
@ -92,8 +92,7 @@ namespace NadekoBot.Modules.Utility
embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Game**").WithValue($"{(user.Game?.Name == null ? "-" : user.Game.Value.Name)}").WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count})** - {string.Join(", ", user.GetRoles().Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count})** - {string.Join(", ", user.GetRoles().Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
.WithThumbnailUrl(user.AvatarUrl)
.WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);

View File

@ -240,15 +240,27 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ChannelTopic()
public async Task ChannelTopic([Remainder]ITextChannel channel = null)
{
var channel = (ITextChannel)Context.Channel;
if (channel == null)
channel = (ITextChannel)Context.Channel;
var topic = channel.Topic;
if (string.IsNullOrWhiteSpace(topic))
await channel.SendErrorAsync("No topic set.");
await channel.SendErrorAsync("No topic set.").ConfigureAwait(false);
else
await channel.SendConfirmAsync("Channel topic", topic);
await channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireBotPermission(ChannelPermission.CreateInstantInvite)]
[RequireUserPermission(ChannelPermission.CreateInstantInvite)]
public async Task CreateInvite()
{
var invite = await ((ITextChannel)Context.Channel).CreateInviteAsync(0, null, isUnique: true);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} https://discord.gg/{invite.Code}");
}
[NadekoCommand, Usage, Description, Aliases]
@ -269,8 +281,10 @@ namespace NadekoBot.Modules.Utility
.AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(stats.OwnerIds).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuilds().Count} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuildsCount()} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true))
#if !GLOBAL_NADEKO
.WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued."))
#endif
);
}
@ -298,7 +312,7 @@ namespace NadekoBot.Modules.Utility
if (page < 0)
return;
var guilds = NadekoBot.Client.GetGuilds().OrderBy(g => g.Name).Skip((page - 1) * 15).Take(15);
var guilds = await Task.Run(() => NadekoBot.Client.GetGuilds().OrderBy(g => g.Name).Skip((page - 1) * 15).Take(15)).ConfigureAwait(false);
if (!guilds.Any())
{

View File

@ -91,7 +91,9 @@ namespace NadekoBot
//connect
await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false);
await Client.ConnectAsync().ConfigureAwait(false);
//await Client.DownloadAllUsersAsync().ConfigureAwait(false);
#if !GLOBAL_NADEKO
await Client.DownloadAllUsersAsync().ConfigureAwait(false);
#endif
_log.Info("Connected");

View File

@ -1841,6 +1841,33 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to createinvite crinv.
/// </summary>
public static string createinvite_cmd {
get {
return ResourceManager.GetString("createinvite_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Creates a new invite which has infinite max uses and never expires..
/// </summary>
public static string createinvite_desc {
get {
return ResourceManager.GetString("createinvite_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}crinv`.
/// </summary>
public static string createinvite_usage {
get {
return ResourceManager.GetString("createinvite_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to createrole cr.
/// </summary>
@ -5135,6 +5162,33 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to pollstats.
/// </summary>
public static string pollstats_cmd {
get {
return ResourceManager.GetString("pollstats_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows the poll results without stopping the poll on this server..
/// </summary>
public static string pollstats_desc {
get {
return ResourceManager.GetString("pollstats_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}pollstats`.
/// </summary>
public static string pollstats_usage {
get {
return ResourceManager.GetString("pollstats_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to prune clr.
/// </summary>

View File

@ -2871,4 +2871,22 @@
<data name="rotaterolecolor_usage" xml:space="preserve">
<value>`{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole`</value>
</data>
<data name="createinvite_cmd" xml:space="preserve">
<value>createinvite crinv</value>
</data>
<data name="createinvite_desc" xml:space="preserve">
<value>Creates a new invite which has infinite max uses and never expires.</value>
</data>
<data name="createinvite_usage" xml:space="preserve">
<value>`{0}crinv`</value>
</data>
<data name="pollstats_cmd" xml:space="preserve">
<value>pollstats</value>
</data>
<data name="pollstats_desc" xml:space="preserve">
<value>Shows the poll results without stopping the poll on this server.</value>
</data>
<data name="pollstats_usage" xml:space="preserve">
<value>`{0}pollstats`</value>
</data>
</root>

View File

@ -29,10 +29,16 @@ namespace Services.CleverBotApi
public static ChatterBot Create(ChatterBotType type, object arg)
{
#if GLOBAL_NADEKO
var url = "http://www.cleverbot.com/webservicemin?uc=321&botapi=nadekobot";
#else
var url = "http://www.cleverbot.com/webservicemin?uc=321";
#endif
switch (type)
{
case ChatterBotType.CLEVERBOT:
return new Cleverbot("http://www.cleverbot.com/", "http://www.cleverbot.com/webservicemin?uc=321", 26);
return new Cleverbot("http://www.cleverbot.com/", url, 26);
case ChatterBotType.JABBERWACKY:
return new Cleverbot("http://jabberwacky.com", "http://jabberwacky.com/webservicemin", 20);
case ChatterBotType.PANDORABOTS:

View File

@ -46,7 +46,7 @@ namespace Services.CleverBotApi
private readonly int endIndex;
private readonly string url;
private readonly IDictionary<string, string> vars;
private readonly CookieCollection cookies;
private readonly CookieCollection cookies;
public CleverbotSession(string baseUrl, string url, int endIndex)
{
@ -60,7 +60,7 @@ namespace Services.CleverBotApi
//vars["fno"] = "0";
//vars["sub"] = "Say";
//vars["cleanslate"] = "false";
cookies = Utils.GetCookies(baseUrl);
cookies = Utils.GetCookies(baseUrl);
}
public async Task<ChatterBotThought> Think(ChatterBotThought thought)

View File

@ -17,6 +17,8 @@ using static NadekoBot.Modules.Administration.Administration;
using NadekoBot.Modules.CustomReactions;
using NadekoBot.Modules.Games;
using System.Collections.Concurrent;
using System.Threading;
using NadekoBot.DataStructures;
namespace NadekoBot.Services
{
@ -28,6 +30,8 @@ namespace NadekoBot.Services
}
public class CommandHandler
{
public const int GlobalCommandsCooldown = 1500;
private ShardedDiscordClient _client;
private CommandService _commandService;
private Logger _log;
@ -39,11 +43,19 @@ namespace NadekoBot.Services
//userid/msg count
public ConcurrentDictionary<ulong, uint> UserMessagesSent { get; } = new ConcurrentDictionary<ulong, uint>();
public ConcurrentHashSet<ulong> UsersOnShortCooldown { get; } = new ConcurrentHashSet<ulong>();
private Timer clearUsersOnShortCooldown { get; }
public CommandHandler(ShardedDiscordClient client, CommandService commandService)
{
_client = client;
_commandService = commandService;
_log = LogManager.GetCurrentClassLogger();
clearUsersOnShortCooldown = new Timer((_) =>
{
UsersOnShortCooldown.Clear();
}, null, GlobalCommandsCooldown, GlobalCommandsCooldown);
}
public async Task StartHandling()
{
@ -62,162 +74,202 @@ namespace NadekoBot.Services
_client.MessageReceived += MessageReceivedHandler;
}
private async void MessageReceivedHandler(SocketMessage msg)
private async Task<bool> TryRunCleverbot(SocketUserMessage usrMsg, IGuild guild)
{
if (guild == null)
return false;
try
{
var usrMsg = msg as SocketUserMessage;
if (usrMsg == null)
return;
//if (!usrMsg.IsAuthor())
// UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
if (msg.Author.IsBot || !NadekoBot.Ready) //no bots
return;
var guild = (msg.Channel as SocketTextChannel)?.Guild;
if (guild != null && guild.OwnerId != msg.Author.Id)
var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg).ConfigureAwait(false);
if (cleverbotExecuted)
{
//todo split checks into their own modules
if (Permissions.FilterCommands.InviteFilteringChannels.Contains(msg.Channel.Id) ||
Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id))
{
if (usrMsg.Content.IsDiscordInvite())
{
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
return;
}
catch (HttpException ex)
{
_log.Warn("I do not have permission to filter invites in channel with id " + msg.Channel.Id, ex);
}
}
}
var filteredWords = Permissions.FilterCommands.FilteredWordsForChannel(msg.Channel.Id, guild.Id).Concat(Permissions.FilterCommands.FilteredWordsForServer(guild.Id));
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
if (filteredWords.Any(w => wordsInMessage.Contains(w)))
{
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
return;
}
catch (HttpException ex)
{
_log.Warn("I do not have permission to filter words in channel with id " + msg.Channel.Id, ex);
}
}
}
BlacklistItem blacklistedItem;
if ((blacklistedItem = Permissions.BlacklistCommands.BlacklistedItems.FirstOrDefault(bi =>
(bi.Type == BlacklistItem.BlacklistType.Server && bi.ItemId == guild?.Id) ||
(bi.Type == BlacklistItem.BlacklistType.Channel && bi.ItemId == msg.Channel.Id) ||
(bi.Type == BlacklistItem.BlacklistType.User && bi.ItemId == msg.Author.Id))) != null)
{
return;
}
try
{
var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg);
if (cleverbotExecuted)
{
_log.Info($@"CleverBot Executed
_log.Info($@"CleverBot Executed
Server: {guild.Name} [{guild.Id}]
Channel: {usrMsg.Channel?.Name} [{usrMsg.Channel?.Id}]
UserId: {usrMsg.Author} [{usrMsg.Author.Id}]
Message: {usrMsg.Content}");
return;
}
return true;
}
catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
}
catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); }
return false;
}
try
{
// maybe this message is a custom reaction
var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false);
private bool IsBlacklisted(IGuild guild, SocketUserMessage usrMsg) =>
(guild != null && BlacklistCommands.BlacklistedGuilds.Contains(guild.Id)) ||
BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) ||
BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id);
//if it was, don't execute the command
if (crExecuted)
return;
}
catch { }
string messageContent = usrMsg.Content;
private async Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, Stopwatch sw)
{
await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false);
_log.Info("Command Executed after {4}s\n\t" +
"User: {0}\n\t" +
"Server: {1}\n\t" +
"Channel: {2}\n\t" +
"Message: {3}",
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content, // {3}
sw.Elapsed.TotalSeconds);
}
var sw = new Stopwatch();
sw.Start();
var exec = await ExecuteCommand(new CommandContext(_client.MainClient, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best);
var command = exec.CommandInfo;
var permCache = exec.PermissionCache;
var result = exec.Result;
sw.Stop();
var channel = (msg.Channel as ITextChannel);
if (result.IsSuccess)
{
await CommandExecuted(usrMsg, command);
_log.Info("Command Executed after {4}s\n\t" +
"User: {0}\n\t" +
"Server: {1}\n\t" +
"Channel: {2}\n\t" +
"Message: {3}",
msg.Author + " [" + msg.Author.Id + "]", // {0}
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content, // {3}
sw.Elapsed.TotalSeconds // {4}
);
}
else if (!result.IsSuccess && result.Error != CommandError.UnknownCommand)
{
_log.Warn("Command Errored after {5}s\n\t" +
private void LogErroredExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, Stopwatch sw)
{
_log.Warn("Command Errored after {5}s\n\t" +
"User: {0}\n\t" +
"Server: {1}\n\t" +
"Channel: {2}\n\t" +
"Message: {3}\n\t" +
"Error: {4}",
msg.Author + " [" + msg.Author.Id + "]", // {0}
usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0}
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content,// {3}
result.ErrorReason, // {4}
exec.Result.ErrorReason, // {4}
sw.Elapsed.TotalSeconds // {5}
);
if (guild != null && command != null && result.Error == CommandError.Exception)
}
private async Task<bool> InviteFiltered(IGuild guild, SocketUserMessage usrMsg)
{
if ((Permissions.FilterCommands.InviteFilteringChannels.Contains(usrMsg.Channel.Id) ||
Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id)) &&
usrMsg.Content.IsDiscordInvite())
{
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
return true;
}
catch (HttpException ex)
{
_log.Warn("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex);
return true;
}
}
return false;
}
private async Task<bool> WordFiltered(IGuild guild, SocketUserMessage usrMsg)
{
var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id);
var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id);
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
{
foreach (var word in wordsInMessage)
{
if (filteredChannelWords.Contains(word) ||
filteredServerWords.Contains(word))
{
if (permCache != null && permCache.Verbose)
try { await msg.Channel.SendMessageAsync("⚠️ " + result.ErrorReason).ConfigureAwait(false); } catch { }
try
{
await usrMsg.DeleteAsync().ConfigureAwait(false);
}
catch (HttpException ex)
{
_log.Warn("I do not have permission to filter words in channel with id " + usrMsg.Channel.Id, ex);
}
return true;
}
}
}
return false;
}
private async void MessageReceivedHandler(SocketMessage msg)
{
try
{
if (msg.Author.IsBot || !NadekoBot.Ready) //no bots, wait until bot connected and initialized
return;
var usrMsg = msg as SocketUserMessage;
if (usrMsg == null) //has to be an user message, not system/other messages.
return;
// track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
// Bot will ignore commands which are ran more often than what specified by
// GlobalCommandsCooldown constant (miliseconds)
if (!UsersOnShortCooldown.Add(usrMsg.Author.Id))
return;
var channel = msg.Channel as SocketTextChannel;
var guild = channel?.Guild;
if (guild != null && guild.OwnerId != msg.Author.Id)
{
if (await InviteFiltered(guild, usrMsg).ConfigureAwait(false))
return;
if (await WordFiltered(guild, usrMsg).ConfigureAwait(false))
return;
}
if (IsBlacklisted(guild, usrMsg))
return;
var cleverBotRan = await TryRunCleverbot(usrMsg, guild).ConfigureAwait(false);
if (cleverBotRan)
return;
// maybe this message is a custom reaction
var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false);
if (crExecuted) //if it was, don't execute the command
return;
string messageContent = usrMsg.Content;
// execute the command and measure the time it took
var sw = Stopwatch.StartNew();
var exec = await ExecuteCommand(new CommandContext(_client.MainClient, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best);
sw.Stop();
if (exec.Result.IsSuccess)
{
await LogSuccessfulExecution(usrMsg, exec, channel, sw).ConfigureAwait(false);
}
else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand)
{
LogErroredExecution(usrMsg, exec, channel, sw);
if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception)
{
if (exec.PermissionCache != null && exec.PermissionCache.Verbose)
try { await msg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { }
}
}
else
{
if (msg.Channel is IPrivateChannel)
{
//rofl, gotta do this to prevent this message from occuring on polls
// rofl, gotta do this to prevent dm help message being sent to
// users who are voting on private polls (sending a number in a DM)
int vote;
if (int.TryParse(msg.Content, out vote)) return;
await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels);
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false);
}
}
}
catch (Exception ex)
{
_log.Warn(ex, "Error in CommandHandler");
_log.Warn("Error in CommandHandler");
_log.Warn(ex);
if (ex.InnerException != null)
_log.Warn(ex.InnerException, "Inner Exception of the error in CommandHandler");
{
_log.Warn("Inner Exception of the error in CommandHandler");
_log.Warn(ex.InnerException);
}
}
return;
}
public Task<ExecuteCommandResult> ExecuteCommandAsync(CommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
=> ExecuteCommand(context, context.Message.Content.Substring(argPos), dependencyMap, multiMatchHandling);
@ -312,19 +364,5 @@ namespace NadekoBot.Services
return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload."));
}
public struct ExecuteCommandResult
{
public readonly CommandInfo CommandInfo;
public readonly PermissionCache PermissionCache;
public readonly IResult Result;
public ExecuteCommandResult(CommandInfo commandInfo, PermissionCache cache, IResult result)
{
this.CommandInfo = commandInfo;
this.PermissionCache = cache;
this.Result = result;
}
}
}
}

View File

@ -0,0 +1,66 @@
using Discord;
using Discord.WebSocket;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Discord
{
public class ReactionEventWrapper : IDisposable
{
public SocketMessage Message { get; }
public event Action<SocketReaction> OnReactionAdded = delegate { };
public event Action<SocketReaction> OnReactionRemoved = delegate { };
public event Action OnReactionsCleared = delegate { };
public ReactionEventWrapper(SocketMessage msg)
{
if (msg == null)
throw new ArgumentNullException(nameof(msg));
Message = msg;
msg.Discord.ReactionAdded += Discord_ReactionAdded;
msg.Discord.ReactionRemoved += Discord_ReactionRemoved;
msg.Discord.ReactionsCleared += Discord_ReactionsCleared;
}
private Task Discord_ReactionsCleared(ulong messageId, Optional<SocketUserMessage> reaction)
{
if (messageId == Message.Id)
OnReactionsCleared?.Invoke();
return Task.CompletedTask;
}
private Task Discord_ReactionRemoved(ulong messageId, Optional<SocketUserMessage> arg2, SocketReaction reaction)
{
if (messageId == Message.Id)
OnReactionRemoved?.Invoke(reaction);
return Task.CompletedTask;
}
private Task Discord_ReactionAdded(ulong messageId, Optional<SocketUserMessage> message, SocketReaction reaction)
{
if(messageId == Message.Id)
OnReactionAdded?.Invoke(reaction);
return Task.CompletedTask;
}
public void UnsubAll()
{
Message.Discord.ReactionAdded -= Discord_ReactionAdded;
Message.Discord.ReactionRemoved -= Discord_ReactionRemoved;
Message.Discord.ReactionsCleared -= Discord_ReactionsCleared;
}
private bool disposing = false;
public void Dispose()
{
if (disposing)
return;
disposing = true;
UnsubAll();
}
}
}

View File

@ -20,12 +20,15 @@ namespace NadekoBot.Services.Impl
public string Library => "Discord.Net";
public int MessageCounter { get; private set; } = 0;
public int CommandsRan { get; private set; } = 0;
public string Heap => Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString();
public double MessagesPerSecond => MessageCounter / (double)GetUptime().TotalSeconds;
public int TextChannels => client.GetGuilds().SelectMany(g => g.Channels.Where(c => c is ITextChannel)).Count();
public int VoiceChannels => client.GetGuilds().SelectMany(g => g.Channels.Where(c => c is IVoiceChannel)).Count();
public string Heap =>
Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString();
public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds;
private int _textChannels = 0;
public int TextChannels => _textChannels;
private int _voiceChannels = 0;
public int VoiceChannels => _voiceChannels;
public string OwnerIds => string.Join(", ", NadekoBot.Credentials.OwnerIds);
Timer carbonitexTimer { get; }
public StatsService(ShardedDiscordClient client, CommandHandler cmdHandler)
@ -39,17 +42,56 @@ namespace NadekoBot.Services.Impl
this.client.Disconnected += _ => Reset();
this.client.Connected += () =>
{
var guilds = this.client.GetGuilds();
_textChannels = guilds.Sum(g => g.Channels.Where(cx => cx is ITextChannel).Count());
_voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels;
};
this.client.ChannelCreated += (c) =>
{
if (c is ITextChannel)
++_textChannels;
else if (c is IVoiceChannel)
++_voiceChannels;
};
this.client.ChannelDestroyed += (c) =>
{
if (c is ITextChannel)
--_textChannels;
else if (c is IVoiceChannel)
--_voiceChannels;
};
this.client.JoinedGuild += (g) =>
{
var tc = g.Channels.Where(cx => cx is ITextChannel).Count();
var vc = g.Channels.Count - tc;
_textChannels += tc;
_voiceChannels += vc;
};
this.client.LeftGuild += (g) =>
{
var tc = g.Channels.Where(cx => cx is ITextChannel).Count();
var vc = g.Channels.Count - tc;
_textChannels -= tc;
_voiceChannels -= vc;
};
this.carbonitexTimer = new Timer(async (state) =>
{
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.CarbonKey))
return;
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.CarbonKey))
return;
try
{
using (var http = new HttpClient())
{
using (var content = new FormUrlEncodedContent(
new Dictionary<string, string> {
{ "servercount", this.client.GetGuilds().Count.ToString() },
{ "servercount", this.client.GetGuildsCount().ToString() },
{ "key", NadekoBot.Credentials.CarbonKey }}))
{
content.Headers.Clear();
@ -71,7 +113,7 @@ Bot Version: [{BotVersion}]
Bot ID: {curUser.Id}
Owner ID(s): {OwnerIds}
Uptime: {GetUptimeString()}
Servers: {client.GetGuilds().Count} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels}
Servers: {client.GetGuildsCount()} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels}
Commands Ran this session: {CommandsRan}
Messages: {MessageCounter} [{MessagesPerSecond:F2}/sec] Heap: [{Heap} MB]");
}

View File

@ -18,6 +18,7 @@ namespace NadekoBot
public event Action<SocketMessage> MessageReceived = delegate { };
public event Action<SocketGuildUser> UserLeft = delegate { };
public event Action<SocketUser, SocketUser> UserUpdated = delegate { };
public event Action<SocketGuildUser, SocketGuildUser> GuildUserUpdated = delegate { };
public event Action<Optional<SocketMessage>, SocketMessage> MessageUpdated = delegate { };
public event Action<ulong, Optional<SocketMessage>> MessageDeleted = delegate { };
public event Action<SocketUser, SocketGuild> UserBanned = delegate { };
@ -27,11 +28,18 @@ namespace NadekoBot
public event Action<SocketChannel> ChannelCreated = delegate { };
public event Action<SocketChannel> ChannelDestroyed = delegate { };
public event Action<SocketChannel, SocketChannel> ChannelUpdated = delegate { };
public event Action<SocketGuild> JoinedGuild = delegate { };
public event Action<SocketGuild> LeftGuild = delegate { };
public event Action<Exception> Disconnected = delegate { };
public event Action Connected = delegate { };
private uint _connectedCount = 0;
private uint _downloadedCount = 0;
private int _guildCount = 0;
private IReadOnlyList<DiscordSocketClient> Clients { get; }
public ShardedDiscordClient(DiscordSocketConfig discordSocketConfig)
@ -54,6 +62,7 @@ namespace NadekoBot
};
client.UserLeft += arg1 => { UserLeft(arg1); return Task.CompletedTask; };
client.UserUpdated += (arg1, gu2) => { UserUpdated(arg1, gu2); return Task.CompletedTask; };
client.GuildMemberUpdated += (arg1, arg2) => { GuildUserUpdated(arg1, arg2); return Task.CompletedTask; };
client.MessageUpdated += (arg1, m2) => { MessageUpdated(arg1, m2); return Task.CompletedTask; };
client.MessageDeleted += (arg1, arg2) => { MessageDeleted(arg1, arg2); return Task.CompletedTask; };
client.UserBanned += (arg1, arg2) => { UserBanned(arg1, arg2); return Task.CompletedTask; };
@ -63,9 +72,13 @@ namespace NadekoBot
client.ChannelCreated += arg => { ChannelCreated(arg); return Task.CompletedTask; };
client.ChannelDestroyed += arg => { ChannelDestroyed(arg); return Task.CompletedTask; };
client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; };
client.JoinedGuild += (arg1) => { JoinedGuild(arg1); ++_guildCount; return Task.CompletedTask; };
client.LeftGuild += (arg1) => { LeftGuild(arg1); --_guildCount; return Task.CompletedTask; };
_log.Info($"Shard #{i} initialized.");
#if GLOBAL_NADEKO
client.Log += Client_Log;
#endif
var j = i;
client.Disconnected += (ex) =>
{
@ -91,11 +104,22 @@ namespace NadekoBot
public SocketSelfUser CurrentUser() =>
Clients[0].CurrentUser;
public IReadOnlyCollection<SocketGuild> GetGuilds() =>
Clients.SelectMany(c => c.Guilds).ToList();
public IEnumerable<SocketGuild> GetGuilds() =>
Clients.SelectMany(c => c.Guilds);
public SocketGuild GetGuild(ulong id) =>
Clients.Select(c => c.GetGuild(id)).FirstOrDefault(g => g != null);
public int GetGuildsCount() =>
_guildCount;
public SocketGuild GetGuild(ulong id)
{
foreach (var c in Clients)
{
var g = c.GetGuild(id);
if (g != null)
return g;
}
return null;
}
public Task<IDMChannel> GetDMChannelAsync(ulong channelId) =>
Clients[0].GetDMChannelAsync(channelId);
@ -120,6 +144,7 @@ namespace NadekoBot
await c.ConnectAsync().ConfigureAwait(false);
sw.Stop();
_log.Info($"Shard #{c.ShardId} connected after {sw.Elapsed.TotalSeconds:F2}s ({++_connectedCount}/{Clients.Count})");
_guildCount += c.Guilds.Count;
}
catch
{
@ -132,6 +157,7 @@ namespace NadekoBot
}
}
}
Connected();
}
internal Task DownloadAllUsersAsync() =>

View File

@ -1,6 +1,8 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using ImageSharp;
using NadekoBot.Services.Discord;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
@ -16,6 +18,13 @@ namespace NadekoBot.Extensions
{
public static class Extensions
{
public static ReactionEventWrapper OnReactionAdded(this SocketMessage msg, Action<SocketReaction> reactionAdded)
{
var wrap = new ReactionEventWrapper(msg);
wrap.OnReactionAdded += reactionAdded;
return wrap;
}
public static void AddFakeHeaders(this HttpClient http)
{
http.DefaultRequestHeaders.Clear();