Discord Companion Bot on Windows
This guide sets up the Discord companion bot on a Windows machine using PowerShell: Windows Server 2016 or later, or Windows 10 or 11. The bot is what carries your Discord replies back to visitors. Visitor messages reach Discord without it.
It picks up after Steps 1 to 4 of the Discord Integration Setup Guide, so you have a Discord bot token, a channel ID, and the Discord tab saved in WordPress. Every command below goes in Windows PowerShell, which every Windows Server and Windows desktop includes.
What you need
- Node.js 18 or later. Download the LTS Windows installer (
.msi) from nodejs.org and install it with the default options. Open a new PowerShell window afterwards and check it withnode --version. - Four values, all shown on the plugin’s Discord tab:
- the Bot Token you pasted there,
- the Channel ID,
- the WordPress Endpoint URL and the Webhook Secret, both under Bot Connection Info.
Step 1: Copy the bot out of your WordPress folder
The bot ships inside the plugin at wp-content\plugins\super-speedy-chat\bot. Run it from its own folder outside your website, for two reasons: its .env file holds your bot token and webhook secret, which must never sit anywhere the web server can serve, and a plugin update replaces the whole plugin folder, which would delete your settings.
Change the first path to wherever your WordPress lives (for example C:\inetpub\wwwroot on IIS or C:\xampp\htdocs on XAMPP):
New-Item -ItemType Directory -Path C:\ssc-discord-bot
Copy-Item -Path "C:\inetpub\wwwroot\wp-content\plugins\super-speedy-chat\bot\*" -Destination C:\ssc-discord-bot
Set-Location C:\ssc-discord-bot
Get-ChildItem -Force
You should see discord-bot.js, package.json and env-example.
Step 2: Install the bot’s dependencies
npm.cmd install
Typing plain npm install on a default Windows setup fails with “npm.ps1 cannot be loaded because running scripts is disabled on this system”. That is PowerShell’s execution policy blocking npm’s PowerShell wrapper, not a problem with the bot. npm.cmd install runs the same npm without the wrapper and needs no policy change.
Step 3: Create your .env file
The bot reads its settings from a file called exactly .env in its folder. Rename the template:
Rename-Item env-example .env
If your bot folder has no env-example file, create .env by pasting this whole block into PowerShell in one go:
@"
# Discord Bot Token (from Discord Developer Portal > Bot > Token)
DISCORD_BOT_TOKEN=your_discord_bot_token_here
# Discord Channel ID (right-click channel > Copy Channel ID)
DISCORD_CHANNEL_ID=your_channel_id_here
# WordPress REST endpoint URL (from Super Speedy Chat > Discord > Bot Connection Info)
WP_ENDPOINT_URL=https://your-site.com/wp-json/ssc/v1/discord/incoming
# Webhook Secret (from Super Speedy Chat > Discord > Bot Connection Info)
WP_WEBHOOK_SECRET=your_webhook_secret_here
"@ | Set-Content .env
Then open it and replace the four placeholder values with yours:
notepad .env
Save and close Notepad. Each line is NAME=value with nothing else around the value: no quotes, no spaces.
Do not create or edit .env with echo ... > .env. In Windows PowerShell that writes the file as UTF-16, which the bot cannot read, and it then reports DISCORD_BOT_TOKEN is not configured in .env even though the token is there. Use Notepad or the Set-Content block above.
Step 4: Check the bot can reach WordPress
Before starting the bot, confirm this machine can reach your site and that the secret matches. This reads both values straight from your .env:
$cfg = Get-Content .env | Where-Object { $_ -match '^\s*[A-Z_]+\s*=' } | Out-String | ConvertFrom-StringData
curl.exe -s -X POST -H "Content-Type: application/json" -H "X-SSC-Secret: $($cfg.WP_WEBHOOK_SECRET)" -d '{}' $cfg.WP_ENDPOINT_URL
| Reply | Meaning |
|---|---|
{"code":"missing_params",...} | Working. The bot can reach WordPress and the secret is right (the empty test message is rejected, as it should be). |
{"code":"unauthorized",...} | The secret in .env does not match Webhook Secret on the Discord tab. |
{"code":"rest_no_route",...} | The URL is wrong. Copy WordPress Endpoint URL from the Discord tab again. |
An HTML page, or Could not resolve host | The URL is wrong, or a firewall or security plugin is blocking the request. |
Type curl.exe, not curl: in Windows PowerShell, curl is a different command with different options. curl.exe is part of Windows 10 (version 1803 or later), Windows 11 and Windows Server 2019 or later; on Windows Server 2016, skip this check and go straight to Step 5.
Step 5: Start the bot
node discord-bot.js
A working bot prints:
[SSC Bot] Logged in as YourBot#1234
[SSC Bot] Monitoring channel: 123456789012345678
[SSC Bot] Relaying to: https://your-site.com/wp-json/ssc/v1/discord/incoming
With the bot running, click Test Connection on the Discord tab in WordPress. Check 10, “Companion bot answers”, passes within a few seconds and the bot logs Answered a Test Connection from WordPress. For what every check means, see Testing the connection.
Reply in one of the chat threads in Discord and the bot logs Message relayed successfully, and the visitor sees your reply in the chat. Press Ctrl+C to stop the bot before the next step.
Step 6: Keep it running
The bot has to run all the time, restart itself if it ever stops, and start again when the server reboots. Two pieces do that.
A restart loop. This creates start-bot.cmd in the bot folder. It runs the bot, writes everything the bot prints to bot.log, and starts it again 10 seconds after it stops:
@"
@echo off
cd /d "%~dp0"
:loop
echo [%date% %time%] Starting bot>> bot.log
node discord-bot.js >> bot.log 2>&1
echo [%date% %time%] Bot stopped, restarting in 10 seconds>> bot.log
ping -n 11 127.0.0.1 > nul
goto loop
"@ | Set-Content C:\ssc-discord-bot\start-bot.cmd
A scheduled task that starts it at boot. Open PowerShell as Administrator (right-click PowerShell, Run as administrator) and run:
$action = New-ScheduledTaskAction -Execute 'C:\ssc-discord-bot\start-bot.cmd' -WorkingDirectory 'C:\ssc-discord-bot'
$trigger = New-ScheduledTaskTrigger -AtStartup
$settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit ([TimeSpan]::Zero) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
Register-ScheduledTask -TaskName 'Super Speedy Chat Discord Bot' -Action $action -Trigger $trigger -Settings $settings -User 'SYSTEM' -RunLevel Highest
Start-ScheduledTask -TaskName 'Super Speedy Chat Discord Bot'
The task runs in the background with no window, whether or not anyone is logged in. Check the bot started:
Get-Content C:\ssc-discord-bot\bot.log -Tail 20
Keep -ExecutionTimeLimit ([TimeSpan]::Zero). A scheduled task’s default time limit is 72 hours, after which Windows stops it, and Discord replies silently stop reaching visitors three days after every reboot.
Why a restart loop and not the task’s own “restart on failure” setting? That setting only covers a task that fails to start. When the bot starts and later exits, Task Scheduler treats the task as finished and leaves it stopped.
Lock down the .env file so only the bot (running as SYSTEM) and administrators can read your token. From the same Administrator PowerShell:
icacls C:\ssc-discord-bot\.env /inheritance:r /grant:r "SYSTEM:F" "Administrators:F"
Day-to-day commands
| Task | Command |
|---|---|
| Watch the log live | Get-Content C:\ssc-discord-bot\bot.log -Tail 20 -Wait |
| Stop the bot | Stop-ScheduledTask -TaskName 'Super Speedy Chat Discord Bot' |
| Start the bot | Start-ScheduledTask -TaskName 'Super Speedy Chat Discord Bot' |
| Is it running? | Get-ScheduledTask -TaskName 'Super Speedy Chat Discord Bot' (State shows Running) |
Updating the bot
When a plugin update changes the bot, stop it, copy the new files over (your .env, start-bot.cmd and bot.log are left alone), reinstall and start it again, from an Administrator PowerShell:
Stop-ScheduledTask -TaskName 'Super Speedy Chat Discord Bot'
Copy-Item -Path "C:\inetpub\wwwroot\wp-content\plugins\super-speedy-chat\bot\discord-bot.js", "C:\inetpub\wwwroot\wp-content\plugins\super-speedy-chat\bot\package.json" -Destination C:\ssc-discord-bot
Set-Location C:\ssc-discord-bot
npm.cmd install
Start-ScheduledTask -TaskName 'Super Speedy Chat Discord Bot'
Then run Test Connection on the Discord tab: check 10 passing confirms the updated bot is running.
Troubleshooting
| What you see | Cause and fix |
|---|---|
npm.ps1 cannot be loaded because running scripts is disabled | Use npm.cmd install (Step 2). |
ERROR: .env file not found | The file is not named exactly .env. Run Get-ChildItem -Force in the bot folder: a name like .env.txt means Notepad or Explorer added an extension. Fix it with Rename-Item .env.txt .env. |
ERROR: DISCORD_BOT_TOKEN is not configured in .env | A value still has its your_... placeholder, or the file was written with > and is UTF-16. Recreate it with Step 3. |
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING) straight after another error | Node crashing on its way out after the real error, which is the line above it. Fix that error. Copying in the discord-bot.js from the latest plugin version removes the crash itself. |
Failed to login: An invalid token was provided | The token is wrong or was reset. Reset it in the Discord Developer Portal and paste the new one into both .env and the WordPress Discord tab. |
| Test Connection check 10: no answer from the companion bot | The scheduled task is not running, DISCORD_CHANNEL_ID is a different channel, the bot cannot reach WP_ENDPOINT_URL (Step 4), or its discord-bot.js is older than 1.1.0. Follow Updating the bot. |
WordPress returned 401 in the log | Webhook secret mismatch. Recopy it from the Discord tab and restart the bot. |
WordPress returned 404 in the log | The reply was in a thread the integration did not create. Reply inside the thread the chat opened. |
'node' is not recognized | Node.js is not installed, or PowerShell was open before it was installed. Open a new PowerShell window. |
| Bot runs but replies do nothing, no log lines | Message Content Intent is off, or DISCORD_CHANNEL_ID is not the channel the threads are in. |

Comments