Skip to content

Commit

Permalink
Add execute command action with payload (#156)
Browse files Browse the repository at this point in the history
* - Add action to execute command, optionally passing the event data to the command as a JSON string by replacing the "{event}" string.
- Include event data on emitEvent
- Add .idea folder to .gitignore

* update run command

* rename command to shell command

* rename run command to shell command

* cleanup

---------

Co-authored-by: Vince Au <vinceau09@gmail.com>
  • Loading branch information
nachoverdon and vinceau authored Jun 22, 2023
1 parent e5e477e commit 9f7a9dc
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ yarn-error.log*
# yalc
.yalc
yalc.lock

# Intellij
.idea
80 changes: 80 additions & 0 deletions src/renderer/containers/actions/ActionRunShellCommand.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { exec } from "child_process";
import * as React from "react";
import { Form, Icon, Message, TextArea } from "semantic-ui-react";

import { Text } from "@/components/Form";
import type { ActionTypeGenerator, Context } from "@/lib/event_actions";

import type { ActionComponent } from "./types";

type ActionRunShellCommandParams = {
command: string;
};

const ActionRunShellCommandFunc: ActionTypeGenerator = (params: ActionRunShellCommandParams) => {
return async (ctx: Context): Promise<Context> => {
// Make sure we actually have a command to run
if (params.command) {
const ctxJson = JSON.stringify(ctx).replace(/"/gm, '\\"');
const command = ctx ? params.command.replace(/{event}/gm, ctxJson) : params.command;

runShellCommand(command);
}

return ctx;
};
};

const ActionIcon = () => {
return <Icon name="terminal" size="large" />;
};

const RunShellCommandInput = ({
value,
onChange,
}: {
value: ActionRunShellCommandParams;
onChange: (val: ActionRunShellCommandParams) => void;
}) => {
const defaultValue = value && value.command ? value.command : "";
const [cmd, setMsg] = React.useState(defaultValue);
return (
<div style={{ marginTop: 10 }}>
<Message warning={true}>
<Icon name="warning sign" />
Running unknown commands can be very dangerous! Only run commands that you fully understand!
</Message>
<Form>
<TextArea
style={{ fontFamily: "monospace", fontSize: 16 }}
onBlur={() => onChange({ command: cmd })}
value={cmd}
onChange={(_: any, { value }: any) => setMsg(value)}
placeholder="Enter a shell command to run..."
/>
</Form>
<Text>Pro tip: Use &#123;event&#125; to get the event data as a JSON string.</Text>
</div>
);
};

export const ActionRunShellCommand: ActionComponent = {
label: "run a shell command",
action: ActionRunShellCommandFunc,
Icon: ActionIcon,
Component: RunShellCommandInput,
};

async function runShellCommand(command: string) {
exec(command, (error: Error | null, stdout: string, stderr: string) => {
if (error) {
console.error(`error: ${error.message}`);
return;
}
if (stderr) {
console.warn(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
}
3 changes: 3 additions & 0 deletions src/renderer/containers/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { EventManager } from "@/lib/event_actions";
import { ActionChangeScene } from "./ActionChangeScene";
import { ActionNotify } from "./ActionNotify";
import { ActionPlaySound } from "./ActionPlaySound";
import { ActionRunShellCommand } from "./ActionRunShellCommand";
import { ActionSaveReplayBuffer } from "./ActionSaveReplayBuffer";
import { ActionToggleRecording } from "./ActionToggleRecording";
import { ActionToggleSource } from "./ActionToggleSource";
Expand All @@ -20,6 +21,7 @@ export enum Action {
WRITE_FILE = "write-file",
SAVE_REPLAY_BUFFER = "save-replay-buffer",
TOGGLE_RECORDING = "toggle-recording",
RUN_SHELL_COMMAND = "run-shell-command",
}

export interface EventActionConfig {
Expand All @@ -46,6 +48,7 @@ export const actionComponents: { [name: string]: ActionComponent } = {
[Action.TOGGLE_SOURCE]: ActionToggleSource,
[Action.SAVE_REPLAY_BUFFER]: ActionSaveReplayBuffer,
[Action.TOGGLE_RECORDING]: ActionToggleRecording,
[Action.RUN_SHELL_COMMAND]: ActionRunShellCommand,
};

for (const [key, value] of Object.entries(actionComponents)) {
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/lib/realtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SlpStreamManager {
this.realtime = new SlpRealTime();
this.eventManager = new EventManager(this.realtime);
this.eventManager.events$.subscribe((event) => {
eventActionManager.emitEvent(event.id);
eventActionManager.emitEvent(event.id, event);
});
}

Expand Down
4 changes: 4 additions & 0 deletions src/renderer/styles/global.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ const makeGlobalStyles = (theme: Theme) => css`
.ui.modal.${ThemeMode.DARK} {
${modalTheme(theme)}
}
.ui.message {
border-radius: 0.5rem;
}
`;

export const GlobalStyle = withTheme(({ theme }) => <Global styles={makeGlobalStyles(theme)} />);

0 comments on commit 9f7a9dc

Please sign in to comment.