Repository navigation
Expand file tree
/
Copy pathworker.js
More file actions
241 lines (223 loc) · 12.7 KB
/
Copy pathworker.js
File metadata and controls
241 lines (223 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
import { handleComments } from './comments.js'
import { handleSuggestions } from './suggestions.js'
export { ShiritoriLobby } from './shiritori-lobby.js'
import { handleHmoji } from './hmojis.js'
import { handleQuicklinks } from './quicklinks.js'
import { handleBlogAdmin, BLOG_ADMIN_PATHS } from './blog-admin.js'
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url)
// sdmp.izie.top is the main address for the Slack datamine pages; the long name redirects to it, keeping the path and query string
// browsers fetch /favicon.ico unasked and refuse a favicon that redirects across origins, so the icon is served in place on both names
if (url.pathname === '/favicon.ico') url.pathname = '/favicon.svg'
const sharedPath = url.pathname === '/favicon.svg' || url.pathname.startsWith('/cdn/')
if (url.hostname === 'slackdatamineplus.izie.top' && !sharedPath) {
return new Response(null, {
status: 302,
headers: { Location: `https://sdmp.izie.top${url.pathname}${url.search}` },
})
}
const quicklinksResponse = handleQuicklinks(url)
if (quicklinksResponse) return quicklinksResponse
if (url.hostname === 'izie.top' && (url.pathname === '/redoc' || url.pathname.startsWith('/redoc/'))) {
const rest = url.pathname.replace(/^\/redoc\/?/, '')
return new Response(null, {
status: 302,
headers: { Location: `https://vs.izie.top/redocumented${rest ? `/${rest}` : ''}${url.search}` },
})
}
if (url.hostname === 'vs.izie.top' && (url.pathname === '/redoc' || url.pathname.startsWith('/redoc/'))) {
const rest = url.pathname.replace(/^\/redoc\/?/, '')
return new Response(null, {
status: 302,
headers: { Location: `/redocumented${rest ? `/${rest}` : ''}${url.search}` },
})
}
if (url.hostname === 'vs.izie.top' && url.pathname.startsWith('/slackapi/')) {
const target = url.pathname.replace(/^\/slackapi/, '/redocumented')
return new Response(null, {
status: 302,
headers: { Location: `${target}${url.search}` },
})
}
if (url.hostname === 'vs.izie.top' && url.pathname.startsWith('/redocumented')) {
return handleRedocumented(request, env, url)
}
if (url.hostname === 'vs.izie.top' && url.pathname === '/dgd/api/ngram') return handleDgdNgram(request, url)
if (url.hostname === 'vs.izie.top' && url.pathname.startsWith('/slack/hmojis/')) {
return handleHmoji(request, env, ctx, url.pathname)
}
// the blog editor and comment moderation (passkey sign-in; see blog-admin.js)
if (url.hostname === 'blog.izie.top' && BLOG_ADMIN_PATHS(url.pathname)) return handleBlogAdmin(request, env, url)
if (url.pathname === '/api/comments') return handleComments(request, env, url)
if (url.hostname === 'vs.izie.top' && url.pathname === '/shatori/api/suggest') return handleSuggestions(request, env)
if (url.hostname === 'vs.izie.top' && url.pathname === '/shatori/ws') {
// one hub holds every lobby and game (see shiritori-lobby.js)
return env.LOBBY.get(env.LOBBY.idFromName('hub')).fetch(request)
}
if (url.pathname.startsWith('/api/graphs/')) {
const response = await fetch(`https://botme.dunkirk.sh${url.pathname}${url.search}`, request)
const headers = new Headers(response.headers)
headers.set('cache-control', 'no-store')
headers.set('access-control-allow-origin', '*')
return new Response(response.body, { status: response.status, headers })
}
if (url.pathname === '/api/leaderboard') {
const response = await fetch('https://botme.dunkirk.sh/api/leaderboard', request)
const headers = new Headers(response.headers)
headers.set('cache-control', 'no-store')
headers.set('access-control-allow-origin', '*')
return new Response(response.body, { status: response.status, headers })
}
const match = url.hostname.match(/^([a-z0-9-]+)\.izie\.top$/)
const prefix = match ? `/__subdomains/${SUBDOMAIN_ALIASES[match[1]] || match[1]}` : null
// shared root assets (fonts, buttons, favicon) are served as-is on every subdomain
const sharedAsset = url.pathname.startsWith('/cdn/') || url.pathname === '/favicon.svg'
if (prefix && !sharedAsset) {
const originalPath = url.pathname
url.pathname = `${prefix}${url.pathname}`
const response = await env.ASSETS.fetch(new Request(url, request))
// subdomains without their own stylesheet (e.g. a bare 404 page) fall back to the root one
if (response.status === 404 && originalPath === '/style.css') return env.ASSETS.fetch(request)
const location = response.headers.get('Location')
if (location?.startsWith(prefix)) {
const headers = new Headers(response.headers)
headers.set('Location', `${location.slice(prefix.length) || '/'}`)
return new Response(response.body, { status: response.status, headers })
}
return response
}
return env.ASSETS.fetch(new Request(url, request))
}
}
// subdomains that serve another subdomain's files (sdmp is the main address for the slackdatamineplus folder; the long hostname redirects to it, see the top of fetch)
const SUBDOMAIN_ALIASES = { sdmp: 'slackdatamineplus' }
// ReDocumented: make the docs easy for agents to fetch.
// - `Accept: text/markdown` (or ?format=md) on the SPA root returns llms.txt instead of a JS shell
// - /redocumented/api/methods/<name> works without an extension (Accept picks .md or .json)
// - /redocumented/<method.name> jumps to that method's entry
// - unknown paths under /redocumented/api/ get a JSON 404 that says where to look
// - everything is CORS-open so browser agents can fetch it
const REDOC_PREFIX = '/__subdomains/vs'
async function handleRedocumented(request, env, url) {
const accept = request.headers.get('Accept') || ''
const wantsMarkdown = url.searchParams.get('format') === 'md'
|| (/text\/(markdown|plain)/.test(accept) && !accept.includes('text/html'))
const wantsJson = url.searchParams.get('format') === 'json'
|| (accept.includes('application/json') && !accept.includes('text/html'))
let path = url.pathname.replace(/\/+$/, '') || '/redocumented'
if (path === '/redocumented' && (wantsMarkdown || wantsJson)) {
path = wantsJson ? '/redocumented/api/index.json' : '/redocumented/llms.txt'
}
const isStaticFile = /\.(txt|json|md|js|css|html|svg|ico|png)$/.test(path)
const method = path.match(/^\/redocumented\/(?:api\/methods\/|m\/)?([A-Za-z][\w-]*(?:\.[\w-]+)+?)(\.json|\.md)?$/)
if (method && (path.startsWith('/redocumented/api/methods/') || !isStaticFile)) {
return redocMethod(request, env, url, method[1], method[2] ? method[2] === '.md' : wantsMarkdown)
}
const target = new URL(url)
target.pathname = `${REDOC_PREFIX}${path === '/redocumented' ? url.pathname : path}`
let response = await env.ASSETS.fetch(new Request(target, request))
if (response.status === 404 && path.startsWith('/redocumented/api/')) response = redocNotFound()
const headers = new Headers(response.headers)
const location = headers.get('Location')
if (location?.startsWith(REDOC_PREFIX)) headers.set('Location', location.slice(REDOC_PREFIX.length) || '/')
headers.set('Access-Control-Allow-Origin', '*')
headers.set('Vary', 'Accept')
if (/\.md$/.test(target.pathname)) headers.set('Content-Type', 'text/markdown; charset=utf-8')
if (/\.txt$/.test(target.pathname)) headers.set('Content-Type', 'text/plain; charset=utf-8')
if ((headers.get('Content-Type') || '').includes('text/html')) {
headers.append('Link', '</redocumented/llms.txt>; rel="alternate"; type="text/plain"; title="llms.txt"')
headers.append('Link', '</redocumented/api/index.json>; rel="alternate"; type="application/json"')
}
return new Response(response.body, { status: response.status, headers })
}
const REDOC_BASE = 'https://vs.izie.top/redocumented'
// keep in sync with is_write_shaped in scripts/build-redocumented-agent.py
const WRITE_VERBS = ['create', 'delete', 'remove', 'set', 'update', 'add', 'post', 'send', 'invite',
'kick', 'archive', 'rename', 'leave', 'join', 'close', 'reset', 'revoke', 'upload',
'edit', 'mark', 'clear', 'disable', 'enable', 'assign', 'approve', 'deny', 'restrict',
'promote', 'demote', 'convert', 'move', 'save', 'unarchive', 'schedule', 'share']
let redocCatalog = null
async function loadRedocCatalog(env, url) {
if (!redocCatalog) {
const res = await env.ASSETS.fetch(new URL(`${REDOC_PREFIX}/redocumented/docs/data/methods.json`, url))
const data = await res.json()
const safety = data.safety_note_for_agents || data.schema?.safety_note_for_agents || ''
redocCatalog = { safety, byName: new Map(data.methods.map((m) => [m.name, m])) }
}
return redocCatalog
}
function redocNotFound(name) {
return Response.json({
ok: false,
error: name ? 'method_not_in_catalog' : 'not_found',
method: name,
hint: 'Method names are case-sensitive (e.g. chat.postMessage). A missing method is unknown to this catalog, not proof it does not exist.',
index: `${REDOC_BASE}/api/index.json`,
grep: `${REDOC_BASE}/api/methods.txt`,
docs: `${REDOC_BASE}/llms.txt`,
}, { status: 404, headers: { 'Access-Control-Allow-Origin': '*' } })
}
async function redocMethod(request, env, url, name, markdown) {
const { safety, byName } = await loadRedocCatalog(env, url)
const found = byName.get(name)
if (!found) return redocNotFound(name)
const verb = name.split('.').pop().toLowerCase()
const m = { ...found, write_shaped: WRITE_VERBS.some((v) => verb.startsWith(v)) }
const headers = { 'Access-Control-Allow-Origin': '*', 'Vary': 'Accept', 'Cache-Control': 'public, max-age=300' }
if (!markdown) {
return Response.json({ ...m, url: `${REDOC_BASE}/api/methods/${name}.json`, safety_note_for_agents: safety }, { headers })
}
return new Response(redocMethodMarkdown(m), { headers: { ...headers, 'Content-Type': 'text/markdown; charset=utf-8' } })
}
function redocMethodMarkdown(m) {
const cell = (v) => String(v ?? '').replace(/\|/g, '\\|').replace(/\n/g, ' ')
const lines = [`# ${m.name}`, '',
`- status: ${m.status}`,
`- verified: ${m.verified}`,
...(m.not_in_client ? [`- not in the current client: ${m.not_in_client_note || 'missing from recent web client builds'}`] : []),
`- tokens: ${m.tokens || 'unknown'}`,
`- write-shaped name: ${m.write_shaped ? 'yes, do not call without a human in the loop' : 'no'}`,
`- source: ${m.source || 'unknown'}`,
`- call: POST https://slack.com/api/${m.name}`, '']
if (m.purpose) lines.push(m.purpose, '')
lines.push('## Params')
if (m.params_known && m.params) {
lines.push('', '| name | required | type | description |', '|---|---|---|---|')
for (const [k, v] of Object.entries(m.params)) {
lines.push(`| \`${k}\` | ${v.required ? 'yes' : 'no'} | ${cell(v.type)} | ${cell(v.desc)} |`)
}
lines.push('', `Source: ${m.params_source}`)
} else if (m.params_observed) {
lines.push('', 'Not a schema. These argument names were seen at call sites in Slack\'s web client; the set is partial and says nothing about which are required.',
'', '| name | type hint | call sites |', '|---|---|---|')
for (const [k, v] of Object.entries(m.params_observed)) lines.push(`| \`${k}\` | ${cell(v.type)} | ${v.sites} |`)
lines.push('', `Source: ${m.params_observed_source}`)
} else {
lines.push('', 'Unknown. Nothing here is guessed; do not invent params for this method.')
}
lines.push('', '## Response')
if (m.response) {
lines.push('', '| field | type |', '|---|---|')
for (const [k, v] of Object.entries(m.response)) lines.push(`| \`${k}\` | ${cell(v)} |`)
}
if (m.response_example) lines.push('', '```json', m.response_example, '```')
if (!m.response_known) lines.push('', 'Unknown.')
else if (m.response_source) lines.push('', `Source: ${m.response_source}`)
lines.push('', '---', `JSON: ${REDOC_BASE}/api/methods/${m.name}.json · Full catalog: ${REDOC_BASE}/llms.txt`, '')
return lines.join('\n')
}
// DeGoogledDict (/dgd): Google Books Ngram sends no CORS headers, so the browser asks us and we relay the JSON.
async function handleDgdNgram(request, url) {
if (request.method !== 'GET') return new Response('[]', { status: 405, headers: { 'content-type': 'application/json' } })
const word = (url.searchParams.get('w') || '').trim()
if (!word || word.length > 40 || /\s/.test(word)) return new Response('[]', { status: 400, headers: { 'content-type': 'application/json' } })
const upstream = await fetch(
`https://books.google.com/ngrams/json?content=${encodeURIComponent(word)}&year_start=1800&year_end=2019&corpus=en-2019&smoothing=3`,
{ cf: { cacheTtl: 604800, cacheEverything: true } },
)
return new Response(upstream.ok ? upstream.body : '[]', {
status: upstream.ok ? 200 : 502,
headers: { 'content-type': 'application/json', 'cache-control': 'public, max-age=86400' },
})
}