ラズパイサーバーでdiscord botを常時稼働させる
合同会社Asmiraiでは、業務委託メンバーの日報(作業ログ)をDiscordで送ってもらう運用をしています。
このログ、使い方次第では案件の稼働時間管理や、今後の見積もり精度アップに使える宝の山では???
ただ GCP や AWS に常時課金するほどでもない…
ということで埃かぶってた Raspberry Pi Zero 2 W を引っ張り出してサーバー化しました!
microSD や カードリーダー なども使用します。
:::note warn
この記事では サービスアカウントの秘密鍵(JSON) を使って Google Sheets にアクセスします。
・鍵ファイルは GitHub などに絶対コミットしない
・権限は最小限(スプレッドシート編集だけ)
・必要に応じて IP 制限やローテーション
など、運用面のセキュリティは各自の判断でお願いします。
:::
概要

転記されるスプシの様子
データは仮です。

目的
案件ごとの稼働時間集計や見積もりのデータをいつか活用したいので、Googleスプレッドシートに蓄積する
この記事で説明すること
- Discord Bot 作成
- Raspberry Pi セットアップ
- Google Sheets API 認証(サービスアカウントJSON)
- Python + discord.py 実装
- systemd 自動起動
手順1: Discord botの作成
-
下記にアクセスして New Application を押して Create します
-
適当なアイコンを設定しましょう
-
左の OAuth2 → URL Generator で、Scopes は
botにチェック、
Bot Permissions はView ChannelsSend MessagesRead Message HistoryAdd Reactionsを選択します

-
画面下で Generated URL が作成されるので、アクセスして Discord サーバーに bot を追加します。
-
左の Bot メニューから Message Content Intent を ON にして Save Changes を押します

手順2: Raspberry Pi の初期設定
下記のリンクから Imager 書き込み用のソフトをインストールします
https://www.raspberrypi.com/software/
- Device は今回使用する Zero 2 W を指定
- OS は Raspberry Pi OS Lite 64bit を選択
- Hostname や User名、Wi-Fi 等の設定を行います。この時 SSH の設定を有効化 してください。
手順3: Raspberry PiにSSH接続
- ターミナルで以下のコマンドを実行します
ssh ユーザー名@ホスト名.local
ユーザー名とホスト名は先ほど設定したものに置き換えてください。
:::note warn
お使いのPCのWi-Fiは先ほど設定したものと同じものに接続します
:::
手順4: zram(スワップ)を増やす
効果があるかわかりませんが、メモリ不足クラッシュ対策に増やしておきます。
- 設定ディレクトリの作成
sudo mkdir -p /etc/systemd/zram-generator.conf.d
- override.conf の作成
sudo nano /etc/systemd/zram-generator.conf.d/override.conf
- 編集
[zram0]
zram-size = 1024
compression-algorithm = zstd
保存(Ctrl+O → Enter)
終了(Ctrl+X)
- 再起動
sudo reboot
- 確認
swapon --show
SIZE が 1024M になっていればOKです。
手順5: Google Sheets API と Google Drive API の有効化
ラズパイ側からスプシを編集するための API 有効化を行います。
-
下記にアクセスし、新規プロジェクトを作成
https://console.cloud.google.com/welcome -
左のメニューから 「有効なAPIとサービス」 をクリックし、
「APIとサービスを有効にする」をクリックします

-
Google Sheets API を有効化します
-
同様の手順で Google Drive API も有効化します
手順6: サービスアカウントと鍵ファイルの作成
6-1. サービスアカウント作成
-
GCP コンソールで
「IAM と管理」 → 「サービスアカウント」 を開く -
「サービスアカウントを作成」から
- 名前:
raspi-worklog-bot - ロールは後から付けるので一旦スキップ or 最小限でOK
- 名前:
作成されるメールアドレスの例:
raspi-worklog-bot@discord-bot-XXXXX.iam.gserviceaccount.com
このメールアドレスは後で使うのでメモしておきます。
6-2. サービスアカウントキー(JSON)の作成
- 作成したサービスアカウントを選択
- 「鍵」タブ → 「鍵を追加」 → 「新しい鍵を作成」
- 種類: JSON を選んでダウンロード
discordbot-sa.json などの名前にして保管しておきます。
:::note warn
この JSON は 超重要な秘密鍵 なので、
GitHub や共有ストレージに上げない・メール添付しない・バラ撒かないよう注意してください。
:::
6-3. スプレッドシートの用意&共有
-
Google スプレッドシートで、以下のようなカラムを持つシートを作成します(シート名は「作業ログ」など)

-
右上の 共有 ボタンを押し、
先ほどのサービスアカウントのメールアドレスを「編集者」として追加します。
手順7: ラズパイに gcloud と鍵ファイルを配置
7-1. gcloud のインストール
ラズパイに SSH で接続した状態で実行。
sudo apt-get install -y apt-transport-https ca-certificates gnupg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \
| sudo tee /etc/apt/sources.list.d/google-cloud-sdk.list
sudo mkdir -p /usr/share/keyrings
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
sudo apt-get update
sudo apt-get install -y google-cloud-cli
gcloud version
(gcloud init は今回は必須ではありませんが、プロジェクト選択などしたい場合は実行しておいてもOKです)
7-2. サービスアカウントキーをラズパイへコピー
Mac からラズパイに discordbot-sa.json をコピーする例(scp):
scp discordbot-sa.json ユーザー名@ホスト名.local:/home/ユーザー名/discord-bot/discordbot-sa.json
ラズパイ側でファイルがあることを確認:
ls /home/ユーザー名/discord-bot/discordbot-sa.json
手順8: Python 環境構築と動作確認
8-1. ディレクトリ作成 & venv
ラズパイ上で:
mkdir -p ~/discord-bot
cd ~/discord-bot
python -m venv venv
source venv/bin/activate
pip install gspread google-auth google-auth-httplib2 google-auth-oauthlib discord.py
8-2. test_sheets.py で単体テスト
nano test_sheets.py
中身:
from google.oauth2 import service_account
import gspread
SCOPES = [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive",
]
SERVICE_ACCOUNT_FILE = "discordbot-sa.json" # 同じディレクトリに置いた場合
def get_sheet():
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE,
scopes=SCOPES,
)
gc = gspread.authorize(creds)
return gc.open("作業ログ").sheet1 # あなたのスプレッドシート名に合わせて変更
def main():
sheet = get_sheet()
sheet.append_row(["テスト案件", "user", "接続テスト", 0.1, "SAで書き込めた"])
print("書き込みOK")
if __name__ == "__main__":
main()
実行:
python test_sheets.py
書き込みOK と表示され、スプレッドシートに1行追記されていれば成功です。
手順9: bot の実装
:::note warn
Discord サーバーに、チャンネルカテゴリ「受託案件」配下に案件ごとのチャンネルがある前提の実装です。
必要に応じてカテゴリ名など修正してください。
:::
9-1. bot.py の作成
cd ~/discord-bot
nano bot.py
中身(トークンは必ず置き換える&本当は環境変数推奨):
import re
import discord
from google.oauth2 import service_account
import gspread
# ==== 設定ここだけ変える ====
DISCORD_TOKEN = "YOUR_DISCORD_BOT_TOKEN" # 実際のトークンは.envなどで管理推奨
SPREADSHEET_NAME = "作業ログ" # スプレッドシート名
TARGET_CATEGORY_NAME = "受託案件" # 監視するカテゴリ名
SERVICE_ACCOUNT_FILE = "discordbot-sa.json" # サービスアカウントJSONのパス
# ==========================
SCOPES = [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive",
]
def get_sheet():
"""サービスアカウントで Google Sheets のシートを返す"""
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE,
scopes=SCOPES,
)
gc = gspread.authorize(creds)
return gc.open(SPREADSHEET_NAME).sheet1
def parse_worklog(content: str, author_name: str, project_name: str):
"""
作業ログメッセージをパースして、スプレッドシートに書き込む用の行リストを返す。
想定フォーマット:
[作業ログ]
作業内容:今後の方針MTG
稼働時間 : 0.25
メモ:aaa
作業内容:API設計
稼働時間(時間): 2.5
メモ: 楽しかった
"""
lines = content.splitlines()
# 先頭が [作業ログ] でなければ無視
if not lines or not lines[0].startswith("[作業ログ]"):
return []
# 全角/半角コロン・スペースに緩く対応
task_re = re.compile(r"^作業内容\s*[::]\s*(.+)$")
hours_re = re.compile(r"^稼働時間.*[::]\s*(.+)$") # 稼働時間(時間): も拾う
memo_re = re.compile(r"^メモ\s*[::]\s*(.+)$")
entries = []
current = {}
for line in lines[1:]:
line = line.strip()
if not line:
continue
m_task = task_re.match(line)
if m_task:
# もし前の作業が残っていたら確定
if "作業内容" in current:
entries.append(current)
current = {}
current["作業内容"] = m_task.group(1)
continue
m_hours = hours_re.match(line)
if m_hours:
value = m_hours.group(1)
# 「0.25時間」みたいな書き方にも対応
value = value.replace("時間", "").strip()
current["稼働時間"] = value
continue
m_memo = memo_re.match(line)
if m_memo:
current["メモ"] = m_memo.group(1)
continue
# 最後の1件を追加
if current.get("作業内容"):
entries.append(current)
rows = []
for e in entries:
try:
hours = float(e.get("稼働時間", "0"))
except ValueError:
hours = 0.0
rows.append([
project_name, # 案件名 = チャンネル名
author_name, # 作業者 = 投稿者名
e.get("作業内容", ""), # 作業内容
hours, # 稼働時間(時間)
e.get("メモ", ""), # メモ
])
return rows
# ==== Discord クライアント ====
intents = discord.Intents.default()
intents.message_content = True # Developer Portal で Message Content Intent を ON にしておく
client = discord.Client(intents=intents)
@client.event
async def on_ready():
print(f"Logged in as {client.user.name} ({client.user.id})")
@client.event
async def on_message(message: discord.Message):
# 自分自身には反応しない
if message.author == client.user:
return
# カテゴリが「受託案件」以外のチャンネルは無視
category = getattr(message.channel, "category", None)
if category is None or category.name != TARGET_CATEGORY_NAME:
return
# 作業ログ以外は無視
if not message.content.startswith("[作業ログ]"):
return
# 投稿者名(ニックネーム優先)
author_name = message.author.display_name
# 案件名 = チャンネル名
project_name = message.channel.name
# パース
rows = parse_worklog(message.content, author_name, project_name)
if not rows:
await message.channel.send("作業ログの形式がパースできませんでした…")
return
# Sheets に書き込み
try:
sheet = get_sheet()
for row in rows:
sheet.append_row(row)
await message.add_reaction("✅")
await message.channel.send(
f"{len(rows)}件の作業ログをスプレッドシートに保存しました。"
)
except Exception as e:
await message.channel.send(
f"スプレッドシート書き込みでエラーが発生しました: {e}"
)
# 起動
client.run(DISCORD_TOKEN)
9-2. bot.py の実行テスト
python bot.py
コンソールに
Logged in as {bot名} (ID)
のように出たら、Discord サーバーの 「受託案件」カテゴリ配下のチャンネルで以下のように送信してみます。
[作業ログ]
作業内容:バグ修正1
稼働時間 : 0.25
メモ:わーい
作業内容:バグ修正2
稼働時間 : 0.25
メモ:わーい2
✅ リアクションと「2件の作業ログをスプレッドシートに保存しました。」のメッセージが返り、スプレッドシートに 2 行追加されていれば OK です。
手順10: systemd 化(自動起動)
ラズパイ起動時に自動で Bot が立ち上がるようにします。
10-1. systemd のサービスファイル作成
sudo nano /etc/systemd/system/worklog-bot.service
中身:{user名} はご自身のユーザーに変更してください。
[Unit]
Description=Discord Worklog Bot
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User={user名}
WorkingDirectory=/home/{user名}/discord-bot
ExecStart=/home/{user名}/discord-bot/venv/bin/python /home/{user名}/discord-bot/bot.py
Restart=always
RestartSec=5
Environment="PYTHONUNBUFFERED=1"
[Install]
WantedBy=multi-user.target
10-2. 設定の再読み込みと自動起動の有効化
# 設定ファイル(.service)の再読み込み
sudo systemctl daemon-reload
# worklog-bot サービスを新しい状態で再起動
sudo systemctl restart worklog-bot
# サービスの状態を確認する
sudo systemctl status worklog-bot
# ラズパイ起動時の自動起動を有効化
sudo systemctl enable worklog-bot
active (running) になっていれば成功です。
完成!!
お疲れ様でした!
以上で完成です!あとはラズパイを適当な USB ポートから電源供給しておけば、
Discord に投げられた [作業ログ] が自動で Google スプレッドシートに溜まっていく小さな「社内専用サーバー」 の出来上がりです。
剥き出しで放置されるサーバー()...

Discussion