Initial split of the modules
This commit is contained in:
147
NadekoBot.Core/Services/Impl/BotConfigProvider.cs
Normal file
147
NadekoBot.Core/Services/Impl/BotConfigProvider.cs
Normal file
@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Services;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class BotConfigProvider : IBotConfigProvider
|
||||
{
|
||||
private readonly DbService _db;
|
||||
public BotConfig BotConfig { get; private set; }
|
||||
|
||||
public BotConfigProvider(DbService db, BotConfig bc)
|
||||
{
|
||||
_db = db;
|
||||
BotConfig = bc;
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
BotConfig = uow.BotConfig.GetOrCreate();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Edit(BotConfigEditType type, string newValue)
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var bc = uow.BotConfig.GetOrCreate();
|
||||
switch (type)
|
||||
{
|
||||
case BotConfigEditType.CurrencyGenerationChance:
|
||||
if (float.TryParse(newValue, out var chance)
|
||||
&& chance >= 0
|
||||
&& chance <= 1)
|
||||
{
|
||||
bc.CurrencyGenerationChance = chance;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case BotConfigEditType.CurrencyGenerationCooldown:
|
||||
if (int.TryParse(newValue, out var cd) && cd >= 1)
|
||||
{
|
||||
bc.CurrencyGenerationCooldown = cd;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case BotConfigEditType.CurrencyName:
|
||||
bc.CurrencyName = newValue ?? "-";
|
||||
break;
|
||||
case BotConfigEditType.CurrencyPluralName:
|
||||
bc.CurrencyPluralName = newValue ?? bc.CurrencyName + "s";
|
||||
break;
|
||||
case BotConfigEditType.CurrencySign:
|
||||
bc.CurrencySign = newValue ?? "-";
|
||||
break;
|
||||
case BotConfigEditType.DmHelpString:
|
||||
bc.DMHelpString = string.IsNullOrWhiteSpace(newValue)
|
||||
? "-"
|
||||
: newValue;
|
||||
break;
|
||||
case BotConfigEditType.HelpString:
|
||||
bc.HelpString = string.IsNullOrWhiteSpace(newValue)
|
||||
? "-"
|
||||
: newValue;
|
||||
break;
|
||||
case BotConfigEditType.CurrencyDropAmount:
|
||||
if (int.TryParse(newValue, out var amount) && amount > 0)
|
||||
bc.CurrencyDropAmount = amount;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.CurrencyDropAmountMax:
|
||||
if (newValue == null)
|
||||
bc.CurrencyDropAmountMax = null;
|
||||
else if (int.TryParse(newValue, out var maxAmount) && maxAmount > 0)
|
||||
bc.CurrencyDropAmountMax = maxAmount;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.MinimumBetAmount:
|
||||
if (int.TryParse(newValue, out var minBetAmount) && minBetAmount > 0)
|
||||
bc.MinimumBetAmount = minBetAmount;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.TriviaCurrencyReward:
|
||||
if (int.TryParse(newValue, out var triviaReward) && triviaReward > 0)
|
||||
bc.TriviaCurrencyReward = triviaReward;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.Betroll100Multiplier:
|
||||
if (float.TryParse(newValue, out var br100) && br100 > 0)
|
||||
bc.Betroll100Multiplier = br100;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.Betroll91Multiplier:
|
||||
if (int.TryParse(newValue, out var br91) && br91 > 0)
|
||||
bc.Betroll91Multiplier = br91;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.Betroll67Multiplier:
|
||||
if (int.TryParse(newValue, out var br67) && br67 > 0)
|
||||
bc.Betroll67Multiplier = br67;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.BetflipMultiplier:
|
||||
if (int.TryParse(newValue, out var bf) && bf > 0)
|
||||
bc.BetflipMultiplier = bf;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.XpPerMessage:
|
||||
if (int.TryParse(newValue, out var xp) && xp > 0)
|
||||
bc.XpPerMessage = xp;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
case BotConfigEditType.XpMinutesTimeout:
|
||||
if (int.TryParse(newValue, out var min) && min > 0)
|
||||
bc.XpMinutesTimeout = min;
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
BotConfig = bc;
|
||||
uow.Complete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
149
NadekoBot.Core/Services/Impl/BotCredentials.cs
Normal file
149
NadekoBot.Core/Services/Impl/BotCredentials.cs
Normal file
@ -0,0 +1,149 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Discord;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Collections.Immutable;
|
||||
using NadekoBot.Common;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class BotCredentials : IBotCredentials
|
||||
{
|
||||
private Logger _log;
|
||||
|
||||
public ulong ClientId { get; }
|
||||
|
||||
public string GoogleApiKey { get; }
|
||||
|
||||
public string MashapeKey { get; }
|
||||
|
||||
public string Token { get; }
|
||||
|
||||
public ImmutableArray<ulong> OwnerIds { get; }
|
||||
|
||||
public string LoLApiKey { get; }
|
||||
public string OsuApiKey { get; }
|
||||
public string CleverbotApiKey { get; }
|
||||
public RestartConfig RestartCommand { get; }
|
||||
public DBConfig Db { get; }
|
||||
public int TotalShards { get; }
|
||||
public string CarbonKey { get; }
|
||||
|
||||
private readonly string _credsFileName = Path.Combine(Directory.GetCurrentDirectory(), "credentials.json");
|
||||
public string PatreonAccessToken { get; }
|
||||
public string ShardRunCommand { get; }
|
||||
public string ShardRunArguments { get; }
|
||||
public int ShardRunPort { get; }
|
||||
|
||||
public string PatreonCampaignId { get; }
|
||||
|
||||
public BotCredentials()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
try { File.WriteAllText("./credentials_example.json", JsonConvert.SerializeObject(new CredentialsModel(), Formatting.Indented)); } catch { }
|
||||
if(!File.Exists(_credsFileName))
|
||||
_log.Warn($"credentials.json is missing. Attempting to load creds from environment variables prefixed with 'NadekoBot_'. Example is in {Path.GetFullPath("./credentials_example.json")}");
|
||||
try
|
||||
{
|
||||
var configBuilder = new ConfigurationBuilder();
|
||||
configBuilder.AddJsonFile(_credsFileName, true)
|
||||
.AddEnvironmentVariables("NadekoBot_");
|
||||
|
||||
var data = configBuilder.Build();
|
||||
|
||||
Token = data[nameof(Token)];
|
||||
if (string.IsNullOrWhiteSpace(Token))
|
||||
{
|
||||
_log.Error("Token is missing from credentials.json or Environment varibles. Add it and restart the program.");
|
||||
Console.ReadKey();
|
||||
Environment.Exit(3);
|
||||
}
|
||||
OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value)).ToImmutableArray();
|
||||
LoLApiKey = data[nameof(LoLApiKey)];
|
||||
GoogleApiKey = data[nameof(GoogleApiKey)];
|
||||
MashapeKey = data[nameof(MashapeKey)];
|
||||
OsuApiKey = data[nameof(OsuApiKey)];
|
||||
PatreonAccessToken = data[nameof(PatreonAccessToken)];
|
||||
PatreonCampaignId = data[nameof(PatreonCampaignId)] ?? "334038";
|
||||
ShardRunCommand = data[nameof(ShardRunCommand)];
|
||||
ShardRunArguments = data[nameof(ShardRunArguments)];
|
||||
CleverbotApiKey = data[nameof(CleverbotApiKey)];
|
||||
|
||||
var restartSection = data.GetSection(nameof(RestartCommand));
|
||||
var cmd = restartSection["cmd"];
|
||||
var args = restartSection["args"];
|
||||
if (!string.IsNullOrWhiteSpace(cmd))
|
||||
RestartCommand = new RestartConfig(cmd, args);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ShardRunCommand))
|
||||
ShardRunCommand = "dotnet";
|
||||
if (string.IsNullOrWhiteSpace(ShardRunArguments))
|
||||
ShardRunArguments = "run -c Release -- {0} {1} {2}";
|
||||
|
||||
var portStr = data[nameof(ShardRunPort)];
|
||||
if (string.IsNullOrWhiteSpace(portStr))
|
||||
ShardRunPort = new NadekoRandom().Next(5000, 6000);
|
||||
else
|
||||
ShardRunPort = int.Parse(portStr);
|
||||
|
||||
int ts = 1;
|
||||
int.TryParse(data[nameof(TotalShards)], out ts);
|
||||
TotalShards = ts < 1 ? 1 : ts;
|
||||
|
||||
ulong.TryParse(data[nameof(ClientId)], out ulong clId);
|
||||
ClientId = clId;
|
||||
|
||||
CarbonKey = data[nameof(CarbonKey)];
|
||||
var dbSection = data.GetSection("db");
|
||||
Db = new DBConfig(string.IsNullOrWhiteSpace(dbSection["Type"])
|
||||
? "sqlite"
|
||||
: dbSection["Type"],
|
||||
string.IsNullOrWhiteSpace(dbSection["ConnectionString"])
|
||||
? "Data Source=data/NadekoBot.db"
|
||||
: dbSection["ConnectionString"]);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Fatal(ex.Message);
|
||||
_log.Fatal(ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class CredentialsModel
|
||||
{
|
||||
public ulong ClientId { get; set; } = 123123123;
|
||||
public string Token { get; set; } = "";
|
||||
public ulong[] OwnerIds { get; set; } = new ulong[1];
|
||||
public string LoLApiKey { get; set; } = "";
|
||||
public string GoogleApiKey { get; set; } = "";
|
||||
public string MashapeKey { get; set; } = "";
|
||||
public string OsuApiKey { get; set; } = "";
|
||||
public string SoundCloudClientId { get; set; } = "";
|
||||
public string CleverbotApiKey { get; } = "";
|
||||
public string CarbonKey { get; set; } = "";
|
||||
public DBConfig Db { get; set; } = new DBConfig("sqlite", "Data Source=data/NadekoBot.db");
|
||||
public int TotalShards { get; set; } = 1;
|
||||
public string PatreonAccessToken { get; set; } = "";
|
||||
public string PatreonCampaignId { get; set; } = "334038";
|
||||
public string RestartCommand { get; set; } = null;
|
||||
|
||||
public string ShardRunCommand { get; set; } = "";
|
||||
public string ShardRunArguments { get; set; } = "";
|
||||
public int? ShardRunPort { get; set; } = null;
|
||||
}
|
||||
|
||||
private class DbModel
|
||||
{
|
||||
public string Type { get; set; }
|
||||
public string ConnectionString { get; set; }
|
||||
}
|
||||
|
||||
public bool IsOwner(IUser u) => OwnerIds.Contains(u.Id);
|
||||
}
|
||||
}
|
384
NadekoBot.Core/Services/Impl/GoogleApiService.cs
Normal file
384
NadekoBot.Core/Services/Impl/GoogleApiService.cs
Normal file
@ -0,0 +1,384 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Apis.YouTube.v3;
|
||||
using Google.Apis.Services;
|
||||
using System.Text.RegularExpressions;
|
||||
using Google.Apis.Urlshortener.v1;
|
||||
using Google.Apis.Urlshortener.v1.Data;
|
||||
using NLog;
|
||||
using Google.Apis.Customsearch.v1;
|
||||
using System.Net.Http;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NadekoBot.Extensions;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class GoogleApiService : IGoogleApiService
|
||||
{
|
||||
const string search_engine_id = "018084019232060951019:hs5piey28-e";
|
||||
|
||||
private YouTubeService yt;
|
||||
private UrlshortenerService sh;
|
||||
private CustomsearchService cs;
|
||||
|
||||
private Logger _log { get; }
|
||||
|
||||
public GoogleApiService(IBotCredentials creds)
|
||||
{
|
||||
_creds = creds;
|
||||
|
||||
var bcs = new BaseClientService.Initializer
|
||||
{
|
||||
ApplicationName = "Nadeko Bot",
|
||||
ApiKey = _creds.GoogleApiKey,
|
||||
};
|
||||
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
yt = new YouTubeService(bcs);
|
||||
sh = new UrlshortenerService(bcs);
|
||||
cs = new CustomsearchService(bcs);
|
||||
}
|
||||
private static readonly Regex plRegex = new Regex("(?:youtu\\.be\\/|list=)(?<id>[\\da-zA-Z\\-_]*)", RegexOptions.Compiled);
|
||||
public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1)
|
||||
{
|
||||
await Task.Yield();
|
||||
if (string.IsNullOrWhiteSpace(keywords))
|
||||
throw new ArgumentNullException(nameof(keywords));
|
||||
|
||||
if (count <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
var match = plRegex.Match(keywords);
|
||||
if (match.Length > 1)
|
||||
{
|
||||
return new[] { match.Groups["id"].Value.ToString() };
|
||||
}
|
||||
var query = yt.Search.List("snippet");
|
||||
query.MaxResults = count;
|
||||
query.Type = "playlist";
|
||||
query.Q = keywords;
|
||||
|
||||
return (await query.ExecuteAsync()).Items.Select(i => i.Id.PlaylistId);
|
||||
}
|
||||
|
||||
//private readonly Regex YtVideoIdRegex = new Regex(@"(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)(?<id>[a-zA-Z0-9_-]{6,11})", RegexOptions.Compiled);
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
public async Task<IEnumerable<string>> GetRelatedVideosAsync(string id, int count = 1)
|
||||
{
|
||||
await Task.Yield();
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
throw new ArgumentNullException(nameof(id));
|
||||
|
||||
if (count <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
var query = yt.Search.List("snippet");
|
||||
query.MaxResults = count;
|
||||
query.RelatedToVideoId = id;
|
||||
query.Type = "video";
|
||||
return (await query.ExecuteAsync()).Items.Select(i => "http://www.youtube.com/watch?v=" + i.Id.VideoId);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetVideoLinksByKeywordAsync(string keywords, int count = 1)
|
||||
{
|
||||
await Task.Yield();
|
||||
if (string.IsNullOrWhiteSpace(keywords))
|
||||
throw new ArgumentNullException(nameof(keywords));
|
||||
|
||||
if (count <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
var query = yt.Search.List("snippet");
|
||||
query.MaxResults = count;
|
||||
query.Q = keywords;
|
||||
query.Type = "video";
|
||||
return (await query.ExecuteAsync()).Items.Select(i => "http://www.youtube.com/watch?v=" + i.Id.VideoId);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<(string Name, string Id, string Url)>> GetVideoInfosByKeywordAsync(string keywords, int count = 1)
|
||||
{
|
||||
await Task.Yield();
|
||||
if (string.IsNullOrWhiteSpace(keywords))
|
||||
throw new ArgumentNullException(nameof(keywords));
|
||||
|
||||
if (count <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
var query = yt.Search.List("snippet");
|
||||
query.MaxResults = count;
|
||||
query.Q = keywords;
|
||||
query.Type = "video";
|
||||
return (await query.ExecuteAsync()).Items.Select(i => (i.Snippet.Title.TrimTo(50), i.Id.VideoId, "http://www.youtube.com/watch?v=" + i.Id.VideoId));
|
||||
}
|
||||
|
||||
public async Task<string> ShortenUrl(string url)
|
||||
{
|
||||
await Task.Yield();
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_creds.GoogleApiKey))
|
||||
return url;
|
||||
|
||||
try
|
||||
{
|
||||
var response = await sh.Url.Insert(new Url { LongUrl = url }).ExecuteAsync();
|
||||
return response.Id;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warn(ex);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetPlaylistTracksAsync(string playlistId, int count = 50)
|
||||
{
|
||||
await Task.Yield();
|
||||
if (string.IsNullOrWhiteSpace(playlistId))
|
||||
throw new ArgumentNullException(nameof(playlistId));
|
||||
|
||||
if (count <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
string nextPageToken = null;
|
||||
|
||||
List<string> toReturn = new List<string>(count);
|
||||
|
||||
do
|
||||
{
|
||||
var toGet = count > 50 ? 50 : count;
|
||||
count -= toGet;
|
||||
|
||||
var query = yt.PlaylistItems.List("contentDetails");
|
||||
query.MaxResults = toGet;
|
||||
query.PlaylistId = playlistId;
|
||||
query.PageToken = nextPageToken;
|
||||
|
||||
var data = await query.ExecuteAsync();
|
||||
|
||||
toReturn.AddRange(data.Items.Select(i => i.ContentDetails.VideoId));
|
||||
nextPageToken = data.NextPageToken;
|
||||
}
|
||||
while (count > 0 && !string.IsNullOrWhiteSpace(nextPageToken));
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds)
|
||||
{
|
||||
await Task.Yield();
|
||||
var videoIdsList = videoIds as List<string> ?? videoIds.ToList();
|
||||
|
||||
Dictionary<string, TimeSpan> toReturn = new Dictionary<string, TimeSpan>();
|
||||
|
||||
if (!videoIdsList.Any())
|
||||
return toReturn;
|
||||
var toGet = 0;
|
||||
var remaining = videoIdsList.Count;
|
||||
|
||||
do
|
||||
{
|
||||
toGet = remaining > 50 ? 50 : remaining;
|
||||
remaining -= toGet;
|
||||
|
||||
var q = yt.Videos.List("contentDetails");
|
||||
q.Id = string.Join(",", videoIdsList.Take(toGet));
|
||||
videoIdsList = videoIdsList.Skip(toGet).ToList();
|
||||
var items = (await q.ExecuteAsync().ConfigureAwait(false)).Items;
|
||||
foreach (var i in items)
|
||||
{
|
||||
toReturn.Add(i.Id, System.Xml.XmlConvert.ToTimeSpan(i.ContentDetails.Duration));
|
||||
}
|
||||
}
|
||||
while (remaining > 0);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
public async Task<ImageResult> GetImageAsync(string query, int start = 1)
|
||||
{
|
||||
await Task.Yield();
|
||||
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);
|
||||
}
|
||||
|
||||
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
|
||||
private readonly Dictionary<string, string> _languageDictionary = new Dictionary<string, string>() {
|
||||
{ "afrikaans", "af"},
|
||||
{ "albanian", "sq"},
|
||||
{ "arabic", "ar"},
|
||||
{ "armenian", "hy"},
|
||||
{ "azerbaijani", "az"},
|
||||
{ "basque", "eu"},
|
||||
{ "belarusian", "be"},
|
||||
{ "bengali", "bn"},
|
||||
{ "bulgarian", "bg"},
|
||||
{ "catalan", "ca"},
|
||||
{ "chinese-traditional", "zh-TW"},
|
||||
{ "chinese-simplified", "zh-CN"},
|
||||
{ "chinese", "zh-CN"},
|
||||
{ "croatian", "hr"},
|
||||
{ "czech", "cs"},
|
||||
{ "danish", "da"},
|
||||
{ "dutch", "nl"},
|
||||
{ "english", "en"},
|
||||
{ "esperanto", "eo"},
|
||||
{ "estonian", "et"},
|
||||
{ "filipino", "tl"},
|
||||
{ "finnish", "fi"},
|
||||
{ "french", "fr"},
|
||||
{ "galician", "gl"},
|
||||
{ "german", "de"},
|
||||
{ "georgian", "ka"},
|
||||
{ "greek", "el"},
|
||||
{ "haitian Creole", "ht"},
|
||||
{ "hebrew", "iw"},
|
||||
{ "hindi", "hi"},
|
||||
{ "hungarian", "hu"},
|
||||
{ "icelandic", "is"},
|
||||
{ "indonesian", "id"},
|
||||
{ "irish", "ga"},
|
||||
{ "italian", "it"},
|
||||
{ "japanese", "ja"},
|
||||
{ "korean", "ko"},
|
||||
{ "lao", "lo"},
|
||||
{ "latin", "la"},
|
||||
{ "latvian", "lv"},
|
||||
{ "lithuanian", "lt"},
|
||||
{ "macedonian", "mk"},
|
||||
{ "malay", "ms"},
|
||||
{ "maltese", "mt"},
|
||||
{ "norwegian", "no"},
|
||||
{ "persian", "fa"},
|
||||
{ "polish", "pl"},
|
||||
{ "portuguese", "pt"},
|
||||
{ "romanian", "ro"},
|
||||
{ "russian", "ru"},
|
||||
{ "serbian", "sr"},
|
||||
{ "slovak", "sk"},
|
||||
{ "slovenian", "sl"},
|
||||
{ "spanish", "es"},
|
||||
{ "swahili", "sw"},
|
||||
{ "swedish", "sv"},
|
||||
{ "tamil", "ta"},
|
||||
{ "telugu", "te"},
|
||||
{ "thai", "th"},
|
||||
{ "turkish", "tr"},
|
||||
{ "ukrainian", "uk"},
|
||||
{ "urdu", "ur"},
|
||||
{ "vietnamese", "vi"},
|
||||
{ "welsh", "cy"},
|
||||
{ "yiddish", "yi"},
|
||||
|
||||
{ "af", "af"},
|
||||
{ "sq", "sq"},
|
||||
{ "ar", "ar"},
|
||||
{ "hy", "hy"},
|
||||
{ "az", "az"},
|
||||
{ "eu", "eu"},
|
||||
{ "be", "be"},
|
||||
{ "bn", "bn"},
|
||||
{ "bg", "bg"},
|
||||
{ "ca", "ca"},
|
||||
{ "zh-tw", "zh-TW"},
|
||||
{ "zh-cn", "zh-CN"},
|
||||
{ "hr", "hr"},
|
||||
{ "cs", "cs"},
|
||||
{ "da", "da"},
|
||||
{ "nl", "nl"},
|
||||
{ "en", "en"},
|
||||
{ "eo", "eo"},
|
||||
{ "et", "et"},
|
||||
{ "tl", "tl"},
|
||||
{ "fi", "fi"},
|
||||
{ "fr", "fr"},
|
||||
{ "gl", "gl"},
|
||||
{ "de", "de"},
|
||||
{ "ka", "ka"},
|
||||
{ "el", "el"},
|
||||
{ "ht", "ht"},
|
||||
{ "iw", "iw"},
|
||||
{ "hi", "hi"},
|
||||
{ "hu", "hu"},
|
||||
{ "is", "is"},
|
||||
{ "id", "id"},
|
||||
{ "ga", "ga"},
|
||||
{ "it", "it"},
|
||||
{ "ja", "ja"},
|
||||
{ "ko", "ko"},
|
||||
{ "lo", "lo"},
|
||||
{ "la", "la"},
|
||||
{ "lv", "lv"},
|
||||
{ "lt", "lt"},
|
||||
{ "mk", "mk"},
|
||||
{ "ms", "ms"},
|
||||
{ "mt", "mt"},
|
||||
{ "no", "no"},
|
||||
{ "fa", "fa"},
|
||||
{ "pl", "pl"},
|
||||
{ "pt", "pt"},
|
||||
{ "ro", "ro"},
|
||||
{ "ru", "ru"},
|
||||
{ "sr", "sr"},
|
||||
{ "sk", "sk"},
|
||||
{ "sl", "sl"},
|
||||
{ "es", "es"},
|
||||
{ "sw", "sw"},
|
||||
{ "sv", "sv"},
|
||||
{ "ta", "ta"},
|
||||
{ "te", "te"},
|
||||
{ "th", "th"},
|
||||
{ "tr", "tr"},
|
||||
{ "uk", "uk"},
|
||||
{ "ur", "ur"},
|
||||
{ "vi", "vi"},
|
||||
{ "cy", "cy"},
|
||||
{ "yi", "yi"},
|
||||
};
|
||||
|
||||
public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage)
|
||||
{
|
||||
await Task.Yield();
|
||||
string text;
|
||||
|
||||
if (!_languageDictionary.ContainsKey(sourceLanguage) ||
|
||||
!_languageDictionary.ContainsKey(targetLanguage))
|
||||
throw new ArgumentException();
|
||||
|
||||
|
||||
var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}",
|
||||
ConvertToLanguageCode(sourceLanguage),
|
||||
ConvertToLanguageCode(targetLanguage),
|
||||
WebUtility.UrlEncode(sourceText));
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
|
||||
text = await http.GetStringAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return (string.Concat(JArray.Parse(text)[0].Select(x => x[0])));
|
||||
}
|
||||
|
||||
private string ConvertToLanguageCode(string language)
|
||||
{
|
||||
_languageDictionary.TryGetValue(language, out var mode);
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
}
|
93
NadekoBot.Core/Services/Impl/ImagesService.cs
Normal file
93
NadekoBot.Core/Services/Impl/ImagesService.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class ImagesService : IImagesService
|
||||
{
|
||||
private readonly Logger _log;
|
||||
|
||||
private const string _basePath = "data/images/";
|
||||
|
||||
private const string _headsPath = _basePath + "coins/heads.png";
|
||||
private const string _tailsPath = _basePath + "coins/tails.png";
|
||||
|
||||
private const string _currencyImagesPath = _basePath + "currency";
|
||||
private const string _diceImagesPath = _basePath + "dice";
|
||||
|
||||
private const string _slotBackgroundPath = _basePath + "slots/background2.png";
|
||||
private const string _slotNumbersPath = _basePath + "slots/numbers/";
|
||||
private const string _slotEmojisPath = _basePath + "slots/emojis/";
|
||||
|
||||
private const string _wifeMatrixPath = _basePath + "rategirl/wifematrix.png";
|
||||
private const string _rategirlDot = _basePath + "rategirl/dot.png";
|
||||
|
||||
private const string _xpCardPath = _basePath + "xp/xp.png";
|
||||
|
||||
|
||||
public ImmutableArray<byte> Heads { get; private set; }
|
||||
public ImmutableArray<byte> Tails { get; private set; }
|
||||
|
||||
public ImmutableArray<(string, ImmutableArray<byte>)> Currency { get; private set; }
|
||||
|
||||
public ImmutableArray<ImmutableArray<byte>> Dice { get; private set; }
|
||||
|
||||
public ImmutableArray<byte> SlotBackground { get; private set; }
|
||||
public ImmutableArray<ImmutableArray<byte>> SlotNumbers { get; private set; }
|
||||
public ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; private set; }
|
||||
|
||||
public ImmutableArray<byte> WifeMatrix { get; private set; }
|
||||
public ImmutableArray<byte> RategirlDot { get; private set; }
|
||||
|
||||
public ImmutableArray<byte> XpCard { get; private set; }
|
||||
|
||||
public ImagesService()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
this.Reload();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
try
|
||||
{
|
||||
Heads = File.ReadAllBytes(_headsPath).ToImmutableArray();
|
||||
Tails = File.ReadAllBytes(_tailsPath).ToImmutableArray();
|
||||
|
||||
Currency = Directory.GetFiles(_currencyImagesPath)
|
||||
.Select(x => (Path.GetFileName(x), File.ReadAllBytes(x).ToImmutableArray()))
|
||||
.ToImmutableArray();
|
||||
|
||||
Dice = Directory.GetFiles(_diceImagesPath)
|
||||
.OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x)))
|
||||
.Select(x => File.ReadAllBytes(x).ToImmutableArray())
|
||||
.ToImmutableArray();
|
||||
|
||||
SlotBackground = File.ReadAllBytes(_slotBackgroundPath).ToImmutableArray();
|
||||
|
||||
SlotNumbers = Directory.GetFiles(_slotNumbersPath)
|
||||
.OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f)))
|
||||
.Select(x => File.ReadAllBytes(x).ToImmutableArray())
|
||||
.ToImmutableArray();
|
||||
|
||||
SlotEmojis = Directory.GetFiles(_slotEmojisPath)
|
||||
.OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f)))
|
||||
.Select(x => File.ReadAllBytes(x).ToImmutableArray())
|
||||
.ToImmutableArray();
|
||||
|
||||
WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray();
|
||||
RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray();
|
||||
|
||||
XpCard = File.ReadAllBytes(_xpCardPath).ToImmutableArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
146
NadekoBot.Core/Services/Impl/Localization.cs
Normal file
146
NadekoBot.Core/Services/Impl/Localization.cs
Normal file
@ -0,0 +1,146 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Discord;
|
||||
using NLog;
|
||||
using NadekoBot.Services.Database.Models;
|
||||
using NadekoBot.Common;
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class Localization : ILocalization
|
||||
{
|
||||
private readonly Logger _log;
|
||||
private readonly DbService _db;
|
||||
|
||||
public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; }
|
||||
public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture;
|
||||
|
||||
private static readonly Dictionary<string, CommandData> _commandData;
|
||||
|
||||
static Localization()
|
||||
{
|
||||
_commandData = JsonConvert.DeserializeObject<Dictionary<string, CommandData>>(
|
||||
File.ReadAllText("./data/command_strings.json"));
|
||||
}
|
||||
|
||||
private Localization() { }
|
||||
public Localization(IBotConfigProvider bcp, IEnumerable<GuildConfig> gcs, DbService db)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
var cultureInfoNames = gcs.ToDictionary(x => x.GuildId, x => x.Locale);
|
||||
var defaultCulture = bcp.BotConfig.Locale;
|
||||
|
||||
_db = db;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(defaultCulture))
|
||||
DefaultCultureInfo = new CultureInfo("en-US");
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
DefaultCultureInfo = new CultureInfo(defaultCulture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_log.Warn("Unable to load default bot's locale/language. Using en-US.");
|
||||
DefaultCultureInfo = new CultureInfo("en-US");
|
||||
}
|
||||
}
|
||||
GuildCultureInfos = new ConcurrentDictionary<ulong, CultureInfo>(cultureInfoNames.ToDictionary(x => x.Key, x =>
|
||||
{
|
||||
CultureInfo cultureInfo = null;
|
||||
try
|
||||
{
|
||||
if (x.Value == null)
|
||||
return null;
|
||||
cultureInfo = new CultureInfo(x.Value);
|
||||
}
|
||||
catch { }
|
||||
return cultureInfo;
|
||||
}).Where(x => x.Value != null));
|
||||
}
|
||||
|
||||
public void SetGuildCulture(IGuild guild, CultureInfo ci) =>
|
||||
SetGuildCulture(guild.Id, ci);
|
||||
|
||||
public void SetGuildCulture(ulong guildId, CultureInfo ci)
|
||||
{
|
||||
if (ci == DefaultCultureInfo)
|
||||
{
|
||||
RemoveGuildCulture(guildId);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(guildId, set => set);
|
||||
gc.Locale = ci.Name;
|
||||
uow.Complete();
|
||||
}
|
||||
|
||||
GuildCultureInfos.AddOrUpdate(guildId, ci, (id, old) => ci);
|
||||
}
|
||||
|
||||
public void RemoveGuildCulture(IGuild guild) =>
|
||||
RemoveGuildCulture(guild.Id);
|
||||
|
||||
public void RemoveGuildCulture(ulong guildId) {
|
||||
|
||||
if (GuildCultureInfos.TryRemove(guildId, out var _))
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var gc = uow.GuildConfigs.For(guildId, set => set);
|
||||
gc.Locale = null;
|
||||
uow.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDefaultCulture(CultureInfo ci)
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
var bc = uow.BotConfig.GetOrCreate();
|
||||
bc.Locale = ci.Name;
|
||||
uow.Complete();
|
||||
}
|
||||
DefaultCultureInfo = ci;
|
||||
}
|
||||
|
||||
public void ResetDefaultCulture() =>
|
||||
SetDefaultCulture(CultureInfo.CurrentCulture);
|
||||
|
||||
public CultureInfo GetCultureInfo(IGuild guild) =>
|
||||
GetCultureInfo(guild?.Id);
|
||||
|
||||
public CultureInfo GetCultureInfo(ulong? guildId)
|
||||
{
|
||||
if (guildId == null)
|
||||
return DefaultCultureInfo;
|
||||
CultureInfo info = null;
|
||||
GuildCultureInfos.TryGetValue(guildId.Value, out info);
|
||||
return info ?? DefaultCultureInfo;
|
||||
}
|
||||
|
||||
public static CommandData LoadCommand(string key)
|
||||
{
|
||||
_commandData.TryGetValue(key, out var toReturn);
|
||||
|
||||
if (toReturn == null)
|
||||
return new CommandData
|
||||
{
|
||||
Cmd = key,
|
||||
Desc = key,
|
||||
Usage = new[] { key },
|
||||
};
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
}
|
||||
}
|
124
NadekoBot.Core/Services/Impl/NadekoStrings.cs
Normal file
124
NadekoBot.Core/Services/Impl/NadekoStrings.cs
Normal file
@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class NadekoStrings : INService
|
||||
{
|
||||
public const string stringsPath = @"_strings/";
|
||||
|
||||
private readonly ImmutableDictionary<string, ImmutableDictionary<string, string>> responseStrings;
|
||||
private readonly Logger _log;
|
||||
/// <summary>
|
||||
/// Used as failsafe in case response key doesn't exist in the selected or default language.
|
||||
/// </summary>
|
||||
private readonly CultureInfo _usCultureInfo = new CultureInfo("en-US");
|
||||
private readonly ILocalization _localization;
|
||||
|
||||
private readonly Regex formatFinder = new Regex(@"{\d}", RegexOptions.Compiled);
|
||||
|
||||
public NadekoStrings(ILocalization loc)
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
_localization = loc;
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
var allLangsDict = new Dictionary<string, ImmutableDictionary<string, string>>(); // lang:(name:value)
|
||||
foreach (var file in Directory.GetFiles(stringsPath))
|
||||
{
|
||||
var langDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(file));
|
||||
|
||||
allLangsDict.Add(GetLocaleName(file).ToLowerInvariant(), langDict.ToImmutableDictionary());
|
||||
}
|
||||
|
||||
responseStrings = allLangsDict.ToImmutableDictionary();
|
||||
sw.Stop();
|
||||
|
||||
_log.Info("Loaded {0} languages in {1:F2}s",
|
||||
responseStrings.Count,
|
||||
//string.Join(",", responseStrings.Keys),
|
||||
sw.Elapsed.TotalSeconds);
|
||||
|
||||
////improper string format checks
|
||||
//var compareTo = responseStrings["en-us"]
|
||||
// .Select(x =>
|
||||
// {
|
||||
// return (StringKey: x.Key, Placeholders: formatFinder.Matches(x.Value).Cast<Match>().Select(y => y.Value).ToArray());
|
||||
// })
|
||||
// .ToDictionary(x => x.StringKey, x => x.Placeholders);
|
||||
|
||||
//var errors = responseStrings
|
||||
// .Select(a => (a.Key, a.Value.Select(x =>
|
||||
// {
|
||||
// if (!compareTo.ContainsKey(x.Key))
|
||||
// return (StringKey: x.Key, Placeholders: new HashSet<string>(), Missing: true);
|
||||
// var hs = new HashSet<string>(compareTo[x.Key]);
|
||||
// hs.SymmetricExceptWith(formatFinder.Matches(x.Value).Cast<Match>().Select(y => y.Value).ToArray());
|
||||
// return (StringKey: x.Key, Placeholders: hs, Missing: false);
|
||||
// })
|
||||
// .Where(x => x.Placeholders.Any() || x.Missing)))
|
||||
// .Where(x => x.Item2.Any());
|
||||
|
||||
//var str = string.Join("\n", errors.Select(x => $"------{x.Item1}------\n" +
|
||||
// string.Join("\n", x.Item2.Select(y =>
|
||||
// y.StringKey + ": " + (y.Missing ? "MISSING" : string.Join(", ", y.Placeholders))))));
|
||||
//if (!string.IsNullOrWhiteSpace(str))
|
||||
// _log.Warn($"Improperly Formatted strings:\n{str}");
|
||||
}
|
||||
|
||||
private string GetLocaleName(string fileName)
|
||||
{
|
||||
var dotIndex = fileName.IndexOf('.') + 1;
|
||||
var secondDotINdex = fileName.LastIndexOf('.');
|
||||
return fileName.Substring(dotIndex, secondDotINdex - dotIndex);
|
||||
}
|
||||
|
||||
private string GetString(string text, CultureInfo cultureInfo)
|
||||
{
|
||||
if (!responseStrings.TryGetValue(cultureInfo.Name.ToLowerInvariant(), out ImmutableDictionary<string, string> strings))
|
||||
return null;
|
||||
|
||||
strings.TryGetValue(text, out string val);
|
||||
return val;
|
||||
}
|
||||
|
||||
public string GetText(string key, ulong? guildId, string lowerModuleTypeName, params object[] replacements) =>
|
||||
GetText(key, _localization.GetCultureInfo(guildId), lowerModuleTypeName, replacements);
|
||||
|
||||
public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName)
|
||||
{
|
||||
var text = GetString(lowerModuleTypeName + "_" + key, cultureInfo);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS.");
|
||||
text = GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} not found!";
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return "I can't tell you if the command is executed, because there was an error printing out the response. Key '" +
|
||||
lowerModuleTypeName + "_" + key + "' " + "is missing from resources. Please report this.";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName,
|
||||
params object[] replacements)
|
||||
{
|
||||
try
|
||||
{
|
||||
return string.Format(GetText(key, cultureInfo, lowerModuleTypeName), replacements);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return "I can't tell you if the command is executed, because there was an error printing out the response. Key '" +
|
||||
lowerModuleTypeName + "_" + key + "' " + "is not properly formatted. Please report this.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
NadekoBot.Core/Services/Impl/RedisCache.cs
Normal file
43
NadekoBot.Core/Services/Impl/RedisCache.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using StackExchange.Redis;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class RedisCache : IDataCache
|
||||
{
|
||||
private ulong _botid;
|
||||
|
||||
public ConnectionMultiplexer Redis { get; }
|
||||
private readonly IDatabase _db;
|
||||
|
||||
public RedisCache(ulong botId)
|
||||
{
|
||||
_botid = botId;
|
||||
Redis = ConnectionMultiplexer.Connect("127.0.0.1");
|
||||
Redis.PreserveAsyncOrder = false;
|
||||
_db = Redis.GetDatabase();
|
||||
}
|
||||
|
||||
public async Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key)
|
||||
{
|
||||
byte[] x = await _db.StringGetAsync("image_" + key);
|
||||
return (x != null, x);
|
||||
}
|
||||
|
||||
public Task SetImageDataAsync(string key, byte[] data)
|
||||
{
|
||||
return _db.StringSetAsync("image_" + key, data);
|
||||
}
|
||||
|
||||
public async Task<(bool Success, string Data)> TryGetAnimeDataAsync(string key)
|
||||
{
|
||||
string x = await _db.StringGetAsync("anime_" + key);
|
||||
return (x != null, x);
|
||||
}
|
||||
|
||||
public Task SetAnimeDataAsync(string key, string data)
|
||||
{
|
||||
return _db.StringSetAsync("anime_" + key, data);
|
||||
}
|
||||
}
|
||||
}
|
143
NadekoBot.Core/Services/Impl/SoundCloudApiService.cs
Normal file
143
NadekoBot.Core/Services/Impl/SoundCloudApiService.cs
Normal file
@ -0,0 +1,143 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class SoundCloudApiService : INService
|
||||
{
|
||||
private readonly IBotCredentials _creds;
|
||||
|
||||
public SoundCloudApiService(IBotCredentials creds)
|
||||
{
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public async Task<SoundCloudVideo> ResolveVideoAsync(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
|
||||
string response = "";
|
||||
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync($"https://scapi.nadekobot.me/resolve?url={url}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
var responseObj = JsonConvert.DeserializeObject<SoundCloudVideo>(response);
|
||||
if (responseObj?.Kind != "track")
|
||||
throw new InvalidOperationException("Url is either not a track, or it doesn't exist.");
|
||||
|
||||
return responseObj;
|
||||
}
|
||||
|
||||
public bool IsSoundCloudLink(string url) =>
|
||||
System.Text.RegularExpressions.Regex.IsMatch(url, "(.*)(soundcloud.com|snd.sc)(.*)");
|
||||
|
||||
public async Task<SoundCloudVideo> GetVideoByQueryAsync(string query)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
throw new ArgumentNullException(nameof(query));
|
||||
|
||||
var response = "";
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
response = await http.GetStringAsync($"https://scapi.nadekobot.me/tracks?q={Uri.EscapeDataString(query)}").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var responseObj = JsonConvert.DeserializeObject<SoundCloudVideo[]>(response).Where(s => s.Streamable).FirstOrDefault();
|
||||
if (responseObj?.Kind != "track")
|
||||
throw new InvalidOperationException("Query yielded no results.");
|
||||
|
||||
return responseObj;
|
||||
}
|
||||
}
|
||||
|
||||
public class SoundCloudVideo
|
||||
{
|
||||
public string Kind { get; set; } = "";
|
||||
public long Id { get; set; } = 0;
|
||||
public SoundCloudUser User { get; set; } = new SoundCloudUser();
|
||||
public string Title { get; set; } = "";
|
||||
[JsonIgnore]
|
||||
public string FullName => User.Name + " - " + Title;
|
||||
public bool Streamable { get; set; } = false;
|
||||
public int Duration { get; set; }
|
||||
[JsonProperty("permalink_url")]
|
||||
public string TrackLink { get; set; } = "";
|
||||
public string artwork_url { get; set; } = "";
|
||||
public async Task<string> StreamLink()
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
return await http.GetStringAsync($"http://scapi.nadekobot.me/stream/{Id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
public class SoundCloudUser
|
||||
{
|
||||
[JsonProperty("username")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
/*
|
||||
{"kind":"track",
|
||||
"id":238888167,
|
||||
"created_at":"2015/12/24 01:04:52 +0000",
|
||||
"user_id":43141975,
|
||||
"duration":120852,
|
||||
"commentable":true,
|
||||
"state":"finished",
|
||||
"original_content_size":4834829,
|
||||
"last_modified":"2015/12/24 01:17:59 +0000",
|
||||
"sharing":"public",
|
||||
"tag_list":"Funky",
|
||||
"permalink":"18-fd",
|
||||
"streamable":true,
|
||||
"embeddable_by":"all",
|
||||
"downloadable":false,
|
||||
"purchase_url":null,
|
||||
"label_id":null,
|
||||
"purchase_title":null,
|
||||
"genre":"Disco",
|
||||
"title":"18 Ж",
|
||||
"description":"",
|
||||
"label_name":null,
|
||||
"release":null,
|
||||
"track_type":null,
|
||||
"key_signature":null,
|
||||
"isrc":null,
|
||||
"video_url":null,
|
||||
"bpm":null,
|
||||
"release_year":null,
|
||||
"release_month":null,
|
||||
"release_day":null,
|
||||
"original_format":"mp3",
|
||||
"license":"all-rights-reserved",
|
||||
"uri":"https://api.soundcloud.com/tracks/238888167",
|
||||
"user":{
|
||||
"id":43141975,
|
||||
"kind":"user",
|
||||
"permalink":"mrb00gi",
|
||||
"username":"Mrb00gi",
|
||||
"last_modified":"2015/12/01 16:06:57 +0000",
|
||||
"uri":"https://api.soundcloud.com/users/43141975",
|
||||
"permalink_url":"http://soundcloud.com/mrb00gi",
|
||||
"avatar_url":"https://a1.sndcdn.com/images/default_avatar_large.png"
|
||||
},
|
||||
"permalink_url":"http://soundcloud.com/mrb00gi/18-fd",
|
||||
"artwork_url":null,
|
||||
"waveform_url":"https://w1.sndcdn.com/gsdLfvEW1cUK_m.png",
|
||||
"stream_url":"https://api.soundcloud.com/tracks/238888167/stream",
|
||||
"playback_count":7,
|
||||
"download_count":0,
|
||||
"favoritings_count":1,
|
||||
"comment_count":0,
|
||||
"attachments_uri":"https://api.soundcloud.com/tracks/238888167/attachments"}
|
||||
|
||||
*/
|
||||
|
||||
}
|
24
NadekoBot.Core/Services/Impl/StartingGuildsListService.cs
Normal file
24
NadekoBot.Core/Services/Impl/StartingGuildsListService.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Discord.WebSocket;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class StartingGuildsService : IEnumerable<long>, INService
|
||||
{
|
||||
private readonly ImmutableList<long> _guilds;
|
||||
|
||||
public StartingGuildsService(DiscordSocketClient client)
|
||||
{
|
||||
this._guilds = client.Guilds.Select(x => (long)x.Id).ToImmutableList();
|
||||
}
|
||||
|
||||
public IEnumerator<long> GetEnumerator() =>
|
||||
_guilds.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() =>
|
||||
_guilds.GetEnumerator();
|
||||
}
|
||||
}
|
229
NadekoBot.Core/Services/Impl/StatsService.cs
Normal file
229
NadekoBot.Core/Services/Impl/StatsService.cs
Normal file
@ -0,0 +1,229 @@
|
||||
using Discord;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class StatsService : IStatsService
|
||||
{
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly DateTime _started;
|
||||
|
||||
public const string BotVersion = "1.10.2";
|
||||
|
||||
public string Author => "Kwoth#2560";
|
||||
public string Library => "Discord.Net";
|
||||
public string Heap =>
|
||||
Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(CultureInfo.InvariantCulture);
|
||||
public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds;
|
||||
|
||||
private long _textChannels;
|
||||
public long TextChannels => Interlocked.Read(ref _textChannels);
|
||||
private long _voiceChannels;
|
||||
public long VoiceChannels => Interlocked.Read(ref _voiceChannels);
|
||||
private long _messageCounter;
|
||||
public long MessageCounter => Interlocked.Read(ref _messageCounter);
|
||||
private long _commandsRan;
|
||||
public long CommandsRan => Interlocked.Read(ref _commandsRan);
|
||||
|
||||
private readonly Timer _carbonitexTimer;
|
||||
private readonly Timer _dataTimer;
|
||||
private readonly ShardsCoordinator _sc;
|
||||
|
||||
public int GuildCount =>
|
||||
_sc?.GuildCount ?? _client.Guilds.Count();
|
||||
|
||||
public StatsService(DiscordSocketClient client, CommandHandler cmdHandler, IBotCredentials creds, NadekoBot nadeko)
|
||||
{
|
||||
_client = client;
|
||||
_creds = creds;
|
||||
_sc = nadeko.ShardCoord;
|
||||
|
||||
_started = DateTime.UtcNow;
|
||||
_client.MessageReceived += _ => Task.FromResult(Interlocked.Increment(ref _messageCounter));
|
||||
cmdHandler.CommandExecuted += (_, e) => Task.FromResult(Interlocked.Increment(ref _commandsRan));
|
||||
|
||||
_client.ChannelCreated += (c) =>
|
||||
{
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
if (c is ITextChannel)
|
||||
Interlocked.Increment(ref _textChannels);
|
||||
else if (c is IVoiceChannel)
|
||||
Interlocked.Increment(ref _voiceChannels);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_client.ChannelDestroyed += (c) =>
|
||||
{
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
if (c is ITextChannel)
|
||||
Interlocked.Decrement(ref _textChannels);
|
||||
else if (c is IVoiceChannel)
|
||||
Interlocked.Decrement(ref _voiceChannels);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_client.GuildAvailable += (g) =>
|
||||
{
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
var tc = g.Channels.Count(cx => cx is ITextChannel);
|
||||
var vc = g.Channels.Count - tc;
|
||||
Interlocked.Add(ref _textChannels, tc);
|
||||
Interlocked.Add(ref _voiceChannels, vc);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_client.JoinedGuild += (g) =>
|
||||
{
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
var tc = g.Channels.Count(cx => cx is ITextChannel);
|
||||
var vc = g.Channels.Count - tc;
|
||||
Interlocked.Add(ref _textChannels, tc);
|
||||
Interlocked.Add(ref _voiceChannels, vc);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_client.GuildUnavailable += (g) =>
|
||||
{
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
var tc = g.Channels.Count(cx => cx is ITextChannel);
|
||||
var vc = g.Channels.Count - tc;
|
||||
Interlocked.Add(ref _textChannels, -tc);
|
||||
Interlocked.Add(ref _voiceChannels, -vc);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_client.LeftGuild += (g) =>
|
||||
{
|
||||
var _ = Task.Run(() =>
|
||||
{
|
||||
var tc = g.Channels.Count(cx => cx is ITextChannel);
|
||||
var vc = g.Channels.Count - tc;
|
||||
Interlocked.Add(ref _textChannels, -tc);
|
||||
Interlocked.Add(ref _voiceChannels, -vc);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
if (_sc != null)
|
||||
{
|
||||
_carbonitexTimer = new Timer(async (state) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_creds.CarbonKey))
|
||||
return;
|
||||
try
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
using (var content = new FormUrlEncodedContent(
|
||||
new Dictionary<string, string> {
|
||||
{ "servercount", _sc.GuildCount.ToString() },
|
||||
{ "key", _creds.CarbonKey }}))
|
||||
{
|
||||
content.Headers.Clear();
|
||||
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
|
||||
|
||||
var platform = "other";
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
platform = "linux";
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
platform = "osx";
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
platform = "windows";
|
||||
|
||||
_dataTimer = new Timer(async (state) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
{
|
||||
using (var content = new FormUrlEncodedContent(
|
||||
new Dictionary<string, string> {
|
||||
{ "id", string.Concat(MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(_creds.ClientId.ToString())).Select(x => x.ToString("X2"))) },
|
||||
{ "guildCount", _sc.GuildCount.ToString() },
|
||||
{ "version", BotVersion },
|
||||
{ "platform", platform }}))
|
||||
{
|
||||
content.Headers.Clear();
|
||||
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
|
||||
|
||||
await http.PostAsync("https://selfstats.nadekobot.me/", content).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}, null, TimeSpan.FromSeconds(1), TimeSpan.FromHours(1));
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
var guilds = _client.Guilds.ToArray();
|
||||
_textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel));
|
||||
_voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels;
|
||||
}
|
||||
|
||||
public Task<string> Print()
|
||||
{
|
||||
SocketSelfUser curUser;
|
||||
while ((curUser = _client.CurrentUser) == null) Task.Delay(1000).ConfigureAwait(false);
|
||||
|
||||
return Task.FromResult($@"
|
||||
Author: [{Author}] | Library: [{Library}]
|
||||
Bot Version: [{BotVersion}]
|
||||
Bot ID: {curUser.Id}
|
||||
Owner ID(s): {string.Join(", ", _creds.OwnerIds)}
|
||||
Uptime: {GetUptimeString()}
|
||||
Servers: {_client.Guilds.Count} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels}
|
||||
Commands Ran this session: {CommandsRan}
|
||||
Messages: {MessageCounter} [{MessagesPerSecond:F2}/sec] Heap: [{Heap} MB]");
|
||||
}
|
||||
|
||||
public TimeSpan GetUptime() =>
|
||||
DateTime.UtcNow - _started;
|
||||
|
||||
public string GetUptimeString(string separator = ", ")
|
||||
{
|
||||
var time = GetUptime();
|
||||
return $"{time.Days} days{separator}{time.Hours} hours{separator}{time.Minutes} minutes";
|
||||
}
|
||||
}
|
||||
}
|
7
NadekoBot.Core/Services/Impl/SyncPreconditionService.cs
Normal file
7
NadekoBot.Core/Services/Impl/SyncPreconditionService.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class SyncPreconditionService
|
||||
{
|
||||
|
||||
}
|
||||
}
|
46
NadekoBot.Core/Services/Impl/Ytdl.cs
Normal file
46
NadekoBot.Core/Services/Impl/Ytdl.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Impl
|
||||
{
|
||||
public class YtdlOperation : IDisposable
|
||||
{
|
||||
private readonly Logger _log;
|
||||
|
||||
public YtdlOperation()
|
||||
{
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
}
|
||||
|
||||
public async Task<string> GetDataAsync(string url)
|
||||
{
|
||||
using (Process process = new Process()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = "youtube-dl",
|
||||
Arguments = $"-f bestaudio -e --get-url --get-id --get-thumbnail --get-duration --no-check-certificate --default-search \"ytsearch:\" \"{url}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true,
|
||||
},
|
||||
})
|
||||
{
|
||||
process.Start();
|
||||
var str = await process.StandardOutput.ReadToEndAsync();
|
||||
var err = await process.StandardError.ReadToEndAsync();
|
||||
if(!string.IsNullOrEmpty(err))
|
||||
_log.Warn(err);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user