Skip to content

Commit ccbab63

Browse files
committed
ENHANCEMENTS:
- servers.yaml: new setting "managed_by" to set specific roles that can manage a server exclusively. - servers.yaml: dedicated audit channel added for servers (optional)
1 parent 34f2b71 commit ccbab63

File tree

19 files changed

+213
-102
lines changed

19 files changed

+213
-102
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,12 +456,15 @@ My Fancy Server: # Your server name, as displayed in the server l
456456
force_voice: false # Optional: enforce the usage of a voice channel (users need to be linked!) - default: false
457457
discord: # Optional: specify discord roles that are allowed to use this server
458458
- '@everyone' # Attention: people cannot self-link on these servers and have to be liked properly already!
459+
managed_by:
460+
- Special Admin # Optional: a list of Discord roles that can manage this server (default: DCS Admin)
459461
channels:
460462
status: 1122334455667788 # The Discord channel to display the server status embed and players embed into. Right-click on your channel and select "Copy Channel ID". You can disable it with -1
461463
chat: 8877665544332211 # The Discord channel for the in-game chat replication. You can disable it by setting it to -1.
462464
events: 1928374619283746 # Optional: if you want to split game events from chat messages, you can enable an optional events channel.
463-
admin: 1188227733664455 # The channel where you can fire admin commands to this server. You can decide if you want to have a central admin channel or server-specific ones. See bot.yaml for more.
464-
voice: 1827364518273645 # The voice channel, where people need to connect to (if force_voice is true).
465+
admin: 1188227733664455 # Optional: The channel where you can fire admin commands to this server. You can decide if you want to have a central admin channel or server-specific ones. See bot.yaml for more.
466+
voice: 1827364518273645 # Optional: The voice channel, where people need to connect to (mandatory if force_voice is true).
467+
audit: 9182736459182736 # Optional: a server-specific audit channel (for those of you who like channels, all others can use the global one)
465468
chat_log:
466469
count: 10 # A log file that holds the in-game chat to check for abuse. Tells how many files will be kept, default is 10.
467470
size: 1048576 # Max logfile size, default is 1 MB.

core/data/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class Channel(Enum):
3939
CHAT = 'chat'
4040
EVENTS = 'events'
4141
VOICE = 'voice'
42+
AUDIT = 'audit'
4243
COALITION_BLUE_CHAT = 'blue'
4344
COALITION_BLUE_EVENTS = 'blue_events'
4445
COALITION_RED_CHAT = 'red'

core/data/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@ def channels(self) -> dict[Channel, int]:
396396
self._channels[Channel.EVENTS] = self._channels[Channel.CHAT]
397397
if Channel.VOICE not in self._channels:
398398
self._channels[Channel.VOICE] = -1
399+
if Channel.AUDIT not in self._channels:
400+
self._channels[Channel.AUDIT] = -1
399401
if Channel.COALITION_BLUE_EVENTS not in self._channels and Channel.COALITION_BLUE_CHAT in self._channels:
400402
self._channels[Channel.COALITION_BLUE_EVENTS] = self._channels[Channel.COALITION_BLUE_CHAT]
401403
if Channel.COALITION_RED_EVENTS not in self._channels and Channel.COALITION_RED_CHAT in self._channels:

core/utils/discord.py

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,9 @@ async def transform(self, interaction: discord.Interaction, value: str | None) -
966966
"""
967967
if value:
968968
server = interaction.client.servers.get(value)
969-
if not server:
969+
if not server or (
970+
server.locals.get('managed_by') and not utils.check_roles(server.locals.get('managed_by'), interaction.user)
971+
):
970972
raise app_commands.TransformerError(value, self.type, self)
971973
else:
972974
server = interaction.client.get_server(interaction)
@@ -999,7 +1001,9 @@ async def autocomplete(self, interaction: discord.Interaction, current: str) ->
9991001
if (value.status != Status.UNREGISTERED and
10001002
(not self.status or value.status in self.status) and
10011003
(not self.maintenance or value.maintenance == self.maintenance) and
1002-
(not current or current.casefold() in name.casefold()))
1004+
(not value.locals.get('managed_by') or utils.check_roles(value.locals.get('managed_by'), interaction.user)) and
1005+
(not current or current.casefold() in name.casefold())
1006+
)
10031007
]
10041008
return choices[:25]
10051009
except Exception as ex:
@@ -1258,40 +1262,40 @@ def _server_filter(server: Server) -> bool:
12581262
return True
12591263

12601264

1261-
async def server_selection(bus: ServiceBus,
1262-
interaction: discord.Interaction | commands.Context, *, title: str,
1263-
multi_select: bool | None = False,
1264-
ephemeral: bool | None = True,
1265-
filter_func: Callable[[Server], bool] = _server_filter
1266-
) -> Server | list[Server] | None:
1267-
"""
1268-
1269-
"""
1270-
all_servers = list(bus.servers.values())
1271-
if len(all_servers) == 0:
1265+
async def server_selection(
1266+
bot: DCSServerBot,
1267+
interaction: discord.Interaction | commands.Context,
1268+
*,
1269+
title: str,
1270+
multi_select: bool | None = False,
1271+
ephemeral: bool | None = True,
1272+
filter_func: Callable[[Server], bool] = _server_filter
1273+
) -> Server | list[Server] | None:
1274+
all_servers = bot.get_servers(manager=interaction.user if isinstance(interaction, discord.Interaction) else interaction.author)
1275+
if not all_servers:
12721276
return []
12731277
elif len(all_servers) == 1:
1274-
return [all_servers[0]]
1278+
return [next(iter(all_servers.values()))]
12751279
if multi_select:
12761280
max_values = len(all_servers)
12771281
else:
12781282
max_values = 1
12791283
server: Server | None = None
12801284
if isinstance(interaction, discord.Interaction):
1281-
server = interaction.client.get_server(interaction)
1285+
server = bot.get_server(interaction)
12821286
s = await selection(interaction, title=title,
12831287
options=[
1284-
SelectOption(label=x.name, value=str(idx), default=(
1288+
SelectOption(label=x.name, value=x.name, default=(
12851289
True if server and server == x else
12861290
True if not server and idx == 0 else
12871291
False
1288-
)) for idx, x in enumerate(all_servers) if filter_func(x)
1292+
)) for idx, x in enumerate(all_servers.values()) if filter_func(x)
12891293
],
12901294
max_values=max_values, ephemeral=ephemeral)
12911295
if isinstance(s, list):
1292-
return [all_servers[int(x)] for x in s]
1296+
return [all_servers[x] for x in s]
12931297
elif s:
1294-
return all_servers[int(s)]
1298+
return all_servers[s]
12951299
return None
12961300

12971301

@@ -1667,9 +1671,8 @@ async def get_server(message: discord.Message, channel_id: int | None = None) ->
16671671
channel_id = bot.locals.get('channels', {}).get('admin')
16681672

16691673
if not server and message.channel.id == channel_id:
1670-
bus = ServiceRegistry.get(ServiceBus)
16711674
ctx = await bot.get_context(message)
1672-
server = await utils.server_selection(bus, ctx, title=_("To which server do you want to upload?"))
1675+
server = await utils.server_selection(bot, ctx, title=_("To which server do you want to upload?"))
16731676
if not server:
16741677
await ctx.send(_('Upload aborted.'))
16751678
return None

plugins/admin/commands.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -765,17 +765,18 @@ async def _list(self, interaction: discord.Interaction):
765765
# noinspection PyUnresolvedReferences
766766
await interaction.response.defer(ephemeral=ephemeral)
767767
embed = discord.Embed(title=_("DCSServerBot Cluster Overview"), color=discord.Color.blue())
768-
for name in self.node.all_nodes.keys():
768+
for name, node in self.node.all_nodes.items():
769769
names = []
770770
instances = []
771771
status = []
772-
for server in [server for server in self.bus.servers.values() if server.node.name == name]:
772+
for server in [x for x in self.bot.servers.values() if x.node.name == node.name]:
773773
instances.append(server.instance.name)
774774
names.append(server.name)
775775
status.append(server.status.name)
776776
if names:
777+
# print the master in bold
777778
title = f"**[{name}]**" if name == self.node.name else f"[{name}]"
778-
if await server.node.upgrade_pending():
779+
if await node.upgrade_pending():
779780
embed.set_footer(text=_("🆕 Update available"))
780781
title += " 🆕"
781782

@@ -856,7 +857,7 @@ async def _node_offline(node_name: str):
856857
tasks = []
857858
if shutdown:
858859
msg = await interaction.followup.send(_("Shutting down all servers on node {} ...").format(node_name))
859-
for server in self.bus.servers.values():
860+
for server in self.bot.servers.values():
860861
if server.node.name == node_name:
861862
server.maintenance = True
862863
if shutdown:
@@ -909,7 +910,7 @@ async def _startup(server: Server):
909910

910911
async def _node_online(node_name: str):
911912
next_startup = 0
912-
for server in [x for x in self.bus.servers.values() if x.node.name == node_name]:
913+
for server in [x for x in self.bot.servers.values() if x.node.name == node_name]:
913914
if startup:
914915
self.loop.call_later(delay=next_startup,
915916
callback=partial(asyncio.create_task, _startup(server)))
@@ -1189,7 +1190,7 @@ async def reload(self, interaction: discord.Interaction, plugin: str | None):
11891190
else:
11901191
await interaction.followup.send(
11911192
_('One or more plugins could not be reloaded, check the log for details.'), ephemeral=ephemeral)
1192-
# for server in self.bus.servers.values():
1193+
# for server in self.bot.servers.values():
11931194
# if server.status == Status.STOPPED:
11941195
# await server.send_to_dcs({"command": "reloadScripts"})
11951196

@@ -1314,7 +1315,7 @@ async def on_message(self, message: discord.Message):
13141315
return
13151316
try:
13161317
server = await utils.server_selection(
1317-
self.bus, ctx, title=_("To which server do you want to upload this configuration to?"))
1318+
self.bot, ctx, title=_("To which server do you want to upload this configuration to?"))
13181319
if not server:
13191320
await ctx.send(_('Aborted.'))
13201321
return

plugins/commands/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ async def do_send(server: Server):
230230
if rc:
231231
ret.append(rc)
232232
else:
233-
for server in self.bot.servers.values():
233+
for server in self.bot.get_servers(manager=interaction.user).values():
234234
rc = await do_send(server)
235235
if rc:
236236
ret.append(rc)

plugins/gamemaster/commands.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ async def broadcast(self, interaction: discord.Interaction, to: Literal['all', '
156156
ephemeral = utils.get_ephemeral(interaction)
157157
# noinspection PyUnresolvedReferences
158158
await interaction.response.defer(ephemeral=ephemeral)
159-
for server in self.bot.servers.values():
159+
for server in self.bot.get_servers(manager=interaction.user).values():
160160
if server.status != Status.RUNNING:
161161
await interaction.followup.send(_('Message NOT sent to server {server} because it is {status}.'
162162
).format(server=server.display_name, status=server.status.name),
@@ -275,7 +275,7 @@ async def reset_coalitions(self, interaction: discord.Interaction):
275275
await interaction.followup.send('Aborted.', ephemeral=ephemeral)
276276
return
277277
try:
278-
for server in self.bot.servers.values():
278+
for server in self.bot.get_servers(manager=interaction.user).values():
279279
if not server.locals.get('coalitions'):
280280
continue
281281
await self.eventlistener.reset_coalitions(server, True)
@@ -348,7 +348,7 @@ async def add(self, interaction: discord.Interaction):
348348
if await modal.wait():
349349
return
350350
try:
351-
servers = await utils.server_selection(self.bus, interaction,
351+
servers = await utils.server_selection(self.bot, interaction,
352352
title=_("Select all servers for this campaign"),
353353
multi_select=True, ephemeral=ephemeral)
354354
if not servers:
@@ -442,7 +442,7 @@ async def start(self, interaction: discord.Interaction, campaign: str):
442442
try:
443443
# noinspection PyUnresolvedReferences
444444
await interaction.response.defer(ephemeral=True)
445-
servers = await utils.server_selection(self.bus, interaction,
445+
servers = await utils.server_selection(self.bot, interaction,
446446
title=_("Select all servers for this campaign"),
447447
multi_select=True, ephemeral=ephemeral)
448448
if not isinstance(servers, list):

plugins/help/commands.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -219,18 +219,20 @@ async def help(self, interaction: discord.Interaction, cmd: str | None):
219219
ephemeral=True)
220220
else:
221221
try:
222-
# shall we display a custom report as greeting page?
222+
# shall we display a custom report as a greeting page?
223223
if os.path.exists(f'reports/{self.plugin_name}/{self.plugin_name}.json'):
224224
report = Report(self.bot, self.plugin_name, filename=f'{self.plugin_name}.json')
225-
env: ReportEnv = await report.render(guild=self.bot.guilds[0],
226-
servers=[
227-
{
228-
"display_name": x.display_name,
229-
"password": x.settings['password'],
230-
"status": x.status.name.title(),
231-
"num_players": len(x.get_active_players())
232-
} for x in self.bot.servers.values()
233-
])
225+
env: ReportEnv = await report.render(
226+
guild=self.bot.guilds[0],
227+
servers=[
228+
{
229+
"display_name": x.display_name,
230+
"password": x.settings['password'],
231+
"status": x.status.name.title(),
232+
"num_players": len(x.get_active_players())
233+
} for x in self.bot.servers.values()
234+
]
235+
)
234236
embed = env.embed
235237
if env.filename:
236238
# noinspection PyUnresolvedReferences
@@ -378,7 +380,7 @@ async def server_info_to_df(self) -> pd.DataFrame:
378380
columns = ['Node', 'Instance', 'Name', 'Password', 'Max Players', 'DCS Port', 'Bot Port']
379381
df = pd.DataFrame(columns=columns)
380382

381-
for server in self.bus.servers.values():
383+
for server in self.bot.servers.values():
382384
server_dict = {
383385
'Node': server.node.name,
384386
'Instance': server.instance.name,

plugins/lotatc/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ async def get_server(self, message: discord.Message) -> Server | None:
5757
return None
5858
try:
5959
return await utils.server_selection(
60-
self.bus, ctx, title=_("To which server do you want to upload this transponder file to?"),
60+
self.bot, ctx, title=_("To which server do you want to upload this transponder file to?"),
6161
filter_func=self.lotatc_server_filter)
6262
except Exception as ex:
6363
self.log.exception(ex)

plugins/mission/commands.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,8 @@ async def afk_check(self):
20892089
exemptions = config.get('exemptions', {})
20902090
if 'discord' in exemptions:
20912091
exemptions['discord'] = list(set(exemptions['discord']) | {"DCS Admin", "GameMaster"})
2092+
if server.locals.get('managed_by'):
2093+
exemptions['discord'].extend(server.locals.get('managed_by'))
20922094
if not player or player.check_exemptions(exemptions):
20932095
continue
20942096
if (datetime.now(timezone.utc) - dt).total_seconds() > max_time:
@@ -2163,7 +2165,7 @@ async def on_message(self, message: discord.Message):
21632165
channel = node.get('uploads', {}).get('channel')
21642166
if message.channel.id == channel:
21652167
server = next((
2166-
server for server in self.bus.servers.values()
2168+
server for server in self.bot.servers.values()
21672169
if server.instance.name == node_name
21682170
), None)
21692171
break
@@ -2172,7 +2174,7 @@ async def on_message(self, message: discord.Message):
21722174
channel = instance.get('uploads', {}).get('channel')
21732175
if message.channel.id == channel:
21742176
server = next((
2175-
server for server in self.bus.servers.values()
2177+
server for server in self.bot.servers.values()
21762178
if server.instance.name == instance_name
21772179
), None)
21782180
break

0 commit comments

Comments
 (0)