Rewrite VoiceRoleSync
Take a more comprehensive approach in finding roles to be removed.
This commit is contained in:
parent
75a807a330
commit
7d4127ca25
3 changed files with 30 additions and 85 deletions
|
@ -1,13 +1,10 @@
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Noikoio.RegexBot.ConfigItem;
|
using Noikoio.RegexBot.ConfigItem;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Noikoio.RegexBot.Module.VoiceRoleSync
|
namespace Noikoio.RegexBot.Module.VoiceRoleSync {
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Synchronizes a user's state in a voice channel with a role.
|
/// Synchronizes a user's state in a voice channel with a role.
|
||||||
/// In other words: applies a role to a user entering a voice channel. Removes the role when exiting.
|
/// In other words: applies a role to a user entering a voice channel. Removes the role when exiting.
|
||||||
|
@ -27,31 +24,30 @@ namespace Noikoio.RegexBot.Module.VoiceRoleSync
|
||||||
if (!(argUser is SocketGuildUser user)) return; // not a guild user
|
if (!(argUser is SocketGuildUser user)) return; // not a guild user
|
||||||
var settings = GetState<GuildSettings>(user.Guild.Id);
|
var settings = GetState<GuildSettings>(user.Guild.Id);
|
||||||
if (settings == null) return; // not enabled here
|
if (settings == null) return; // not enabled here
|
||||||
var deafened = after.IsDeafened || after.IsSelfDeafened;
|
|
||||||
var (settingBefore, settingAfter) = settings.GetChannelSettings(before.VoiceChannel, after.VoiceChannel);
|
|
||||||
|
|
||||||
// Determine action(s) to take
|
async Task RemoveAllAssociatedRoles()
|
||||||
if (before.VoiceChannel?.Id != after.VoiceChannel?.Id)
|
=> await user.RemoveRolesAsync(settings.GetTrackedRoles(user.Guild).Intersect(user.Roles));
|
||||||
{
|
|
||||||
// Joined / Left / Moved voice channels.
|
if (after.VoiceChannel == null) {
|
||||||
if (settingBefore?.Id != settingAfter?.Id)
|
// Not in any voice channel. Remove all roles being tracked by this instance. Clear.
|
||||||
{
|
await RemoveAllAssociatedRoles();
|
||||||
// Replace roles only if the roles to be applied are different.
|
} else {
|
||||||
if (settingBefore != null && user.Roles.Contains(settingBefore)) await user.RemoveRoleAsync(settingBefore);
|
// In a voice channel, and...
|
||||||
if (settingAfter != null && !user.Roles.Contains(settingAfter) &&
|
if (after.IsDeafened || after.IsSelfDeafened) {
|
||||||
(!(after.IsDeafened || after.IsSelfDeafened))) await user.AddRoleAsync(settingAfter);
|
// Is defeaned, which is like not being in a voice channel for our purposes. Clear.
|
||||||
}
|
await RemoveAllAssociatedRoles();
|
||||||
}
|
} else {
|
||||||
else
|
var targetRole = settings.GetAssociatedRoleFor(after.VoiceChannel);
|
||||||
{
|
if (targetRole == null) {
|
||||||
// In same voice channel. Deafen state may have changed.
|
// In an untracked voice channel. Clear.
|
||||||
if (after.IsDeafened || after.IsSelfDeafened)
|
await RemoveAllAssociatedRoles();
|
||||||
{
|
} else {
|
||||||
if (settingAfter != null && user.Roles.Contains(settingAfter)) await user.RemoveRoleAsync(settingAfter);
|
// In a tracked voice channel: Clear all except target, add target if needed.
|
||||||
}
|
await user.RemoveRolesAsync(settings.GetTrackedRoles(user.Guild)
|
||||||
else
|
.Where(role => role.Id != targetRole.Id)
|
||||||
{
|
.Intersect(user.Roles));
|
||||||
if (settingAfter != null && !user.Roles.Contains(settingAfter)) await user.AddRoleAsync(settingAfter);
|
if (!user.Roles.Contains(targetRole)) await user.AddRoleAsync(targetRole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,58 +61,5 @@ namespace Noikoio.RegexBot.Module.VoiceRoleSync
|
||||||
}
|
}
|
||||||
return Task.FromResult<object>(new GuildSettings((JObject)configSection));
|
return Task.FromResult<object>(new GuildSettings((JObject)configSection));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dictionary wrapper. Key = voice channel ID, Value = role.
|
|
||||||
/// </summary>
|
|
||||||
private class GuildSettings
|
|
||||||
{
|
|
||||||
private ReadOnlyDictionary<ulong, ulong> _values { get; }
|
|
||||||
|
|
||||||
public GuildSettings(JObject config)
|
|
||||||
{
|
|
||||||
// Configuration format is expected to be an object that contains other objects.
|
|
||||||
// The objects themselves should have their name be the voice channel,
|
|
||||||
// and the value be the role to be applied.
|
|
||||||
|
|
||||||
// TODO Make it accept names; currently only accepts ulongs
|
|
||||||
|
|
||||||
var values = new Dictionary<ulong, ulong>();
|
|
||||||
|
|
||||||
foreach (var item in config.Properties())
|
|
||||||
{
|
|
||||||
if (!ulong.TryParse(item.Name, out var voice))
|
|
||||||
{
|
|
||||||
throw new RuleImportException($"{item.Name} is not a voice channel ID.");
|
|
||||||
}
|
|
||||||
var valstr = item.Value.Value<string>();
|
|
||||||
if (!ulong.TryParse(valstr, out var role))
|
|
||||||
{
|
|
||||||
throw new RuleImportException($"{valstr} is not a role ID.");
|
|
||||||
}
|
|
||||||
|
|
||||||
values[voice] = role;
|
|
||||||
}
|
|
||||||
|
|
||||||
_values = new ReadOnlyDictionary<ulong, ulong>(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets designated roles for the given two channels (before, after).
|
|
||||||
/// Returns null in either for no result/specified role.
|
|
||||||
/// </summary>
|
|
||||||
public (SocketRole, SocketRole) GetChannelSettings(SocketVoiceChannel before, SocketVoiceChannel after)
|
|
||||||
=> (GetIndividualResult(before), GetIndividualResult(after));
|
|
||||||
|
|
||||||
private SocketRole GetIndividualResult(SocketVoiceChannel ch)
|
|
||||||
{
|
|
||||||
if (ch == null) return null;
|
|
||||||
if (_values.TryGetValue(ch.Id, out var roleId))
|
|
||||||
{
|
|
||||||
return ch.Guild.GetRole(roleId);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,9 @@ namespace Noikoio.RegexBot
|
||||||
LogLevel = LogSeverity.Info,
|
LogLevel = LogSeverity.Info,
|
||||||
AlwaysDownloadUsers = true,
|
AlwaysDownloadUsers = true,
|
||||||
DefaultRetryMode = RetryMode.AlwaysRetry,
|
DefaultRetryMode = RetryMode.AlwaysRetry,
|
||||||
MessageCacheSize = 0
|
MessageCacheSize = 0,
|
||||||
|
GatewayIntents = GatewayIntents.All & ~GatewayIntents.GuildPresences,
|
||||||
|
LogGatewayIntentWarnings = false
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hook up basic handlers and other references
|
// Hook up basic handlers and other references
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<Description>Highly configurable Discord moderation bot</Description>
|
<Description>Highly configurable Discord moderation bot</Description>
|
||||||
<Authors>Noikoio</Authors>
|
<Authors>Noikoio</Authors>
|
||||||
<Company />
|
<Company />
|
||||||
<Version>2.6.8</Version>
|
<Version>2.6.9</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
@ -15,9 +15,9 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net" Version="3.4.1" />
|
<PackageReference Include="Discord.Net" Version="3.6.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="Npgsql" Version="5.0.12" />
|
<PackageReference Include="Npgsql" Version="5.0.13" />
|
||||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue