diff --git a/Module/VoiceRoleSync/VoiceRoleSync.cs b/Module/VoiceRoleSync/VoiceRoleSync.cs
new file mode 100644
index 0000000..73afec9
--- /dev/null
+++ b/Module/VoiceRoleSync/VoiceRoleSync.cs
@@ -0,0 +1,120 @@
+using Discord.WebSocket;
+using Newtonsoft.Json.Linq;
+using Noikoio.RegexBot.ConfigItem;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Noikoio.RegexBot.Module.VoiceRoleSync
+{
+ ///
+ /// 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.
+ ///
+ class VoiceRoleSync : BotModule
+ {
+ // Wishlist: specify multiple definitions - multiple channels associated with multiple roles.
+
+ public VoiceRoleSync(DiscordSocketClient client) : base(client)
+ {
+ client.UserVoiceStateUpdated += Client_UserVoiceStateUpdated;
+ }
+
+ private async Task Client_UserVoiceStateUpdated(SocketUser argUser, SocketVoiceState before, SocketVoiceState after)
+ {
+ // Gather data.
+ if (!(argUser is SocketGuildUser user)) return; // not a guild user
+ var settings = GetState(user.Guild.Id);
+ var deafened = after.IsDeafened || after.IsSelfDeafened;
+ var (settingBefore, settingAfter) = settings.GetChannelSettings(before.VoiceChannel, after.VoiceChannel);
+
+ // Determine action(s) to take
+ if (before.VoiceChannel?.Id != after.VoiceChannel?.Id)
+ {
+ // Joined / Left / Moved voice channels.
+ if (settingBefore?.Id != settingAfter?.Id)
+ {
+ // Replace roles only if the roles to be applied are different.
+ if (settingBefore != null && user.Roles.Contains(settingBefore)) await user.RemoveRoleAsync(settingBefore);
+ if (settingAfter != null && !user.Roles.Contains(settingAfter)) await user.AddRoleAsync(settingAfter);
+ }
+ }
+ else
+ {
+ // In same voice channel. Deafen state may have changed.
+ if (after.IsDeafened || after.IsSelfDeafened)
+ {
+ if (settingAfter != null && user.Roles.Contains(settingAfter)) await user.RemoveRoleAsync(settingAfter);
+ }
+ else
+ {
+ if (settingAfter != null && !user.Roles.Contains(settingAfter)) await user.AddRoleAsync(settingAfter);
+ }
+ }
+ }
+
+ public override Task