0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【目指せ完走賞】なりかくんのDiscordAdvent Calendar 2022

Day 15

【15日目】discord.jsでシェルコマンドを作ってみよう

Posted at

はじめに

こんにちは、なりかくんです。
今回は、discord.jsでシェルコマンドを作ってみようと思います。

コマンド実行にchild_process

今回は、child_processというライブラリを使ってNode.js上でシェルを実行させます。その中でもストリーム形式という非同期実行でリアルタイムに更新されるようにします。

以下のようなコードでシェルを動かすことが出来ます。

const childProcess = require('child_process');
const ping = childProcess.spawn('ping google.com');

ping.stdout.on('data', (data) => {
    console.log(`stdout: ${data}`);
});

ping.stderr.on('data', (data) => {
    console.error(`stderr: ${data}`);
});

ping.on('close', (code) => {
    console.log(`Exited code: ${code}`);
});

ただしWindowsではこのままだと文字化けしてしまうのでエンコードします。以下のコードでエンコードできます。なお、encoding-japaneseモジュールを使っています。

const childProcess = require('child_process');
const encoding = require('encoding-japanese');

const ping = childProcess.spawn('ping google.com', { encoding: 'Shift_JIS' });

ping.stdout.on('data', (data) => {
    console.log(`stdout: ${toString(data)}`);
});

ping.stderr.on('data', (data) => {
    console.error(`stderr: ${toString(data)}`);
});

ping.on('close', (code) => {
    console.log(`Exited code: ${code}`);
});

const toString = (bytes) => {
    return encoding.convert(bytes, {
        from: 'SJIS',
        to: 'UNICODE',
        type: 'string',
    });
};

これで文字化けさせずに正常に表示させることが出来ます。

コマンドにする

では、discord.jsでBotからコマンドを実行ようにコードを変えていきます。今回は、以下のようなコードを作りました。

exec.js
const { SlashCommandBuilder } = require('discord.js');
const childProcess = require('child_process');
const encoding = require('encoding-japanese');

module.exports = {
	data: new SlashCommandBuilder()
		.setName('exec')
		.setDescription('コマンドを実行します')
		.addStringOption(option =>
			option.setName('cmd')
				.setDescription('コマンドを入力')
				.setRequired(true)),
	async execute(interaction) {
		let cmd = interaction.options.getString("cmd");
		await interaction.reply(`\`\`\`shell\n# ${cmd}\n\`\`\``);
		let stdMsg = await interaction.channel.send(`\`\`\`\n\`\`\``);
		const spawn = childProcess.spawn(cmd, { shell: true, timeout: 6000, encoding: 'Shift_JIS' });
		let stdout = [], stderr = [];
		spawn.stdout.on('data', async (data) => {
			stdout.push(toString(data));
			await stdMsg.edit(toMsg());
		});
		spawn.stderr.on('data', async (data) => {
			stderr.push(toString(data));
			await stdMsg.edit(toMsg());
		});
		spawn.on('close', (code) => {
			interaction.channel.send(`\`\`\`shell\n[CLOSE] Code: ${code}\n\`\`\``);
		});
		const toMsg = () => {
			return `\`\`\`shell${(stdout.length!=0?`\n[STDOUT]${stdout.join("")}`:"" + stderr.length!=0?`\n[STDERR]${stderr.join("")}`:"").substring(0, 1800)}\n\`\`\``;
		}
		const toString = (bytes) => {
			return encoding.convert(bytes, {
				from: 'SJIS',
				to: 'UNICODE',
				type: 'string',
			});
		};
	},
};

なお、出力部分に関してはDiscordの文字制限の都合以上、以下のようなコードで1800文字までに制限しています。

String.substring(0, 1800)

実際に動作させてみるとこのようになります。
image.png

正しく動いていますね。
このコマンドは権限などの設定を正しくしないと悪用されるので非常に注意が必要です。

以上です、最後までお読みいただきありがとうございました。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?