Skip to content

Commit c11ddae

Browse files
committed
Add means to send to stdin using jsk shell, lints
1 parent 08148a6 commit c11ddae

File tree

3 files changed

+77
-16
lines changed

3 files changed

+77
-16
lines changed

jishaku/features/shell.py

+35-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import tempfile
2020
import typing
2121

22+
import discord
23+
from discord import ui
2224
from discord.ext import commands
2325

2426
from jishaku.codeblocks import Codeblock, codeblock_converter
@@ -70,6 +72,32 @@ class ShellFeature(Feature):
7072
Feature containing the shell-related commands
7173
"""
7274

75+
class ShellStandardInputModal(ui.Modal, title="Type input"):
76+
"""Modal that prompts users for text to provide to stdin"""
77+
78+
stdin_content: ui.TextInput[ui.Modal] = ui.TextInput(label="Text", style=discord.TextStyle.short)
79+
80+
def __init__(self, reader: ShellReader, *args: typing.Any, **kwargs: typing.Any):
81+
super().__init__(*args, timeout=300, **kwargs)
82+
self.reader = reader
83+
84+
async def on_submit(self, interaction: discord.Interaction, /):
85+
value = self.stdin_content.value or ""
86+
87+
if self.reader.process.stdin and self.reader.process.stdin.writable():
88+
self.reader.process.stdin.write(f"{value}\r".encode('utf-8'))
89+
self.reader.process.stdin.flush()
90+
91+
await interaction.response.send_message(
92+
content="Sent into stdin",
93+
ephemeral=True
94+
)
95+
else:
96+
await interaction.response.send_message(
97+
content="Stdin is not writable",
98+
ephemeral=True
99+
)
100+
73101
@Feature.Command(parent="jsk", name="shell", aliases=["bash", "sh", "powershell", "ps1", "ps", "cmd", "terminal"])
74102
async def jsk_shell(self, ctx: ContextA, *, argument: codeblock_converter): # type: ignore
75103
"""
@@ -90,7 +118,13 @@ async def jsk_shell(self, ctx: ContextA, *, argument: codeblock_converter): # t
90118
paginator = WrappedPaginator(prefix=prefix, max_size=1975)
91119
paginator.add_line(f"{reader.ps1} {argument.content}\n")
92120

93-
interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author)
121+
async def send_standard_input(interaction: discord.Interaction):
122+
await interaction.response.send_modal(self.ShellStandardInputModal(reader))
123+
124+
stdin_button = ui.Button(label="\N{KEYBOARD} Send standard input")
125+
stdin_button.callback = send_standard_input
126+
127+
interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author, additional_buttons=[stdin_button])
94128
self.bot.loop.create_task(interface.send_to(ctx))
95129

96130
async for line in reader:

jishaku/paginators.py

+36-9
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ def to_component_dict(self) -> 'ButtonComponent':
250250
return self._underlying.to_dict()
251251

252252

253-
class PaginatorInterface(ui.View): # pylint: disable=too-many-instance-attributes
253+
class PaginatorInterface(ui.View): # pylint: disable=too-many-instance-attributes, too-many-public-methods
254254
"""
255255
A message and reaction based interface for paginators.
256256
@@ -287,7 +287,13 @@ class PaginatorInterface(ui.View): # pylint: disable=too-many-instance-attribut
287287
await interface.add_line("I'm still here!")
288288
"""
289289

290-
def __init__(self, bot: BotT, paginator: commands.Paginator, additional_buttons: typing.Optional[typing.List[ui.Button[typing.Self]]] = None, **kwargs: typing.Any):
290+
def __init__(
291+
self,
292+
bot: BotT,
293+
paginator: commands.Paginator,
294+
additional_buttons: typing.Optional[typing.List[ui.Button[typing.Self]]] = None,
295+
**kwargs: typing.Any
296+
):
291297
if not isinstance(paginator, commands.Paginator): # type: ignore
292298
raise TypeError('paginator must be a commands.Paginator instance')
293299

@@ -318,13 +324,27 @@ def __init__(self, bot: BotT, paginator: commands.Paginator, additional_buttons:
318324

319325
super().__init__(timeout=self.timeout_length)
320326

321-
self.button_start: DynamicButton[typing.Self] = DynamicButton(self.button_start_callback, self.button_start_label, style=discord.ButtonStyle.secondary)
322-
self.button_previous: DynamicButton[typing.Self] = DynamicButton(self.button_previous_callback, self.button_previous_label, style=discord.ButtonStyle.secondary)
323-
self.button_current: DynamicButton[typing.Self] = DynamicButton(self.button_current_callback, self.button_current_label, style=discord.ButtonStyle.primary)
324-
self.button_next: DynamicButton[typing.Self] = DynamicButton(self.button_next_callback, self.button_next_label, style=discord.ButtonStyle.secondary)
325-
self.button_last: DynamicButton[typing.Self] = DynamicButton(self.button_last_callback, self.button_last_label, style=discord.ButtonStyle.secondary)
326-
self.button_goto: DynamicButton[typing.Self] = DynamicButton(self.button_goto_callback, self.button_goto_label, style=discord.ButtonStyle.primary)
327-
self.button_close: DynamicButton[typing.Self] = DynamicButton(self.button_close_callback, self.button_close_label, style=discord.ButtonStyle.danger)
327+
self.button_start: DynamicButton[typing.Self] = DynamicButton(
328+
self.button_start_callback, self.button_start_label, style=discord.ButtonStyle.secondary
329+
)
330+
self.button_previous: DynamicButton[typing.Self] = DynamicButton(
331+
self.button_previous_callback, self.button_previous_label, style=discord.ButtonStyle.secondary
332+
)
333+
self.button_current: DynamicButton[typing.Self] = DynamicButton(
334+
self.button_current_callback, self.button_current_label, style=discord.ButtonStyle.primary
335+
)
336+
self.button_next: DynamicButton[typing.Self] = DynamicButton(
337+
self.button_next_callback, self.button_next_label, style=discord.ButtonStyle.secondary
338+
)
339+
self.button_last: DynamicButton[typing.Self] = DynamicButton(
340+
self.button_last_callback, self.button_last_label, style=discord.ButtonStyle.secondary
341+
)
342+
self.button_goto: DynamicButton[typing.Self] = DynamicButton(
343+
self.button_goto_callback, self.button_goto_label, style=discord.ButtonStyle.primary
344+
)
345+
self.button_close: DynamicButton[typing.Self] = DynamicButton(
346+
self.button_close_callback, self.button_close_label, style=discord.ButtonStyle.danger
347+
)
328348

329349
self.additional_buttons = additional_buttons or []
330350

@@ -530,6 +550,7 @@ async def button_start_callback(self, interaction: discord.Interaction): # pyli
530550
await interaction.response.edit_message(**self.send_kwargs)
531551

532552
def button_start_label(self, _button: ui.Button[typing.Self]) -> str:
553+
"""Label for returning to the first page (constant)"""
533554
return f"1 \u200b {self.emojis.start}"
534555

535556
async def button_previous_callback(self, interaction: discord.Interaction): # pylint: disable=unused-argument
@@ -539,6 +560,7 @@ async def button_previous_callback(self, interaction: discord.Interaction): # p
539560
await interaction.response.edit_message(**self.send_kwargs)
540561

541562
def button_previous_label(self, _button: ui.Button[typing.Self]) -> str:
563+
"""Left arrow label for going to the previous page (constant)"""
542564
return str(self.emojis.back)
543565

544566
async def button_current_callback(self, interaction: discord.Interaction): # pylint: disable=unused-argument
@@ -547,6 +569,7 @@ async def button_current_callback(self, interaction: discord.Interaction): # py
547569
await interaction.response.edit_message(**self.send_kwargs)
548570

549571
def button_current_label(self, _button: ui.Button[typing.Self]) -> str:
572+
"""Current page label (changes on page updates)"""
550573
return str(self.display_page + 1)
551574

552575
async def button_next_callback(self, interaction: discord.Interaction): # pylint: disable=unused-argument
@@ -556,6 +579,7 @@ async def button_next_callback(self, interaction: discord.Interaction): # pylin
556579
await interaction.response.edit_message(**self.send_kwargs)
557580

558581
def button_next_label(self, _button: ui.Button[typing.Self]) -> str:
582+
"""Right arrow label for going to the next page (constant)"""
559583
return str(self.emojis.forward)
560584

561585
async def button_last_callback(self, interaction: discord.Interaction): # pylint: disable=unused-argument
@@ -565,6 +589,7 @@ async def button_last_callback(self, interaction: discord.Interaction): # pylin
565589
await interaction.response.edit_message(**self.send_kwargs)
566590

567591
def button_last_label(self, _button: ui.Button[typing.Self]) -> str:
592+
"""Endstop label for going to the last page (changes on page count)"""
568593
return f"{self.emojis.end} \u200b {self.page_count}"
569594

570595
class PageChangeModal(ui.Modal, title="Go to page"):
@@ -599,6 +624,7 @@ async def button_goto_callback(self, interaction: discord.Interaction): # pylin
599624
await interaction.response.send_modal(self.PageChangeModal(self))
600625

601626
def button_goto_label(self, _button: ui.Button[typing.Self]) -> str:
627+
"""Label for selecting a page (constant)"""
602628
return "\N{RIGHTWARDS ARROW WITH HOOK} \u200b Go to page"
603629

604630
async def button_close_callback(self, interaction: discord.Interaction): # pylint: disable=unused-argument
@@ -613,6 +639,7 @@ async def button_close_callback(self, interaction: discord.Interaction): # pyli
613639
await message.delete()
614640

615641
def button_close_label(self, _button: ui.Button[typing.Self]) -> str:
642+
"""Label for closing the paginator (constant)"""
616643
return f"{self.emojis.close} \u200b Close paginator"
617644

618645

jishaku/repl/walkers.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def visit_Return(self, node: ast.Return) -> typing.Union[ast.Return, ast.If]:
4343

4444
# Otherwise, replace the return with a yield & valueless return
4545
return ast.If(
46-
test=ast.NameConstant(
46+
test=ast.Constant(
4747
value=True, # if True; aka unconditional, will be optimized out
4848
lineno=node.lineno,
4949
col_offset=node.col_offset
@@ -94,7 +94,7 @@ def visit_Delete(self, node: ast.Delete) -> ast.If:
9494
"""
9595

9696
return ast.If(
97-
test=ast.NameConstant(
97+
test=ast.Constant(
9898
value=True, # if True; aka unconditional, will be optimized out
9999
lineno=node.lineno,
100100
col_offset=node.col_offset
@@ -104,8 +104,8 @@ def visit_Delete(self, node: ast.Delete) -> ast.If:
104104
# if 'x' in globals():
105105
test=ast.Compare(
106106
# 'x'
107-
left=ast.Str(
108-
s=target.id,
107+
left=ast.Constant(
108+
value=target.id,
109109
lineno=node.lineno,
110110
col_offset=node.col_offset
111111
),
@@ -137,8 +137,8 @@ def visit_Delete(self, node: ast.Delete) -> ast.If:
137137
),
138138
args=[
139139
# 'x'
140-
ast.Str(
141-
s=target.id,
140+
ast.Constant(
141+
value=target.id,
142142
lineno=node.lineno,
143143
col_offset=node.col_offset
144144
)

0 commit comments

Comments
 (0)