1
1
from __future__ import annotations
2
2
import textwrap
3
-
3
+ import re
4
4
from typing import TYPE_CHECKING , Optional
5
5
6
6
import aioretry
10
10
import cachetools
11
11
import pygtrie
12
12
import discord
13
- from discord import ui
13
+ from discord import ButtonStyle , ComponentType , InteractionType , ui
14
14
from discord .ext import commands
15
15
from luhack_bot import secrets
16
16
import sqlalchemy .dialects .postgresql as psa
17
17
18
18
from luhack_bot .cogs .challenges import logging
19
19
from luhack_bot .cogs .verification import app_commands
20
20
from luhack_bot .db .helpers import db
21
- from luhack_bot .db .models import Machine , MachineDisplay
21
+ from luhack_bot .db .models import Machine
22
22
from luhack_bot .utils .async_cache import async_cached
23
23
from luhack_bot .utils .checks import is_admin_int , is_authed_int
24
+ from luhack_bot .utils .list_sep_transform import ListSepTransformer , list_sep_choices
24
25
25
26
26
27
logger = logging .getLogger (__name__ )
@@ -170,36 +171,45 @@ async def generate_invite(node: str):
170
171
return invite .json ()["data" ]["code" ]
171
172
172
173
173
- class MachineInfoView (ui .View ):
174
+ @app_commands .guild_only ()
175
+ class Infra (commands .GroupCog , name = "infra" ):
174
176
def __init__ (self , bot : LUHackBot ):
175
177
self .bot = bot
176
- super ().__init__ (timeout = None )
178
+
179
+ super ().__init__ ()
180
+
181
+ async def interaction_check (self , interaction : discord .Interaction ):
182
+ return await is_authed_int (interaction )
183
+
184
+ @commands .GroupCog .listener ()
185
+ async def on_interaction (self , interaction : discord .Interaction ):
186
+ if interaction .type != InteractionType .component :
187
+ return
188
+ assert interaction .data is not None
189
+ if interaction .data .get ("component_type" ) != ComponentType .button .value :
190
+ return
191
+ custom_id = interaction .data .get ("custom_id" )
192
+ print (custom_id )
193
+ assert isinstance (custom_id , str )
194
+ if (m := re .fullmatch (r"machine_info_join:(\S+)" , custom_id )) is not None :
195
+ hostname = m .group (1 )
196
+ await self .send_join_info (interaction , hostname )
177
197
178
198
async def _borked (self , interaction : discord .Interaction ):
179
199
await interaction .followup .send (
180
200
"This machine info card is borked, complain to ben" , ephemeral = True
181
201
)
182
202
183
- @ui .button (label = "Click here to join" , custom_id = "machine_info_join" )
184
- async def join (self , interaction : discord .Interaction , button : ui .Button ):
185
- assert interaction .message is not None
186
-
203
+ async def send_join_info (self , interaction : discord .Interaction , hostname : str ):
187
204
await interaction .response .defer (ephemeral = True )
188
205
189
- display : Optional [MachineDisplay ] = await MachineDisplay .get (
190
- interaction .message .id
191
- )
192
- if display is None :
193
- await self ._borked (interaction )
194
- return
195
-
196
- machines = await get_devices_with_hostname (display .machine_hostname )
206
+ machines = await get_devices_with_hostname (hostname )
197
207
if len (machines ) == 0 :
198
208
await self ._borked (interaction )
199
209
return
200
210
201
211
if len (machines ) > 1 :
202
- logger .warn ("Got more than one machine for %s" , display . machine_hostname )
212
+ logger .warn ("Got more than one machine for %s" , hostname )
203
213
204
214
machine = machines [0 ]
205
215
@@ -218,20 +228,6 @@ async def join(self, interaction: discord.Interaction, button: ui.Button):
218
228
msg , view = discord .ui .View ().add_item (button ), ephemeral = True
219
229
)
220
230
221
-
222
- @app_commands .guild_only ()
223
- class Infra (commands .GroupCog , name = "infra" ):
224
- def __init__ (self , bot : LUHackBot ):
225
- self .bot = bot
226
-
227
- self ._machine_info_view = MachineInfoView (bot )
228
- self .bot .add_view (self ._machine_info_view )
229
-
230
- super ().__init__ ()
231
-
232
- async def interaction_check (self , interaction : discord .Interaction ):
233
- return await is_authed_int (interaction )
234
-
235
231
@app_commands .command (name = "join" )
236
232
@app_commands .describe (name = "The machine to join" )
237
233
@app_commands .autocomplete (name = machine_autocomplete )
@@ -271,35 +267,53 @@ async def join_server(self, interaction: discord.Interaction, *, name: str):
271
267
await interaction .followup .send (msg , view = discord .ui .View ().add_item (button ))
272
268
273
269
@app_commands .command (name = "display" )
274
- @app_commands .describe (name = "The machine to generate a display for" )
275
- @app_commands .autocomplete (name = machine_autocomplete )
270
+ @app_commands .describe (hostnames = "The machine to generate a display for" )
271
+ @app_commands .autocomplete (hostnames = list_sep_choices ( hostname_autocomplete ) )
276
272
@app_commands .default_permissions (manage_channels = True )
277
273
@app_commands .check (is_admin_int )
278
- async def display_server (self , interaction : discord .Interaction , * , name : str ):
279
- """Generate a message with info about a machine."""
274
+ async def display_server (
275
+ self ,
276
+ interaction : discord .Interaction ,
277
+ * ,
278
+ hostnames : app_commands .Transform [list [str ], ListSepTransformer ],
279
+ ):
280
+ """Generate a message with info about some machines."""
280
281
281
- if (device := await get_device (name )) is None :
282
- await interaction .response .send_message (
283
- "I don't know that device" , ephemeral = True
284
- )
282
+ await interaction .response .defer ()
283
+
284
+ machines : list [Device ] = []
285
+ for hostname in hostnames :
286
+ machines .extend (await get_devices_with_hostname (hostname ))
287
+
288
+ if not machines :
289
+ await interaction .followup .send ("No servers lol" )
285
290
return
286
291
287
- ip = device .addresses [0 ]
292
+ names = ", " .join (f"`{ machine .name } `" for machine in machines )
293
+ ips = ", " .join (f"`{ machine .addresses [0 ]} `" for machine in machines )
294
+ s = "s" if len (machines ) > 1 else ""
288
295
289
296
msg = textwrap .dedent (
290
297
f"""
291
- **Machine:** `{ device .name } `
292
- **IP:** `{ ip } `
298
+ **Machine{ s } :** { names }
299
+ **IP{ s } :** { ips }
300
+ Click one of the buttons to join
293
301
"""
294
302
)
295
303
296
- await interaction .response .send_message (
304
+ view = ui .View (timeout = None )
305
+ for machine in machines :
306
+ view .add_item (
307
+ ui .Button (
308
+ style = discord .ButtonStyle .grey ,
309
+ label = f"Join { machine .name } " ,
310
+ custom_id = f"machine_info_join:{ machine .hostname } " ,
311
+ )
312
+ )
313
+
314
+ await interaction .followup .send (
297
315
msg ,
298
- view = self ._machine_info_view ,
299
- )
300
- message = await interaction .original_response ()
301
- await MachineDisplay .create (
302
- discord_message_id = message .id , machine_hostname = device .hostname
316
+ view = view ,
303
317
)
304
318
305
319
@app_commands .command (name = "describe" )
0 commit comments