Merge pull request #74 from Kwoth/dev

ups
This commit is contained in:
samvaio 2017-01-29 19:08:05 +05:30 committed by GitHub
commit c75a625aa7
33 changed files with 512 additions and 192 deletions

@ -1 +1 @@
Subproject commit 7c0cce6d35b04d883cf5ec2d775b051e4bc8739f Subproject commit 4506fc5a54fe31d826649dc413467c52a3cd7896

View File

@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Administration
var channel = msg.Channel as SocketTextChannel; var channel = msg.Channel as SocketTextChannel;
if (channel == null) if (channel == null)
return; return;
if (DeleteMessagesOnCommand.Contains(channel.Guild.Id)) if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
await msg.DeleteAsync().ConfigureAwait(false); await msg.DeleteAsync().ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -25,7 +25,6 @@ namespace NadekoBot.Modules.Administration
static AutoAssignRoleCommands() static AutoAssignRoleCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0) AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId)); .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
@ -46,9 +45,6 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
}; };
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -10,6 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
@ -22,8 +23,8 @@ namespace NadekoBot.Modules.Administration
private static Logger _log { get; } private static Logger _log { get; }
public static List<PlayingStatus> RotatingStatusMessages { get; } public static List<PlayingStatus> RotatingStatusMessages { get; }
public static bool RotatingStatuses { get; private set; } = false; public static bool RotatingStatuses { get; private set; } = false;
private static Timer _t { get; }
//todo wtf is with this while(true) in constructor
static PlayingRotateCommands() static PlayingRotateCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
@ -31,25 +32,25 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages; RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses; RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
var t = Task.Run(async () =>
_t = new Timer(async (_) =>
{ {
var index = 0; var index = 0;
do
{
try try
{ {
if (!RotatingStatuses) if (!RotatingStatuses)
continue; return;
else else
{ {
if (index >= RotatingStatusMessages.Count) if (index >= RotatingStatusMessages.Count)
index = 0; index = 0;
if (!RotatingStatusMessages.Any()) if (!RotatingStatusMessages.Any())
continue; return;
var status = RotatingStatusMessages[index++].Status; var status = RotatingStatusMessages[index++].Status;
if (string.IsNullOrWhiteSpace(status)) if (string.IsNullOrWhiteSpace(status))
continue; return;
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value())); PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
var shards = NadekoBot.Client.Shards; var shards = NadekoBot.Client.Shards;
for (int i = 0; i < shards.Count; i++) for (int i = 0; i < shards.Count; i++)
@ -67,12 +68,7 @@ namespace NadekoBot.Modules.Administration
{ {
_log.Warn("Rotating playing status errored.\n" + ex); _log.Warn("Rotating playing status errored.\n" + ex);
} }
finally }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
{
await Task.Delay(TimeSpan.FromMinutes(1));
}
} while (true);
});
} }
public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } = public static Dictionary<string, Func<string>> PlayingPlaceholders { get; } =
@ -92,7 +88,7 @@ namespace NadekoBot.Modules.Administration
} }
}, },
{ "%queued%", () => Music.Music.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()}, { "%queued%", () => Music.Music.MusicPlayers.Sum(kvp => kvp.Value.Playlist.Count).ToString()},
{ "%time%", () => DateTime.Now.ToString("hh:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) }, { "%time%", () => DateTime.Now.ToString("HH:mm " + TimeZoneInfo.Local.StandardName.GetInitials()) },
{ "%shardcount%", () => NadekoBot.Client.Shards.Count.ToString() }, { "%shardcount%", () => NadekoBot.Client.Shards.Count.ToString() },
}; };

View File

@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Administration
if (ids[1].ToUpperInvariant().StartsWith("C:")) if (ids[1].ToUpperInvariant().StartsWith("C:"))
{ {
var cid = ulong.Parse(ids[1].Substring(2)); var cid = ulong.Parse(ids[1].Substring(2));
var ch = (await server.GetTextChannelsAsync()).Where(c => c.Id == cid).FirstOrDefault(); var ch = server.TextChannels.Where(c => c.Id == cid).FirstOrDefault();
if (ch == null) if (ch == null)
{ {
return; return;
@ -159,9 +159,7 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task Announce([Remainder] string message) public async Task Announce([Remainder] string message)
{ {
var channels = await Task.WhenAll(NadekoBot.Client.GetGuilds().Select(g => var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray();
g.GetDefaultChannelAsync()
)).ConfigureAwait(false);
if (channels == null) if (channels == null)
return; return;
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message))) await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message)))

View File

@ -4,9 +4,11 @@ using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,24 +19,76 @@ namespace NadekoBot.Modules.Administration
[Group] [Group]
public class ServerGreetCommands : ModuleBase public class ServerGreetCommands : ModuleBase
{ {
//make this to a field in the guildconfig table
class GreetSettings
{
public int AutoDeleteGreetMessagesTimer { get; set; }
public int AutoDeleteByeMessagesTimer { get; set; }
public ulong GreetMessageChannelId { get; set; }
public ulong ByeMessageChannelId { get; set; }
public bool SendDmGreetMessage { get; set; }
public string DmGreetMessageText { get; set; }
public bool SendChannelGreetMessage { get; set; }
public string ChannelGreetMessageText { get; set; }
public bool SendChannelByeMessage { get; set; }
public string ChannelByeMessageText { get; set; }
public static GreetSettings Create(GuildConfig g) => new GreetSettings()
{
AutoDeleteByeMessagesTimer = g.AutoDeleteByeMessagesTimer,
AutoDeleteGreetMessagesTimer = g.AutoDeleteGreetMessagesTimer,
GreetMessageChannelId = g.GreetMessageChannelId,
ByeMessageChannelId = g.ByeMessageChannelId,
SendDmGreetMessage = g.SendDmGreetMessage,
DmGreetMessageText = g.DmGreetMessageText,
SendChannelGreetMessage = g.SendChannelGreetMessage,
ChannelGreetMessageText = g.ChannelGreetMessageText,
SendChannelByeMessage = g.SendChannelByeMessage,
ChannelByeMessageText = g.ChannelByeMessageText,
};
}
private static Logger _log { get; } private static Logger _log { get; }
private static ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache { get; } = new ConcurrentDictionary<ulong, GreetSettings>();
static ServerGreetCommands() static ServerGreetCommands()
{ {
NadekoBot.Client.UserJoined += UserJoined; NadekoBot.Client.UserJoined += UserJoined;
NadekoBot.Client.UserLeft += UserLeft; NadekoBot.Client.UserLeft += UserLeft;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, (g) => GreetSettings.Create(g)));
} }
private static GreetSettings GetOrAddSettingsForGuild(ulong guildId)
{
GreetSettings settings;
GuildConfigsCache.TryGetValue(guildId, out settings);
if (settings != null)
return settings;
using (var uow = DbHandler.UnitOfWork())
{
var gc = uow.GuildConfigs.For(guildId, set => set);
settings = GreetSettings.Create(gc);
}
GuildConfigsCache.TryAdd(guildId, settings);
return settings;
}
//todo optimize ASAP //todo optimize ASAP
private static async Task UserLeft(IGuildUser user) private static async Task UserLeft(IGuildUser user)
{ {
try try
{ {
GuildConfig conf; var conf = GetOrAddSettingsForGuild(user.GuildId);
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (!conf.SendChannelByeMessage) return; if (!conf.SendChannelByeMessage) return;
var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId); var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId);
@ -62,11 +116,7 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
GuildConfig conf; var conf = GetOrAddSettingsForGuild(user.GuildId);
using (var uow = DbHandler.UnitOfWork())
{
conf = uow.GuildConfigs.For(user.Guild.Id, set => set);
}
if (conf.SendChannelGreetMessage) if (conf.SendChannelGreetMessage)
{ {
@ -133,6 +183,9 @@ namespace NadekoBot.Modules.Administration
var conf = uow.GuildConfigs.For(id, set => set); var conf = uow.GuildConfigs.For(id, set => set);
conf.AutoDeleteGreetMessagesTimer = timer; conf.AutoDeleteGreetMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
} }
@ -159,6 +212,9 @@ namespace NadekoBot.Modules.Administration
enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage; enabled = conf.SendChannelGreetMessage = value ?? !conf.SendChannelGreetMessage;
conf.GreetMessageChannelId = channelId; conf.GreetMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
return enabled; return enabled;
@ -201,6 +257,9 @@ namespace NadekoBot.Modules.Administration
conf.ChannelGreetMessageText = message; conf.ChannelGreetMessageText = message;
greetMsgEnabled = conf.SendChannelGreetMessage; greetMsgEnabled = conf.SendChannelGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
return greetMsgEnabled; return greetMsgEnabled;
@ -227,6 +286,9 @@ namespace NadekoBot.Modules.Administration
var conf = uow.GuildConfigs.For(guildId, set => set); var conf = uow.GuildConfigs.For(guildId, set => set);
enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage; enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
return enabled; return enabled;
@ -269,6 +331,9 @@ namespace NadekoBot.Modules.Administration
conf.DmGreetMessageText = message; conf.DmGreetMessageText = message;
greetMsgEnabled = conf.SendDmGreetMessage; greetMsgEnabled = conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
return greetMsgEnabled; return greetMsgEnabled;
@ -296,6 +361,9 @@ namespace NadekoBot.Modules.Administration
enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage; enabled = conf.SendChannelByeMessage = value ?? !conf.SendChannelByeMessage;
conf.ByeMessageChannelId = channelId; conf.ByeMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
return enabled; return enabled;
@ -338,6 +406,9 @@ namespace NadekoBot.Modules.Administration
conf.ChannelByeMessageText = message; conf.ChannelByeMessageText = message;
byeMsgEnabled = conf.SendChannelByeMessage; byeMsgEnabled = conf.SendChannelByeMessage;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
return byeMsgEnabled; return byeMsgEnabled;
@ -356,16 +427,19 @@ namespace NadekoBot.Modules.Administration
await Context.Channel.SendConfirmAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false); await Context.Channel.SendConfirmAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false);
} }
private static async Task SetByeDel(ulong id, int timer) private static async Task SetByeDel(ulong guildId, int timer)
{ {
if (timer < 0 || timer > 600) if (timer < 0 || timer > 600)
return; return;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var conf = uow.GuildConfigs.For(id, set => set); var conf = uow.GuildConfigs.For(guildId, set => set);
conf.AutoDeleteByeMessagesTimer = timer; conf.AutoDeleteByeMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
} }

View File

@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
await (await guild.GetOwnerAsync()).SendErrorAsync( await guild.Owner.SendErrorAsync(
"⚠️ I don't have **manage server** and/or **manage channels** permission," + "⚠️ I don't have **manage server** and/or **manage channels** permission," +
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false); $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false);
} }
@ -75,16 +75,16 @@ namespace NadekoBot.Modules.Administration
var beforeVch = before.VoiceChannel; var beforeVch = before.VoiceChannel;
if (beforeVch != null) if (beforeVch != null)
{ {
var textChannel = (await guild.GetTextChannelsAsync()).Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault(); var textChannel = guild.TextChannels.Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault();
if (textChannel != null) if (textChannel != null)
await textChannel.AddPermissionOverwriteAsync(user, await textChannel.AddPermissionOverwriteAsync(user,
new OverwritePermissions(readMessages: PermValue.Deny, new OverwritePermissions(readMessages: PermValue.Deny,
sendMessages: PermValue.Deny)).ConfigureAwait(false); sendMessages: PermValue.Deny)).ConfigureAwait(false);
} }
var afterVch = after.VoiceChannel; var afterVch = after.VoiceChannel;
if (afterVch != null && guild.AFKChannelId != afterVch.Id) if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
{ {
var textChannel = (await guild.GetTextChannelsAsync()) ITextChannel textChannel = guild.TextChannels
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) .Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant())
.FirstOrDefault(); .FirstOrDefault();
if (textChannel == null) if (textChannel == null)

View File

@ -36,10 +36,8 @@ namespace NadekoBot.Modules.ClashOfClans
.GetAllWars() .GetAllWars()
.Select(cw => .Select(cw =>
{ {
cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId) cw.Channel = NadekoBot.Client.GetGuild(cw.GuildId)?
?.GetTextChannelAsync(cw.ChannelId) .GetTextChannel(cw.ChannelId);
.GetAwaiter()
.GetResult();
return cw; return cw;
}) })
.Where(cw => cw.Channel != null) .Where(cw => cw.Channel != null)
@ -322,7 +320,7 @@ namespace NadekoBot.Modules.ClashOfClans
public static async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId) public static async Task<ClashWar> CreateWar(string enemyClan, int size, ulong serverId, ulong channelId)
{ {
var channel = await NadekoBot.Client.GetGuild(serverId)?.GetTextChannelAsync(channelId); var channel = NadekoBot.Client.GetGuild(serverId)?.GetTextChannel(channelId);
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var cw = new ClashWar var cw = new ClashWar

View File

@ -10,14 +10,16 @@ using NadekoBot.Extensions;
using NLog; using NLog;
using System.Diagnostics; using System.Diagnostics;
using Discord.WebSocket; using Discord.WebSocket;
using System;
namespace NadekoBot.Modules.CustomReactions namespace NadekoBot.Modules.CustomReactions
{ {
[NadekoModule("CustomReactions", ".")] [NadekoModule("CustomReactions", ".")]
public class CustomReactions : DiscordModule public class CustomReactions : DiscordModule
{ {
public static ConcurrentHashSet<CustomReaction> GlobalReactions { get; } = new ConcurrentHashSet<CustomReaction>(); private static CustomReaction[] _globalReactions = new CustomReaction[] { };
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>> GuildReactions { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(); public static CustomReaction[] GlobalReactions => _globalReactions;
public static ConcurrentDictionary<ulong, CustomReaction[]> GuildReactions { get; } = new ConcurrentDictionary<ulong, CustomReaction[]>();
public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>(); public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
@ -30,8 +32,8 @@ namespace NadekoBot.Modules.CustomReactions
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var items = uow.CustomReactions.GetAll(); var items = uow.CustomReactions.GetAll();
GuildReactions = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new ConcurrentHashSet<CustomReaction>(g))); GuildReactions = new ConcurrentDictionary<ulong, CustomReaction[]>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => g.ToArray()));
GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null || g.GuildId == 0)); _globalReactions = items.Where(g => g.GuildId == null || g.GuildId == 0).ToArray();
} }
sw.Stop(); sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
@ -46,17 +48,24 @@ namespace NadekoBot.Modules.CustomReactions
return false; return false;
var content = umsg.Content.Trim().ToLowerInvariant(); var content = umsg.Content.Trim().ToLowerInvariant();
ConcurrentHashSet<CustomReaction> reactions; CustomReaction[] reactions;
GuildReactions.TryGetValue(channel.Guild.Id, out reactions); GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
if (reactions != null && reactions.Any()) if (reactions != null && reactions.Any())
{ {
var reaction = reactions.Where(cr => var rs = reactions.Where(cr =>
{ {
if (cr == null)
return false;
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant(); var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault(); }).ToArray();
if (rs.Length != 0)
{
var reaction = rs[new NadekoRandom().Next(0, rs.Length)];
if (reaction != null) if (reaction != null)
{ {
if (reaction.Response != "-") if (reaction.Response != "-")
@ -66,12 +75,19 @@ namespace NadekoBot.Modules.CustomReactions
return true; return true;
} }
} }
var greaction = GlobalReactions.Where(cr => }
var grs = GlobalReactions.Where(cr =>
{ {
if (cr == null)
return false;
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%"); var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant(); var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger); return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault(); }).ToArray();
if (grs.Length == 0)
return false;
var greaction = grs[new NadekoRandom().Next(0, grs.Length)];
if (greaction != null) if (greaction != null)
{ {
@ -114,12 +130,19 @@ namespace NadekoBot.Modules.CustomReactions
if (channel == null) if (channel == null)
{ {
GlobalReactions.Add(cr); Array.Resize(ref _globalReactions, _globalReactions.Length + 1);
_globalReactions[_globalReactions.Length - 1] = cr;
} }
else else
{ {
var reactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); var reactions = GuildReactions.AddOrUpdate(Context.Guild.Id,
reactions.Add(cr); Array.Empty<CustomReaction>(),
(k, old) =>
{
Array.Resize(ref old, old.Length + 1);
old[old.Length - 1] = cr;
return old;
});
} }
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
@ -136,17 +159,17 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (page < 1 || page > 1000) if (page < 1 || page > 1000)
return; return;
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions.Where(cr => cr != null).ToArray();
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else else
{ {
var lastPage = customReactions.Count / 20; var lastPage = customReactions.Length / 20;
await Context.Channel.SendPaginatedConfirmAsync(page, curPage => await Context.Channel.SendPaginatedConfirmAsync(page, curPage =>
new EmbedBuilder().WithOkColor() new EmbedBuilder().WithOkColor()
.WithTitle("Custom reactions") .WithTitle("Custom reactions")
@ -167,11 +190,11 @@ namespace NadekoBot.Modules.CustomReactions
[Priority(1)] [Priority(1)]
public async Task ListCustReact(All x) public async Task ListCustReact(All x)
{ {
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions.Where(cr => cr != null).ToArray();
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
@ -195,11 +218,11 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (page < 1 || page > 10000) if (page < 1 || page > 10000)
return; return;
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions.Where(cr => cr != null).ToArray();
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
@ -225,13 +248,13 @@ namespace NadekoBot.Modules.CustomReactions
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task ShowCustReact(int id) public async Task ShowCustReact(int id)
{ {
ConcurrentHashSet<CustomReaction> customReactions; CustomReaction[] customReactions;
if (Context.Guild == null) if (Context.Guild == null)
customReactions = GlobalReactions; customReactions = GlobalReactions;
else else
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ });
var found = customReactions.FirstOrDefault(cr => cr.Id == id); var found = customReactions.FirstOrDefault(cr => cr?.Id == id);
if (found == null) if (found == null)
await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false); await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
@ -265,13 +288,17 @@ namespace NadekoBot.Modules.CustomReactions
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null) if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
{ {
uow.CustomReactions.Remove(toDelete); uow.CustomReactions.Remove(toDelete);
GlobalReactions.RemoveWhere(cr => cr.Id == toDelete.Id); //todo i can dramatically improve performance of this, if Ids are ordered.
_globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
success = true; success = true;
} }
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId) else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
{ {
uow.CustomReactions.Remove(toDelete); uow.CustomReactions.Remove(toDelete);
GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()).RemoveWhere(cr => cr.Id == toDelete.Id); GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
{
return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
});
success = true; success = true;
} }
if (success) if (success)
@ -312,8 +339,10 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if (page < 1) if (page < 1)
return; return;
var ordered = ReactionStats.OrderByDescending(x => x.Value).ToList(); var ordered = ReactionStats.OrderByDescending(x => x.Value).ToArray();
var lastPage = ordered.Count / 9; if (!ordered.Any())
return;
var lastPage = ordered.Length / 9;
await Context.Channel.SendPaginatedConfirmAsync(page, await Context.Channel.SendPaginatedConfirmAsync(page,
(curPage) => ordered.Skip((curPage - 1) * 9) (curPage) => ordered.Skip((curPage - 1) * 9)
.Take(9) .Take(9)

View File

@ -1,9 +1,11 @@
using Discord; using Discord;
using Discord.WebSocket;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -25,9 +27,13 @@ namespace NadekoBot.Modules.CustomReactions
if(ch == null) if(ch == null)
return ""; return "";
var usrs = (ch.Guild.GetUsersAsync().GetAwaiter().GetResult()); var g = ch.Guild as SocketGuild;
if(g == null)
return "";
return usrs.Skip(new NadekoRandom().Next(0,usrs.Count-1)).Shuffle().FirstOrDefault()?.Mention ?? ""; var users = g.Users.ToArray();
return users[new NadekoRandom().Next(0, users.Length-1)].Mention;
} } } }
//{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } } //{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } }
}; };

View File

@ -59,14 +59,14 @@ namespace NadekoBot.Modules.Gambling
catch catch
{ {
try { await msg.DeleteAsync().ConfigureAwait(false); } try { await msg.DeleteAsync().ConfigureAwait(false); }
catch { } catch { return; }
} }
} }
using (msg.OnReaction(async (r) => using (msg.OnReaction(async (r) =>
{ {
try try
{ {
if (r.Emoji.Name == "🌸" && r.User.IsSpecified && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
{ {
try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { } try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { }
} }

View File

@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Gambling
var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown); var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
var membersArray = members as IUser[] ?? members.ToArray(); var membersArray = members as IUser[] ?? members.ToArray();
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)]; var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}** ID: `{usr.Id}`").ConfigureAwait(false); await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -69,6 +69,7 @@ namespace NadekoBot.Modules.Games
private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>(); private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>();
public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions; public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions;
private readonly ConcurrentHashSet<ulong> usersWhoSubmitted = new ConcurrentHashSet<ulong>();
private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>();
private int spamCount = 0; private int spamCount = 0;
@ -190,10 +191,6 @@ namespace NadekoBot.Modules.Games
try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
catch { } catch { }
} }
//user didn't input something already
IGuildUser throwaway;
if (submissions.TryGetValue(input, out throwaway))
return;
var inputWords = input.Split(' '); //get all words var inputWords = input.Split(' '); //get all words
if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters
@ -207,9 +204,15 @@ namespace NadekoBot.Modules.Games
return; return;
} }
if (!usersWhoSubmitted.Add(guildUser.Id))
return;
//try adding it to the list of answers //try adding it to the list of answers
if (!submissions.TryAdd(input, guildUser)) if (!submissions.TryAdd(input, guildUser))
{
usersWhoSubmitted.TryRemove(guildUser.Id);
return; return;
}
// all good. valid input. answer recorded // all good. valid input. answer recorded
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)"); await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)");

View File

@ -189,13 +189,13 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
} }
public string GetHangman() => $@"\_\_\_\_\_\_\_\_\_ public string GetHangman() => $@". ┌─────┐
| | ................
| | ................
{(Errors > 0 ? "😲" : " ")} | .{(Errors > 0 ? ".............😲" : "")}
{(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} | .{(Errors > 1 ? "............./" : "")} {(Errors > 2 ? "|" : "")} {(Errors > 3 ? "\\" : "")}
{(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} | .{(Errors > 4 ? "............../" : "")} {(Errors > 5 ? "\\" : "")}
/-\"; /-\";
public void Dispose() public void Dispose()
{ {

View File

@ -61,7 +61,7 @@ namespace NadekoBot.Modules.Games
return; return;
} }
await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord); await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman());
} }
} }
} }

View File

@ -76,9 +76,9 @@ namespace NadekoBot.Modules.Games.Trivia
questionMessage = await channel.EmbedAsync(questionEmbed).ConfigureAwait(false); questionMessage = await channel.EmbedAsync(questionEmbed).ConfigureAwait(false);
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound ||
ex.StatusCode == System.Net.HttpStatusCode.Forbidden || ex.HttpCode == System.Net.HttpStatusCode.Forbidden ||
ex.StatusCode == System.Net.HttpStatusCode.BadRequest) ex.HttpCode == System.Net.HttpStatusCode.BadRequest)
{ {
return; return;
} }
@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Games.Trivia
await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build()) await questionMessage.ModifyAsync(m => m.Embed = questionEmbed.WithFooter(efb => efb.WithText(CurrentQuestion.GetHint())).Build())
.ConfigureAwait(false); .ConfigureAwait(false);
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound || ex.StatusCode == System.Net.HttpStatusCode.Forbidden) catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound || ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
{ {
break; break;
} }

View File

@ -712,13 +712,10 @@ namespace NadekoBot.Modules.Music
} }
//todo only author or owner
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task DeletePlaylist([Remainder] int id) public async Task DeletePlaylist([Remainder] int id)
{ {
bool success = false; bool success = false;
MusicPlaylist pl = null; MusicPlaylist pl = null;
try try
@ -747,7 +744,7 @@ namespace NadekoBot.Modules.Music
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex); _log.Warn(ex);
} }
} }

View File

@ -1,4 +1,7 @@
using Discord; using AngleSharp;
using AngleSharp.Dom.Html;
using AngleSharp.Extensions;
using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
@ -8,6 +11,7 @@ using Newtonsoft.Json.Linq;
using NLog; using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -52,6 +56,116 @@ namespace NadekoBot.Modules.Searches
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29)); }, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
} }
[NadekoCommand, Usage, Description, Aliases]
[Priority(1)]
public async Task Mal([Remainder] string name)
{
if (string.IsNullOrWhiteSpace(name))
return;
var fullQueryLink = "https://myanimelist.net/profile/" + name;
var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
var imageElem = document.QuerySelector("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-left > div.user-profile > div.user-image > img");
var imageUrl = ((IHtmlImageElement)imageElem)?.Source ?? "http://icecream.me/uploads/870b03f36b59cc16ebfe314ef2dde781.png";
var stats = document.QuerySelectorAll("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-right > div#statistics > div.user-statistics-stats > div.stats > div.clearfix > ul.stats-status > li > span").Select(x => x.InnerHtml).ToList();
var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc");
var favAnime = "No favorite anime yet";
if (favorites[0].QuerySelector("p") == null)
favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
.Shuffle()
.Take(3)
.Select(x =>
{
var elem = (IHtmlAnchorElement)x;
return $"[{elem.InnerHtml}]({elem.Href})";
}));
//var favManga = "No favorite manga yet.";
//if (favorites[1].QuerySelector("p") == null)
// favManga = string.Join("\n", favorites[1].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
// .Take(3)
// .Select(x =>
// {
// var elem = (IHtmlAnchorElement)x;
// return $"[{elem.InnerHtml}]({elem.Href})";
// }));
var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li")
.Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml))
.ToList();
var daysAndMean = document.QuerySelectorAll("div.anime:nth-child(1) > div:nth-child(2) > div")
.Select(x => x.TextContent.Split(':').Select(y => y.Trim()).ToArray())
.ToArray();
var embed = new EmbedBuilder()
.WithOkColor()
.WithTitle($"{name}'s MAL profile")
.AddField(efb => efb.WithName("💚 Watching").WithValue(stats[0]).WithIsInline(true))
.AddField(efb => efb.WithName("💙 Completed").WithValue(stats[1]).WithIsInline(true));
if (info.Count < 3)
embed.AddField(efb => efb.WithName("💛 On-Hold").WithValue(stats[2]).WithIsInline(true));
embed
.AddField(efb => efb.WithName("💔 Dropped").WithValue(stats[3]).WithIsInline(true))
.AddField(efb => efb.WithName("⚪ Plan to watch").WithValue(stats[4]).WithIsInline(true))
.AddField(efb => efb.WithName("🕐 " + daysAndMean[0][0]).WithValue(daysAndMean[0][1]).WithIsInline(true))
.AddField(efb => efb.WithName("📊 " + daysAndMean[1][0]).WithValue(daysAndMean[1][1]).WithIsInline(true))
.AddField(efb => efb.WithName(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1).WithValue(info[0].Item2.TrimTo(20)).WithIsInline(true))
.AddField(efb => efb.WithName(MalInfoToEmoji(info[1].Item1) + " " + info[1].Item1).WithValue(info[1].Item2.TrimTo(20)).WithIsInline(true));
if (info.Count > 2)
embed.AddField(efb => efb.WithName(MalInfoToEmoji(info[2].Item1) + " " + info[2].Item1).WithValue(info[2].Item2.TrimTo(20)).WithIsInline(true));
//if(info.Count > 3)
// embed.AddField(efb => efb.WithName(MalInfoToEmoji(info[3].Item1) + " " + info[3].Item1).WithValue(info[3].Item2).WithIsInline(true))
embed
.WithDescription($@"
** https://myanimelist.net/animelist/{ name } **
**Top 3 Favorite Anime:**
{favAnime}"
//**[Manga List](https://myanimelist.net/mangalist/{name})**
//💚`Reading:` {stats[5]}
//💙`Completed:` {stats[6]}
//💔`Dropped:` {stats[8]}
//⚪`Plan to read:` {stats[9]}
//**Top 3 Favorite Manga:**
//{favManga}"
)
.WithUrl(fullQueryLink)
.WithImageUrl(imageUrl);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
private static string MalInfoToEmoji(string info) {
info = info.Trim().ToLowerInvariant();
switch (info)
{
case "gender":
return "🚁";
case "location":
return "🗺";
case "last online":
return "👥";
case "birthday":
return "📆";
default:
return "❔";
}
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(0)]
public Task Mal(IUser usr) => Mal(usr.Username);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Anime([Remainder] string query) public async Task Anime([Remainder] string query)
{ {

View File

@ -45,7 +45,7 @@ namespace NadekoBot.Modules.Searches
MemoryStream ms = new MemoryStream(); MemoryStream ms = new MemoryStream();
res.CopyTo(ms); res.CopyTo(ms);
ms.Position = 0; ms.Position = 0;
await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link: **https://osu.ppy.sh/u/{Uri.EscapeDataString(usr)}\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false); await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link:** <https://new.ppy.sh/u/{Uri.EscapeDataString(usr)}>\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -60,9 +60,9 @@ namespace NadekoBot.Modules.Searches
.WithThumbnailUrl(rankimg) .WithThumbnailUrl(rankimg)
.AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true)) .AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) .AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
.AddField(fb => fb.WithName("**Current Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true)) .AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true)) .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true))

View File

@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Searches
var server = NadekoBot.Client.GetGuild(fs.GuildId); var server = NadekoBot.Client.GetGuild(fs.GuildId);
if (server == null) if (server == null)
return; return;
var channel = await server.GetTextChannelAsync(fs.ChannelId); var channel = server.GetTextChannel(fs.ChannelId);
if (channel == null) if (channel == null)
return; return;
try try

View File

@ -117,6 +117,23 @@ namespace NadekoBot.Modules.Searches
terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
try
{
var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link)
.WithImageUrl(res.Link)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
catch
{
_log.Warn("Falling back to Imgur search.");
var fullQueryLink = $"http://imgur.com/search?q={ terms }"; var fullQueryLink = $"http://imgur.com/search?q={ terms }";
var config = Configuration.Default.WithDefaultLoader(); var config = Configuration.Default.WithDefaultLoader();
var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink); var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink);
@ -143,6 +160,7 @@ namespace NadekoBot.Modules.Searches
.WithTitle(Context.User.Mention); .WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task RandomImage([Remainder] string terms = null) public async Task RandomImage([Remainder] string terms = null)
@ -150,7 +168,23 @@ namespace NadekoBot.Modules.Searches
terms = terms?.Trim(); terms = terms?.Trim();
if (string.IsNullOrWhiteSpace(terms)) if (string.IsNullOrWhiteSpace(terms))
return; return;
terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
try
{
var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link)
.WithImageUrl(res.Link)
.WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
catch
{
_log.Warn("Falling back to Imgur");
terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); terms = WebUtility.UrlEncode(terms).Replace(' ', '+');
var fullQueryLink = $"http://imgur.com/search?q={ terms }"; var fullQueryLink = $"http://imgur.com/search?q={ terms }";
@ -179,6 +213,7 @@ namespace NadekoBot.Modules.Searches
.WithTitle(Context.User.Mention); .WithTitle(Context.User.Mention);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Lmgtfy([Remainder] string ffs = null) public async Task Lmgtfy([Remainder] string ffs = null)

View File

@ -50,7 +50,7 @@ namespace NadekoBot.Modules.Utility
} }
private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) => private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) =>
$"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content; $"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions();
public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>(); public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
private static Logger _log { get; } private static Logger _log { get; }

View File

@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Utility
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
this.Repeater = repeater; this.Repeater = repeater;
this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannelAsync(repeater.ChannelId).GetAwaiter().GetResult(); this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId);
if (Channel == null) if (Channel == null)
return; return;
Task.Run(Run); Task.Run(Run);
@ -69,12 +69,12 @@ namespace NadekoBot.Modules.Utility
{ {
oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false);
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Forbidden) catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
{ {
_log.Warn("Missing permissions. Repeater stopped. ChannelId : {0}", Channel?.Id); _log.Warn("Missing permissions. Repeater stopped. ChannelId : {0}", Channel?.Id);
return; return;
} }
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound)
{ {
_log.Warn("Channel not found. Repeater stopped. ChannelId : {0}", Channel?.Id); _log.Warn("Channel not found. Repeater stopped. ChannelId : {0}", Channel?.Id);
return; return;

View File

@ -68,9 +68,7 @@ namespace NadekoBot.Modules.Utility
} }
else else
{ {
var t = NadekoBot.Client.GetGuild(r.ServerId)?.GetTextChannelAsync(r.ChannelId).ConfigureAwait(false); ch = NadekoBot.Client.GetGuild(r.ServerId)?.GetTextChannel(r.ChannelId);
if (t != null)
ch = await t.Value;
} }
if (ch == null) if (ch == null)
return; return;

View File

@ -116,16 +116,19 @@ namespace NadekoBot.Modules.Utility
var arr = (await (Context.Channel as IGuildChannel).Guild.GetUsersAsync()) var arr = (await (Context.Channel as IGuildChannel).Guild.GetUsersAsync())
.Where(u => u.Game?.Name?.ToUpperInvariant() == game) .Where(u => u.Game?.Name?.ToUpperInvariant() == game)
.Select(u => u.Username) .Select(u => u.Username)
.Shuffle()
.Take(60)
.ToList(); .ToList();
int i = 0; int i = 0;
if (!arr.Any()) if (!arr.Any())
await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false); await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false);
else else {
await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2) await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2)
.Select(ig => string.Concat(ig.Select(el => $"• {el,-27}")))) + "\n```") .Select(ig => string.Concat(ig.Select(el => $"• {el,-27}")))) + "\n```")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]

View File

@ -75,7 +75,8 @@ namespace NadekoBot
//initialize Services //initialize Services
CommandService = new CommandService(new CommandServiceConfig() { CommandService = new CommandService(new CommandServiceConfig() {
CaseSensitiveCommands = false CaseSensitiveCommands = false,
DefaultRunMode = RunMode.Sync
}); });
Google = new GoogleApiService(); Google = new GoogleApiService();
CommandHandler = new CommandHandler(Client, CommandService); CommandHandler = new CommandHandler(Client, CommandService);

View File

@ -177,7 +177,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%. /// Looks up a localized string similar to Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds%.
/// </summary> /// </summary>
public static string addplaying_desc { public static string addplaying_desc {
get { get {
@ -4406,6 +4406,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to mal.
/// </summary>
public static string mal_cmd {
get {
return ResourceManager.GetString("mal_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows basic info from myanimelist profile..
/// </summary>
public static string mal_desc {
get {
return ResourceManager.GetString("mal_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}mal straysocks`.
/// </summary>
public static string mal_usage {
get {
return ResourceManager.GetString("mal_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to manga mang mq. /// Looks up a localized string similar to manga mang mq.
/// </summary> /// </summary>

View File

@ -292,7 +292,7 @@
<value>addplaying adpl</value> <value>addplaying adpl</value>
</data> </data>
<data name="addplaying_desc" xml:space="preserve"> <data name="addplaying_desc" xml:space="preserve">
<value>Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%</value> <value>Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds%</value>
</data> </data>
<data name="addplaying_usage" xml:space="preserve"> <data name="addplaying_usage" xml:space="preserve">
<value>`{0}adpl`</value> <value>`{0}adpl`</value>
@ -1376,7 +1376,6 @@
</data> </data>
<data name="plant_usage" xml:space="preserve"> <data name="plant_usage" xml:space="preserve">
<value>`{0}plant` or `{0}plant 5`</value> <value>`{0}plant` or `{0}plant 5`</value>
<comment> </comment>
</data> </data>
<data name="gencurrency_cmd" xml:space="preserve"> <data name="gencurrency_cmd" xml:space="preserve">
<value>gencurrency gc</value> <value>gencurrency gc</value>
@ -3025,4 +3024,13 @@
<data name="waifuinfo_usage" xml:space="preserve"> <data name="waifuinfo_usage" xml:space="preserve">
<value>`{0}waifuinfo @MyCrush` or `{0}waifuinfo`</value> <value>`{0}waifuinfo @MyCrush` or `{0}waifuinfo`</value>
</data> </data>
<data name="mal_cmd" xml:space="preserve">
<value>mal</value>
</data>
<data name="mal_desc" xml:space="preserve">
<value>Shows basic info from myanimelist profile.</value>
</data>
<data name="mal_usage" xml:space="preserve">
<value>`{0}mal straysocks`</value>
</data>
</root> </root>

View File

@ -159,8 +159,8 @@ namespace NadekoBot.Services
private async Task<bool> WordFiltered(IGuild guild, SocketUserMessage usrMsg) private async Task<bool> WordFiltered(IGuild guild, SocketUserMessage usrMsg)
{ {
var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id); var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id); var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' '); var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0) if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
{ {
@ -222,6 +222,7 @@ namespace NadekoBot.Services
return; return;
// maybe this message is a custom reaction // maybe this message is a custom reaction
// todo log custom reaction executions. return struct with info
var crExecuted = await Task.Run(() => CustomReactions.TryExecuteCustomReaction(usrMsg)).ConfigureAwait(false); var crExecuted = await Task.Run(() => CustomReactions.TryExecuteCustomReaction(usrMsg)).ConfigureAwait(false);
if (crExecuted) //if it was, don't execute the command if (crExecuted) //if it was, don't execute the command
return; return;

View File

@ -8,13 +8,19 @@ using System.Text.RegularExpressions;
using Google.Apis.Urlshortener.v1; using Google.Apis.Urlshortener.v1;
using Google.Apis.Urlshortener.v1.Data; using Google.Apis.Urlshortener.v1.Data;
using NLog; using NLog;
using Google.Apis.Customsearch.v1;
using Google.Apis.Customsearch.v1.Data;
namespace NadekoBot.Services.Impl namespace NadekoBot.Services.Impl
{ {
public class GoogleApiService : IGoogleApiService public class GoogleApiService : IGoogleApiService
{ {
const string search_engine_id = "018084019232060951019:hs5piey28-e";
private YouTubeService yt; private YouTubeService yt;
private UrlshortenerService sh; private UrlshortenerService sh;
private CustomsearchService cs;
private Logger _log { get; } private Logger _log { get; }
public GoogleApiService() public GoogleApiService()
@ -22,13 +28,14 @@ namespace NadekoBot.Services.Impl
var bcs = new BaseClientService.Initializer var bcs = new BaseClientService.Initializer
{ {
ApplicationName = "Nadeko Bot", ApplicationName = "Nadeko Bot",
ApiKey = NadekoBot.Credentials.GoogleApiKey ApiKey = NadekoBot.Credentials.GoogleApiKey,
}; };
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
yt = new YouTubeService(bcs); yt = new YouTubeService(bcs);
sh = new UrlshortenerService(bcs); sh = new UrlshortenerService(bcs);
cs = new CustomsearchService(bcs);
} }
public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1) public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1)
{ {
@ -150,7 +157,7 @@ namespace NadekoBot.Services.Impl
return toReturn; return toReturn;
} }
//todo AsyncEnumerable //todo AsyncEnumerable
public async Task<IReadOnlyDictionary<string,TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds) public async Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds)
{ {
var videoIdsList = videoIds as List<string> ?? videoIds.ToList(); var videoIdsList = videoIds as List<string> ?? videoIds.ToList();
@ -179,5 +186,34 @@ namespace NadekoBot.Services.Impl
return toReturn; return toReturn;
} }
public struct ImageResult
{
public Result.ImageData Image { get; }
public string Link { get; }
public ImageResult(Result.ImageData image, string link)
{
this.Image = image;
this.Link = link;
}
}
public async Task<ImageResult> GetImageAsync(string query, int start = 1)
{
if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query));
var req = cs.Cse.List(query);
req.Cx = search_engine_id;
req.Num = 1;
req.Fields = "items(image(contextLink,thumbnailLink),link)";
req.SearchType = CseResource.ListRequest.SearchTypeEnum.Image;
req.Start = start;
var search = await req.ExecuteAsync().ConfigureAwait(false);
return new ImageResult(search.Items[0].Image, search.Items[0].Link);
}
} }
} }

View File

@ -15,7 +15,7 @@ namespace NadekoBot.Services.Impl
private DiscordShardedClient client; private DiscordShardedClient client;
private DateTime started; private DateTime started;
public const string BotVersion = "1.1.4"; public const string BotVersion = "1.1.5";
public string Author => "Kwoth#2560"; public string Author => "Kwoth#2560";
public string Library => "Discord.Net"; public string Library => "Discord.Net";

View File

@ -237,7 +237,7 @@
}, },
{ {
"Title":"Careers in Psychology: Opportunities in a Changing World", "Title":"Careers in Psychology: Opportunities in a Changing World",
"Text":"This text addresses the growing need among students and faculty for information about the careers available in psychology at the bachelor’s and graduate level." "Text":"This text addresses the growing need among students and faculty for information about the careers available in psychology at the bachelors and graduate level."
}, },
{ {
"Title":"Philosophy of Psychology", "Title":"Philosophy of Psychology",