Refactored Searches
This commit is contained in:
@ -7,7 +7,7 @@ using System.Globalization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Search.Commands
|
||||
namespace NadekoBot.Modules.Searches.Commands
|
||||
{
|
||||
class ConverterCommand : DiscordCommand
|
||||
{
|
380
NadekoBot/Modules/Searches/Commands/LoLCommands.cs
Normal file
380
NadekoBot/Modules/Searches/Commands/LoLCommands.cs
Normal file
@ -0,0 +1,380 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using NadekoBot.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Searches.Commands
|
||||
{
|
||||
internal class LoLCommands : DiscordCommand
|
||||
{
|
||||
|
||||
private class CachedChampion
|
||||
{
|
||||
public System.IO.Stream ImageStream { get; set; }
|
||||
public DateTime AddedAt { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
private static Dictionary<string, CachedChampion> CachedChampionImages = new Dictionary<string, CachedChampion>();
|
||||
private readonly object cacheLock = new object();
|
||||
|
||||
|
||||
private System.Timers.Timer clearTimer { get; } = new System.Timers.Timer();
|
||||
public LoLCommands(DiscordModule module) : base(module)
|
||||
{
|
||||
clearTimer.Interval = new TimeSpan(0, 10, 0).TotalMilliseconds;
|
||||
clearTimer.Start();
|
||||
clearTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (cacheLock)
|
||||
CachedChampionImages = CachedChampionImages
|
||||
.Where(kvp => DateTime.Now - kvp.Value.AddedAt > new TimeSpan(1, 0, 0))
|
||||
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
}
|
||||
catch { }
|
||||
};
|
||||
}
|
||||
|
||||
private readonly string[] trashTalk = { "Better ban your counters. You are going to carry the game anyway.",
|
||||
"Go with the flow. Don't think. Just ban one of these.",
|
||||
"DONT READ BELOW! Ban Urgot mid OP 100%. Im smurf Diamond 1.",
|
||||
"Ask your teammates what would they like to play, and ban that.",
|
||||
"If you consider playing teemo, do it. If you consider teemo, you deserve him.",
|
||||
"Doesn't matter what you ban really. Enemy will ban your main and you will lose." };
|
||||
|
||||
public Func<CommandEventArgs, Task> DoFunc()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class MatchupModel
|
||||
{
|
||||
public int Games { get; set; }
|
||||
public float WinRate { get; set; }
|
||||
[Newtonsoft.Json.JsonProperty("key")]
|
||||
public string Name { get; set; }
|
||||
public float StatScore { get; set; }
|
||||
}
|
||||
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "lolchamp")
|
||||
.Description("Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role.\n**Usage**:~lolchamp Riven or ~lolchamp Annie sup")
|
||||
.Parameter("champ", ParameterType.Required)
|
||||
.Parameter("position", ParameterType.Unparsed)
|
||||
.Do(async e =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//get role
|
||||
var role = ResolvePos(e.GetArg("position"));
|
||||
var resolvedRole = role;
|
||||
var name = e.GetArg("champ").Replace(" ", "").ToLower();
|
||||
CachedChampion champ = null;
|
||||
lock (cacheLock)
|
||||
{
|
||||
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
|
||||
}
|
||||
if (champ != null)
|
||||
{
|
||||
champ.ImageStream.Position = 0;
|
||||
await e.Channel.SendFile("champ.png", champ.ImageStream);
|
||||
return;
|
||||
}
|
||||
var allData = JArray.Parse(await Classes.SearchHelper.GetResponseStringAsync($"http://api.champion.gg/champion/{name}?api_key={NadekoBot.Creds.LOLAPIKey}"));
|
||||
JToken data = null;
|
||||
if (role != null)
|
||||
{
|
||||
for (var i = 0; i < allData.Count; i++)
|
||||
{
|
||||
if (allData[i]["role"].ToString().Equals(role))
|
||||
{
|
||||
data = allData[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data == null)
|
||||
{
|
||||
await e.Channel.SendMessage("💢 Data for that role does not exist.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data = allData[0];
|
||||
role = allData[0]["role"].ToString();
|
||||
resolvedRole = ResolvePos(role);
|
||||
}
|
||||
lock (cacheLock)
|
||||
{
|
||||
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
|
||||
}
|
||||
if (champ != null)
|
||||
{
|
||||
Console.WriteLine("Sending lol image from cache.");
|
||||
champ.ImageStream.Position = 0;
|
||||
await e.Channel.SendFile("champ.png", champ.ImageStream);
|
||||
return;
|
||||
}
|
||||
//name = data["title"].ToString();
|
||||
// get all possible roles, and "select" the shown one
|
||||
var roles = new string[allData.Count];
|
||||
for (var i = 0; i < allData.Count; i++)
|
||||
{
|
||||
roles[i] = allData[i]["role"].ToString();
|
||||
if (roles[i] == role)
|
||||
roles[i] = ">" + roles[i] + "<";
|
||||
}
|
||||
var general = JArray.Parse(await SearchHelper.GetResponseStringAsync($"http://api.champion.gg/stats/" +
|
||||
$"champs/{name}?api_key={NadekoBot.Creds.LOLAPIKey}"))
|
||||
.FirstOrDefault(jt => jt["role"].ToString() == role)?["general"];
|
||||
if (general == null)
|
||||
{
|
||||
Console.WriteLine("General is null.");
|
||||
return;
|
||||
}
|
||||
//get build data for this role
|
||||
var buildData = data["items"]["mostGames"]["items"];
|
||||
var items = new string[6];
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
items[i] = buildData[i]["id"].ToString();
|
||||
}
|
||||
|
||||
//get matchup data to show counters and countered champions
|
||||
var matchupDataIE = data["matchups"].ToObject<List<MatchupModel>>();
|
||||
|
||||
var matchupData = matchupDataIE.OrderBy(m => m.StatScore).ToArray();
|
||||
|
||||
var countered = new[] { matchupData[0].Name, matchupData[1].Name, matchupData[2].Name };
|
||||
var counters = new[] { matchupData[matchupData.Length - 1].Name, matchupData[matchupData.Length - 2].Name, matchupData[matchupData.Length - 3].Name };
|
||||
|
||||
//get runes data
|
||||
var runesJArray = data["runes"]["mostGames"]["runes"] as JArray;
|
||||
var runes = string.Join("\n", runesJArray.OrderBy(jt => int.Parse(jt["number"].ToString())).Select(jt => jt["number"].ToString() + "x" + jt["name"]));
|
||||
|
||||
// get masteries data
|
||||
|
||||
var masteries = (data["masteries"]["mostGames"]["masteries"] as JArray);
|
||||
|
||||
//get skill order data<API_KEY>
|
||||
|
||||
var orderArr = (data["skills"]["mostGames"]["order"] as JArray);
|
||||
|
||||
var img = Image.FromFile("data/lol/bg.png");
|
||||
using (var g = Graphics.FromImage(img))
|
||||
{
|
||||
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
|
||||
//g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
|
||||
const int margin = 5;
|
||||
const int imageSize = 75;
|
||||
var normalFont = new Font("Monaco", 8, FontStyle.Regular);
|
||||
var smallFont = new Font("Monaco", 7, FontStyle.Regular);
|
||||
//draw champ image
|
||||
var champName = data["key"].ToString().Replace(" ", "");
|
||||
|
||||
g.DrawImage(GetImage(champName), new Rectangle(margin, margin, imageSize, imageSize));
|
||||
//draw champ name
|
||||
if (champName == "MonkeyKing")
|
||||
champName = "Wukong";
|
||||
g.DrawString($"{champName}", new Font("Times New Roman", 24, FontStyle.Regular), Brushes.WhiteSmoke, margin + imageSize + margin, margin);
|
||||
//draw champ surname
|
||||
|
||||
//draw skill order
|
||||
float orderFormula = 120 / orderArr.Count;
|
||||
const float orderVerticalSpacing = 10;
|
||||
for (var i = 0; i < orderArr.Count; i++)
|
||||
{
|
||||
var orderX = margin + margin + imageSize + orderFormula * i + i;
|
||||
float orderY = margin + 35;
|
||||
var spellName = orderArr[i].ToString().ToLowerInvariant();
|
||||
|
||||
switch (spellName)
|
||||
{
|
||||
case "w":
|
||||
orderY += orderVerticalSpacing;
|
||||
break;
|
||||
case "e":
|
||||
orderY += orderVerticalSpacing * 2;
|
||||
break;
|
||||
case "r":
|
||||
orderY += orderVerticalSpacing * 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
g.DrawString(spellName.ToUpperInvariant(), new Font("Monaco", 7), Brushes.LimeGreen, orderX, orderY);
|
||||
}
|
||||
//draw roles
|
||||
g.DrawString("Roles: " + string.Join(", ", roles), normalFont, Brushes.WhiteSmoke, margin, margin + imageSize + margin);
|
||||
|
||||
//draw average stats
|
||||
g.DrawString(
|
||||
$@" Average Stats
|
||||
|
||||
Kills: {general["kills"]} CS: {general["minionsKilled"]}
|
||||
Deaths: {general["deaths"]} Win: {general["winPercent"]}%
|
||||
Assists: {general["assists"]} Ban: {general["banRate"]}%
|
||||
", normalFont, Brushes.WhiteSmoke, img.Width - 150, margin);
|
||||
//draw masteries
|
||||
g.DrawString($"Masteries: {string.Join(" / ", masteries?.Select(jt => jt["total"]))}", normalFont, Brushes.WhiteSmoke, margin, margin + imageSize + margin + 20);
|
||||
//draw runes
|
||||
g.DrawString($"{runes}", smallFont, Brushes.WhiteSmoke, margin, margin + imageSize + margin + 40);
|
||||
//draw counters
|
||||
g.DrawString($"Best against", smallFont, Brushes.WhiteSmoke, margin, img.Height - imageSize + margin);
|
||||
var smallImgSize = 50;
|
||||
|
||||
for (var i = 0; i < counters.Length; i++)
|
||||
{
|
||||
g.DrawImage(GetImage(counters[i]),
|
||||
new Rectangle(i * (smallImgSize + margin) + margin, img.Height - smallImgSize - margin,
|
||||
smallImgSize,
|
||||
smallImgSize));
|
||||
}
|
||||
//draw countered by
|
||||
g.DrawString($"Worst against", smallFont, Brushes.WhiteSmoke, img.Width - 3 * (smallImgSize + margin), img.Height - imageSize + margin);
|
||||
|
||||
for (var i = 0; i < countered.Length; i++)
|
||||
{
|
||||
var j = countered.Length - i;
|
||||
g.DrawImage(GetImage(countered[i]),
|
||||
new Rectangle(img.Width - (j * (smallImgSize + margin) + margin), img.Height - smallImgSize - margin,
|
||||
smallImgSize,
|
||||
smallImgSize));
|
||||
}
|
||||
//draw item build
|
||||
g.DrawString("Popular build", normalFont, Brushes.WhiteSmoke, img.Width - (3 * (smallImgSize + margin) + margin), 77);
|
||||
|
||||
for (var i = 0; i < 6; i++)
|
||||
{
|
||||
var inverseI = 5 - i;
|
||||
var j = inverseI % 3 + 1;
|
||||
var k = inverseI / 3;
|
||||
g.DrawImage(GetImage(items[i], GetImageType.Item),
|
||||
new Rectangle(img.Width - (j * (smallImgSize + margin) + margin), 92 + k * (smallImgSize + margin),
|
||||
smallImgSize,
|
||||
smallImgSize));
|
||||
}
|
||||
}
|
||||
var cachedChamp = new CachedChampion { AddedAt = DateTime.Now, ImageStream = img.ToStream(System.Drawing.Imaging.ImageFormat.Png), Name = name.ToLower() + "_" + resolvedRole };
|
||||
CachedChampionImages.Add(cachedChamp.Name, cachedChamp);
|
||||
await e.Channel.SendFile(data["title"] + "_stats.png", cachedChamp.ImageStream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
await e.Channel.SendMessage("💢 Failed retreiving data for that champion.");
|
||||
}
|
||||
});
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "lolban")
|
||||
.Description("Shows top 6 banned champions ordered by ban rate. Ban these champions and you will be Plat 5 in no time.")
|
||||
.Do(async e =>
|
||||
{
|
||||
|
||||
var showCount = 6;
|
||||
//http://api.champion.gg/stats/champs/mostBanned?api_key=YOUR_API_TOKEN&page=1&limit=2
|
||||
try
|
||||
{
|
||||
var data = JObject.Parse(
|
||||
await Classes
|
||||
.SearchHelper
|
||||
.GetResponseStringAsync($"http://api.champion.gg/stats/champs/mostBanned?" +
|
||||
$"api_key={NadekoBot.Creds.LOLAPIKey}&page=1&" +
|
||||
$"limit={showCount}"))["data"] as JArray;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine($"**Showing {showCount} top banned champions.**");
|
||||
sb.AppendLine($"`{trashTalk[new Random().Next(0, trashTalk.Length)]}`");
|
||||
for (var i = 0; i < data.Count; i++)
|
||||
{
|
||||
if (i % 2 == 0 && i != 0)
|
||||
sb.AppendLine();
|
||||
sb.Append($"`{i + 1}.` **{data[i]["name"]}** ");
|
||||
//sb.AppendLine($" ({data[i]["general"]["banRate"]}%)");
|
||||
}
|
||||
|
||||
await e.Channel.SendMessage(sb.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await e.Channel.SendMessage($":anger: Fail: Champion.gg didsabled ban data until next patch. Sorry for the inconvenience.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private enum GetImageType
|
||||
{
|
||||
Champion,
|
||||
Item
|
||||
}
|
||||
private static Image GetImage(string id, GetImageType imageType = GetImageType.Champion)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (imageType)
|
||||
{
|
||||
case GetImageType.Champion:
|
||||
return Image.FromFile($"data/lol/champions/{id}.png");
|
||||
case GetImageType.Item:
|
||||
default:
|
||||
return Image.FromFile($"data/lol/items/{id}.png");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return Image.FromFile("data/lol/_ERROR.png");
|
||||
}
|
||||
}
|
||||
|
||||
private static string ResolvePos(string pos)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pos))
|
||||
return null;
|
||||
switch (pos.ToLowerInvariant())
|
||||
{
|
||||
case "m":
|
||||
case "mid":
|
||||
case "midorfeed":
|
||||
case "midd":
|
||||
case "middle":
|
||||
return "Middle";
|
||||
case "top":
|
||||
case "topp":
|
||||
case "t":
|
||||
case "toporfeed":
|
||||
return "Top";
|
||||
case "j":
|
||||
case "jun":
|
||||
case "jungl":
|
||||
case "jungle":
|
||||
return "Jungle";
|
||||
case "a":
|
||||
case "ad":
|
||||
case "adc":
|
||||
case "carry":
|
||||
case "ad carry":
|
||||
case "adcarry":
|
||||
case "c":
|
||||
return "ADC";
|
||||
case "s":
|
||||
case "sup":
|
||||
case "supp":
|
||||
case "support":
|
||||
return "Support";
|
||||
default:
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
NadekoBot/Modules/Searches/Commands/RedditCommand.cs
Normal file
18
NadekoBot/Modules/Searches/Commands/RedditCommand.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Commands;
|
||||
using System;
|
||||
|
||||
namespace NadekoBot.Modules.Searches.Commands
|
||||
{
|
||||
class RedditCommand : DiscordCommand
|
||||
{
|
||||
public RedditCommand(DiscordModule module) : base(module)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
258
NadekoBot/Modules/Searches/Commands/StreamNotifications.cs
Normal file
258
NadekoBot/Modules/Searches/Commands/StreamNotifications.cs
Normal file
@ -0,0 +1,258 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using NadekoBot.Classes.JSONModels;
|
||||
using NadekoBot.Classes.Permissions;
|
||||
using NadekoBot.Commands;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
|
||||
namespace NadekoBot.Modules.Searches.Commands
|
||||
{
|
||||
internal class StreamNotifications : DiscordCommand
|
||||
{
|
||||
|
||||
private readonly Timer checkTimer = new Timer
|
||||
{
|
||||
Interval = new TimeSpan(0, 0, 15).TotalMilliseconds,
|
||||
};
|
||||
|
||||
private ConcurrentDictionary<string, Tuple<bool, string>> cachedStatuses = new ConcurrentDictionary<string, Tuple<bool, string>>();
|
||||
|
||||
public StreamNotifications(DiscordModule module) : base(module)
|
||||
{
|
||||
|
||||
checkTimer.Elapsed += async (s, e) =>
|
||||
{
|
||||
cachedStatuses.Clear();
|
||||
try
|
||||
{
|
||||
var streams = SpecificConfigurations.Default.AllConfigs.SelectMany(c => c.ObservingStreams);
|
||||
if (!streams.Any()) return;
|
||||
|
||||
foreach (var stream in streams)
|
||||
{
|
||||
Tuple<bool, string> data;
|
||||
try
|
||||
{
|
||||
data = await GetStreamStatus(stream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data.Item1 != stream.LastStatus)
|
||||
{
|
||||
stream.LastStatus = data.Item1;
|
||||
var server = NadekoBot.Client.GetServer(stream.ServerId);
|
||||
var channel = server?.GetChannel(stream.ChannelId);
|
||||
if (channel == null)
|
||||
continue;
|
||||
var msg = $"`{stream.Username}`'s stream is now " +
|
||||
$"**{(data.Item1 ? "ONLINE" : "OFFLINE")}** with " +
|
||||
$"**{data.Item2}** viewers.";
|
||||
if (stream.LastStatus)
|
||||
if (stream.Type == StreamNotificationConfig.StreamType.Hitbox)
|
||||
msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】";
|
||||
else if (stream.Type == StreamNotificationConfig.StreamType.Twitch)
|
||||
msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】";
|
||||
else if (stream.Type == StreamNotificationConfig.StreamType.Beam)
|
||||
msg += $"\n`Here is the Link:`【 http://www.beam.pro/{stream.Username}/ 】";
|
||||
else if (stream.Type == StreamNotificationConfig.StreamType.YoutubeGaming)
|
||||
msg += $"\n`Here is the Link:`【 not implemented yet - {stream.Username} 】";
|
||||
await channel.SendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
ConfigHandler.SaveConfig();
|
||||
};
|
||||
checkTimer.Start();
|
||||
}
|
||||
|
||||
private async Task<Tuple<bool, string>> GetStreamStatus(StreamNotificationConfig stream)
|
||||
{
|
||||
bool isLive;
|
||||
string response;
|
||||
JObject data;
|
||||
Tuple<bool, string> result;
|
||||
switch (stream.Type)
|
||||
{
|
||||
case StreamNotificationConfig.StreamType.Hitbox:
|
||||
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username}";
|
||||
if (cachedStatuses.TryGetValue(hitboxUrl, out result))
|
||||
return result;
|
||||
response = await SearchHelper.GetResponseStringAsync(hitboxUrl);
|
||||
data = JObject.Parse(response);
|
||||
isLive = data["media_is_live"].ToString() == "1";
|
||||
result = new Tuple<bool, string>(isLive, data["media_views"].ToString());
|
||||
cachedStatuses.TryAdd(hitboxUrl, result);
|
||||
return result;
|
||||
case StreamNotificationConfig.StreamType.Twitch:
|
||||
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username)}";
|
||||
if (cachedStatuses.TryGetValue(twitchUrl, out result))
|
||||
return result;
|
||||
response = await SearchHelper.GetResponseStringAsync(twitchUrl);
|
||||
data = JObject.Parse(response);
|
||||
isLive = !string.IsNullOrWhiteSpace(data["stream"].ToString());
|
||||
result = new Tuple<bool, string>(isLive, isLive ? data["stream"]["viewers"].ToString() : "0");
|
||||
cachedStatuses.TryAdd(twitchUrl, result);
|
||||
return result;
|
||||
case StreamNotificationConfig.StreamType.Beam:
|
||||
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username}";
|
||||
if (cachedStatuses.TryGetValue(beamUrl, out result))
|
||||
return result;
|
||||
response = await SearchHelper.GetResponseStringAsync(beamUrl);
|
||||
data = JObject.Parse(response);
|
||||
isLive = data["online"].ToObject<bool>() == true;
|
||||
result = new Tuple<bool, string>(isLive, data["viewersCurrent"].ToString());
|
||||
cachedStatuses.TryAdd(beamUrl, result);
|
||||
return result;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return new Tuple<bool, string>(false, "0");
|
||||
}
|
||||
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "hitbox")
|
||||
.Alias(Module.Prefix + "hb")
|
||||
.Description("Notifies this channel when a certain user starts streaming." +
|
||||
"\n**Usage**: ~hitbox SomeStreamer")
|
||||
.Parameter("username", ParameterType.Unparsed)
|
||||
.AddCheck(SimpleCheckers.ManageServer())
|
||||
.Do(TrackStream(StreamNotificationConfig.StreamType.Hitbox));
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "twitch")
|
||||
.Alias(Module.Prefix + "tw")
|
||||
.Description("Notifies this channel when a certain user starts streaming." +
|
||||
"\n**Usage**: ~twitch SomeStreamer")
|
||||
.AddCheck(SimpleCheckers.ManageServer())
|
||||
.Parameter("username", ParameterType.Unparsed)
|
||||
.Do(TrackStream(StreamNotificationConfig.StreamType.Twitch));
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "beam")
|
||||
.Alias(Module.Prefix + "bm")
|
||||
.Description("Notifies this channel when a certain user starts streaming." +
|
||||
"\n**Usage**: ~beam SomeStreamer")
|
||||
.AddCheck(SimpleCheckers.ManageServer())
|
||||
.Parameter("username", ParameterType.Unparsed)
|
||||
.Do(TrackStream(StreamNotificationConfig.StreamType.Beam));
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "removestream")
|
||||
.Alias(Module.Prefix + "rms")
|
||||
.Description("Removes notifications of a certain streamer on this channel." +
|
||||
"\n**Usage**: ~rms SomeGuy")
|
||||
.AddCheck(SimpleCheckers.ManageServer())
|
||||
.Parameter("username", ParameterType.Unparsed)
|
||||
.Do(async e =>
|
||||
{
|
||||
var username = e.GetArg("username")?.ToLower().Trim();
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
return;
|
||||
|
||||
var config = SpecificConfigurations.Default.Of(e.Server.Id);
|
||||
|
||||
var toRemove = config.ObservingStreams
|
||||
.FirstOrDefault(snc => snc.ChannelId == e.Channel.Id &&
|
||||
snc.Username.ToLower().Trim() == username);
|
||||
if (toRemove == null)
|
||||
{
|
||||
await e.Channel.SendMessage(":anger: No such stream.");
|
||||
return;
|
||||
}
|
||||
|
||||
config.ObservingStreams.Remove(toRemove);
|
||||
ConfigHandler.SaveConfig();
|
||||
await e.Channel.SendMessage($":ok: Removed `{toRemove.Username}`'s stream from notifications.");
|
||||
});
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "liststreams")
|
||||
.Alias(Module.Prefix + "ls")
|
||||
.Description("Lists all streams you are following on this server." +
|
||||
"\n**Usage**: ~ls")
|
||||
.Do(async e =>
|
||||
{
|
||||
|
||||
var config = SpecificConfigurations.Default.Of(e.Server.Id);
|
||||
|
||||
var streams = config.ObservingStreams.Where(snc =>
|
||||
snc.ServerId == e.Server.Id);
|
||||
|
||||
var streamsArray = streams as StreamNotificationConfig[] ?? streams.ToArray();
|
||||
|
||||
if (streamsArray.Length == 0)
|
||||
{
|
||||
await e.Channel.SendMessage("You are not following any streams on this server.");
|
||||
return;
|
||||
}
|
||||
|
||||
var text = string.Join("\n", streamsArray.Select(snc =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return $"`{snc.Username}`'s stream on **{e.Server.GetChannel(e.Channel.Id).Name}** channel. 【`{snc.Type.ToString()}`】";
|
||||
}
|
||||
catch { }
|
||||
return "";
|
||||
}));
|
||||
|
||||
await e.Channel.SendMessage($"You are following **{streamsArray.Length}** streams on this server.\n\n" + text);
|
||||
});
|
||||
}
|
||||
|
||||
private Func<CommandEventArgs, Task> TrackStream(StreamNotificationConfig.StreamType type) =>
|
||||
async e =>
|
||||
{
|
||||
var username = e.GetArg("username")?.ToLowerInvariant();
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
return;
|
||||
|
||||
var config = SpecificConfigurations.Default.Of(e.Server.Id);
|
||||
|
||||
var stream = new StreamNotificationConfig
|
||||
{
|
||||
ServerId = e.Server.Id,
|
||||
ChannelId = e.Channel.Id,
|
||||
Username = username,
|
||||
Type = type,
|
||||
};
|
||||
var exists = config.ObservingStreams.Contains(stream);
|
||||
if (exists)
|
||||
{
|
||||
await e.Channel.SendMessage(":anger: I am already notifying that stream on this channel.");
|
||||
return;
|
||||
}
|
||||
Tuple<bool, string> data;
|
||||
try
|
||||
{
|
||||
data = await GetStreamStatus(stream);
|
||||
}
|
||||
catch
|
||||
{
|
||||
await e.Channel.SendMessage(":anger: Stream probably doesn't exist.");
|
||||
return;
|
||||
}
|
||||
var msg = $"Stream is currently **{(data.Item1 ? "ONLINE" : "OFFLINE")}** with **{data.Item2}** viewers";
|
||||
if (data.Item1)
|
||||
if (type == StreamNotificationConfig.StreamType.Hitbox)
|
||||
msg += $"\n`Here is the Link:`【 http://www.hitbox.tv/{stream.Username}/ 】";
|
||||
else if (type == StreamNotificationConfig.StreamType.Twitch)
|
||||
msg += $"\n`Here is the Link:`【 http://www.twitch.tv/{stream.Username}/ 】";
|
||||
else if (type == StreamNotificationConfig.StreamType.Beam)
|
||||
msg += $"\n`Here is the Link:`【 https://beam.pro/{stream.Username}/ 】";
|
||||
else if (type == StreamNotificationConfig.StreamType.YoutubeGaming)
|
||||
msg += $"\n`Here is the Link:` not implemented yet - {stream.Username}";
|
||||
stream.LastStatus = data.Item1;
|
||||
if (!exists)
|
||||
msg = $":ok: I will notify this channel when status changes.\n{msg}";
|
||||
await e.Channel.SendMessage(msg);
|
||||
config.ObservingStreams.Add(stream);
|
||||
};
|
||||
}
|
||||
}
|
@ -3,9 +3,8 @@ using Discord.Modules;
|
||||
using NadekoBot.Classes;
|
||||
using NadekoBot.Classes.IMDB;
|
||||
using NadekoBot.Classes.JSONModels;
|
||||
using NadekoBot.Commands;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Search.Commands;
|
||||
using NadekoBot.Modules.Searches.Commands;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
@ -15,7 +14,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace NadekoBot.Modules
|
||||
namespace NadekoBot.Modules.Searches
|
||||
{
|
||||
internal class Searches : DiscordModule
|
||||
{
|
||||
@ -25,6 +24,7 @@ namespace NadekoBot.Modules
|
||||
commands.Add(new LoLCommands(this));
|
||||
commands.Add(new StreamNotifications(this));
|
||||
commands.Add(new ConverterCommand(this));
|
||||
commands.Add(new RedditCommand(this));
|
||||
rng = new Random();
|
||||
}
|
||||
|
Reference in New Issue
Block a user