mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-12 02:08:27 -04:00
Restructured the project structure back to the way it was, there's no reasonable way to split the modules
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
#nullable disable
|
||||
using NadekoBot.Modules.Utility.Common;
|
||||
using NadekoBot.Modules.Utility.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Utility;
|
||||
|
||||
public partial class Utility
|
||||
{
|
||||
public partial class StreamRoleCommands : NadekoModule<StreamRoleService>
|
||||
{
|
||||
[Cmd]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task StreamRole(IRole fromRole, IRole addRole)
|
||||
{
|
||||
await _service.SetStreamRole(fromRole, addRole);
|
||||
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_enabled(Format.Bold(fromRole.ToString()),
|
||||
Format.Bold(addRole.ToString())));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task StreamRole()
|
||||
{
|
||||
await _service.StopStreamRole(ctx.Guild);
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_disabled);
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task StreamRoleKeyword([Leftover] string keyword = null)
|
||||
{
|
||||
var kw = await _service.SetKeyword(ctx.Guild, keyword);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(keyword))
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_kw_reset);
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_kw_set(Format.Bold(kw)));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task StreamRoleBlacklist(AddRemove action, [Leftover] IGuildUser user)
|
||||
{
|
||||
var success = await _service.ApplyListAction(StreamRoleListType.Blacklist,
|
||||
ctx.Guild,
|
||||
action,
|
||||
user.Id,
|
||||
user.ToString());
|
||||
|
||||
if (action == AddRemove.Add)
|
||||
{
|
||||
if (success)
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_bl_add(Format.Bold(user.ToString())));
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_bl_add_fail(Format.Bold(user.ToString())));
|
||||
}
|
||||
else if (success)
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_bl_rem(Format.Bold(user.ToString())));
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.stream_role_bl_rem_fail(Format.Bold(user.ToString())));
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
[BotPerm(GuildPerm.ManageRoles)]
|
||||
[UserPerm(GuildPerm.ManageRoles)]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task StreamRoleWhitelist(AddRemove action, [Leftover] IGuildUser user)
|
||||
{
|
||||
var success = await _service.ApplyListAction(StreamRoleListType.Whitelist,
|
||||
ctx.Guild,
|
||||
action,
|
||||
user.Id,
|
||||
user.ToString());
|
||||
|
||||
if (action == AddRemove.Add)
|
||||
{
|
||||
if (success)
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_wl_add(Format.Bold(user.ToString())));
|
||||
else
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_wl_add_fail(Format.Bold(user.ToString())));
|
||||
}
|
||||
else if (success)
|
||||
await ReplyConfirmLocalizedAsync(strs.stream_role_wl_rem(Format.Bold(user.ToString())));
|
||||
else
|
||||
await ReplyErrorLocalizedAsync(strs.stream_role_wl_rem_fail(Format.Bold(user.ToString())));
|
||||
}
|
||||
}
|
||||
}
|
339
src/NadekoBot/Modules/Utility/StreamRole/StreamRoleService.cs
Normal file
339
src/NadekoBot/Modules/Utility/StreamRole/StreamRoleService.cs
Normal file
@@ -0,0 +1,339 @@
|
||||
using NadekoBot.Common.ModuleBehaviors;
|
||||
using NadekoBot.Db;
|
||||
using NadekoBot.Modules.Utility.Common;
|
||||
using NadekoBot.Modules.Utility.Common.Exceptions;
|
||||
using Nadeko.Bot.Db.Models;
|
||||
using System.Net;
|
||||
|
||||
namespace NadekoBot.Modules.Utility.Services;
|
||||
|
||||
public class StreamRoleService : IReadyExecutor, INService
|
||||
{
|
||||
private readonly DbService _db;
|
||||
private readonly DiscordSocketClient _client;
|
||||
private readonly ConcurrentDictionary<ulong, StreamRoleSettings> _guildSettings;
|
||||
private readonly QueueRunner _queueRunner;
|
||||
|
||||
public StreamRoleService(DiscordSocketClient client, DbService db, IBot bot)
|
||||
{
|
||||
_db = db;
|
||||
_client = client;
|
||||
|
||||
_guildSettings = bot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.StreamRole)
|
||||
.Where(x => x.Value is { Enabled: true })
|
||||
.ToConcurrent();
|
||||
|
||||
_client.PresenceUpdated += OnPresenceUpdate;
|
||||
|
||||
_queueRunner = new QueueRunner();
|
||||
}
|
||||
|
||||
private Task OnPresenceUpdate(SocketUser user, SocketPresence? oldPresence, SocketPresence? newPresence)
|
||||
{
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (oldPresence?.Activities?.Count != newPresence?.Activities?.Count)
|
||||
{
|
||||
var guildUsers = _client.Guilds
|
||||
.Select(x => x.GetUser(user.Id))
|
||||
.Where(x => x is not null);
|
||||
|
||||
foreach (var guildUser in guildUsers)
|
||||
{
|
||||
if (_guildSettings.TryGetValue(guildUser.Guild.Id, out var s))
|
||||
await RescanUser(guildUser, s);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnReadyAsync()
|
||||
=> Task.WhenAll(_client.Guilds.Select(RescanUsers).WhenAll(), _queueRunner.RunAsync());
|
||||
|
||||
/// <summary>
|
||||
/// Adds or removes a user from a blacklist or a whitelist in the specified guild.
|
||||
/// </summary>
|
||||
/// <param name="listType">List type</param>
|
||||
/// <param name="guild">Guild</param>
|
||||
/// <param name="action">Add or rem action</param>
|
||||
/// <param name="userId">User's Id</param>
|
||||
/// <param name="userName">User's name#discrim</param>
|
||||
/// <returns>Whether the operation was successful</returns>
|
||||
public async Task<bool> ApplyListAction(
|
||||
StreamRoleListType listType,
|
||||
IGuild guild,
|
||||
AddRemove action,
|
||||
ulong userId,
|
||||
string userName)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(userName, nameof(userName));
|
||||
|
||||
var success = false;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var streamRoleSettings = uow.GetStreamRoleSettings(guild.Id);
|
||||
|
||||
if (listType == StreamRoleListType.Whitelist)
|
||||
{
|
||||
var userObj = new StreamRoleWhitelistedUser
|
||||
{
|
||||
UserId = userId,
|
||||
Username = userName
|
||||
};
|
||||
|
||||
if (action == AddRemove.Rem)
|
||||
{
|
||||
var toDelete = streamRoleSettings.Whitelist.FirstOrDefault(x => x.Equals(userObj));
|
||||
if (toDelete is not null)
|
||||
{
|
||||
uow.Remove(toDelete);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
success = streamRoleSettings.Whitelist.Add(userObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
var userObj = new StreamRoleBlacklistedUser
|
||||
{
|
||||
UserId = userId,
|
||||
Username = userName
|
||||
};
|
||||
|
||||
if (action == AddRemove.Rem)
|
||||
{
|
||||
var toRemove = streamRoleSettings.Blacklist.FirstOrDefault(x => x.Equals(userObj));
|
||||
if (toRemove is not null)
|
||||
success = streamRoleSettings.Blacklist.Remove(toRemove);
|
||||
}
|
||||
else
|
||||
success = streamRoleSettings.Blacklist.Add(userObj);
|
||||
}
|
||||
|
||||
await uow.SaveChangesAsync();
|
||||
UpdateCache(guild.Id, streamRoleSettings);
|
||||
}
|
||||
|
||||
if (success)
|
||||
await RescanUsers(guild);
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets keyword on a guild and updates the cache.
|
||||
/// </summary>
|
||||
/// <param name="guild">Guild Id</param>
|
||||
/// <param name="keyword">Keyword to set</param>
|
||||
/// <returns>The keyword set</returns>
|
||||
public async Task<string?> SetKeyword(IGuild guild, string? keyword)
|
||||
{
|
||||
keyword = keyword?.Trim().ToLowerInvariant();
|
||||
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var streamRoleSettings = uow.GetStreamRoleSettings(guild.Id);
|
||||
|
||||
streamRoleSettings.Keyword = keyword;
|
||||
UpdateCache(guild.Id, streamRoleSettings);
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await RescanUsers(guild);
|
||||
return keyword;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently set keyword on a guild.
|
||||
/// </summary>
|
||||
/// <param name="guildId">Guild Id</param>
|
||||
/// <returns>The keyword set</returns>
|
||||
public string GetKeyword(ulong guildId)
|
||||
{
|
||||
if (_guildSettings.TryGetValue(guildId, out var outSetting))
|
||||
return outSetting.Keyword;
|
||||
|
||||
StreamRoleSettings setting;
|
||||
using (var uow = _db.GetDbContext())
|
||||
{
|
||||
setting = uow.GetStreamRoleSettings(guildId);
|
||||
}
|
||||
|
||||
UpdateCache(guildId, setting);
|
||||
|
||||
return setting.Keyword;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the role to monitor, and a role to which to add to
|
||||
/// the user who starts streaming in the monitored role.
|
||||
/// </summary>
|
||||
/// <param name="fromRole">Role to monitor</param>
|
||||
/// <param name="addRole">Role to add to the user</param>
|
||||
public async Task SetStreamRole(IRole fromRole, IRole addRole)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(fromRole, nameof(fromRole));
|
||||
ArgumentNullException.ThrowIfNull(addRole, nameof(addRole));
|
||||
|
||||
StreamRoleSettings setting;
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var streamRoleSettings = uow.GetStreamRoleSettings(fromRole.Guild.Id);
|
||||
|
||||
streamRoleSettings.Enabled = true;
|
||||
streamRoleSettings.AddRoleId = addRole.Id;
|
||||
streamRoleSettings.FromRoleId = fromRole.Id;
|
||||
|
||||
setting = streamRoleSettings;
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
UpdateCache(fromRole.Guild.Id, setting);
|
||||
|
||||
foreach (var usr in await fromRole.GetMembersAsync())
|
||||
{
|
||||
await RescanUser(usr, setting, addRole);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the stream role feature on the specified guild.
|
||||
/// </summary>
|
||||
/// <param name="guild">Guild</param>
|
||||
/// <param name="cleanup">Whether to rescan users</param>
|
||||
public async Task StopStreamRole(IGuild guild, bool cleanup = false)
|
||||
{
|
||||
await using (var uow = _db.GetDbContext())
|
||||
{
|
||||
var streamRoleSettings = uow.GetStreamRoleSettings(guild.Id);
|
||||
streamRoleSettings.Enabled = false;
|
||||
streamRoleSettings.AddRoleId = 0;
|
||||
streamRoleSettings.FromRoleId = 0;
|
||||
await uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
if (_guildSettings.TryRemove(guild.Id, out _) && cleanup)
|
||||
await RescanUsers(guild);
|
||||
}
|
||||
|
||||
private async ValueTask RescanUser(IGuildUser user, StreamRoleSettings setting, IRole? addRole = null)
|
||||
=> await _queueRunner.EnqueueAsync(() => RescanUserInternal(user, setting, addRole));
|
||||
|
||||
private async Task RescanUserInternal(IGuildUser user, StreamRoleSettings setting, IRole? addRole = null)
|
||||
{
|
||||
if (user.IsBot)
|
||||
return;
|
||||
|
||||
var g = (StreamingGame?)user.Activities.FirstOrDefault(a
|
||||
=> a is StreamingGame
|
||||
&& (string.IsNullOrWhiteSpace(setting.Keyword)
|
||||
|| a.Name.ToUpperInvariant().Contains(setting.Keyword.ToUpperInvariant())
|
||||
|| setting.Whitelist.Any(x => x.UserId == user.Id)));
|
||||
|
||||
if (g is not null
|
||||
&& setting.Enabled
|
||||
&& setting.Blacklist.All(x => x.UserId != user.Id)
|
||||
&& user.RoleIds.Contains(setting.FromRoleId))
|
||||
{
|
||||
await _queueRunner.EnqueueAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
addRole ??= user.Guild.GetRole(setting.AddRoleId);
|
||||
if (addRole is null)
|
||||
{
|
||||
await StopStreamRole(user.Guild);
|
||||
Log.Warning("Stream role in server {RoleId} no longer exists. Stopping", setting.AddRoleId);
|
||||
return;
|
||||
}
|
||||
|
||||
//check if he doesn't have addrole already, to avoid errors
|
||||
if (!user.RoleIds.Contains(addRole.Id))
|
||||
{
|
||||
await user.AddRoleAsync(addRole);
|
||||
Log.Information("Added stream role to user {User} in {Server} server",
|
||||
user.ToString(),
|
||||
user.Guild.ToString());
|
||||
}
|
||||
}
|
||||
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
await StopStreamRole(user.Guild);
|
||||
Log.Warning(ex, "Error adding stream role(s). Forcibly disabling stream role feature");
|
||||
throw new StreamRolePermissionException();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Failed adding stream role");
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
//check if user is in the addrole
|
||||
if (user.RoleIds.Contains(setting.AddRoleId))
|
||||
{
|
||||
await _queueRunner.EnqueueAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
addRole ??= user.Guild.GetRole(setting.AddRoleId);
|
||||
if (addRole is null)
|
||||
{
|
||||
await StopStreamRole(user.Guild);
|
||||
Log.Warning(
|
||||
"Addrole doesn't exist in {GuildId} server. Forcibly disabling stream role feature",
|
||||
user.Guild.Id);
|
||||
return;
|
||||
}
|
||||
|
||||
// need to check again in case queuer is taking too long to execute
|
||||
if (user.RoleIds.Contains(setting.AddRoleId))
|
||||
{
|
||||
await user.RemoveRoleAsync(addRole);
|
||||
}
|
||||
|
||||
Log.Information("Removed stream role from the user {User} in {Server} server",
|
||||
user.ToString(),
|
||||
user.Guild.ToString());
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
if (ex.HttpCode == HttpStatusCode.Forbidden)
|
||||
{
|
||||
await StopStreamRole(user.Guild);
|
||||
Log.Warning(ex, "Error removing stream role(s). Forcibly disabling stream role feature");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RescanUsers(IGuild guild)
|
||||
{
|
||||
if (!_guildSettings.TryGetValue(guild.Id, out var setting))
|
||||
return;
|
||||
|
||||
var addRole = guild.GetRole(setting.AddRoleId);
|
||||
if (addRole is null)
|
||||
return;
|
||||
|
||||
if (setting.Enabled)
|
||||
{
|
||||
var users = await guild.GetUsersAsync(CacheMode.CacheOnly);
|
||||
foreach (var usr in users.Where(x
|
||||
=> x.RoleIds.Contains(setting.FromRoleId) || x.RoleIds.Contains(addRole.Id)))
|
||||
{
|
||||
if (usr is { } x)
|
||||
await RescanUser(x, setting, addRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCache(ulong guildId, StreamRoleSettings setting)
|
||||
=> _guildSettings.AddOrUpdate(guildId, _ => setting, (_, _) => setting);
|
||||
}
|
Reference in New Issue
Block a user