-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
Add sample Facebook Messenger webhook server and README #1239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| Setup and testing for the sample Facebook Messenger webhook | ||
|
|
||
| 1) Install and run the server | ||
| cd webhook-server | ||
| npm init -y | ||
| npm install express body-parser | ||
| n # set a verify token and (optionally) PAGE_ACCESS_TOKEN | ||
| set VERIFY_TOKEN=your_verify_token_here # Windows PowerShell: $env:VERIFY_TOKEN = "..." | ||
| set PAGE_ACCESS_TOKEN=your_page_access_token_here | ||
|
|
||
| node server.js | ||
|
|
||
| 2) Expose locally with ngrok (HTTPS required by Facebook) | ||
| ngrok http 3000 | ||
| # copy the https URL (e.g. https://abcd1234.ngrok.io) | ||
|
|
||
| 3) Configure the App (developers.facebook.com) | ||
| - My Apps → Create App (choose Business or appropriate type) | ||
| - App Dashboard → Add Product → Messenger → Set up | ||
| - Under Webhooks: Callback URL = https://<NGROK_HOST>/webhook | ||
| Verify token = the same VERIFY_TOKEN you set | ||
| - Subscribe to fields: messages, messaging_postbacks, messaging_optins, message_deliveries | ||
|
|
||
| 4) Subscribe your Page to the app | ||
| Using Graph API (replace PAGE_ID and PAGE_ACCESS_TOKEN): | ||
| curl -X POST "https://graph.facebook.com/v17.0/PAGE_ID/subscribed_apps?access_token=PAGE_ACCESS_TOKEN" | ||
|
Comment on lines
+25
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: On the official Meta Graph API documentation, Graph API v17.0 is not supported on August 5, 2026. According to the Meta Graph API changelog, v17.0 reached its end-of-life on September 12, 2025 [1][2][3]. For the /{page-id}/subscribed_apps endpoint, you should use the latest supported Graph API version, which is currently v26.0 [1][4][5]. Meta recommends using the most recent version to ensure access to the latest features and security updates [6][7]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== repository files matching webhook-server/README.md =="
git ls-files | grep -F 'webhook-server/README.md' || true
echo
echo "== current webhook-server/README.md lines 18-32 =="
sed -n '18,32p' webhook-server/README.md | cat -n
echo
echo "== Graph API version mentions in webhook-server =="
rg -n "graph\.facebook\.com|subscribed_apps|api-version|api version|Graph API version|v17\.0|v26\.0" webhook-server || trueRepository: f/prompts.chat Length of output: 1422 Use a supported Graph API version in the subscription command.
🤖 Prompt for AI Agents |
||
|
|
||
| 5) Notes | ||
| - In development mode the app only receives events for Pages where the app admin/tester is added. | ||
| - To receive events from real users you must submit for App Review and switch the app live. | ||
| - Implement Send API calls using the PAGE_ACCESS_TOKEN to reply to users. | ||
|
|
||
| If you want, update the server to send replies (I can add Send API example and a .env + npm scripts). | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,46 @@ | ||||||||||||||||||||
| // Simple Facebook Messenger webhook server (Express) | ||||||||||||||||||||
| const express = require('express'); | ||||||||||||||||||||
| const bodyParser = require('body-parser'); | ||||||||||||||||||||
| const app = express(); | ||||||||||||||||||||
| app.use(bodyParser.json()); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const VERIFY_TOKEN = process.env.VERIFY_TOKEN || 'verify_token_here'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Webhook verification handshake | ||||||||||||||||||||
| app.get('/webhook', (req, res) => { | ||||||||||||||||||||
| const mode = req.query['hub.mode']; | ||||||||||||||||||||
| const token = req.query['hub.verify_token']; | ||||||||||||||||||||
| const challenge = req.query['hub.challenge']; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (mode === 'subscribe' && token === VERIFY_TOKEN) { | ||||||||||||||||||||
| return res.status(200).send(challenge); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| return res.sendStatus(403); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Webhook event receiver | ||||||||||||||||||||
| app.post('/webhook', (req, res) => { | ||||||||||||||||||||
| console.log('Webhook event:', JSON.stringify(req.body, null, 2)); | ||||||||||||||||||||
| n // Basic handling: iterate entries and messaging events | ||||||||||||||||||||
| if (req.body.object === 'page') { | ||||||||||||||||||||
|
Comment on lines
+22
to
+25
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Remove the stray Line 24 evaluates - n // Basic handling: iterate entries and messaging events
+ // Basic handling: iterate entries and messaging events📝 Committable suggestion
Suggested change
🧰 Tools🪛 ast-grep (0.45.0)[warning] 22-22: Logging request-derived input unsanitized allows log forging (CRLF injection); strip newlines / encode the value before logging. (log-injection-javascript) 🤖 Prompt for AI Agents |
||||||||||||||||||||
| req.body.entry.forEach(entry => { | ||||||||||||||||||||
| const events = entry.messaging || []; | ||||||||||||||||||||
| events.forEach(event => { | ||||||||||||||||||||
| if (event.message) { | ||||||||||||||||||||
| const senderId = event.sender.id; | ||||||||||||||||||||
| const text = event.message.text; | ||||||||||||||||||||
| console.log(`Message from ${senderId}: ${text}`); | ||||||||||||||||||||
| // TODO: reply using Send API with PAGE_ACCESS_TOKEN | ||||||||||||||||||||
| } else if (event.postback) { | ||||||||||||||||||||
| console.log('Postback:', event.postback); | ||||||||||||||||||||
|
Comment on lines
+29
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '--- file excerpt ---\n'
sed -n '1,80p' webhook-server/server.js
printf '\n--- relevant logging patterns ---\n'
rg -n "console\\.(log|error|warn|info)|senderId|event\\.message|event\\.sender" webhook-server/server.js
printf '\n--- deterministic newline-injection probes ---\n'
node - <<'JS'
const senderId = 'ALICE\n[BOT] MALICE: injected line';
const text = 'ok\n[YOUR MESSAGE] forged entry';
console.log(`Message from ${senderId}: ${text}`);
JSRepository: f/prompts.chat Length of output: 243 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- file excerpt ---'
sed -n '1,80p' webhook-server/server.js
echo
echo '--- relevant logging patterns ---'
rg -n "console\\.|senderId|event\\.message|event\\.sender" webhook-server/server.js || true
echo
echo '--- deterministic newline-injection probes ---'
node - <<'JS'
const senderId = 'ALICE\n[BOT] MALICE: injected line';
const text = 'ok\n[YOUR MESSAGE] forged entry';
console.log(`Message from ${senderId}: ${text}`);
JSRepository: f/prompts.chat Length of output: 2265 Log Injection (CWE-117) Reachability: External · Exploitability: Trivial Encode sender-controlled data before logging.
🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||
| } | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Must respond 200 quickly | ||||||||||||||||||||
| res.sendStatus(200); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const PORT = process.env.PORT || 3000; | ||||||||||||||||||||
| app.listen(PORT, () => console.log(`Webhook server listening on port ${PORT}`)); | ||||||||||||||||||||
|
|
||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use shell-specific environment-variable commands.
Line 9 uses Command Prompt syntax. PowerShell users will not set
PAGE_ACCESS_TOKEN. Show separate commands for Command Prompt, PowerShell, and POSIX shells.🤖 Prompt for AI Agents