Skip to content

Commit 9247828

Browse files
committed
fix: probe discord auth modes with browser headers
1 parent 9a51b3e commit 9247828

2 files changed

Lines changed: 77 additions & 56 deletions

File tree

.github/workflows/discord-test.yml

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,60 +12,72 @@ jobs:
1212
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
1313
DISCORD_CHANNEL_ID: "1483655502251688027"
1414
run: |
15-
python - <<'PY'
16-
import json
17-
import os
18-
import urllib.error
19-
import urllib.request
15+
set -euo pipefail
2016
21-
token = os.environ["DISCORD_TOKEN"]
22-
channel = os.environ["DISCORD_CHANNEL_ID"]
17+
api="https://discord.com/api/v10"
18+
ua='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36'
2319
24-
def auths(token: str):
25-
if token.startswith("Bot ") or token.startswith("Bearer "):
26-
return [("provided", token)]
27-
return [("user", token), ("bot", f"Bot {token}")]
28-
29-
def call(path: str, auth: str, method="GET", body=None):
30-
req = urllib.request.Request(
31-
f"https://discord.com/api/v10{path}",
32-
data=None if body is None else json.dumps(body).encode(),
33-
headers={
34-
"Authorization": auth,
35-
"Content-Type": "application/json",
36-
},
37-
method=method,
38-
)
39-
try:
40-
with urllib.request.urlopen(req) as res:
41-
return res.status, res.read().decode()
42-
except urllib.error.HTTPError as err:
43-
return err.code, err.read().decode()
20+
call() {
21+
local auth="$1"
22+
local path="$2"
23+
local method="${3:-GET}"
24+
local body="${4:-}"
25+
local out bodyf code
26+
bodyf=$(mktemp)
27+
if [ -n "$body" ]; then
28+
code=$(curl -sS --http1.1 -o "$bodyf" -w '%{http_code}' \
29+
-X "$method" "$api$path" \
30+
-H "Authorization: $auth" \
31+
-H 'Accept: application/json' \
32+
-H 'Content-Type: application/json' \
33+
-H "User-Agent: $ua" \
34+
--data "$body")
35+
else
36+
code=$(curl -sS --http1.1 -o "$bodyf" -w '%{http_code}' \
37+
-X "$method" "$api$path" \
38+
-H "Authorization: $auth" \
39+
-H 'Accept: application/json' \
40+
-H 'Content-Type: application/json' \
41+
-H "User-Agent: $ua")
42+
fi
43+
printf '%s\n' "$code"
44+
cat "$bodyf"
45+
rm -f "$bodyf"
46+
}
4447
45-
auth = None
46-
for name, value in auths(token):
47-
status, body = call("/users/@me", value)
48-
print(f"probe {name}: {status}")
49-
print(body[:300])
50-
if status == 200:
51-
auth = value
52-
break
48+
pick() {
49+
local body code auth label
50+
while IFS='|' read -r label auth; do
51+
mapfile -t out < <(call "$auth" /users/@me)
52+
code="${out[0]}"
53+
body="$(printf '%s\n' "${out[@]:1}")"
54+
echo "probe $label: $code"
55+
printf '%s\n' "$body" | head -c 300; echo
56+
if [ "$code" = "200" ]; then
57+
printf '%s' "$auth"
58+
return 0
59+
fi
60+
done <<EOF
61+
user|$DISCORD_TOKEN
62+
bot|Bot $DISCORD_TOKEN
63+
bearer|Bearer $DISCORD_TOKEN
64+
EOF
65+
return 1
66+
}
5367

54-
if not auth:
55-
raise SystemExit("could not authenticate to Discord")
68+
auth="$(pick)" || { echo "could not authenticate to Discord"; exit 1; }
5669

57-
status, body = call(f"/channels/{channel}", auth)
58-
print(f"channel lookup: {status}")
59-
print(body[:500])
60-
if status != 200:
61-
raise SystemExit("could not access target channel")
70+
mapfile -t out < <(call "$auth" "/channels/$DISCORD_CHANNEL_ID")
71+
code="${out[0]}"
72+
body="$(printf '%s\n' "${out[@]:1}")"
73+
echo "channel lookup: $code"
74+
printf '%s\n' "$body" | head -c 500; echo
75+
[ "$code" = "200" ] || { echo "could not access target channel"; exit 1; }
6276

63-
msg = {
64-
"content": "**shuvcode Discord test**\n\n- workflow_dispatch smoke test\n- target channel: 1483655502251688027\n- if you can read this, the posting flow still works"
65-
}
66-
status, body = call(f"/channels/{channel}/messages", auth, method="POST", body=msg)
67-
print(f"post: {status}")
68-
print(body[:500])
69-
if status not in (200, 201):
70-
raise SystemExit("post failed")
71-
PY
77+
msg='{"content":"**shuvcode Discord test**\n\n- workflow_dispatch smoke test\n- target channel: 1483655502251688027\n- if you can read this, the posting flow still works"}'
78+
mapfile -t out < <(call "$auth" "/channels/$DISCORD_CHANNEL_ID/messages" POST "$msg")
79+
code="${out[0]}"
80+
body="$(printf '%s\n' "${out[@]:1}")"
81+
echo "post: $code"
82+
printf '%s\n' "$body" | head -c 500; echo
83+
[[ "$code" = "200" || "$code" = "201" ]] || { echo "post failed"; exit 1; }

script/discord-notify.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
*
66
* Supports posting to any existing Discord text channel or thread by ID.
77
* Auth mode is auto-detected so the same script can work with either a raw
8-
* user token or a standard bot token stored without the `Bot ` prefix.
8+
* user token, a standard bot token stored without the `Bot ` prefix, or a
9+
* bearer token.
910
*
1011
* Inputs:
1112
* - DISCORD_TOKEN required
@@ -18,6 +19,9 @@
1819

1920
const api = "https://discord.com/api/v10"
2021
const max = 2000
22+
const agent =
23+
process.env.DISCORD_USER_AGENT ||
24+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
2125

2226
function trim(text: string) {
2327
return text.length <= max ? text : text.slice(0, max)
@@ -63,7 +67,9 @@ async function req(path: string, init: RequestInit, auth: string) {
6367
...init,
6468
headers: {
6569
Authorization: auth,
70+
Accept: "application/json",
6671
"Content-Type": "application/json",
72+
"User-Agent": agent,
6773
...(init.headers || {}),
6874
},
6975
})
@@ -73,22 +79,25 @@ async function req(path: string, init: RequestInit, auth: string) {
7379

7480
function auths(token: string) {
7581
if (token.startsWith("Bot ") || token.startsWith("Bearer ")) return [token]
76-
return [token, `Bot ${token}`]
82+
return [token, `Bot ${token}`, `Bearer ${token}`]
7783
}
7884

7985
async function pick(token: string) {
8086
for (const auth of auths(token)) {
81-
const mode = auth.startsWith("Bot ") ? "bot" : "user"
87+
const mode = auth.startsWith("Bot ") ? "bot" : auth.startsWith("Bearer ") ? "bearer" : "user"
8288
const out = await req("/users/@me", { method: "GET" }, auth)
8389
console.log(`Discord auth probe (${mode}) -> ${out.res.status}`)
84-
if (!out.res.ok) continue
90+
if (!out.res.ok) {
91+
console.log(out.text.slice(0, 300))
92+
continue
93+
}
8594
const info = JSON.parse(out.text) as { username?: string; discriminator?: string; id?: string; bot?: boolean }
8695
console.log(
8796
`Using Discord auth (${mode}) as ${info.username || "unknown"}${info.discriminator ? `#${info.discriminator}` : ""} (${info.id || "unknown"})`,
8897
)
8998
return auth
9099
}
91-
throw new Error("Could not authenticate with Discord using either raw or bot token style")
100+
throw new Error("Could not authenticate with Discord using user, bot, or bearer token style")
92101
}
93102

94103
async function inspect(id: string, auth: string) {

0 commit comments

Comments
 (0)