forked from EllieBotDevs/elliebot
Merge pull request 'v6' (#3) from EllieBotDevs/elliebot:v6 into v6
Reviewed-on: #3
This commit is contained in:
commit
4642607ccc
10 changed files with 199 additions and 144 deletions
CHANGELOG.md
src/EllieBot
EllieBot.csproj
Modules
Administration
Permissions/Filter
Searches
StreamNotification
_common/StreamNotifications
Utility
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -2,6 +2,34 @@
|
||||||
|
|
||||||
*a,c,f,r,o*
|
*a,c,f,r,o*
|
||||||
|
|
||||||
|
## [6.0.12] - 20.03.2025
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `.antispamignore` fixed for the last time hopefully
|
||||||
|
- protection commands are some of the oldest commands, and they might get overhauled in future updates
|
||||||
|
- please report if you find any other weird issue with them
|
||||||
|
|
||||||
|
## [6.0.11] - 20.03.2025
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- wordfilter, invitefilter and linkfilter will now properly detect forwarded messages, as forwards were used to circumvent filtering.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- `.dmc` fixed
|
||||||
|
- Fixed .streamremove - now showing proper youtube name when removing instead of channel id
|
||||||
|
|
||||||
|
## [6.0.10] - 20.03.2025
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Live channels `.lcha` is limited to 1 for now. It will be reverted back to 5 in a couple of days at most as some things need to be implemented.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `.antispam` won't break if you have thread channels in the server anymore
|
||||||
|
- `.ve` now works properly
|
||||||
|
- selfhosters: `.yml` parsing errors will now tell you which .yml file is causing the issue and why.
|
||||||
|
|
||||||
## [6.0.9] - 19.03.2025
|
## [6.0.9] - 19.03.2025
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>true</ImplicitUsings>
|
<ImplicitUsings>true</ImplicitUsings>
|
||||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||||
<Version>6.0.9</Version>
|
<Version>6.0.12</Version>
|
||||||
|
|
||||||
<!-- Output/build -->
|
<!-- Output/build -->
|
||||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||||
|
|
|
@ -107,12 +107,12 @@ public class AdministrationService : IEService, IReadyExecutor
|
||||||
.UpdateWithOutputAsync(x => new()
|
.UpdateWithOutputAsync(x => new()
|
||||||
{
|
{
|
||||||
DeleteMessageOnCommand = !x.DeleteMessageOnCommand
|
DeleteMessageOnCommand = !x.DeleteMessageOnCommand
|
||||||
});
|
}, (old, newVal) => newVal);
|
||||||
|
|
||||||
if (conf.Length == 0)
|
if (conf.Length == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var val = conf[0].Inserted.DeleteMessageOnCommand;
|
var val = conf[0].DeleteMessageOnCommand;
|
||||||
|
|
||||||
if (val)
|
if (val)
|
||||||
deleteMessagesOnCommand.Add(guildId);
|
deleteMessagesOnCommand.Add(guildId);
|
||||||
|
|
|
@ -27,6 +27,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
||||||
private readonly UserPunishService _punishService;
|
private readonly UserPunishService _punishService;
|
||||||
private readonly INotifySubscriber _notifySub;
|
private readonly INotifySubscriber _notifySub;
|
||||||
private readonly ShardData _shardData;
|
private readonly ShardData _shardData;
|
||||||
|
|
||||||
private readonly Channel<PunishQueueItem> _punishUserQueue =
|
private readonly Channel<PunishQueueItem> _punishUserQueue =
|
||||||
Channel.CreateUnbounded<PunishQueueItem>(new()
|
Channel.CreateUnbounded<PunishQueueItem>(new()
|
||||||
{
|
{
|
||||||
|
@ -176,10 +177,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings)
|
if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out var spamSettings)
|
||||||
|| spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new()
|
|| spamSettings.AntiSpamSettings.IgnoredChannels.Any(x => x.ChannelId == channel.Id))
|
||||||
{
|
|
||||||
ChannelId = channel.Id
|
|
||||||
}))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id,
|
var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id,
|
||||||
|
@ -275,23 +273,25 @@ public class ProtectionService : IReadyExecutor, IEService
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
await uow.GetTable<AntiRaidSetting>()
|
await uow.GetTable<AntiRaidSetting>()
|
||||||
.InsertOrUpdateAsync(() => new()
|
.InsertOrUpdateAsync(() => new()
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
Action = action,
|
Action = action,
|
||||||
Seconds = seconds,
|
Seconds = seconds,
|
||||||
UserThreshold = userThreshold,
|
UserThreshold = userThreshold,
|
||||||
PunishDuration = minutesDuration
|
PunishDuration = minutesDuration
|
||||||
}, _ => new()
|
},
|
||||||
{
|
_ => new()
|
||||||
Action = action,
|
{
|
||||||
Seconds = seconds,
|
Action = action,
|
||||||
UserThreshold = userThreshold,
|
Seconds = seconds,
|
||||||
PunishDuration = minutesDuration
|
UserThreshold = userThreshold,
|
||||||
}, () => new()
|
PunishDuration = minutesDuration
|
||||||
{
|
},
|
||||||
GuildId = guildId
|
() => new()
|
||||||
});
|
{
|
||||||
|
GuildId = guildId
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
|
@ -364,23 +364,25 @@ public class ProtectionService : IReadyExecutor, IEService
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
await uow.GetTable<AntiSpamSetting>()
|
await uow.GetTable<AntiSpamSetting>()
|
||||||
.InsertOrUpdateAsync(() => new()
|
.InsertOrUpdateAsync(() => new()
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
Action = stats.AntiSpamSettings.Action,
|
Action = stats.AntiSpamSettings.Action,
|
||||||
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
||||||
MuteTime = stats.AntiSpamSettings.MuteTime,
|
MuteTime = stats.AntiSpamSettings.MuteTime,
|
||||||
RoleId = stats.AntiSpamSettings.RoleId
|
RoleId = stats.AntiSpamSettings.RoleId
|
||||||
}, (old) => new()
|
},
|
||||||
{
|
(old) => new()
|
||||||
GuildId = guildId,
|
{
|
||||||
Action = stats.AntiSpamSettings.Action,
|
GuildId = guildId,
|
||||||
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
Action = stats.AntiSpamSettings.Action,
|
||||||
MuteTime = stats.AntiSpamSettings.MuteTime,
|
MessageThreshold = stats.AntiSpamSettings.MessageThreshold,
|
||||||
RoleId = stats.AntiSpamSettings.RoleId
|
MuteTime = stats.AntiSpamSettings.MuteTime,
|
||||||
}, () => new()
|
RoleId = stats.AntiSpamSettings.RoleId
|
||||||
{
|
},
|
||||||
GuildId = guildId
|
() => new()
|
||||||
});
|
{
|
||||||
|
GuildId = guildId
|
||||||
|
});
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
@ -405,7 +407,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
||||||
if (spam.IgnoredChannels.All(x => x.ChannelId != channelId))
|
if (spam.IgnoredChannels.All(x => x.ChannelId != channelId))
|
||||||
{
|
{
|
||||||
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
|
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
|
||||||
temp.AntiSpamSettings.IgnoredChannels.Add(obj); // add to local cache
|
temp.AntiSpamSettings.IgnoredChannels.Add(obj);
|
||||||
|
|
||||||
spam.IgnoredChannels.Add(obj);
|
spam.IgnoredChannels.Add(obj);
|
||||||
added = true;
|
added = true;
|
||||||
|
@ -417,7 +419,7 @@ public class ProtectionService : IReadyExecutor, IEService
|
||||||
uow.Set<AntiSpamIgnore>().Remove(toRemove);
|
uow.Set<AntiSpamIgnore>().Remove(toRemove);
|
||||||
|
|
||||||
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
|
if (_antiSpamGuilds.TryGetValue(guildId, out var temp))
|
||||||
temp.AntiSpamSettings.IgnoredChannels.Remove(toRemove); // remove from local cache
|
temp.AntiSpamSettings.IgnoredChannels.RemoveAll(x => x.ChannelId == channelId);
|
||||||
|
|
||||||
added = false;
|
added = false;
|
||||||
}
|
}
|
||||||
|
@ -462,22 +464,24 @@ public class ProtectionService : IReadyExecutor, IEService
|
||||||
|
|
||||||
await uow.GetTable<AntiAltSetting>()
|
await uow.GetTable<AntiAltSetting>()
|
||||||
.InsertOrUpdateAsync(() => new()
|
.InsertOrUpdateAsync(() => new()
|
||||||
{
|
{
|
||||||
GuildId = guildId,
|
GuildId = guildId,
|
||||||
Action = action,
|
Action = action,
|
||||||
ActionDurationMinutes = actionDurationMinutes,
|
ActionDurationMinutes = actionDurationMinutes,
|
||||||
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
||||||
RoleId = roleId
|
RoleId = roleId
|
||||||
}, _ => new()
|
},
|
||||||
{
|
_ => new()
|
||||||
Action = action,
|
{
|
||||||
ActionDurationMinutes = actionDurationMinutes,
|
Action = action,
|
||||||
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
ActionDurationMinutes = actionDurationMinutes,
|
||||||
RoleId = roleId
|
MinAge = TimeSpan.FromMinutes(minAgeMinutes),
|
||||||
}, () => new()
|
RoleId = roleId
|
||||||
{
|
},
|
||||||
GuildId = guildId
|
() => new()
|
||||||
});
|
{
|
||||||
|
GuildId = guildId
|
||||||
|
});
|
||||||
|
|
||||||
_antiAltGuilds[guildId] = new(new()
|
_antiAltGuilds[guildId] = new(new()
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,12 +52,12 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
var confs = await uow.GetTable<GuildFilterConfig>()
|
var confs = await uow.GetTable<GuildFilterConfig>()
|
||||||
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
|
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId))
|
||||||
.LoadWith(x => x.FilterInvitesChannelIds)
|
.LoadWith(x => x.FilterInvitesChannelIds)
|
||||||
.LoadWith(x => x.FilterWordsChannelIds)
|
.LoadWith(x => x.FilterWordsChannelIds)
|
||||||
.LoadWith(x => x.FilterLinksChannelIds)
|
.LoadWith(x => x.FilterLinksChannelIds)
|
||||||
.LoadWith(x => x.FilteredWords)
|
.LoadWith(x => x.FilteredWords)
|
||||||
.ToListAsyncLinqToDB();
|
.ToListAsyncLinqToDB();
|
||||||
|
|
||||||
foreach (var conf in confs)
|
foreach (var conf in confs)
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,7 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
var fc = uow.FilterConfigForId(guildId,
|
var fc = uow.FilterConfigForId(guildId,
|
||||||
set => set.Include(x => x.FilteredWords)
|
set => set.Include(x => x.FilteredWords)
|
||||||
.Include(x => x.FilterWordsChannelIds));
|
.Include(x => x.FilterWordsChannelIds));
|
||||||
|
|
||||||
WordFilteringServers.TryRemove(guildId);
|
WordFilteringServers.TryRemove(guildId);
|
||||||
ServerFilteredWords.TryRemove(guildId, out _);
|
ServerFilteredWords.TryRemove(guildId, out _);
|
||||||
|
@ -140,7 +140,8 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
||||||
var filteredChannelWords =
|
var filteredChannelWords =
|
||||||
FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
|
FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id) ?? new ConcurrentHashSet<string>();
|
||||||
var filteredServerWords = FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
|
var filteredServerWords = FilteredWordsForServer(guild.Id) ?? new ConcurrentHashSet<string>();
|
||||||
var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' ');
|
var wordsInMessage = (usrMsg.Content + " " + usrMsg.ForwardedMessages.FirstOrDefault().Message?.Content)
|
||||||
|
.ToLowerInvariant().Split(' ');
|
||||||
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
|
if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0)
|
||||||
{
|
{
|
||||||
foreach (var word in wordsInMessage)
|
foreach (var word in wordsInMessage)
|
||||||
|
@ -183,7 +184,8 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id) || InviteFilteringServers.Contains(guild.Id))
|
if ((InviteFilteringChannels.Contains(usrMsg.Channel.Id) || InviteFilteringServers.Contains(guild.Id))
|
||||||
&& usrMsg.Content.IsDiscordInvite())
|
&& (usrMsg.Content.IsDiscordInvite() ||
|
||||||
|
usrMsg.ForwardedMessages.Any(x => x.Message?.Content.IsDiscordInvite() ?? false)))
|
||||||
{
|
{
|
||||||
Log.Information("User {UserName} [{UserId}] sent a filtered invite to {ChannelId} channel",
|
Log.Information("User {UserName} [{UserId}] sent a filtered invite to {ChannelId} channel",
|
||||||
usrMsg.Author.ToString(),
|
usrMsg.Author.ToString(),
|
||||||
|
@ -219,7 +221,8 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ((LinkFilteringChannels.Contains(usrMsg.Channel.Id) || LinkFilteringServers.Contains(guild.Id))
|
if ((LinkFilteringChannels.Contains(usrMsg.Channel.Id) || LinkFilteringServers.Contains(guild.Id))
|
||||||
&& usrMsg.Content.TryGetUrlPath(out _))
|
&& (usrMsg.Content.TryGetUrlPath(out _) ||
|
||||||
|
usrMsg.ForwardedMessages.Any(x => x.Message?.Content.TryGetUrlPath(out _) ?? false)))
|
||||||
{
|
{
|
||||||
Log.Information("User {UserName} [{UserId}] sent a filtered link to {ChannelId} channel",
|
Log.Information("User {UserName} [{UserId}] sent a filtered link to {ChannelId} channel",
|
||||||
usrMsg.Author.ToString(),
|
usrMsg.Author.ToString(),
|
||||||
|
@ -246,10 +249,10 @@ public sealed class FilterService : IExecOnMessage, IReadyExecutor
|
||||||
await using var uow = _db.GetDbContext();
|
await using var uow = _db.GetDbContext();
|
||||||
|
|
||||||
var conf = await uow.GetTable<GuildFilterConfig>()
|
var conf = await uow.GetTable<GuildFilterConfig>()
|
||||||
.Where(fi => fi.GuildId == guildId)
|
.Where(fi => fi.GuildId == guildId)
|
||||||
.LoadWith(x => x.FilterInvitesChannelIds)
|
.LoadWith(x => x.FilterInvitesChannelIds)
|
||||||
.LoadWith(x => x.FilterLinksChannelIds)
|
.LoadWith(x => x.FilterLinksChannelIds)
|
||||||
.FirstOrDefaultAsyncLinqToDB();
|
.FirstOrDefaultAsyncLinqToDB();
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,7 +48,10 @@ public partial class Searches
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Response().Confirm(strs.stream_removed(Format.Bold(fs.Username), fs.Type)).SendAsync();
|
await Response()
|
||||||
|
.Confirm(strs.stream_removed(
|
||||||
|
Format.Bold(string.IsNullOrWhiteSpace(fs.PrettyName) ? fs.Username : fs.PrettyName),
|
||||||
|
fs.Type)).SendAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
|
|
|
@ -63,20 +63,19 @@ public class NotifChecker
|
||||||
.ToDictionary(x => x.Key.Name, x => x.Value));
|
.ToDictionary(x => x.Key.Name, x => x.Value));
|
||||||
|
|
||||||
var newStreamData = await oldStreamDataDict
|
var newStreamData = await oldStreamDataDict
|
||||||
.Select(x =>
|
.Select(async x =>
|
||||||
{
|
{
|
||||||
// get all stream data for the streams of this type
|
// get all stream data for the streams of this type
|
||||||
if (_streamProviders.TryGetValue(x.Key,
|
if (_streamProviders.TryGetValue(x.Key,
|
||||||
out var provider))
|
out var provider))
|
||||||
{
|
{
|
||||||
return provider.GetStreamDataAsync(x.Value
|
return await provider.GetStreamDataAsync(x.Value
|
||||||
.Select(entry => entry.Key)
|
.Select(entry => entry.Key)
|
||||||
.ToList());
|
.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
// this means there's no provider for this stream data, (and there was before?)
|
// this means there's no provider for this stream data, (and there was before?)
|
||||||
return Task.FromResult<IReadOnlyCollection<StreamData>>(
|
return [];
|
||||||
new List<StreamData>());
|
|
||||||
})
|
})
|
||||||
.WhenAll();
|
.WhenAll();
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
using System.Net;
|
using EllieBot.Db.Models;
|
||||||
using EllieBot.Db.Models;
|
|
||||||
using EllieBot.Services;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Xml.Linq;
|
|
||||||
using AngleSharp.Browser;
|
|
||||||
|
|
||||||
namespace EllieBot.Modules.Searches.Common.StreamNotifications.Providers;
|
namespace EllieBot.Modules.Searches.Common.StreamNotifications.Providers;
|
||||||
|
|
||||||
|
@ -113,56 +108,65 @@ public sealed partial class YouTubeProvider : Provider
|
||||||
/// <returns><see cref="StreamData"/> of the channel. Null if none found</returns>
|
/// <returns><see cref="StreamData"/> of the channel. Null if none found</returns>
|
||||||
public override async Task<StreamData?> GetStreamDataAsync(string channelId)
|
public override async Task<StreamData?> GetStreamDataAsync(string channelId)
|
||||||
{
|
{
|
||||||
var instances = _scs.Data.InvidiousInstances;
|
try
|
||||||
|
|
||||||
if (instances is not { Count: > 0 })
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var invInstance = instances[_rng.Next(0, instances.Count)];
|
|
||||||
var client = _httpFactory.CreateClient();
|
|
||||||
client.BaseAddress = new Uri(invInstance);
|
|
||||||
|
|
||||||
var channel = await client.GetFromJsonAsync<InvidiousChannelResponse>($"/api/v1/channels/{channelId}");
|
|
||||||
if (channel is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var response =
|
|
||||||
await client.GetFromJsonAsync<InvChannelStreamsResponse>($"/api/v1/channels/{channelId}/streams");
|
|
||||||
if (response is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming && x.LengthSeconds == 0);
|
|
||||||
var isLive = false;
|
|
||||||
if (vid is null)
|
|
||||||
{
|
{
|
||||||
vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming);
|
var instances = _scs.Data.InvidiousInstances;
|
||||||
|
|
||||||
|
if (instances is not { Count: > 0 })
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var invInstance = instances[_rng.Next(0, instances.Count)];
|
||||||
|
var client = _httpFactory.CreateClient();
|
||||||
|
client.BaseAddress = new Uri(invInstance);
|
||||||
|
|
||||||
|
var channel = await client.GetFromJsonAsync<InvidiousChannelResponse>($"/api/v1/channels/{channelId}");
|
||||||
|
if (channel is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var response =
|
||||||
|
await client.GetFromJsonAsync<InvChannelStreamsResponse>($"/api/v1/channels/{channelId}/streams");
|
||||||
|
if (response is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming && x.LengthSeconds == 0);
|
||||||
|
var isLive = false;
|
||||||
|
if (vid is null)
|
||||||
|
{
|
||||||
|
vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isLive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vid is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var avatarUrl = channel?.AuthorThumbnails?.Select(x => x.Url).LastOrDefault();
|
||||||
|
|
||||||
|
return new StreamData()
|
||||||
|
{
|
||||||
|
Game = "Livestream",
|
||||||
|
Name = vid.Author,
|
||||||
|
Preview = vid.Thumbnails
|
||||||
|
.Skip(1)
|
||||||
|
.Select(x => "https://i.ytimg.com/" + x.Url)
|
||||||
|
.FirstOrDefault(),
|
||||||
|
Title = vid.Title,
|
||||||
|
Viewers = vid.ViewCount,
|
||||||
|
AvatarUrl = avatarUrl,
|
||||||
|
IsLive = isLive,
|
||||||
|
StreamType = FollowedStream.FType.Youtube,
|
||||||
|
StreamUrl = "https://youtube.com/watch?v=" + vid.VideoId,
|
||||||
|
UniqueName = vid.AuthorId,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
else
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
isLive = true;
|
Log.Warning(ex, "Unable to get stream data for a youtube channel {ChannelId}", channelId);
|
||||||
}
|
_failingStreams.TryAdd(channelId, DateTime.UtcNow);
|
||||||
|
|
||||||
if (vid is null)
|
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
var avatarUrl = channel?.AuthorThumbnails?.Select(x => x.Url).LastOrDefault();
|
|
||||||
|
|
||||||
return new StreamData()
|
|
||||||
{
|
|
||||||
Game = "Livestream",
|
|
||||||
Name = vid.Author,
|
|
||||||
Preview = vid.Thumbnails
|
|
||||||
.Skip(1)
|
|
||||||
.Select(x => "https://i.ytimg.com/" + x.Url)
|
|
||||||
.FirstOrDefault(),
|
|
||||||
Title = vid.Title,
|
|
||||||
Viewers = vid.ViewCount,
|
|
||||||
AvatarUrl = avatarUrl,
|
|
||||||
IsLive = isLive,
|
|
||||||
StreamType = FollowedStream.FType.Youtube,
|
|
||||||
StreamUrl = "https://youtube.com/watch?v=" + vid.VideoId,
|
|
||||||
UniqueName = vid.AuthorId,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class LiveChannelService(
|
||||||
IReplacementService repSvc,
|
IReplacementService repSvc,
|
||||||
ShardData shardData) : IReadyExecutor, IEService
|
ShardData shardData) : IReadyExecutor, IEService
|
||||||
{
|
{
|
||||||
public const int MAX_LIVECHANNELS = 5;
|
public const int MAX_LIVECHANNELS = 1;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, LiveChannelConfig>> _liveChannels = new();
|
private readonly ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, LiveChannelConfig>> _liveChannels = new();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
using LinqToDB;
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
using EllieBot.Common.ModuleBehaviors;
|
using EllieBot.Common.ModuleBehaviors;
|
||||||
using EllieBot.Db.Models;
|
using EllieBot.Db.Models;
|
||||||
|
@ -36,10 +37,10 @@ public class VerboseErrorsService : IReadyExecutor, IEService
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var embed = _hs.GetCommandHelp(cmd, channel.Guild)
|
var embed = _hs.GetCommandHelp(cmd, channel.Guild)
|
||||||
.WithTitle("Command Error")
|
.WithTitle("Command Error")
|
||||||
.WithDescription(reason)
|
.WithDescription(reason)
|
||||||
.WithFooter("Admin may disable verbose errors via `.ve` command")
|
.WithFooter("Admin may disable verbose errors via `.ve` command")
|
||||||
.WithErrorColor();
|
.WithErrorColor();
|
||||||
|
|
||||||
await _sender.Response(channel).Embed(embed).SendAsync();
|
await _sender.Response(channel).Embed(embed).SendAsync();
|
||||||
}
|
}
|
||||||
|
@ -50,16 +51,29 @@ public class VerboseErrorsService : IReadyExecutor, IEService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles or sets verbose errors for the specified guild.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="guildId">The ID of the guild to toggle verbose errors for.</param>
|
||||||
|
/// <param name="maybeEnabled">If specified, sets to this value; otherwise toggles current value.</param>
|
||||||
|
/// <returns>Returns the new state of verbose errors (true = enabled, false = disabled).</returns>
|
||||||
public async Task<bool> ToggleVerboseErrors(ulong guildId, bool? maybeEnabled = null)
|
public async Task<bool> ToggleVerboseErrors(ulong guildId, bool? maybeEnabled = null)
|
||||||
{
|
{
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
|
|
||||||
var isEnabled = ctx.GetTable<GuildConfig>()
|
var current = await ctx.GetTable<GuildConfig>()
|
||||||
.Where(x => x.GuildId == guildId)
|
.Where(x => x.GuildId == guildId)
|
||||||
.Select(x => x.VerboseErrors)
|
.Select(x => x.VerboseErrors)
|
||||||
.FirstOrDefault();
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
if (isEnabled) // This doesn't need to be duplicated inside the using block
|
var newState = maybeEnabled ?? !current;
|
||||||
|
|
||||||
|
await ctx.GetTable<GuildConfig>()
|
||||||
|
.Where(x => x.GuildId == guildId)
|
||||||
|
.Set(x => x.VerboseErrors, newState)
|
||||||
|
.UpdateAsync();
|
||||||
|
|
||||||
|
if (newState)
|
||||||
{
|
{
|
||||||
_guildsDisabled.TryRemove(guildId);
|
_guildsDisabled.TryRemove(guildId);
|
||||||
}
|
}
|
||||||
|
@ -68,15 +82,15 @@ public class VerboseErrorsService : IReadyExecutor, IEService
|
||||||
_guildsDisabled.Add(guildId);
|
_guildsDisabled.Add(guildId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isEnabled;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task OnReadyAsync()
|
public async Task OnReadyAsync()
|
||||||
{
|
{
|
||||||
await using var ctx = _db.GetDbContext();
|
await using var ctx = _db.GetDbContext();
|
||||||
var disabledOn = ctx.GetTable<GuildConfig>()
|
var disabledOn = ctx.GetTable<GuildConfig>()
|
||||||
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && !x.VerboseErrors)
|
.Where(x => Queries.GuildOnShard(x.GuildId, _shardData.TotalShards, _shardData.ShardId) && !x.VerboseErrors)
|
||||||
.Select(x => x.GuildId);
|
.Select(x => x.GuildId);
|
||||||
|
|
||||||
foreach (var guildId in disabledOn)
|
foreach (var guildId in disabledOn)
|
||||||
_guildsDisabled.Add(guildId);
|
_guildsDisabled.Add(guildId);
|
||||||
|
|
Loading…
Add table
Reference in a new issue