Skip to content

Commit eae3ee2

Browse files
committed
CHANGES:
- GameMaster: Campaigns can have an icon now. - GameMaster: /campaign edit added
1 parent 591c739 commit eae3ee2

File tree

9 files changed

+90
-21
lines changed

9 files changed

+90
-21
lines changed

core/utils/campaigns.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ async def get_campaign(self, campaign: str) -> dict:
4646
async with self.apool.connection() as conn:
4747
async with conn.cursor(row_factory=dict_row) as cursor:
4848
await cursor.execute("""
49-
SELECT id, name, description, start AT TIME ZONE 'UTC' AS start, stop AT TIME ZONE 'UTC' AS stop
49+
SELECT id, name, description, image_url,
50+
start AT TIME ZONE 'UTC' AS start, stop AT TIME ZONE 'UTC' AS stop
5051
FROM campaigns
5152
WHERE name = %s
5253
""", (campaign, ))

core/utils/discord.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,13 +273,15 @@ def __init__(self):
273273
super().__init__(timeout=120)
274274
self.result = None
275275

276+
# noinspection PyTypeChecker
276277
@discord.ui.button(label='Yes', style=ButtonStyle.green, custom_id='yn_yes')
277278
async def on_yes(self, interaction: Interaction, _: Button):
278279
# noinspection PyUnresolvedReferences
279280
await interaction.response.defer()
280281
self.result = True
281282
self.stop()
282283

284+
# noinspection PyTypeChecker
283285
@discord.ui.button(label='No', style=ButtonStyle.red, custom_id='yn_no')
284286
async def on_no(self, interaction: Interaction, _: Button):
285287
# noinspection PyUnresolvedReferences
@@ -330,20 +332,23 @@ def __init__(self):
330332
super().__init__(timeout=120)
331333
self.result = None
332334

335+
# noinspection PyTypeChecker
333336
@discord.ui.button(label='Yes', style=ButtonStyle.green, custom_id='pl_yes')
334337
async def on_yes(self, interaction: Interaction, _: Button):
335338
# noinspection PyUnresolvedReferences
336339
await interaction.response.defer()
337340
self.result = 'yes'
338341
self.stop()
339342

343+
# noinspection PyTypeChecker
340344
@discord.ui.button(label='Later', style=ButtonStyle.primary, custom_id='pl_later', emoji='⏱')
341345
async def on_later(self, interaction: Interaction, _: Button):
342346
# noinspection PyUnresolvedReferences
343347
await interaction.response.defer()
344348
self.result = 'later'
345349
self.stop()
346350

351+
# noinspection PyTypeChecker
347352
@discord.ui.button(label='Cancel', style=ButtonStyle.red, custom_id='pl_cancel')
348353
async def on_cancel(self, interaction: Interaction, _: Button):
349354
# noinspection PyUnresolvedReferences
@@ -1420,12 +1425,14 @@ async def on_select(self, interaction: discord.Interaction, select: Select):
14201425
except Exception as ex:
14211426
interaction.client.log.exception(ex)
14221427

1428+
# noinspection PyTypeChecker
14231429
@discord.ui.button(label="Upload", style=ButtonStyle.green, row=2)
14241430
async def on_upload(self, interaction: discord.Interaction, button: Button):
14251431
# noinspection PyUnresolvedReferences
14261432
await interaction.response.defer()
14271433
self.stop()
14281434

1435+
# noinspection PyTypeChecker
14291436
@discord.ui.button(label="Up", style=ButtonStyle.secondary)
14301437
async def on_up(self, interaction: discord.Interaction, button: Button):
14311438
# noinspection PyUnresolvedReferences
@@ -1434,6 +1441,7 @@ async def on_up(self, interaction: discord.Interaction, button: Button):
14341441
self.dir = os.path.dirname(self.dir)
14351442
await self.refresh(interaction)
14361443

1444+
# noinspection PyTypeChecker
14371445
@discord.ui.button(label="Create", style=ButtonStyle.primary)
14381446
async def on_create(self, interaction: discord.Interaction, button: Button):
14391447
class TextModal(Modal, title="Create Directory"):
@@ -1456,6 +1464,7 @@ async def on_submit(derived, interaction: discord.Interaction) -> None:
14561464
self.dir = modal.name.value
14571465
await self.refresh(interaction)
14581466

1467+
# noinspection PyTypeChecker
14591468
@discord.ui.button(label="Cancel", style=ButtonStyle.red, row=2)
14601469
async def on_cancel(self, interaction: discord.Interaction, button: Button):
14611470
# noinspection PyUnresolvedReferences

plugins/gamemaster/commands.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,17 +306,45 @@ async def info(self, interaction: discord.Interaction, campaign: str):
306306
# noinspection PyUnresolvedReferences
307307
await interaction.response.send_message(embed=env.embed, ephemeral=utils.get_ephemeral(interaction))
308308

309+
@campaign.command(description=_("Edit Campaign"))
310+
@app_commands.guild_only()
311+
@utils.app_has_role('DCS Admin')
312+
@app_commands.autocomplete(campaign=utils.campaign_autocomplete)
313+
async def edit(self, interaction: discord.Interaction, campaign: str):
314+
ephemeral = utils.get_ephemeral(interaction)
315+
async with self.apool.connection() as conn:
316+
async with conn.cursor(row_factory=dict_row) as cursor:
317+
await cursor.execute("""
318+
SELECT * FROM campaigns WHERE name = %s
319+
""", (campaign, ))
320+
row = await cursor.fetchone()
321+
if not row:
322+
# noinspection PyUnresolvedReferences
323+
await interaction.response.send_message(_('Campaign not found.'), ephemeral=True)
324+
return
325+
modal = CampaignModal(name=campaign, start=row['start'], end=row['stop'], description=row['description'],
326+
image_url=row['image_url'])
327+
# noinspection PyUnresolvedReferences
328+
await interaction.response.send_modal(modal)
329+
if await modal.wait():
330+
return
331+
async with conn.transaction():
332+
await conn.execute("""
333+
UPDATE campaigns SET start=%s, stop=%s, description=%s, image_url=%s
334+
WHERE name=%s
335+
""", (modal.start, modal.end, modal.description.value, modal.image_url.value, campaign))
336+
await interaction.followup.send(_('Campaign {} updated.').format(campaign),
337+
ephemeral=ephemeral)
338+
309339
@campaign.command(description=_("Add a campaign"))
310340
@app_commands.guild_only()
311341
@utils.app_has_role('DCS Admin')
312342
async def add(self, interaction: discord.Interaction):
313343
ephemeral = utils.get_ephemeral(interaction)
314-
modal = CampaignModal(self.eventlistener)
344+
modal = CampaignModal()
315345
# noinspection PyUnresolvedReferences
316346
await interaction.response.send_modal(modal)
317347
if await modal.wait():
318-
# noinspection PyUnresolvedReferences
319-
await interaction.response.send_message(_('Aborted.'), ephemeral=True)
320348
return
321349
try:
322350
servers = await utils.server_selection(self.bus, interaction,
@@ -328,8 +356,15 @@ async def add(self, interaction: discord.Interaction):
328356
if not isinstance(servers, list):
329357
servers = [servers]
330358
try:
331-
await self.eventlistener.campaign('add', servers=servers, name=modal.name.value,
332-
description=modal.description.value, start=modal.start, end=modal.end)
359+
await self.eventlistener.campaign(
360+
'add',
361+
servers=servers,
362+
name=modal.name.value,
363+
description=modal.description.value,
364+
image_url=modal.image_url.value,
365+
start=modal.start,
366+
end=modal.end
367+
)
333368
await interaction.followup.send(_("Campaign {} added.").format(modal.name.value), ephemeral=ephemeral)
334369
except psycopg.errors.ExclusionViolation:
335370
await interaction.followup.send(_("A campaign is already configured for this timeframe!"),

plugins/gamemaster/db/tables.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
CREATE EXTENSION IF NOT EXISTS btree_gist;
2-
CREATE TABLE IF NOT EXISTS campaigns (id SERIAL PRIMARY KEY, name TEXT NOT NULL, description TEXT, start TIMESTAMP NOT NULL DEFAULT (now() AT TIME ZONE 'utc'), stop TIMESTAMP);
2+
CREATE TABLE IF NOT EXISTS campaigns (id SERIAL PRIMARY KEY, name TEXT NOT NULL, description TEXT, image_url TEXT, start TIMESTAMP NOT NULL DEFAULT (now() AT TIME ZONE 'utc'), stop TIMESTAMP);
33
CREATE TABLE IF NOT EXISTS campaigns_servers (campaign_id INTEGER NOT NULL, server_name TEXT NOT NULL, PRIMARY KEY (campaign_id, server_name));
44
ALTER TABLE campaigns ADD CONSTRAINT campaigns_prevent_double EXCLUDE USING gist(tsrange(start, stop, '[)') WITH &&);
55
ALTER TABLE campaigns ADD CONSTRAINT campaigns_check_start_before_stop CHECK (start < stop);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE campaigns ADD image_url TEXT;

plugins/gamemaster/listener.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,20 +175,25 @@ async def onMissionEvent(self, server: Server, data: dict) -> None:
175175
asyncio.create_task(self.send_player_message(player))
176176

177177
async def campaign(self, command: str, *, servers: Optional[list[Server]] = None, name: Optional[str] = None,
178-
description: Optional[str] = None, start: Optional[datetime] = None,
179-
end: Optional[datetime] = None):
178+
description: Optional[str] = None, image_url: Optional[str] = None,
179+
start: Optional[datetime] = None, end: Optional[datetime] = None):
180180
async with self.apool.connection() as conn:
181181
async with conn.transaction():
182182
if command == 'add':
183-
await conn.execute('INSERT INTO campaigns (name, description, start, stop) VALUES (%s, %s, %s, %s)',
184-
(name, description, start, end))
183+
await conn.execute("""
184+
INSERT INTO campaigns (name, description, image_url, start, stop)
185+
VALUES (%s, %s, %s, %s, %s)
186+
""", (name, description, image_url, start, end))
185187
if servers:
186188
cursor = await conn.execute('SELECT id FROM campaigns WHERE name ILIKE %s', (name,))
187189
campaign_id = (await cursor.fetchone())[0]
188190
for server in servers:
189191
# add this server to the server list
190-
await conn.execute('INSERT INTO campaigns_servers VALUES (%s, %s) ON CONFLICT DO NOTHING',
191-
(campaign_id, server.name))
192+
await conn.execute("""
193+
INSERT INTO campaigns_servers
194+
VALUES (%s, %s)
195+
ON CONFLICT DO NOTHING
196+
""", (campaign_id, server.name))
192197
elif command == 'start':
193198
cursor = await conn.execute("""
194199
SELECT id FROM campaigns WHERE name ILIKE %s

plugins/gamemaster/reports/campaign.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"color": "blue",
33
"title": "{title}",
4+
"img": "{campaign[image_url]}",
45
"elements":
56
[
67
{
@@ -24,7 +25,12 @@
2425
"inline": false
2526
}
2627
},
27-
"Ruler",
28+
{
29+
"type": "Ruler",
30+
"params": {
31+
"ruler_length": 28
32+
}
33+
},
2834
{
2935
"type": "SQLTable",
3036
"params": {

plugins/gamemaster/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "3.4"
1+
__version__ = "3.5"

plugins/gamemaster/views.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from core import Server, get_translation, utils
44
from datetime import datetime, timezone
55
from discord import TextStyle, ButtonStyle
6-
from discord.ui import Modal, TextInput, View
6+
from discord.ui import Modal, TextInput, View, TextDisplay, Label
77
from typing import Union, Optional
88

99
from .listener import GameMasterEventListener
@@ -20,26 +20,38 @@ class CampaignModal(Modal):
2020
end = TextInput(label=_("End (UTC)"), placeholder="yyyy-mm-dd hh24:mi", required=False)
2121
# noinspection PyTypeChecker
2222
description = TextInput(label=_("Description"), required=False, style=TextStyle.long)
23+
# noinspection PyTypeChecker
24+
image_url = TextInput(label=_("Image URL"), required=False, style=TextStyle.short)
2325

24-
def __init__(self, eventlistener: GameMasterEventListener):
26+
def __init__(self, name: Optional[str] = None, start: Optional[datetime] = None, end: Optional[datetime] = None,
27+
description: Optional[str] = None, image_url: Optional[str] = None):
2528
super().__init__(title=_("Campaign Info"))
26-
self.eventlistener = eventlistener
27-
self.start.default = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M")
29+
if name:
30+
self.remove_item(self.name)
31+
self.title = _("Edit Campaign {}".format(name))
32+
if start:
33+
self.start.default = start.strftime("%Y-%m-%d %H:%M")
34+
else:
35+
self.start.default = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M")
36+
if end:
37+
self.end.default = end.strftime("%Y-%m-%d %H:%M")
38+
self.description.default = description
39+
self.image_url.default = image_url
2840

2941
async def on_submit(self, interaction: discord.Interaction) -> None:
3042
try:
3143
self.start = datetime.strptime(self.start.value, '%Y-%m-%d %H:%M')
3244
except ValueError:
3345
# noinspection PyUnresolvedReferences
3446
await interaction.response.send_message(
35-
_("Format for {} needs to be yyyy-mm-dd hh24:mi!").format(self.start.label), ephemeral=True)
47+
_("Format for {} needs to be yyyy-mm-dd hh24:mi!").format(self.start.value), ephemeral=True)
3648
raise
3749
try:
3850
self.end = datetime.strptime(self.end.value, '%Y-%m-%d %H:%M') if self.end.value else None
3951
except ValueError:
4052
# noinspection PyUnresolvedReferences
4153
await interaction.response.send_message(
42-
_("Format for {} needs to be yyyy-mm-dd hh24:mi!").format(self.end.label), ephemeral=True)
54+
_("Format for {} needs to be yyyy-mm-dd hh24:mi!").format(self.end.value), ephemeral=True)
4355
raise
4456
# noinspection PyUnresolvedReferences
4557
await interaction.response.defer()

0 commit comments

Comments
 (0)