Port VoiceRoleSync from legacy
Since it was recently rewritten, the code was copied over nearly as-is from the legacy branch without any other major changes considered.
This commit is contained in:
parent
2dafdf07db
commit
08afc224ed
2 changed files with 95 additions and 0 deletions
42
RegexBot-Modules/VoiceRoleSync/ModuleConfig.cs
Normal file
42
RegexBot-Modules/VoiceRoleSync/ModuleConfig.cs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace RegexBot.Modules.VoiceRoleSync;
|
||||||
|
/// <summary>
|
||||||
|
/// Dictionary wrapper. Key = voice channel ID, Value = role.
|
||||||
|
/// </summary>
|
||||||
|
class ModuleConfig {
|
||||||
|
private readonly ReadOnlyDictionary<ulong, ulong> _values;
|
||||||
|
|
||||||
|
public ModuleConfig(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 ModuleLoadException($"{item.Name} is not a voice channel ID.");
|
||||||
|
var valstr = item.Value.Value<string>();
|
||||||
|
if (!ulong.TryParse(valstr, out var role)) throw new ModuleLoadException($"{valstr} is not a role ID.");
|
||||||
|
|
||||||
|
values[voice] = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
_values = new ReadOnlyDictionary<ulong, ulong>(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocketRole? GetAssociatedRoleFor(SocketVoiceChannel voiceChannel) {
|
||||||
|
if (voiceChannel == null) return null;
|
||||||
|
if (_values.TryGetValue(voiceChannel.Id, out var roleId)) return voiceChannel.Guild.GetRole(roleId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<SocketRole> GetTrackedRoles(SocketGuild guild) {
|
||||||
|
foreach (var pair in _values) {
|
||||||
|
var r = guild.GetRole(pair.Value);
|
||||||
|
if (r != null) yield return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
RegexBot-Modules/VoiceRoleSync/VoiceRoleSync.cs
Normal file
53
RegexBot-Modules/VoiceRoleSync/VoiceRoleSync.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
namespace RegexBot.Modules.VoiceRoleSync;
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[RegexbotModule]
|
||||||
|
public class VoiceRoleSync : RegexbotModule {
|
||||||
|
// TODO wishlist? specify multiple definitions - multiple channels associated with multiple roles.
|
||||||
|
|
||||||
|
public VoiceRoleSync(RegexbotClient bot) : base(bot) {
|
||||||
|
DiscordClient.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Client_UserVoiceStateUpdated(SocketUser argUser, SocketVoiceState before, SocketVoiceState after) {
|
||||||
|
// Gather data.
|
||||||
|
if (argUser is not SocketGuildUser user) return; // not a guild user
|
||||||
|
var settings = GetGuildState<ModuleConfig>(user.Guild.Id);
|
||||||
|
if (settings == null) return; // not enabled here
|
||||||
|
|
||||||
|
async Task RemoveAllAssociatedRoles()
|
||||||
|
=> await user.RemoveRolesAsync(settings.GetTrackedRoles(user.Guild).Intersect(user.Roles));
|
||||||
|
|
||||||
|
if (after.VoiceChannel == null) {
|
||||||
|
// Not in any voice channel. Remove all roles being tracked by this instance. Clear.
|
||||||
|
await RemoveAllAssociatedRoles();
|
||||||
|
} else {
|
||||||
|
// In a voice channel, and...
|
||||||
|
if (after.IsDeafened || after.IsSelfDeafened) {
|
||||||
|
// Is defeaned, which is like not being in a voice channel for our purposes. Clear.
|
||||||
|
await RemoveAllAssociatedRoles();
|
||||||
|
} else {
|
||||||
|
var targetRole = settings.GetAssociatedRoleFor(after.VoiceChannel);
|
||||||
|
if (targetRole == null) {
|
||||||
|
// In an untracked voice channel. Clear.
|
||||||
|
await RemoveAllAssociatedRoles();
|
||||||
|
} else {
|
||||||
|
// In a tracked voice channel: Clear all except target, add target if needed.
|
||||||
|
await user.RemoveRolesAsync(settings.GetTrackedRoles(user.Guild)
|
||||||
|
.Where(role => role.Id != targetRole.Id)
|
||||||
|
.Intersect(user.Roles));
|
||||||
|
if (!user.Roles.Contains(targetRole)) await user.AddRoleAsync(targetRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<object?> CreateGuildStateAsync(ulong guildID, JToken config) {
|
||||||
|
if (config == null) return Task.FromResult<object?>(null);
|
||||||
|
if (config.Type != JTokenType.Object)
|
||||||
|
throw new ModuleLoadException("Configuration for this section is invalid.");
|
||||||
|
return Task.FromResult<object?>(new ModuleConfig((JObject)config));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue