Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/registry-format.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ public/registry/plugins/my-plugin/
try/catch は無いので、失敗しうる呼び出しは事前確認で避ける(例: `meta` の
`translatorAvailable` を見てから `notes/translate`)。`Core:type(res) == "error"` 判定は
本家互換のために残してよい([notedeck#1249](https://github.com/notedeck-dev/notedeck/issues/1249))
- `Nd:http` の通信断(DNS 失敗・接続拒否・タイムアウト)は throw ではなく `status: 0` +
`error` の値で返る([notedeck#1294](https://github.com/notedeck-dev/notedeck/issues/1294))。
HTTP の 4xx / 5xx と同じ `status` 分岐に乗せ、`res.status == 0` で「通信できません」を描く。
`Nd:call("http.fetch")` / `Nd:call("vault.fetch")` も同形
- `Nd:call` が `error` 値を返すのは確認ダイアログのキャンセル(`user_cancelled`)だけ。
権限拒否は throw するので、既定プロファイルで許可されていない capability
(`account.write` / `styles.write` / `network.external` 等)を使うなら
Expand Down
12 changes: 6 additions & 6 deletions public/registry/index.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
{
"version": 1,
"updatedAt": "2026-10-28T23:08:30.161Z",
"updatedAt": "2026-10-29T19:40:04.494Z",
"plugins": {
"count": 43,
"updatedAt": "2026-10-28T23:08:30.161Z"
"updatedAt": "2026-10-29T19:40:04.494Z"
},
"themes": {
"count": 77,
"updatedAt": "2026-10-28T23:08:30.161Z"
"updatedAt": "2026-10-29T19:40:04.494Z"
},
"widgets": {
"count": 70,
"updatedAt": "2026-10-28T23:08:30.161Z"
"updatedAt": "2026-10-29T19:40:04.494Z"
},
"skills": {
"count": 44,
"updatedAt": "2026-10-28T23:08:30.161Z"
"updatedAt": "2026-10-29T19:40:04.494Z"
},
"queries": {
"count": 35,
"updatedAt": "2026-10-28T23:08:30.161Z"
"updatedAt": "2026-10-29T19:40:04.494Z"
}
}
6 changes: 3 additions & 3 deletions public/registry/plugins.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-10-28T23:08:30.161Z",
"updatedAt": "2026-10-29T19:40:04.494Z",
"plugins": [
{
"id": "ai-actions",
Expand Down Expand Up @@ -338,7 +338,7 @@
],
"sourceUrl": "https://store.notedeck.io/registry/plugins/url-safety-check/plugin.is",
"apiUrl": "https://store.notedeck.io/registry/plugins/url-safety-check/api.json",
"sha512": "9e7561efeb8441d3bf360d05c85156c205a99572b66eefd12767fb52c19c93a4175e3c3a81c22b79f7a60abcfeb6d81c62c656e3a81feb3f6ffcfe5f7437d46b",
"sha512": "461a8a39058c16cd51c333fd8ce071191ab86b698a3249305d8ec127c693293505fa6586742883373617fdab7017a2b2ab8364ed849f91e78942967dca380c4a",
"createdAt": "2026-07-14T15:09:18+09:00",
"updatedAt": "2026-09-25T18:20:03+09:00",
"authorUrl": "https://github.com/hitalin",
Expand Down Expand Up @@ -1934,7 +1934,7 @@
"apiUrl": "https://store.notedeck.io/registry/plugins/tech-autolink/api.json",
"sha512": "e5e2d778c1b30c7a34bd4de6cc3a34f88b839887794f569f3989e04723bfa1b981c3c50a275e14d0538bde393f88dc9344f4d5f3a44fe1670ab5ab60439c56ec",
"createdAt": "2026-07-31T15:01:56+09:00",
"updatedAt": "2026-10-29T07:57:04+09:00",
"updatedAt": "2026-10-29T08:08:37+09:00",
"authorUrl": "https://github.com/hitalin",
"license": "MIT",
"locales": {
Expand Down
2 changes: 1 addition & 1 deletion public/registry/plugins/url-safety-check/api.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"type":"plugin","data":"/// @ 1.2.1\n\n// URL 安全チェック — ノート内の URL をキー不要の脅威 DB で照合\n//\n// ノートメニューから本文中の URL を抽出し、以下の 3 ソースで照合する。\n// いずれも API キー不要 (Vault 不要、vault.fetch の plugin 開放 #759 に依存しない):\n// - Cloudflare Malware DNS (security.cloudflare-dns.com):\n// 脅威リスト掲載ドメインは 0.0.0.0 に censored されるのを判定に使う\n// - PhishTank (checkurl.phishtank.com): フィッシング URL データベース照合\n// - Sucuri SiteCheck (sitecheck.sucuri.net): ブラックリスト照合 + 総合評価\n//\n// Nd:http は呼び出しごとに確認ダイアログが出る (network.external)。\n// 「今後この操作を確認しない」で以降はスキップされる。\n// AiScript に try/catch は無いため、いずれかのサービスがネットワーク的に\n// 落ちている場合はハンドラごと停止する (HTTP エラーステータスは処理する)。\n\n### {\n\tname: \"URL 安全チェック\"\n\tversion: \"1.0.1\"\n\tauthor: \"@hitalin\"\n\tdescription: \"ノート本文の URL を Cloudflare Malware DNS / PhishTank / Sucuri SiteCheck で照合し、フィッシング・マルウェアの兆候を確認します。チェック時に URL が各サービスに送信されます。API キーは不要です\"\n\tpermissions: [\"network.external\"]\n\tconfig: {\n\t\tcheckSucuri: {\n\t\t\ttype: \"boolean\"\n\t\t\tlabel: \"Sucuri SiteCheck も実行する\"\n\t\t\tdescription: \"ブラックリスト照合と総合評価。未スキャンのドメインは応答に時間がかかることがあります\"\n\t\t\tdefault: true\n\t\t}\n\t\tmaxUrls: {\n\t\t\ttype: \"number\"\n\t\t\tlabel: \"1回にチェックする URL の上限\"\n\t\t\tdefault: 3\n\t\t}\n\t}\n}\n\nlet trailingClosers = [\")\", \")\", \"」\", \"]\", \",\", \"、\", \"。\", \"\\\"\", \">\"]\n\n// 文末で URL に括弧や句読点が続くケース (「(https://...)」等) の末尾記号を剥がす\n@stripTail(token) {\n\tvar body = token\n\tloop {\n\t\tif (body.len == 0) break\n\t\tlet last = body.slice(body.len - 1, body.len)\n\t\tif (!trailingClosers.incl(last)) break\n\t\tbody = body.slice(0, body.len - 1)\n\t}\n\tbody\n}\n\n@extractUrls(text) {\n\tlet normalized = text.replace(\" \", Str:lf).replace(\" \", Str:lf)\n\tvar urls = []\n\teach (let token, normalized.split(Str:lf)) {\n\t\tvar hIdx = token.index_of(\"https://\")\n\t\tif (hIdx == -1) hIdx = token.index_of(\"http://\")\n\t\tif (hIdx != -1) {\n\t\t\tlet url = stripTail(token.slice(hIdx, token.len))\n\t\t\tif (url.index_of(\"://\") != -1 && !urls.incl(url)) {\n\t\t\t\turls.push(url)\n\t\t\t}\n\t\t}\n\t}\n\turls\n}\n\n@extractDomain(url) {\n\tlet idx = url.index_of(\"://\")\n\tif (idx == -1) return null\n\tvar rest = url.slice(idx + 3, url.len)\n\teach (let sep, [\"/\", \"?\", \"#\"]) {\n\t\tlet i = rest.index_of(sep)\n\t\tif (i != -1) rest = rest.slice(0, i)\n\t}\n\tlet atIdx = rest.index_of(\"@\")\n\tif (atIdx != -1) rest = rest.slice(atIdx + 1, rest.len)\n\tlet colonIdx = rest.index_of(\":\")\n\tif (colonIdx != -1) rest = rest.slice(0, colonIdx)\n\tif (rest == \"\") return null\n\trest\n}\n\n// Cloudflare Malware DNS (1.1.1.2 相当の DoH)。脅威リスト掲載ドメインは\n// 0.0.0.0 への censored 応答になる\n@checkCloudflare(domain) {\n\tlet res = Nd:http(`https://security.cloudflare-dns.com/dns-query?name={Uri:encode_component(domain)}&type=A`, {\n\t\tmethod: \"GET\"\n\t\theaders: { \"accept\": \"application/dns-json\" }\n\t})\n\tif (res.status != 200) return `❓ 取得失敗 (HTTP {res.status})`\n\tlet data = Json:parse(res.body)\n\tvar blocked = false\n\tif (data.Answer != null) {\n\t\teach (let a, data.Answer) {\n\t\t\tif (a.data == \"0.0.0.0\") blocked = true\n\t\t}\n\t}\n\tif (blocked) return \"🚫 危険 — 脅威リストでブロックされています\"\n\tif (data.Status == 3) return \"❓ ドメインが存在しません\"\n\t\"✅ 検出なし\"\n}\n\n@checkPhishtank(url) {\n\t// PhishTank は phishtank/ prefix の User-Agent を要求する (それ以外は 403)\n\tlet res = Nd:http(\"https://checkurl.phishtank.com/checkurl/\", {\n\t\tmethod: \"POST\"\n\t\theaders: {\n\t\t\t\"content-type\": \"application/x-www-form-urlencoded\"\n\t\t\t\"user-agent\": \"phishtank/notedeck\"\n\t\t}\n\t\tbody: `url={Uri:encode_component(url)}&format=json`\n\t})\n\tif (res.status != 200) return `❓ 取得失敗 (HTTP {res.status})`\n\tlet data = Json:parse(res.body)\n\tif (data.results == null) return \"❓ 応答を解釈できませんでした\"\n\tif (data.results.in_database == true && data.results.valid == true) {\n\t\treturn \"🚨 危険 — フィッシングサイトとして登録済み\"\n\t}\n\t\"✅ 検出なし\"\n}\n\n@checkSucuri(domain) {\n\tlet res = Nd:http(`https://sitecheck.sucuri.net/api/v3/?scan={Uri:encode_component(domain)}`, {\n\t\tmethod: \"GET\"\n\t\ttimeoutMs: 60000\n\t})\n\tif (res.status != 200) return `❓ 取得失敗 (HTTP {res.status})`\n\tlet data = Json:parse(res.body)\n\tvar parts = []\n\tif (data.ratings != null && data.ratings.total != null) {\n\t\tparts.push(`総合評価 {data.ratings.total.rating}`)\n\t}\n\tif (data.warnings != null) {\n\t\teach (let kv, Obj:kvs(data.warnings)) {\n\t\t\tlet key = kv[0]\n\t\t\tlet n = if (Core:type(kv[1]) == \"arr\") kv[1].len else 1\n\t\t\tif (key == \"blacklist\") {\n\t\t\t\tparts.push(`🚨 ブラックリスト掲載 {n}件`)\n\t\t\t} elif (key == \"scan_failed\") {\n\t\t\t\tparts.push(\"スキャン未完了\")\n\t\t\t} else {\n\t\t\t\tparts.push(`⚠️ 警告 ({key}) {n}件`)\n\t\t\t}\n\t\t}\n\t}\n\tif (parts.len == 0) return \"✅ 警告なし\"\n\tparts.join(\" / \")\n}\n\nPlugin:register_note_action(\"URL の安全性をチェック\", @(note) {\n\tif (note.text == null) {\n\t\tMk:dialog(\"URL 安全チェック\", \"テキストのないノートです\", \"info\")\n\t\treturn null\n\t}\n\tlet urls = extractUrls(note.text)\n\tif (urls.len == 0) {\n\t\tMk:dialog(\"URL 安全チェック\", \"ノートに URL が見つかりませんでした\", \"info\")\n\t\treturn null\n\t}\n\tvar targets = []\n\teach (let url, urls) {\n\t\tif (targets.len < Plugin:config.maxUrls) targets.push(url)\n\t}\n\n\tvar lines = []\n\tvar flaggedDomain = null\n\teach (let url, targets) {\n\t\tlet domain = extractDomain(url)\n\t\tif (domain != null) {\n\t\t\tlines.push(`🔗 {url}`)\n\t\t\tlet cf = checkCloudflare(domain)\n\t\t\tlines.push(`Cloudflare DNS: {cf}`)\n\t\t\tlet pt = checkPhishtank(url)\n\t\t\tlines.push(`PhishTank: {pt}`)\n\t\t\tif (Plugin:config.checkSucuri) {\n\t\t\t\tlines.push(`Sucuri: {checkSucuri(domain)}`)\n\t\t\t}\n\t\t\tif (cf.index_of(\"🚫\") != -1 || pt.index_of(\"🚨\") != -1) {\n\t\t\t\tif (flaggedDomain == null) flaggedDomain = domain\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\t}\n\tif (urls.len > targets.len) {\n\t\tlines.push(`(URL {urls.len}件のうち先頭{targets.len}件をチェックしました)`)\n\t}\n\n\tlet dtype = if (flaggedDomain != null) \"warning\" else \"info\"\n\tMk:dialog(\"URL 安全チェック結果\", lines.join(Str:lf), dtype)\n\n\tif (flaggedDomain != null) {\n\t\tif (Mk:confirm(\"URL 安全チェック\", `危険判定が出ました。urlscan.io で {flaggedDomain} を詳しく調べますか?`)) {\n\t\t\tPlugin:open_url(`https://urlscan.io/domain/{flaggedDomain}`)\n\t\t}\n\t}\n})\n"}
{"type":"plugin","data":"/// @ 1.2.1\n\n// URL 安全チェック — ノート内の URL をキー不要の脅威 DB で照合\n//\n// ノートメニューから本文中の URL を抽出し、以下の 3 ソースで照合する。\n// いずれも API キー不要 (Vault 不要、vault.fetch の plugin 開放 #759 に依存しない):\n// - Cloudflare Malware DNS (security.cloudflare-dns.com):\n// 脅威リスト掲載ドメインは 0.0.0.0 に censored されるのを判定に使う\n// - PhishTank (checkurl.phishtank.com): フィッシング URL データベース照合\n// - Sucuri SiteCheck (sitecheck.sucuri.net): ブラックリスト照合 + 総合評価\n//\n// Nd:http は呼び出しごとに確認ダイアログが出る (network.external)。\n// 「今後この操作を確認しない」で以降はスキップされる。\n// AiScript に try/catch は無いため、いずれかのサービスがネットワーク的に\n// 落ちている場合はハンドラごと停止する (HTTP エラーステータスは処理する)。\n\n### {\n\tname: \"URL 安全チェック\"\n\tversion: \"1.0.1\"\n\tauthor: \"@hitalin\"\n\tdescription: \"ノート本文の URL を Cloudflare Malware DNS / PhishTank / Sucuri SiteCheck で照合し、フィッシング・マルウェアの兆候を確認します。チェック時に URL が各サービスに送信されます。API キーは不要です\"\n\tpermissions: [\"network.external\"]\n\tconfig: {\n\t\tcheckSucuri: {\n\t\t\ttype: \"boolean\"\n\t\t\tlabel: \"Sucuri SiteCheck も実行する\"\n\t\t\tdescription: \"ブラックリスト照合と総合評価。未スキャンのドメインは応答に時間がかかることがあります\"\n\t\t\tdefault: true\n\t\t}\n\t\tmaxUrls: {\n\t\t\ttype: \"number\"\n\t\t\tlabel: \"1回にチェックする URL の上限\"\n\t\t\tdefault: 3\n\t\t}\n\t}\n}\n\nlet trailingClosers = [\")\", \")\", \"」\", \"]\", \",\", \"、\", \"。\", \"\\\"\", \">\"]\n\n// 文末で URL に括弧や句読点が続くケース (「(https://...)」等) の末尾記号を剥がす\n/// Nd:http の失敗理由。status 0 は通信断 (DNS 失敗・接続拒否・タイムアウト) で、\n/// NoteDeck は throw せず値で返す (notedeck#1294)\n@httpLabel(st) { if (st == 0) { \"通信できません\" } else { `HTTP {st}` } }\n\n@stripTail(token) {\n\tvar body = token\n\tloop {\n\t\tif (body.len == 0) break\n\t\tlet last = body.slice(body.len - 1, body.len)\n\t\tif (!trailingClosers.incl(last)) break\n\t\tbody = body.slice(0, body.len - 1)\n\t}\n\tbody\n}\n\n@extractUrls(text) {\n\tlet normalized = text.replace(\" \", Str:lf).replace(\" \", Str:lf)\n\tvar urls = []\n\teach (let token, normalized.split(Str:lf)) {\n\t\tvar hIdx = token.index_of(\"https://\")\n\t\tif (hIdx == -1) hIdx = token.index_of(\"http://\")\n\t\tif (hIdx != -1) {\n\t\t\tlet url = stripTail(token.slice(hIdx, token.len))\n\t\t\tif (url.index_of(\"://\") != -1 && !urls.incl(url)) {\n\t\t\t\turls.push(url)\n\t\t\t}\n\t\t}\n\t}\n\turls\n}\n\n@extractDomain(url) {\n\tlet idx = url.index_of(\"://\")\n\tif (idx == -1) return null\n\tvar rest = url.slice(idx + 3, url.len)\n\teach (let sep, [\"/\", \"?\", \"#\"]) {\n\t\tlet i = rest.index_of(sep)\n\t\tif (i != -1) rest = rest.slice(0, i)\n\t}\n\tlet atIdx = rest.index_of(\"@\")\n\tif (atIdx != -1) rest = rest.slice(atIdx + 1, rest.len)\n\tlet colonIdx = rest.index_of(\":\")\n\tif (colonIdx != -1) rest = rest.slice(0, colonIdx)\n\tif (rest == \"\") return null\n\trest\n}\n\n// Cloudflare Malware DNS (1.1.1.2 相当の DoH)。脅威リスト掲載ドメインは\n// 0.0.0.0 への censored 応答になる\n@checkCloudflare(domain) {\n\tlet res = Nd:http(`https://security.cloudflare-dns.com/dns-query?name={Uri:encode_component(domain)}&type=A`, {\n\t\tmethod: \"GET\"\n\t\theaders: { \"accept\": \"application/dns-json\" }\n\t})\n\tif (res.status != 200) return `❓ 取得失敗 ({httpLabel(res.status)})`\n\tlet data = Json:parse(res.body)\n\tvar blocked = false\n\tif (data.Answer != null) {\n\t\teach (let a, data.Answer) {\n\t\t\tif (a.data == \"0.0.0.0\") blocked = true\n\t\t}\n\t}\n\tif (blocked) return \"🚫 危険 — 脅威リストでブロックされています\"\n\tif (data.Status == 3) return \"❓ ドメインが存在しません\"\n\t\"✅ 検出なし\"\n}\n\n@checkPhishtank(url) {\n\t// PhishTank は phishtank/ prefix の User-Agent を要求する (それ以外は 403)\n\tlet res = Nd:http(\"https://checkurl.phishtank.com/checkurl/\", {\n\t\tmethod: \"POST\"\n\t\theaders: {\n\t\t\t\"content-type\": \"application/x-www-form-urlencoded\"\n\t\t\t\"user-agent\": \"phishtank/notedeck\"\n\t\t}\n\t\tbody: `url={Uri:encode_component(url)}&format=json`\n\t})\n\tif (res.status != 200) return `❓ 取得失敗 ({httpLabel(res.status)})`\n\tlet data = Json:parse(res.body)\n\tif (data.results == null) return \"❓ 応答を解釈できませんでした\"\n\tif (data.results.in_database == true && data.results.valid == true) {\n\t\treturn \"🚨 危険 — フィッシングサイトとして登録済み\"\n\t}\n\t\"✅ 検出なし\"\n}\n\n@checkSucuri(domain) {\n\tlet res = Nd:http(`https://sitecheck.sucuri.net/api/v3/?scan={Uri:encode_component(domain)}`, {\n\t\tmethod: \"GET\"\n\t\ttimeoutMs: 60000\n\t})\n\tif (res.status != 200) return `❓ 取得失敗 ({httpLabel(res.status)})`\n\tlet data = Json:parse(res.body)\n\tvar parts = []\n\tif (data.ratings != null && data.ratings.total != null) {\n\t\tparts.push(`総合評価 {data.ratings.total.rating}`)\n\t}\n\tif (data.warnings != null) {\n\t\teach (let kv, Obj:kvs(data.warnings)) {\n\t\t\tlet key = kv[0]\n\t\t\tlet n = if (Core:type(kv[1]) == \"arr\") kv[1].len else 1\n\t\t\tif (key == \"blacklist\") {\n\t\t\t\tparts.push(`🚨 ブラックリスト掲載 {n}件`)\n\t\t\t} elif (key == \"scan_failed\") {\n\t\t\t\tparts.push(\"スキャン未完了\")\n\t\t\t} else {\n\t\t\t\tparts.push(`⚠️ 警告 ({key}) {n}件`)\n\t\t\t}\n\t\t}\n\t}\n\tif (parts.len == 0) return \"✅ 警告なし\"\n\tparts.join(\" / \")\n}\n\nPlugin:register_note_action(\"URL の安全性をチェック\", @(note) {\n\tif (note.text == null) {\n\t\tMk:dialog(\"URL 安全チェック\", \"テキストのないノートです\", \"info\")\n\t\treturn null\n\t}\n\tlet urls = extractUrls(note.text)\n\tif (urls.len == 0) {\n\t\tMk:dialog(\"URL 安全チェック\", \"ノートに URL が見つかりませんでした\", \"info\")\n\t\treturn null\n\t}\n\tvar targets = []\n\teach (let url, urls) {\n\t\tif (targets.len < Plugin:config.maxUrls) targets.push(url)\n\t}\n\n\tvar lines = []\n\tvar flaggedDomain = null\n\teach (let url, targets) {\n\t\tlet domain = extractDomain(url)\n\t\tif (domain != null) {\n\t\t\tlines.push(`🔗 {url}`)\n\t\t\tlet cf = checkCloudflare(domain)\n\t\t\tlines.push(`Cloudflare DNS: {cf}`)\n\t\t\tlet pt = checkPhishtank(url)\n\t\t\tlines.push(`PhishTank: {pt}`)\n\t\t\tif (Plugin:config.checkSucuri) {\n\t\t\t\tlines.push(`Sucuri: {checkSucuri(domain)}`)\n\t\t\t}\n\t\t\tif (cf.index_of(\"🚫\") != -1 || pt.index_of(\"🚨\") != -1) {\n\t\t\t\tif (flaggedDomain == null) flaggedDomain = domain\n\t\t\t}\n\t\t\tlines.push(\"\")\n\t\t}\n\t}\n\tif (urls.len > targets.len) {\n\t\tlines.push(`(URL {urls.len}件のうち先頭{targets.len}件をチェックしました)`)\n\t}\n\n\tlet dtype = if (flaggedDomain != null) \"warning\" else \"info\"\n\tMk:dialog(\"URL 安全チェック結果\", lines.join(Str:lf), dtype)\n\n\tif (flaggedDomain != null) {\n\t\tif (Mk:confirm(\"URL 安全チェック\", `危険判定が出ました。urlscan.io で {flaggedDomain} を詳しく調べますか?`)) {\n\t\t\tPlugin:open_url(`https://urlscan.io/domain/{flaggedDomain}`)\n\t\t}\n\t}\n})\n"}
10 changes: 7 additions & 3 deletions public/registry/plugins/url-safety-check/plugin.is
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
let trailingClosers = [")", ")", "」", "]", ",", "、", "。", "\"", ">"]

// 文末で URL に括弧や句読点が続くケース (「(https://...)」等) の末尾記号を剥がす
/// Nd:http の失敗理由。status 0 は通信断 (DNS 失敗・接続拒否・タイムアウト) で、
/// NoteDeck は throw せず値で返す (notedeck#1294)
@httpLabel(st) { if (st == 0) { "通信できません" } else { `HTTP {st}` } }

@stripTail(token) {
var body = token
loop {
Expand Down Expand Up @@ -88,7 +92,7 @@ let trailingClosers = [")", ")", "」", "]", ",", "、", "。", "\"", ">"]
method: "GET"
headers: { "accept": "application/dns-json" }
})
if (res.status != 200) return `❓ 取得失敗 (HTTP {res.status})`
if (res.status != 200) return `❓ 取得失敗 ({httpLabel(res.status)})`
let data = Json:parse(res.body)
var blocked = false
if (data.Answer != null) {
Expand All @@ -111,7 +115,7 @@ let trailingClosers = [")", ")", "」", "]", ",", "、", "。", "\"", ">"]
}
body: `url={Uri:encode_component(url)}&format=json`
})
if (res.status != 200) return `❓ 取得失敗 (HTTP {res.status})`
if (res.status != 200) return `❓ 取得失敗 ({httpLabel(res.status)})`
let data = Json:parse(res.body)
if (data.results == null) return "❓ 応答を解釈できませんでした"
if (data.results.in_database == true && data.results.valid == true) {
Expand All @@ -125,7 +129,7 @@ let trailingClosers = [")", ")", "」", "]", ",", "、", "。", "\"", ">"]
method: "GET"
timeoutMs: 60000
})
if (res.status != 200) return `❓ 取得失敗 (HTTP {res.status})`
if (res.status != 200) return `❓ 取得失敗 ({httpLabel(res.status)})`
let data = Json:parse(res.body)
var parts = []
if (data.ratings != null && data.ratings.total != null) {
Expand Down
2 changes: 1 addition & 1 deletion public/registry/queries.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updatedAt": "2026-10-28T23:08:30.161Z",
"updatedAt": "2026-10-29T19:40:04.494Z",
"queries": [
{
"id": "bot-only",
Expand Down
Loading
Loading