2017-05-27 08:19:27 +00:00
|
|
|
|
using Discord;
|
|
|
|
|
using Discord.WebSocket;
|
|
|
|
|
using NadekoBot.Extensions;
|
2017-10-13 04:14:54 +00:00
|
|
|
|
using NadekoBot.Core.Services;
|
2017-05-27 08:19:27 +00:00
|
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using NLog;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2017-05-22 23:59:31 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2017-07-17 19:42:36 +00:00
|
|
|
|
using NadekoBot.Modules.Searches.Common;
|
2017-10-13 04:14:54 +00:00
|
|
|
|
using NadekoBot.Core.Services.Database.Models;
|
2017-07-21 01:04:44 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
2017-09-10 21:44:24 +00:00
|
|
|
|
using System.Net.Http;
|
2017-09-22 22:42:15 +00:00
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
using AngleSharp;
|
2017-10-08 22:54:10 +00:00
|
|
|
|
using System.Threading;
|
2017-10-09 00:52:46 +00:00
|
|
|
|
using NadekoBot.Modules.Searches.Exceptions;
|
2017-05-22 23:59:31 +00:00
|
|
|
|
|
2017-07-17 19:42:36 +00:00
|
|
|
|
namespace NadekoBot.Modules.Searches.Services
|
2017-05-22 23:59:31 +00:00
|
|
|
|
{
|
2017-10-08 22:54:10 +00:00
|
|
|
|
public class SearchesService : INService, IUnloadableService
|
2017-05-22 23:59:31 +00:00
|
|
|
|
{
|
2017-09-10 21:44:24 +00:00
|
|
|
|
public HttpClient Http { get; }
|
|
|
|
|
|
2017-06-19 13:42:10 +00:00
|
|
|
|
private readonly DiscordSocketClient _client;
|
2017-05-27 08:19:27 +00:00
|
|
|
|
private readonly IGoogleApiService _google;
|
2017-05-29 04:13:22 +00:00
|
|
|
|
private readonly DbService _db;
|
2017-05-27 08:19:27 +00:00
|
|
|
|
private readonly Logger _log;
|
|
|
|
|
|
|
|
|
|
public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
|
|
|
|
|
public ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
|
|
|
|
|
|
|
|
|
|
public readonly string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
|
|
|
|
|
public readonly string PokemonListFile = "data/pokemon/pokemon_list7.json";
|
|
|
|
|
public Dictionary<string, SearchPokemon> Pokemons { get; } = new Dictionary<string, SearchPokemon>();
|
|
|
|
|
public Dictionary<string, SearchPokemonAbility> PokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>();
|
|
|
|
|
|
|
|
|
|
public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>();
|
|
|
|
|
public List<MagicItem> MagicItems { get; } = new List<MagicItem>();
|
|
|
|
|
|
2017-08-11 20:53:31 +00:00
|
|
|
|
private readonly ConcurrentDictionary<ulong, SearchImageCacher> _imageCacher = new ConcurrentDictionary<ulong, SearchImageCacher>();
|
2017-10-09 00:59:00 +00:00
|
|
|
|
|
2017-10-09 00:52:46 +00:00
|
|
|
|
public ConcurrentDictionary<ulong, Timer> AutoHentaiTimers { get; } = new ConcurrentDictionary<ulong, Timer>();
|
|
|
|
|
public ConcurrentDictionary<ulong, Timer> AutoBoobTimers { get; } = new ConcurrentDictionary<ulong, Timer>();
|
|
|
|
|
public ConcurrentDictionary<ulong, Timer> AutoButtTimers { get; } = new ConcurrentDictionary<ulong, Timer>();
|
2017-10-08 22:54:10 +00:00
|
|
|
|
|
2017-07-21 01:04:44 +00:00
|
|
|
|
private readonly ConcurrentDictionary<ulong, HashSet<string>> _blacklistedTags = new ConcurrentDictionary<ulong, HashSet<string>>();
|
|
|
|
|
|
2017-10-13 00:21:39 +00:00
|
|
|
|
public SearchesService(DiscordSocketClient client, IGoogleApiService google,
|
|
|
|
|
DbService db, NadekoBot bot)
|
2017-05-27 08:19:27 +00:00
|
|
|
|
{
|
2017-09-10 21:44:24 +00:00
|
|
|
|
Http = new HttpClient();
|
|
|
|
|
Http.AddFakeHeaders();
|
2017-05-27 08:19:27 +00:00
|
|
|
|
_client = client;
|
|
|
|
|
_google = google;
|
|
|
|
|
_db = db;
|
|
|
|
|
_log = LogManager.GetCurrentClassLogger();
|
|
|
|
|
|
2017-07-21 01:04:44 +00:00
|
|
|
|
_blacklistedTags = new ConcurrentDictionary<ulong, HashSet<string>>(
|
2017-10-13 00:21:39 +00:00
|
|
|
|
bot.AllGuildConfigs.ToDictionary(
|
2017-07-21 01:04:44 +00:00
|
|
|
|
x => x.GuildId,
|
|
|
|
|
x => new HashSet<string>(x.NsfwBlacklistedTags.Select(y => y.Tag))));
|
|
|
|
|
|
2017-05-27 08:19:27 +00:00
|
|
|
|
//translate commands
|
2017-06-15 23:55:14 +00:00
|
|
|
|
_client.MessageReceived += (msg) =>
|
2017-05-27 08:19:27 +00:00
|
|
|
|
{
|
2017-06-05 22:46:58 +00:00
|
|
|
|
var _ = Task.Run(async () =>
|
2017-05-27 08:19:27 +00:00
|
|
|
|
{
|
2017-06-05 22:46:58 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var umsg = msg as SocketUserMessage;
|
|
|
|
|
if (umsg == null)
|
|
|
|
|
return;
|
2017-05-27 08:19:27 +00:00
|
|
|
|
|
2017-06-05 22:46:58 +00:00
|
|
|
|
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out var autoDelete))
|
|
|
|
|
return;
|
2017-05-27 08:19:27 +00:00
|
|
|
|
|
2017-06-05 22:46:58 +00:00
|
|
|
|
var key = new UserChannelPair()
|
|
|
|
|
{
|
|
|
|
|
UserId = umsg.Author.Id,
|
|
|
|
|
ChannelId = umsg.Channel.Id,
|
|
|
|
|
};
|
2017-05-27 08:19:27 +00:00
|
|
|
|
|
2017-06-05 22:46:58 +00:00
|
|
|
|
if (!UserLanguages.TryGetValue(key, out string langs))
|
|
|
|
|
return;
|
2017-05-27 08:19:27 +00:00
|
|
|
|
|
2017-06-05 22:46:58 +00:00
|
|
|
|
var text = await Translate(langs, umsg.Resolve(TagHandling.Ignore))
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
if (autoDelete)
|
|
|
|
|
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
|
|
|
|
|
await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
});
|
2017-06-15 23:55:14 +00:00
|
|
|
|
return Task.CompletedTask;
|
2017-05-27 08:19:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//pokemon commands
|
|
|
|
|
if (File.Exists(PokemonListFile))
|
|
|
|
|
{
|
|
|
|
|
Pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
_log.Warn(PokemonListFile + " is missing. Pokemon abilities not loaded.");
|
|
|
|
|
if (File.Exists(PokemonAbilitiesFile))
|
|
|
|
|
PokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(PokemonAbilitiesFile));
|
|
|
|
|
else
|
|
|
|
|
_log.Warn(PokemonAbilitiesFile + " is missing. Pokemon abilities not loaded.");
|
|
|
|
|
|
|
|
|
|
//joke commands
|
|
|
|
|
if (File.Exists("data/wowjokes.json"))
|
|
|
|
|
{
|
|
|
|
|
WowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
_log.Warn("data/wowjokes.json is missing. WOW Jokes are not loaded.");
|
|
|
|
|
|
|
|
|
|
if (File.Exists("data/magicitems.json"))
|
|
|
|
|
{
|
|
|
|
|
MagicItems = JsonConvert.DeserializeObject<List<MagicItem>>(File.ReadAllText("data/magicitems.json"));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
_log.Warn("data/magicitems.json is missing. Magic items are not loaded.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<string> Translate(string langs, string text = null)
|
|
|
|
|
{
|
|
|
|
|
var langarr = langs.ToLowerInvariant().Split('>');
|
|
|
|
|
if (langarr.Length != 2)
|
|
|
|
|
throw new ArgumentException();
|
|
|
|
|
var from = langarr[0];
|
|
|
|
|
var to = langarr[1];
|
|
|
|
|
text = text?.Trim();
|
|
|
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
|
|
|
throw new ArgumentException();
|
|
|
|
|
return (await _google.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions();
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 15:58:16 +00:00
|
|
|
|
public Task<ImageCacherObject> DapiSearch(string tag, DapiSearchType type, ulong? guild, bool isExplicit = false)
|
2017-05-22 23:59:31 +00:00
|
|
|
|
{
|
2017-09-10 21:44:24 +00:00
|
|
|
|
if (guild.HasValue)
|
2017-07-21 01:04:44 +00:00
|
|
|
|
{
|
2017-09-10 21:44:24 +00:00
|
|
|
|
var blacklistedTags = GetBlacklistedTags(guild.Value);
|
|
|
|
|
|
|
|
|
|
if (blacklistedTags
|
|
|
|
|
.Any(x => tag.ToLowerInvariant().Contains(x)))
|
|
|
|
|
{
|
2017-10-09 00:52:46 +00:00
|
|
|
|
throw new TagBlacklistedException();
|
2017-09-10 21:44:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cacher = _imageCacher.GetOrAdd(guild.Value, (key) => new SearchImageCacher());
|
|
|
|
|
|
|
|
|
|
return cacher.GetImage(tag, isExplicit, type, blacklistedTags);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var cacher = _imageCacher.GetOrAdd(guild ?? 0, (key) => new SearchImageCacher());
|
|
|
|
|
|
|
|
|
|
return cacher.GetImage(tag, isExplicit, type);
|
2017-07-21 01:04:44 +00:00
|
|
|
|
}
|
2017-05-22 23:59:31 +00:00
|
|
|
|
}
|
2017-07-21 01:04:44 +00:00
|
|
|
|
|
|
|
|
|
public HashSet<string> GetBlacklistedTags(ulong guildId)
|
|
|
|
|
{
|
|
|
|
|
if (_blacklistedTags.TryGetValue(guildId, out var tags))
|
|
|
|
|
return tags;
|
|
|
|
|
return new HashSet<string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ToggleBlacklistedTag(ulong guildId, string tag)
|
|
|
|
|
{
|
|
|
|
|
var tagObj = new NsfwBlacklitedTag
|
|
|
|
|
{
|
|
|
|
|
Tag = tag
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool added;
|
|
|
|
|
using (var uow = _db.UnitOfWork)
|
|
|
|
|
{
|
|
|
|
|
var gc = uow.GuildConfigs.For(guildId, set => set.Include(y => y.NsfwBlacklistedTags));
|
|
|
|
|
if (gc.NsfwBlacklistedTags.Add(tagObj))
|
|
|
|
|
added = true;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gc.NsfwBlacklistedTags.Remove(tagObj);
|
|
|
|
|
added = false;
|
|
|
|
|
}
|
|
|
|
|
var newTags = new HashSet<string>(gc.NsfwBlacklistedTags.Select(x => x.Tag));
|
|
|
|
|
_blacklistedTags.AddOrUpdate(guildId, newTags, delegate { return newTags; });
|
|
|
|
|
|
|
|
|
|
uow.Complete();
|
|
|
|
|
}
|
|
|
|
|
return added;
|
|
|
|
|
}
|
2017-09-11 17:30:02 +00:00
|
|
|
|
|
|
|
|
|
public void ClearCache()
|
|
|
|
|
{
|
|
|
|
|
foreach (var c in _imageCacher)
|
|
|
|
|
{
|
|
|
|
|
c.Value?.Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-22 22:42:15 +00:00
|
|
|
|
|
|
|
|
|
public async Task<string> GetYomamaJoke()
|
|
|
|
|
{
|
|
|
|
|
var response = await Http.GetStringAsync("http://api.yomomma.info/").ConfigureAwait(false);
|
|
|
|
|
return JObject.Parse(response)["joke"].ToString() + " 😆";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<(string Text, string BaseUri)> GetRandomJoke()
|
|
|
|
|
{
|
|
|
|
|
var config = Configuration.Default.WithDefaultLoader();
|
|
|
|
|
var document = await BrowsingContext.New(config).OpenAsync("http://www.goodbadjokes.com/random");
|
|
|
|
|
|
|
|
|
|
var html = document.QuerySelector(".post > .joke-content");
|
|
|
|
|
|
|
|
|
|
var part1 = html.QuerySelector("dt").TextContent;
|
|
|
|
|
var part2 = html.QuerySelector("dd").TextContent;
|
|
|
|
|
|
|
|
|
|
return (part1 + "\n\n" + part2, document.BaseUri);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<string> GetChuckNorrisJoke()
|
|
|
|
|
{
|
|
|
|
|
var response = await Http.GetStringAsync("http://api.icndb.com/jokes/random/").ConfigureAwait(false);
|
|
|
|
|
return JObject.Parse(response)["value"]["joke"].ToString() + " 😆";
|
|
|
|
|
}
|
2017-10-08 22:54:10 +00:00
|
|
|
|
|
|
|
|
|
public Task Unload()
|
|
|
|
|
{
|
2017-10-09 00:52:46 +00:00
|
|
|
|
AutoBoobTimers.ForEach(x => x.Value.Change(Timeout.Infinite, Timeout.Infinite));
|
|
|
|
|
AutoBoobTimers.Clear();
|
|
|
|
|
AutoButtTimers.ForEach(x => x.Value.Change(Timeout.Infinite, Timeout.Infinite));
|
|
|
|
|
AutoButtTimers.Clear();
|
|
|
|
|
AutoHentaiTimers.ForEach(x => x.Value.Change(Timeout.Infinite, Timeout.Infinite));
|
|
|
|
|
AutoHentaiTimers.Clear();
|
2017-10-08 22:54:10 +00:00
|
|
|
|
|
|
|
|
|
_imageCacher.Clear();
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2017-05-22 23:59:31 +00:00
|
|
|
|
}
|
2017-05-27 08:19:27 +00:00
|
|
|
|
|
|
|
|
|
public struct UserChannelPair
|
|
|
|
|
{
|
|
|
|
|
public ulong UserId { get; set; }
|
|
|
|
|
public ulong ChannelId { get; set; }
|
|
|
|
|
}
|
2017-05-22 23:59:31 +00:00
|
|
|
|
}
|