(Python + Discord API + cron + Ubuntu Server)
🧭 はじめに
この記事では、Ubuntuサーバー上でDiscord Botを使って自動的に出欠確認アンケートを投稿し、
締切時に未回答者をメンションするシステムを構築する手順を紹介します。
対象読者は:
- 学生プロジェクトやサークルなどでDiscordを使っている人
- 定期的にメンバーの予定を集めたい代表者
- 無料でサーバーを運用したい人
この構成では、Google Apps Script(GAS)ではできなかったDiscord Botの直接操作を
Ubuntuサーバー+Pythonで実現します。
🖥️ 環境の概要
| 項目 | 使用技術 |
|---|---|
| OS | Ubuntu Server(例:24.04) |
| 言語 | Python 3.12 |
| Botライブラリ | Discord API(requestsモジュール) |
| スケジュール管理 | cron(サーバーの定期実行機能) |
| 保存形式 | JSONファイル(メッセージIDなどを保持) |
Botは次の2つのタスクを行います:
- 金曜15時:各班チャンネルにアンケートを自動投稿(リアクション付き)
- 日曜18時:未回答者を自動検出しメンション通知
🧩 Step 1. Discord Botの準備
- Discord Developer Portal にアクセス
- 「New Application」をクリックしBotを作成
- 「Bot」タブに移動し
- 「Privileged Gateway Intents」から
- ✅ Server Members Intent
- ✅ Message Content Intent(任意)
をONにして保存
- 「Privileged Gateway Intents」から
- 「TOKEN」をコピー(これはPythonスクリプトで使う)
- 「OAuth2 → URL Generator」で以下を選択:
- Scopes:
bot - Permissions:
Send Messages,Read Message History,Add Reactions - 生成されたURLからBotをサーバーに招待
- Scopes:
🐍 Step 2. Ubuntu上でPython環境を構築
sudo apt update
sudo apt install -y python3 python3-venv
仮想環境の作成
mkdir ~/discord-bot
cd ~/discord-bot
python3 -m venv venv
source venv/bin/activate
ライブラリのインストール
pip install requests
✉️ Step 3. 金曜15時に自動投稿されるアンケートBot
ファイル名:send_schedule.py
import os, json, requests, urllib.parse, pathlib
DISCORD_BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"
TEAMS = [
{"name": "車体製作A班", "channel_id": 111111111111111111, "role_id": 999999999999999991},
{"name": "車体製作B班", "channel_id": 222222222222222222, "role_id": 999999999999999992},
{"name": "車体製作C班", "channel_id": 333333333333333333, "role_id": 999999999999999993},
]
REACTIONS = ["🌝", "🔥", "💧", "🌳", "🪙"]
BASE_URL = "https://discord.com/api/v10"
BASE_HEADERS = {"Authorization": f"Bot {DISCORD_BOT_TOKEN}", "User-Agent": "EcoRunBot/1.0"}
DATA_DIR = pathlib.Path("data")
DATA_DIR.mkdir(exist_ok=True)
POLL_FILE = DATA_DIR / "last_polls.json"
def send_message(channel_id, text):
url = f"{BASE_URL}/channels/{channel_id}/messages"
headers = {**BASE_HEADERS, "Content-Type": "application/json"}
r = requests.post(url, headers=headers, json={"content": text})
if r.status_code not in (200, 201):
print(f"[ERROR] send_message {channel_id} -> {r.status_code} {r.text}")
return None
return r.json()["id"]
def add_reactions(channel_id, message_id, emojis):
for emoji in emojis:
e = urllib.parse.quote(emoji)
url = f"{BASE_URL}/channels/{channel_id}/messages/{message_id}/reactions/{e}/@me"
r = requests.put(url, headers=BASE_HEADERS)
if r.status_code not in (204, 200):
print(f"[WARN] reaction {emoji} -> {r.status_code} {r.text}")
def main():
polls = {}
for team in TEAMS:
ch, role, name = team["channel_id"], team["role_id"], team["name"]
content = (
f"<@&{role}> 来週の参加可能な日をリアクションで教えて下さい!\n"
"月曜日:🌝\n火曜日:🔥\n水曜日:💧\n木曜日:🌳\n金曜日:🪙\n"
"※締切は日曜日の18時まで"
)
print(f"[INFO] sending poll to {name} ({ch})")
mid = send_message(ch, content)
if mid:
add_reactions(ch, mid, REACTIONS)
polls[str(ch)] = mid
POLL_FILE.write_text(json.dumps(polls, ensure_ascii=False, indent=2))
print("[INFO] アンケート送信完了")
if __name__ == "__main__":
main()
このスクリプトは:
- 各班のチャンネルにメッセージを投稿
- 5つのリアクション(月〜金)を自動で付与
- 投稿されたメッセージのIDを
data/last_polls.jsonに保存
⏰ Step 4. 日曜18時に未回答者をメンションするBot
ファイル名:remind_unanswered.py
import json, requests, urllib.parse, pathlib
DISCORD_BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"
TEAMS = [
{"name": "車体製作A班", "channel_id": 111111111111111111, "role_id": 999999999999999991},
{"name": "車体製作B班", "channel_id": 222222222222222222, "role_id": 999999999999999992},
{"name": "車体製作C班", "channel_id": 333333333333333333, "role_id": 999999999999999993},
]
REACTIONS = ["🌝", "🔥", "💧", "🌳", "🪙"]
BASE_URL = "https://discord.com/api/v10"
HEADERS = {"Authorization": f"Bot {DISCORD_BOT_TOKEN}", "User-Agent": "EcoRunBot/1.0"}
POLL_FILE = pathlib.Path("data/last_polls.json")
def list_role_members(guild_id, role_id):
members = []
limit, after = 1000, None
while True:
url = f"{BASE_URL}/guilds/{guild_id}/members?limit={limit}"
if after: url += f"&after={after}"
r = requests.get(url, headers=HEADERS)
if r.status_code == 403:
raise PermissionError("Server Members Intentが無効です。")
batch = r.json()
members.extend(batch)
if len(batch) < limit: break
after = batch[-1]["user"]["id"]
return {m["user"]["id"] for m in members if str(role_id) in m.get("roles", [])}
def list_reactors(channel_id, message_id, emojis):
users = set()
for emoji in emojis:
e = urllib.parse.quote(emoji)
r = requests.get(f"{BASE_URL}/channels/{channel_id}/messages/{message_id}/reactions/{e}?limit=100", headers=HEADERS)
if r.status_code == 200:
users |= {u["id"] for u in r.json()}
return users
def post_message(channel_id, text):
requests.post(f"{BASE_URL}/channels/{channel_id}/messages",
headers={**HEADERS, "Content-Type": "application/json"},
json={"content": text})
def main():
polls = json.loads(POLL_FILE.read_text())
for team in TEAMS:
ch, role, name = team["channel_id"], team["role_id"], team["name"]
mid = polls.get(str(ch))
if not mid:
continue
guild_id = requests.get(f"{BASE_URL}/channels/{ch}", headers=HEADERS).json()["guild_id"]
role_members = list_role_members(guild_id, role)
reactors = list_reactors(ch, mid, REACTIONS)
not_answered = sorted(role_members - reactors)
if not_answered:
mentions = " ".join(f"<@{uid}>" for uid in not_answered)
post_message(ch, f"{mentions}\n参加アンケートの回答ができていません。回答願います🙏")
print(f"[INFO] {name}: {len(not_answered)}名にリマインドを送信")
else:
print(f"[INFO] {name}: 全員回答済み")
if __name__ == "__main__":
main()
⏲️ Step 5. 定期実行(cron設定)
crontab -e
以下を追記:
# 金曜15:00にアンケートを送信
0 15 * * 5 /usr/bin/bash -lc 'cd /home/ubuntu/discord-bot && source venv/bin/activate && python3 send_schedule.py >> cron.log 2>&1'
# 日曜18:00に未回答者を通知
0 18 * * 0 /usr/bin/bash -lc 'cd /home/ubuntu/discord-bot && source venv/bin/activate && python3 remind_unanswered.py >> cron.log 2>&1'
✅ 動作確認ポイント
| チェック項目 | 確認方法 |
|---|---|
| Botがサーバーにいる | Discord上で確認 |
| Tokenが正しい | send_schedule.py実行で200が返る |
| Privileged Intent有効 | Developer Portal → BotタブでON |
| cronが動いている | cat cron.log で確認 |
| data/last_polls.jsonがある | cat data/last_polls.json |
💡 応用編
- Googleスプレッドシートと連携して班メンバーを動的に管理
- カスタム絵文字を使用(
<:emoji_name:emoji_id>形式) - フォールバック用メンバーIDリストを手動指定
- Replitなど無料クラウドへの移植も可能
🧾 まとめ
| 項目 | 内容 |
|---|---|
| 使用技術 | Python + Discord REST API + cron |
| 成果 | Discord上で自動的に出欠確認を実施 |
| メリット | サーバーが無料・安定・再利用可能 |
| 拡張性 | シート連携やカスタム通知も簡単に追加可能 |
この仕組みにより、
「毎週の出欠確認」「リーダーの手動リマインド」「メンバーの確認漏れ」
といった問題を完全に自動化できます。
📘 最終的な成果:
金曜15時 → 各班にアンケート投稿
日曜18時 → 未回答者を自動メンション通知すべてUbuntuサーバー上で24時間無料稼働。
人の手を介さず、Discordで自動出欠管理が可能に!

コメント