はじめに
こんにちは、なりかくんです。
今回は、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)
正しく動いていますね。
このコマンドは権限などの設定を正しくしないと悪用されるので非常に注意が必要です。
以上です、最後までお読みいただきありがとうございました。