MENU
  • はじめての n8n
  • 作り方
  • エラー対処
  • Claude 活用
  • 運営者
n8n × Claude 自動化の実践記録 | 自動化ラボ
自動化ラボ
  • はじめての n8n
  • 作り方
  • エラー対処
  • Claude 活用
  • 運営者
  • はじめての n8n
  • 作り方
  • エラー対処
  • Claude 活用
自動化ラボ
  • はじめての n8n
  • 作り方
  • エラー対処
  • Claude 活用
  • 運営者
  1. ホーム
  2. ワークフローの作り方
  3. n8nでTelegramのボットに送ったメモをGoogleスプレッドシートに記録するワークフロー(公開URLなし)

n8nでTelegramのボットに送ったメモをGoogleスプレッドシートに記録するワークフロー(公開URLなし)

2026 10/07
ワークフローの作り方
Google スプレッドシート Telegram
2026年10月1日2026年10月7日
当ページのリンクには広告が含まれています。
ワークフロー全体(Webhook → Get Offset → Get Updates → Pick Memos → Has Memo → Google Sheets → Save Offset)
  • 動作確認日:2026-10-01
  • n8nのバージョン:2.41.3(Docker、セルフホスト)
目次

① 結論:このワークフローでできること

自分のTelegramボットに送ったメモを、n8nが取りに行き、「受信日時・本文・メッセージID」をGoogleスプレッドシートに1通1行で記録します。

  • n8nを localhost で動かしていても使えます。Telegramからn8nを呼んでもらうのではなく、n8nのほうからメッセージを取りに行く(getUpdates)作りにしました。
  • どこまで読んだか(offset)を覚えているので、何回実行しても、同じメモは1回しか記録しません。
  • 読んだ位置は、シートへの記録が済んでから進めます。途中で失敗しても、メモがなくなりません。
  • /start のようなコマンドや、自分以外からのメッセージは記録しません。
  • 動作確認のため、起動にはWebhookを使っています。実際に使うときは、Schedule Triggerで数分ごとに実行する想定です。

② 完成イメージ

ワークフロー全体(Webhook → Get Offset → Get Updates → Pick Memos → Has Memo → Google Sheets → Save Offset)

ボットに、テスト用のメモを3通送ってから、WebhookのURLにGETでアクセスしました。次の文章が返ってきます(実際の出力です)。

3件のメモをシートに記録しました

記録されたシートは、次のとおりです(messageId の列は省略しています)。

receivedAttext
2026-10-01 11:30「買い物:牛乳」
2026-10-01 11:30「アイデア:天気とLINEを組み合わせる」
2026-10-01 11:30「読みたい本:n8n入門」

続けてもう一度実行すると、次の文章が返り、シートの行は増えませんでした。

実行返ってきた文章シート
1回目3件のメモをシートに記録しました3行追記
2回目新しいメモはありませんでした変わらない

実行したときの件数の流れは、次のとおりです。

ノード1回目2回目
Webhook11
Get Offset11
Get Updates(HTTP Request)11
Pick Memos31(空)
Has Memo3(true側)1(false側)
Google Sheets3動かない
Save Offset / Save Offset (none)Save Offset:3Save Offset (none):1
Respond Saved / Respond NoneRespond Saved:3Respond None:1

③ 使うノードと必要なもの

必要なもの

  • n8n(今回はバージョン2.41.3をDockerで動かしました)
  • Telegramのアカウントと、自分で作ったボットの「トークン」
  • 自分の「チャットID」
  • Googleスプレッドシートと、タブ(今回は telegram-log という名前のタブ。中身は空で大丈夫です)
  • 認証情報:Google Sheets OAuth2 API

ボットの作り方とチャットIDの調べ方は、「n8nでブログの新着記事をTelegramに通知するワークフロー」の記事で説明しています。

なぜTelegram Triggerを使わないのか

n8nには「Telegram Trigger」があり、ボットにメッセージが届いた瞬間にワークフローを動かせます。ただし、Telegram Triggerは、Telegramのサーバーからn8nを呼んでもらうしくみです。そのため、インターネットから見えるURL(https)でn8nを動かしている必要があります。

今回のn8nは localhost で動かしているので、Telegramからは届きません。そこで、n8nのほうから、TelegramのAPI(getUpdates)で新しいメッセージを取りに行く作りにしました。

方法外から見えるURL届いてから記録するまで
Telegram Trigger必要すぐ
getUpdatesで取りに行く(今回)不要次に実行したとき(Schedule Triggerの間隔しだい)

使うノード

ノード役割設定したこと
Webhook動作確認用の起動ボタンPath:test-telegram-sheets、Respond:Using 'Respond to Webhook' Node
Code(Get Offset)前回どこまで読んだか(offset)を取り出す$getWorkflowStaticData('global').offset を返す
HTTP Request(Get Updates)新しいメッセージを取るURL:https://api.telegram.org/botトークン/getUpdates?offset={{ $json.offset }}&allowed_updates=["message"]
Code(Pick Memos)自分とのチャットのテキストだけを取り出す下のコード
If(Has Memo)メモがあるか確かめるtext が空でない
Google Sheetsメモを記録するOperation:Append or Update Row、Sheet:By Name telegram-log、Column to match on:messageId
Code(Save Offset / Save Offset (none))次に読む位置を覚える下のコード
Respond to Webhook(Respond Saved / Respond None)結果を返す記録したとき:○件のメモをシートに記録しました、なかったとき:新しいメモはありませんでした

TelegramのAPIは、URLの中にトークンを入れるしくみです。そのため、Get UpdatesのURLにはトークンが入ります。このワークフローのJSONを人に渡すときは、トークンの部分を必ず消してください(この記事の④のJSONでは YOUR_BOT_TOKEN に置き換えています)。

Pick Memos(Codeノード)の設定画面

Pick Memosでは、次の処理をしています(画像では、チャットIDを隠しています)。

// 自分とのチャットのテキストだけを取り出す
const MY_CHAT_ID = YOUR_CHAT_ID;
const updates = $json.result || [];

const rows = updates
  .map(u => u.message)
  .filter(m => m && m.chat.id === MY_CHAT_ID && m.text && !m.text.startsWith('/'))
  .map(m => ({
    receivedAt: DateTime.fromSeconds(m.date).setZone('Asia/Tokyo').toFormat('yyyy-MM-dd HH:mm'),
    text: m.text,
    messageId: m.message_id,
  }));
return rows.length ? rows.map(r => ({ json: r })) : [{ json: { text: '' } }];
  • m.chat.id === MY_CHAT_ID で、自分とのチャット以外のメッセージを除いています。ボットのユーザー名を知っている人は、誰でもボットに話しかけられるからです。
  • !m.text.startsWith('/') で、/start のようなコマンドを除いています。
  • Telegramの日時(date)は、1970年からの秒数で返ってくるので、日本時間の日時に直しています。
  • メモがないときは、空の text を1つだけ返して、次のIfノードでfalse側に流しています。
Save Offset(Codeノード)の設定画面

Save Offsetでは、次の処理をしています。Save Offset (none) も、コメント以外は同じです。

// シートに記録できたあとで、次に読む位置を覚える
const state = $getWorkflowStaticData('global');
const updates = $('Get Updates').first().json.result || [];
if (updates.length) state.offset = Math.max(...updates.map(u => u.update_id)) + 1;
return $input.all();
  • getUpdates に offset(最後に読んだ update_id + 1)を付けて呼ぶと、Telegramは、それより前のメッセージを「読んだ」とみなして、もう返さなくなります。
  • そのため、offsetを覚えるのは、シートへの記録が済んだあとにしています(理由は⑤に書きました)。
Google Sheetsノードの設定画面

Google Sheetsノードでは、照合する列(Column to match on)を messageId にしました。万一、同じメモをもう一度受け取っても、行が増えずに上書きされます。

④ JSON

n8nの公開API(POST /api/v1/workflows)でこのJSONからワークフローを作成し、有効化してから、上の結果を確認しました。

使うときは、次の箇所を自分の環境に合わせて書き換えてください。

  • YOUR_BOT_TOKEN:ボットのトークン(Get UpdatesのURLの中)
  • YOUR_CHAT_ID:自分のチャットID(Pick Memosのコードの中。数字をそのまま書きます)
  • YOUR_SPREADSHEET_ID:記録先のスプレッドシートのID
  • YOUR_CREDENTIAL_ID:n8nに登録したGoogle Sheetsの認証情報のID
{
    "name":  "test-telegram-sheets",
    "nodes":  [
                  {
                      "id":  "b3300000-0001-4000-8000-000000000001",
                      "name":  "Webhook",
                      "webhookId":  "b3300000-aaaa-4000-8000-000000000001",
                      "type":  "n8n-nodes-base.webhook",
                      "typeVersion":  2,
                      "position":  [
                                       0,
                                       0
                                   ],
                      "parameters":  {
                                         "path":  "test-telegram-sheets",
                                         "responseMode":  "responseNode",
                                         "options":  {

                                                     }
                                     }
                  },
                  {
                      "id":  "b3300000-0002-4000-8000-000000000002",
                      "name":  "Get Offset",
                      "type":  "n8n-nodes-base.code",
                      "typeVersion":  2,
                      "position":  [
                                       220,
                                       0
                                   ],
                      "parameters":  {
                                         "jsCode":  "// 前回どこまで読んだか(offset)を取り出す\nconst state = $getWorkflowStaticData(\u0027global\u0027);\nreturn [{ json: { offset: state.offset || 0 } }];"
                                     }
                  },
                  {
                      "id":  "b3300000-0003-4000-8000-000000000003",
                      "name":  "Get Updates",
                      "type":  "n8n-nodes-base.httpRequest",
                      "typeVersion":  4.2,
                      "position":  [
                                       440,
                                       0
                                   ],
                      "parameters":  {
                                         "url":  "=https://api.telegram.org/botYOUR_BOT_TOKEN/getUpdates?offset={{ $json.offset }}\u0026allowed_updates=[\"message\"]",
                                         "options":  {

                                                     }
                                     }
                  },
                  {
                      "id":  "b3300000-0004-4000-8000-000000000004",
                      "name":  "Pick Memos",
                      "type":  "n8n-nodes-base.code",
                      "typeVersion":  2,
                      "position":  [
                                       660,
                                       0
                                   ],
                      "parameters":  {
                                         "jsCode":  "// 自分とのチャットのテキストだけを取り出す\nconst MY_CHAT_ID = YOUR_CHAT_ID;\nconst updates = $json.result || [];\n\nconst rows = updates\n  .map(u =\u003e u.message)\n  .filter(m =\u003e m \u0026\u0026 m.chat.id === MY_CHAT_ID \u0026\u0026 m.text \u0026\u0026 !m.text.startsWith(\u0027/\u0027))\n  .map(m =\u003e ({\n    receivedAt: DateTime.fromSeconds(m.date).setZone(\u0027Asia/Tokyo\u0027).toFormat(\u0027yyyy-MM-dd HH:mm\u0027),\n    text: m.text,\n    messageId: m.message_id,\n  }));\nreturn rows.length ? rows.map(r =\u003e ({ json: r })) : [{ json: { text: \u0027\u0027 } }];"
                                     }
                  },
                  {
                      "id":  "b3300000-0005-4000-8000-000000000005",
                      "name":  "Has Memo",
                      "type":  "n8n-nodes-base.if",
                      "typeVersion":  2.2,
                      "position":  [
                                       880,
                                       0
                                   ],
                      "parameters":  {
                                         "conditions":  {
                                                            "options":  {
                                                                            "caseSensitive":  true,
                                                                            "leftValue":  "",
                                                                            "typeValidation":  "loose",
                                                                            "version":  2
                                                                        },
                                                            "conditions":  [
                                                                               {
                                                                                   "id":  "e1",
                                                                                   "leftValue":  "={{ $json.text }}",
                                                                                   "rightValue":  "",
                                                                                   "operator":  {
                                                                                                    "type":  "string",
                                                                                                    "operation":  "notEmpty",
                                                                                                    "singleValue":  true
                                                                                                }
                                                                               }
                                                                           ],
                                                            "combinator":  "and"
                                                        },
                                         "looseTypeValidation":  true,
                                         "options":  {

                                                     }
                                     }
                  },
                  {
                      "id":  "b3300000-0006-4000-8000-000000000006",
                      "name":  "Google Sheets",
                      "type":  "n8n-nodes-base.googleSheets",
                      "typeVersion":  4.5,
                      "position":  [
                                       1100,
                                       -100
                                   ],
                      "parameters":  {
                                         "operation":  "appendOrUpdate",
                                         "documentId":  {
                                                            "__rl":  true,
                                                            "value":  "YOUR_SPREADSHEET_ID",
                                                            "mode":  "id"
                                                        },
                                         "sheetName":  {
                                                           "__rl":  true,
                                                           "value":  "telegram-log",
                                                           "mode":  "name"
                                                       },
                                         "columns":  {
                                                         "mappingMode":  "autoMapInputData",
                                                         "value":  {

                                                                   },
                                                         "matchingColumns":  [
                                                                                 "messageId"
                                                                             ],
                                                         "schema":  [

                                                                    ]
                                                     },
                                         "options":  {

                                                     }
                                     },
                      "credentials":  {
                                          "googleSheetsOAuth2Api":  {
                                                                        "id":  "YOUR_CREDENTIAL_ID",
                                                                        "name":  "Google Sheets account"
                                                                    }
                                      }
                  },
                  {
                      "id":  "b3300000-0009-4000-8000-000000000009",
                      "name":  "Save Offset",
                      "type":  "n8n-nodes-base.code",
                      "typeVersion":  2,
                      "position":  [
                                       1320,
                                       -100
                                   ],
                      "parameters":  {
                                         "jsCode":  "// シートに記録できたあとで、次に読む位置を覚える\nconst state = $getWorkflowStaticData(\u0027global\u0027);\nconst updates = $(\u0027Get Updates\u0027).first().json.result || [];\nif (updates.length) state.offset = Math.max(...updates.map(u =\u003e u.update_id)) + 1;\nreturn $input.all();"
                                     }
                  },
                  {
                      "id":  "b3300000-0010-4000-8000-000000000010",
                      "name":  "Save Offset (none)",
                      "type":  "n8n-nodes-base.code",
                      "typeVersion":  2,
                      "position":  [
                                       1100,
                                       120
                                   ],
                      "parameters":  {
                                         "jsCode":  "// メモがなくても、読んだ位置(/start などのコマンド)は進めておく\nconst state = $getWorkflowStaticData(\u0027global\u0027);\nconst updates = $(\u0027Get Updates\u0027).first().json.result || [];\nif (updates.length) state.offset = Math.max(...updates.map(u =\u003e u.update_id)) + 1;\nreturn $input.all();"
                                     }
                  },
                  {
                      "id":  "b3300000-0007-4000-8000-000000000007",
                      "name":  "Respond Saved",
                      "type":  "n8n-nodes-base.respondToWebhook",
                      "typeVersion":  1.1,
                      "position":  [
                                       1540,
                                       -100
                                   ],
                      "parameters":  {
                                         "respondWith":  "text",
                                         "responseBody":  "={{ $input.all().length }}件のメモをシートに記録しました",
                                         "options":  {

                                                     }
                                     }
                  },
                  {
                      "id":  "b3300000-0008-4000-8000-000000000008",
                      "name":  "Respond None",
                      "type":  "n8n-nodes-base.respondToWebhook",
                      "typeVersion":  1.1,
                      "position":  [
                                       1320,
                                       120
                                   ],
                      "parameters":  {
                                         "respondWith":  "text",
                                         "responseBody":  "新しいメモはありませんでした",
                                         "options":  {

                                                     }
                                     }
                  }
              ],
    "connections":  {
                        "Webhook":  {
                                        "main":  [
                                                     [
                                                         {
                                                             "node":  "Get Offset",
                                                             "type":  "main",
                                                             "index":  0
                                                         }
                                                     ]
                                                 ]
                                    },
                        "Get Offset":  {
                                           "main":  [
                                                        [
                                                            {
                                                                "node":  "Get Updates",
                                                                "type":  "main",
                                                                "index":  0
                                                            }
                                                        ]
                                                    ]
                                       },
                        "Get Updates":  {
                                            "main":  [
                                                         [
                                                             {
                                                                 "node":  "Pick Memos",
                                                                 "type":  "main",
                                                                 "index":  0
                                                             }
                                                         ]
                                                     ]
                                        },
                        "Pick Memos":  {
                                           "main":  [
                                                        [
                                                            {
                                                                "node":  "Has Memo",
                                                                "type":  "main",
                                                                "index":  0
                                                            }
                                                        ]
                                                    ]
                                       },
                        "Has Memo":  {
                                         "main":  [
                                                      [
                                                          {
                                                              "node":  "Google Sheets",
                                                              "type":  "main",
                                                              "index":  0
                                                          }
                                                      ],
                                                      [
                                                          {
                                                              "node":  "Save Offset (none)",
                                                              "type":  "main",
                                                              "index":  0
                                                          }
                                                      ]
                                                  ]
                                     },
                        "Save Offset (none)":  {
                                                   "main":  [
                                                                [
                                                                    {
                                                                        "node":  "Respond None",
                                                                        "type":  "main",
                                                                        "index":  0
                                                                    }
                                                                ]
                                                            ]
                                               },
                        "Save Offset":  {
                                            "main":  [
                                                         [
                                                             {
                                                                 "node":  "Respond Saved",
                                                                 "type":  "main",
                                                                 "index":  0
                                                             }
                                                         ]
                                                     ]
                                        },
                        "Google Sheets":  {
                                              "main":  [
                                                           [
                                                               {
                                                                   "node":  "Save Offset",
                                                                   "type":  "main",
                                                                   "index":  0
                                                               }
                                                           ]
                                                       ]
                                          }
                    },
    "settings":  {
                     "executionOrder":  "v1"
                 }
}

⑤ つまずきポイントと対処

このワークフローは、2回作り直してから動きました。どちらも、実際に起きたことです。

1回目:メモが「なし」と判定され、しかも取り直せなくなった

  • 起きたこと:4通のメモを受け取れていたのに、「メモがあるか」を判定するIfノードで、4通ともfalse側(メモなし)に流れました。シートには何も書かれませんでした。
  • 原因1:Ifノードの条件を、「empty が true ではない」という書き方にしていて、メモの行では期待どおりに判定されませんでした。
  • 原因2:最初は、メッセージを受け取った時点で、読んだ位置(offset)を先に進めていました。そのため、Telegramはこの4通を「読んだ」とみなし、もう返さなくなりました。メモがなくなってしまったわけです。
  • 対処:Ifノードの条件を「text が空でない」に変えました。さらに、offsetを覚えるのを、シートへの記録が済んだあと(Save Offset)に移しました。

2回目:同じメモが2回記録された

  • 起きたこと:作り直したあとに2回実行すると、2回目にも同じ3通が記録され、シートが6行になりました。
  • 原因:Save Offsetが、次に読む位置を受け取れておらず、offsetが保存されていませんでした。そのため、2回目もTelegramから同じメッセージが返ってきました。
  • 対処:次に読む位置を、Get Updatesの結果から直接計算するようにしました。また、Google Sheetsノードを「Append or Update Row」(messageId で照合)に変えて、同じメモを受け取っても行が増えないようにしました。テストで重複した6行は消してから、テストし直しました。

⑥ 応用アイデア

どれも、まだ試していないアイデアです。

  • Webhookを「Schedule Trigger」に替えて、5分ごとにメモを取りに行く
  • 「買い物:」「アイデア:」のような頭の言葉で、シートのタブを分ける
  • 記録できたら、ボットから「記録しました」と返事をする(Telegramノードの「Send Message」)
  • n8nをインターネットから見えるURLで動かしているなら、Telegram Triggerに替えて、届いた瞬間に記録する
ワークフローの作り方
Google スプレッドシート Telegram
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
  • n8nでブログの新着記事をTelegramに通知するワークフロー(重複なし)
  • n8nの新しいバージョンが出たらDiscordに通知するワークフロー(GitHub API・重複なし)

この記事を書いた人

自動化ラボ管理人のアバター 自動化ラボ管理人

コードは書けないけれど、n8n と Claude(Claude Code)で仕事とブログを自動化しています。実際に動かしたワークフローを、動作確認日・JSON・つまずいたところまで記録しています。

関連記事

  • localhostのn8nで、Telegram TriggerやGitHub Triggerが使えないときの方法
    2026年10月3日
  • n8nのGoogle連携まとめ:Sheets・Gmail・Calendar・Driveを、1つのOAuthクライアントで使う
    2026年10月3日
  • n8nで通知を送る方法まとめ:Discord・LINE・Telegram・Slackを実際に試して比べた
    2026年10月3日
  • n8nのRespond to Webhookが返事をしない(本文が空になる)ときの対処
    2026年10月3日
  • n8nでTelegramのメッセージが消える・2回記録されるとき(getUpdatesのoffset)の対処
    2026年10月3日
  • n8nのGoogle Sheetsで「Unable to sign without access token」になるときの対処
    2026年10月3日
  • n8nでGoogleにログインすると「エラー 401: invalid_client」になるときの対処
    2026年10月3日
  • ワークフロー全体(Webhook → RSS Read → Pick New → Has New → Telegram / Respond None)
    n8nでブログの新着記事をTelegramに通知するワークフロー(重複なし)
    2026年10月1日

記事の種類

  • Claude 活用
  • n8n リリース情報
  • はじめての n8n
  • エラー・つまずき対処
  • サーバー・WordPress
  • テンプレート配布
  • ワークフローの作り方
  • 運営記録
つなぐサービス
Google スプレッドシートRSSLINESlackGmailDiscordTelegramWebhookn8n API天気 APIGoogle カレンダーGoogle ドライブ
目次
  • プロフィール
  • お問い合わせ
  • プライバシーポリシー

© 自動化ラボ.

目次