@@ -17,6 +17,8 @@ class Application : ProfileInfoProvider, MmrListener {
17
17
private BeefUserConfigManager _userManager ;
18
18
private PresentationManager _presentationManager ;
19
19
private String [ ] _leaderRoles ;
20
+ private String [ ] _dynamicChannels ; // Names of channels that should be cloned to maintain 1 empty at all times
21
+ private bool _dynamicVoiceChannelsEnabled = false ;
20
22
private String _exePath ;
21
23
private MmrReader . MmrReader _mmrReader ;
22
24
private DispatcherSynchronizationContext _mainContext ;
@@ -36,6 +38,7 @@ public Application(BeefConfig config, String exePath) {
36
38
_botPrefix = config . BotPrefix ;
37
39
_beefCommand = config . BeefCommand ;
38
40
_leaderRoles = config . LeaderRoles ;
41
+ _dynamicChannels = config . DynamicChannels ;
39
42
40
43
_presentationManager = new PresentationManager (
41
44
_config . GoogleApiPresentationId ,
@@ -87,6 +90,83 @@ private Boolean IsLeader(SocketUser socketUser) {
87
90
return false ;
88
91
}
89
92
93
+ private void EnableDynamicChannels ( ) {
94
+ if ( ! _dynamicVoiceChannelsEnabled ) {
95
+ _discordClient . UserVoiceStateUpdated += OnUserVoiceStateUpdated ;
96
+ _dynamicVoiceChannelsEnabled = true ;
97
+ }
98
+ }
99
+
100
+ private void DisableDynamicChannels ( ) {
101
+ if ( _dynamicVoiceChannelsEnabled ) {
102
+ _discordClient . UserVoiceStateUpdated -= OnUserVoiceStateUpdated ;
103
+ _dynamicVoiceChannelsEnabled = false ;
104
+ }
105
+ }
106
+
107
+ private async Task OnUserVoiceStateUpdated ( SocketUser user , SocketVoiceState state1 , SocketVoiceState state2 ) {
108
+ await CheckDynamicChannels ( ) ;
109
+ }
110
+
111
+ private async Task CheckDynamicChannels ( ) {
112
+ if ( _dynamicChannels == null )
113
+ return ; // Dynamic channels are not configured.
114
+
115
+ List < Task > tasks = new List < Task > ( ) ;
116
+ foreach ( SocketGuild guild in _discordClient . Guilds ) {
117
+ String [ ] channelNamesToCopy = _dynamicChannels ;
118
+ List < SocketVoiceChannel > [ ] emptyChannels = new List < SocketVoiceChannel > [ channelNamesToCopy . Length ] ;
119
+ List < SocketVoiceChannel > [ ] occupiedChannels = new List < SocketVoiceChannel > [ channelNamesToCopy . Length ] ;
120
+
121
+ for ( int channelIndex = 0 ; channelIndex < channelNamesToCopy . Length ; channelIndex ++ ) {
122
+ occupiedChannels [ channelIndex ] = new List < SocketVoiceChannel > ( ) ;
123
+ emptyChannels [ channelIndex ] = new List < SocketVoiceChannel > ( ) ;
124
+ }
125
+
126
+ foreach ( SocketVoiceChannel channel in guild . VoiceChannels ) {
127
+ int channelIndex = 0 ;
128
+ foreach ( String channelNameToCopy in channelNamesToCopy ) {
129
+ if ( channelNameToCopy . Equals ( channel . Name ) ) {
130
+ if ( channel . Users . Count == 0 ) {
131
+ emptyChannels [ channelIndex ] . Add ( channel ) ;
132
+ } else {
133
+ occupiedChannels [ channelIndex ] . Add ( channel ) ;
134
+ }
135
+ }
136
+
137
+ channelIndex ++ ;
138
+ }
139
+ }
140
+
141
+ for ( int channelIndex = 0 ; channelIndex < emptyChannels . Length ; channelIndex ++ ) {
142
+ List < SocketVoiceChannel > emptyChannelList = emptyChannels [ channelIndex ] ;
143
+
144
+ if ( emptyChannelList . Count > 1 ) {
145
+ // Remove channels until there's 1 empty left
146
+ for ( int emptyIndex = 0 ; emptyIndex < emptyChannelList . Count - 1 ; emptyIndex ++ ) {
147
+ SocketVoiceChannel channel = emptyChannelList [ emptyIndex ] ;
148
+ tasks . Add ( channel . DeleteAsync ( ) ) ;
149
+ }
150
+ } else if ( emptyChannelList . Count == 0 ) {
151
+ List < SocketVoiceChannel > occupiedChannelList = occupiedChannels [ channelIndex ] ;
152
+ // Add a channel with the same name
153
+ if ( occupiedChannelList . Count > 0 ) {
154
+ SocketVoiceChannel firstOccupiedChannel = occupiedChannelList [ 0 ] ;
155
+ tasks . Add ( guild . CreateVoiceChannelAsync ( firstOccupiedChannel . Name , channelProperties => {
156
+ channelProperties . UserLimit = firstOccupiedChannel . UserLimit ;
157
+ channelProperties . CategoryId = firstOccupiedChannel . CategoryId ;
158
+ channelProperties . Position = firstOccupiedChannel . Position ;
159
+ } ) ) ;
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ // Await the various tasks we started.
166
+ foreach ( Task task in tasks )
167
+ await task ;
168
+ }
169
+
90
170
private void HandleCommand ( SocketMessage userInput ) {
91
171
// This is for Cloud...
92
172
if ( userInput . Content . StartsWith ( "I love you Beef bot!" , StringComparison . CurrentCultureIgnoreCase ) ) {
@@ -121,6 +201,15 @@ private void HandleCommand(SocketMessage userInput) {
121
201
String beefLink = sc2Beef + " **Settle the Beef** " + sc2Beef + "\n " ;
122
202
beefLink += _config . BeefLadderLink ;
123
203
MessageChannel ( channel , beefLink ) . GetAwaiter ( ) . GetResult ( ) ;
204
+ } else if ( arguments [ 1 ] == "enableDynamicChannels" ) {
205
+ MessageChannel ( channel , "Enabling dynamic channels." ) . GetAwaiter ( ) . GetResult ( ) ;
206
+ EnableDynamicChannels ( ) ;
207
+ CheckDynamicChannels ( ) ;
208
+ code = ErrorCode . Success ;
209
+ } else if ( arguments [ 1 ] == "disableDynamicChannels" ) {
210
+ MessageChannel ( channel , "Disabling dynamic channels." ) . GetAwaiter ( ) . GetResult ( ) ;
211
+ DisableDynamicChannels ( ) ;
212
+ code = ErrorCode . Success ;
124
213
} else if ( arguments [ 1 ] == "register" ) {
125
214
if ( ! IsLeader ( author ) ) {
126
215
MessageChannel ( channel , "You don't have permission to do that." ) . GetAwaiter ( ) . GetResult ( ) ;
@@ -504,6 +593,8 @@ private void HandleCommand(SocketMessage userInput) {
504
593
help += "\t **%beef% switch <PlayerOrRank> <OtherPlayerOrRank>** - Switches the two players on the ladder leaving everyone else in place.\n " ;
505
594
help += "\t **%beef% refresh** - Requests a refresh to the MMRs for each player. Note that this can take a minute.\n " ;
506
595
help += "\t **%beef% undo** - Undoes the last change to the ladder (renames, wins, etc..).\n " ;
596
+ help += "\t **%beef% enableDynamicChannels** - Enables dynamic channels (default). If there are dynamic channels set, the bot will ensure there is always exactly 1 empty of each configured dynamic channel.\n " ;
597
+ help += "\t **%beef% disableDynamicChannels** - Disables dynamic channels. If there are dynamic channels set, the bot will no longer ensure there is always exactly 1 empty of each configured dynamic channel.\n " ;
507
598
help += "\t **%beef% version** - Prints the version of BeefBot\n " ;
508
599
help = help . Replace ( "%beef%" , _botPrefix + _beefCommand ) ;
509
600
@@ -531,10 +622,14 @@ private Task LogAsync(LogMessage log) {
531
622
/// Called when the bot is ready.
532
623
/// </summary>
533
624
/// <returns>An async task.</returns>
534
- private Task ReadyAsync ( ) {
625
+ private async Task ReadyAsync ( ) {
535
626
Console . WriteLine ( $ "{ _discordClient . CurrentUser } is connected!") ;
536
627
537
- return Task . CompletedTask ;
628
+ // Enable dynamic voice channels if requested.
629
+ if ( _dynamicChannels != null ) {
630
+ EnableDynamicChannels ( ) ;
631
+ await CheckDynamicChannels ( ) ;
632
+ }
538
633
}
539
634
540
635
/// <summary>
0 commit comments