0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

最小最短で!)Render&discord.jsでbotを作る Part.1

Last updated at Posted at 2025-12-22

初めに

こんにちは、のいたみなです。
今回は今まで使っていたbotを動かしていたGlitchがサ終したのでRenderで作り直してみました。

discord botの登録、renderへのログインについては割愛します。

今回すること(目次)

renderプロジェクトの作成
コマンド機能を作る
コマンドの再読み込み

githubのリポジトリの作成

GitHubのリポジトリを作成します。

ログインしたらここを押して、
スクリーンショット 2025-12-22 173209.png
適当な名前を付けて作成します。
スクリーンショット 2025-12-22 173040.png
ファイルの新規作成をクリックして、、、
スクリーンショット 2025-12-22 173927.png
dockerfileを作成します。

dockerfile
FROM node:22
WORKDIR ./
COPY package*.json ./
RUN npm install
COPY .
EXPOSE 3000
CMD ["npm", "start"]

CommitChanges...→Commit changesをクリックしてファイルを保存します。
スクリーンショット 2025-12-22 174727.png

今後はこの方法でファイルを作成します。

おなじディレクトリにpackage.jsonを作成します。

paskage.json
{
  "name": "nodejs",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "scripts": {
    "start": "node ./index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@types/node": "^22.13.11",
    "discord.js": "^14.25.1",
    "dotenv": "^17.2.3",
    "express": "^5.2.1",
    "nodejs": "file:"
  }
}

Renderプロジェクトの作成

Renderを開いて、GitHubアカウントでログインします。

ログイン出来たら、事前に適当なProject NameとEnvironmant nameを設定してください。
スクリーンショット 2025-12-22 172441.png

Githubからデプロイします。

スクリーンショット 2025-12-22 175929.png
先ほど作成したリポジトリを選択して、
スクリーンショット 2025-12-22 180148.png
ランタイムをFreeに設定します。
スクリーンショット 2025-12-22 180209.png

スクリーンショットには映っていませんが、Start Commandには

npm run start

を設定してください。

Add Environment Variableをクリックして KeyにTOKEN, ValueにDiscord botのトークンを入力して、 Add Secretをクリックします。
スクリーンショット 2025-12-22 180442.png

同じようにApplication IDも、
Key:ApplicationID
Value:BotのApplicationID
を設定してください。

これで環境変数の設定は終わりです。

TokenはDiscord devのBOTから、
Application IDは同じくDiscord devのGeneral Informationから確認できます。

あとはDeploy web serviceをクリックして作成してください。

index.jsに次のコードを書き込んでください。

index.js

import { Client, GatewayIntentBits, Events } from "discord.js";
import express from "express";
var client = new Client({
  intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
});
client.login(process.env.TOKEN);

client.on("clientReady", () => {
  console.log(`サーバーが起動しました!!`);
});
var app = express();
app.get("/", (req, res) => {
  res.send(`ok`);
});
var port = 3000;
app.listen(port, () => {
  console.log(`Good morning!!`);
});
解説
index.js

import { Client, GatewayIntentBits, Events } from "discord.js";
import express from "express";
//必要なファイルのインポート
var client = new Client({
  intents: [GatewayIntentBits.Guilds/*Gulid(サーバーの情報)を読み書きする権限*/, GatewayIntentBits.GuildMessages/*Guildのメッセージを読み書きする権限*/],
});
client.login(process.env.TOKEN/*環境変数TOKENを取得*/);
client.on("clientReady", () => {//サーバーが起動したとき
  console.log(`サーバーが起動しました!!`);
});
var app = express();
app.get("/", (req, res) => {//httpリクエストのルートを作成
  res.send(`ok`);
});
var port = 3000;
app.listen(port, () => {//ポート3000にリクエストが来たとき
  console.log(`Good morning!!`);
});

githubのコミットを自動で検知してデプロイも自動で開始します。
サーバーが起動しました!!の文字が表示されていればokです。
スクリーンショット 2025-12-22 183222.png

コマンド機能を作る

まずはindex.jsのトップに次のコードを書き足します。

index.js
+ import fs from "fs";
+ import path from "path";
import { Client, GatewayIntentBits } from "discord.js";
~~~

そして、どこでもいいのでわかりやすい名前のjsファイルを作成します。
ここでは /commands/aisatu.jsとしました。

/commands/aisatu.js
const { SlashCommandBuilder } = await import("discord.js");
export default {
  data: new SlashCommandBuilder()
    .setName("aisatu")
    .setDescription("あいさつに反応してbotが返事します。"),
  execute: async function (interaction) {
    await interaction.reply("こんにちは~☆");
  },
};
解説
/commands/aisatu.js
const { SlashCommandBuilder } = await import("discord.js");
export default {//jsファイルをインポートしてそのまま関数を使えるように設定
  data: new SlashCommandBuilder()
    .setName("aisatu")//コマンド名
    .setDescription("あいさつに反応してbotが返事します。"),//コマンドの説明
  execute: async/*await(実行完了まで待つ)を使いますよ~*/ function (interaction) {//関数の中身
    await interaction.reply("こんにちは~☆");//コマンドに返信します。
  },
};

保存したらindex.jsに戻って上の方に先ほど作成したjsファイルを読み込むコードを追加します。

index.js
~~~
import express from "express";
+ import aisatu from "./commands/aisatu.js";
~~~

そしてテキストが送信されたとき、認識できるようにするため最下部へ次のコードを追加します。

index.js
client.on(Events.InteractionCreate, async interaction => {
    if (!interaction.isChatInputCommand()) return;
    if (interaction.commandName == aisatu.data.name) {
        try {
            await aisatu.execute(interaction);
        } catch (error) {
            console.error(error);
            await interaction.reply({content: 'コマンド実行時にエラーになりました。',ephemeral:true});
        }
    } else {
        await interaction.reply(`不明なコマンドが実行されました。`)
    }
});
解説
index.js
client.on(Events.InteractionCreate, async interaction => {//投稿があった時…
    if (!interaction.isChatInputCommand()) return;//投稿がコマンドでないならスキップ
    if (interaction.commandName == aisatu.data.name) {//コマンド名がaisatu.data.neme(aisatu)だったら…
        try {//とりあえず実行
            await aisatu.execute(interaction);//定義した関数に投稿の中身を渡して実行
        } catch (error) {//エラーが出たら…
            console.error(error);
            await interaction.reply({content: 'コマンド実行時にエラーになりました。',ephemeral:true});
        }
    } else {
        await interaction.reply(`不明なコマンドが実行されました。`)
    }
});

これでコマンド機能は完成です。

コマンドの再読み込み

コマンドを実際に使用するためにはコマンドを再読み込みさせなければいけません。
index.jsを同じディレクトリにupdate-commands.jsを作成してコードを書いてください。

update-commands.js
import { REST, Routes } from "discord.js";
import fs from "fs";
import path from "path";
import "dotenv/config";
const commands = [];
const foldersPath = path.join(process.cwd(), "commands");
const commandFiles = fs
  .readdirSync(foldersPath)
  .filter((file) => file.endsWith(".js") || file.endsWith(".mjs"));
export default async () => {
  for (const file of commandFiles) {
    const filePath = path.join(foldersPath, file);
    const module = await import(`file://${filePath}`);
    const command = module.default || module;
    if (command.data) {
      commands.push(command.data.toJSON());
    }
  }
  const rest = new REST().setToken(process.env.TOKEN);
  try {
    console.log(
      `[INIT] ${commands.length}つのスラッシュコマンドを更新します。`,
    );
    await rest.put(Routes.applicationCommands(process.env.ApplicationID), {
      body: commands,
    });
    console.log(
      `[INIT] ${commands.length}つのスラッシュコマンドを更新しました。`,
    );
  } catch (error) {
    console.error(error);
  }
};

解説
update-commands.js
import { REST, Routes } from "discord.js";
import fs from "fs";
import path from "path";
import "dotenv/config";
const commands = [];
const foldersPath = path.join(process.cwd(), "commands");//今のディレクトリにcommandsを書き足します
const commandFiles = fs
  .readdirSync(foldersPath)//commandsフォルダ内の、
  .filter((file) => file.endsWith(".js") || file.endsWith(".mjs"));//.jsと.mjsファイルを読み込みます。
export default async () => {
  for (const file of commandFiles) {//読み込んだファイルを一つずつfile変数に入れて…
    const filePath = path.join(foldersPath, file);//絶対パスに変換します。
    const module = await import(`file://${filePath}`);//それをimportします。
    const command = module.default || module;
    if (command.data) {//有効なファイルだったら…
      commands.push(command.data.toJSON());//配列commandにjsonとして挿入します。
    }
  }//繰り返し~
  const rest = new REST().setToken(process.env.TOKEN);
  try {
    console.log(
      `[INIT] ${commands.length}つのスラッシュコマンドを更新します。`,
    );
    await rest.put(Routes.applicationCommands(process.env.ApplicationID), {
      body: commands,
    });//コマンドの再読み込みをコマンドの一覧と説明とともにやらせます
    console.log(
      `[INIT] ${commands.length}つのスラッシュコマンドを更新しました。`,
    );
  } catch (error) {
    console.error(error);//エラーだお
  }
};

そしてindex.jsに追記します。

index.js
~~~
import * as aisatu from "./commands/aisatu.js";
+ import UpdateCommands from "./update-commands.js";
+ UpdateCommands();
~~~

~~~
client.on("clientReady", () => {
  console.log(`サーバーが起動しました!!`);
+ console.log(`招待URL:https://discord.com/api/oauth2/authorize?client_id=${process.env.ApplicationID}&permissions=8&scope=applications.commands+bot`)
});
~~~

実行してみる

実行してみます。
スクリーンショット 2025-12-22 164255.png

きちんと動いているみたいですね!!

実行したままdiscordへ移動します。

招待URLから招待できます。

スクリーンショット 2025-12-22 165116.png

きちんと返ってきました!
応答速度も遅くはありませんでした。

終わりに

かなり駆け足でしたがスラッシュコマンド実装までは完了しました。
次回はコマンドの拡張と通常のメッセージに対しての返信機能などを作っていきます。

前回:なし
次回:なし

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?