// ==UserScript== // @name ChatGPT Team 长链接生成器(小红书前后网络) // @namespace https://chatgpt.com/ // @version 1.0.5 // @description 在 chatgpt.com 任意页面生成 ChatGPT Team hosted checkout 长链接,默认澳洲 firstfocus,支持优惠码自动匹配国家/币种、自定义优惠码按钮、手动填入 /api/auth/session 内容、居中方形可拖拽窗口。 // @author 小红书前后网络 // @match https://chatgpt.com/* // @match https://www.chatgpt.com/* // @include /^https:\/\/chatgpt\.com(?:\/.*)?$/ // @include /^https:\/\/www\.chatgpt\.com(?:\/.*)?$/ // @run-at document-start // @grant GM_addStyle // @grant GM_setClipboard // ==/UserScript== (function () { 'use strict'; const VERSION = '1.0.5'; const ROOT_ID = 'tm-team-link-root'; const LAUNCHER_ID = 'tm-team-link-launcher'; const MASK_ID = 'tm-team-link-mask'; const PANEL_ID = 'tm-team-link-panel'; const HEADER_ID = 'tm-team-link-header'; const SESSION_URL = 'https://chatgpt.com/api/auth/session'; const CHECKOUT_URL = 'https://chatgpt.com/backend-api/payments/checkout'; const COUPONS = [ { group: '澳洲优惠码', label: '澳洲 · firstfocus(默认)', promoCode: 'firstfocus', country: 'AU', currency: 'AUD' }, { group: '美国优惠码', label: '美国 · talentgeniusus', promoCode: 'talentgeniusus', country: 'US', currency: 'USD' }, { group: '美国优惠码', label: '美国 · thealloynetwork', promoCode: 'thealloynetwork', country: 'US', currency: 'USD' }, { group: '美国优惠码', label: '美国 · alongsideus', promoCode: 'alongsideus', country: 'US', currency: 'USD' }, { group: '美国优惠码', label: '美国 · monicaius', promoCode: 'monicaius', country: 'US', currency: 'USD' }, { group: '英国优惠码', label: '英国 · aibuildgroupgb', promoCode: 'aibuildgroupgb', country: 'GB', currency: 'GBP' }, { group: '英国优惠码', label: '英国 · geccogb', promoCode: 'geccogb', country: 'GB', currency: 'GBP' }, { group: '英国优惠码', label: '英国 · codestonegb', promoCode: 'codestonegb', country: 'GB', currency: 'GBP' }, { group: '肯尼亚优惠码', label: '肯尼亚 · wildmangoke', promoCode: 'wildmangoke', country: 'KE', currency: 'USD' }, ]; const state = { lastUrl: '', lastData: null, lastPayload: null, dragged: false, }; function addStyle(css) { if (typeof GM_addStyle === 'function') { GM_addStyle(css); return; } const style = document.createElement('style'); style.textContent = css; document.documentElement.appendChild(style); } addStyle(` #${LAUNCHER_ID} { position: fixed !important; right: 22px !important; bottom: 22px !important; z-index: 2147483644 !important; border: 0 !important; border-radius: 999px !important; padding: 13px 18px !important; background: #111827 !important; color: #fff !important; font: 600 14px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important; box-shadow: 0 16px 45px rgba(0,0,0,.24) !important; cursor: pointer !important; user-select: none !important; } #${LAUNCHER_ID}:hover { filter: brightness(1.08) !important; } #${MASK_ID} { display: none; position: fixed; inset: 0; z-index: 2147483645; background: rgba(17, 24, 39, .36); backdrop-filter: blur(2px); } #${PANEL_ID} { display: none; position: fixed; z-index: 2147483646; width: min(720px, calc(100vw - 24px)); height: min(720px, calc(100vh - 24px)); left: 50%; top: 50%; transform: translate(-50%, -50%); overflow: hidden; box-sizing: border-box; background: #fff; color: #111827; border: 1px solid #d9d9e3; border-radius: 20px; box-shadow: 0 26px 90px rgba(0, 0, 0, .26); font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; } #${PANEL_ID} * { box-sizing: border-box; } #${HEADER_ID} { height: 58px; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 0 16px; border-bottom: 1px solid #ececf1; background: #fff; cursor: move; user-select: none; } .tm-team-title { display: flex; flex-direction: column; gap: 2px; min-width: 0; } .tm-team-title strong { font-size: 17px; line-height: 1.1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .tm-team-title span { font-size: 12px; color: #6b7280; } .tm-team-header-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; } .tm-icon-btn { width: 34px; height: 34px; border: 0; border-radius: 10px; background: #f3f4f6; color: #111827; cursor: pointer; font-size: 18px; line-height: 1; } .tm-icon-btn:hover { background: #e5e7eb; } .tm-team-body { height: calc(100% - 128px); overflow: auto; padding: 16px; background: #fff; } .tm-team-footer { height: 70px; display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-top: 1px solid #ececf1; background: #fff; } .tm-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; } .tm-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; } .tm-field-full { grid-column: 1 / -1; } .tm-label { font-weight: 700; color: #1f2937; } .tm-hint { color: #6b7280; font-size: 12px; } .tm-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } #${PANEL_ID} input, #${PANEL_ID} select, #${PANEL_ID} textarea { width: 100%; min-height: 44px; border: 1px solid #d1d5db; border-radius: 12px; padding: 10px 12px; outline: none; background: #fff; color: #111827; font: 14px/1.35 -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; } #${PANEL_ID} textarea { min-height: 92px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; font-size: 12.5px; } #${PANEL_ID} input:focus, #${PANEL_ID} select:focus, #${PANEL_ID} textarea:focus { border-color: #111827; box-shadow: 0 0 0 3px rgba(17, 24, 39, .10); } .tm-card, #${PANEL_ID} details { border: 1px solid #ececf1; border-radius: 14px; background: #fafafa; padding: 12px; margin: 12px 0; } #${PANEL_ID} details summary { cursor: pointer; font-weight: 800; color: #1f2937; user-select: none; } #${PANEL_ID} details .tm-details-inner { padding-top: 12px; } .tm-status { border: 1px solid #e5e7eb; border-radius: 12px; background: #f9fafb; padding: 10px 12px; color: #374151; margin-top: 10px; min-height: 40px; white-space: pre-wrap; word-break: break-word; } .tm-status.ok { border-color: #bbf7d0; background: #f0fdf4; color: #166534; } .tm-status.err { border-color: #fecaca; background: #fef2f2; color: #991b1b; } .tm-status.warn { border-color: #fde68a; background: #fffbeb; color: #92400e; } .tm-btn { min-height: 44px; border: 0; border-radius: 12px; padding: 10px 14px; cursor: pointer; font-weight: 800; white-space: nowrap; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; } .tm-primary { background: #111827; color: #fff; } .tm-secondary { background: #f3f4f6; color: #111827; } .tm-danger { background: #b91c1c; color: #fff; } .tm-btn:disabled { opacity: .48; cursor: not-allowed; } .tm-table-wrap { overflow: auto; max-height: 220px; border: 1px solid #e5e7eb; border-radius: 12px; background: #fff; } .tm-table { width: 100%; border-collapse: collapse; min-width: 520px; } .tm-table th, .tm-table td { padding: 9px 10px; border-bottom: 1px solid #f1f5f9; text-align: left; font-size: 12px; } .tm-table th { position: sticky; top: 0; background: #f9fafb; color: #374151; } .tm-table code { font-size: 12px; } @media (max-width: 680px) { #${PANEL_ID} { width: calc(100vw - 16px); height: calc(100vh - 16px); border-radius: 16px; } .tm-grid { grid-template-columns: 1fr; } .tm-team-footer { overflow-x: auto; } #${LAUNCHER_ID} { right: 14px !important; bottom: 14px !important; } } `); function waitForBody(cb) { if (document.body) { cb(); return; } const timer = setInterval(() => { if (document.body) { clearInterval(timer); cb(); } }, 80); } function escapeHtml(value) { return String(value ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function getEl(id) { return document.getElementById(id); } function getCouponByCode(code) { return COUPONS.find((x) => x.promoCode === code) || COUPONS[0]; } function couponOptionsHtml() { const groups = [...new Set(COUPONS.map((x) => x.group))]; return groups.map((group) => { const options = COUPONS .filter((x) => x.group === group) .map((x) => ``) .join(''); return `${options}`; }).join(''); } function couponTableHtml() { return COUPONS.map((x) => ` ${escapeHtml(x.group.replace('优惠码', ''))} ${escapeHtml(x.promoCode)} ${escapeHtml(x.country)} ${escapeHtml(x.currency)} `).join(''); } function ensureUi() { if (getEl(ROOT_ID) && getEl(LAUNCHER_ID) && getEl(PANEL_ID)) return; const oldRoot = getEl(ROOT_ID); if (oldRoot) oldRoot.remove(); const root = document.createElement('div'); root.id = ROOT_ID; root.innerHTML = `
生成 Team 长链接 v${escapeHtml(VERSION)} · 居中方形窗口 · 支持拖拽和手动 Session
自定义优惠码
点击按钮后会写入“高级设置”的覆盖优惠码/国家/币种;不填国家币种时,默认沿用当前选中优惠码的国家币种。
高级设置 / 国家币种手动覆盖

留空时自动按优惠码匹配国家和币种。肯尼亚默认使用 KE / USD,可在这里手动改币种。

Session Token 来源 / 支持手动填入
查看优惠码对照表
${couponTableHtml()}
国家/地区优惠码countrycurrency
准备就绪。选择优惠码后会自动匹配国家和币种。

提示:生成后请在 checkout 页面核对国家、币种、价格和优惠是否正确。不要使用不适用于你真实计费信息的优惠。

`; document.body.appendChild(root); bindEvents(); refreshCouponHint(); } function setStatus(message, type = '') { const el = getEl('tm-status'); if (!el) return; el.textContent = message; el.className = `tm-status${type ? ' ' + type : ''}`; } function centerPanel() { const panel = getEl(PANEL_ID); if (!panel) return; const w = Math.min(720, window.innerWidth - 24); const h = Math.min(720, window.innerHeight - 24); panel.style.width = `${Math.max(320, w)}px`; panel.style.height = `${Math.max(420, h)}px`; panel.style.left = `${Math.max(8, (window.innerWidth - Math.max(320, w)) / 2)}px`; panel.style.top = `${Math.max(8, (window.innerHeight - Math.max(420, h)) / 2)}px`; panel.style.transform = 'none'; state.dragged = false; } function clampPanelIntoView() { const panel = getEl(PANEL_ID); if (!panel || panel.style.display === 'none') return; const rect = panel.getBoundingClientRect(); const left = Math.max(8, Math.min(rect.left, window.innerWidth - rect.width - 8)); const top = Math.max(8, Math.min(rect.top, window.innerHeight - rect.height - 8)); panel.style.left = `${left}px`; panel.style.top = `${top}px`; panel.style.transform = 'none'; } function showPanel() { ensureUi(); const mask = getEl(MASK_ID); const panel = getEl(PANEL_ID); if (!mask || !panel) return; mask.style.display = 'block'; panel.style.display = 'block'; centerPanel(); } function hidePanel() { const mask = getEl(MASK_ID); const panel = getEl(PANEL_ID); if (mask) mask.style.display = 'none'; if (panel) panel.style.display = 'none'; } function bindEvents() { getEl(LAUNCHER_ID)?.addEventListener('click', showPanel); getEl(MASK_ID)?.addEventListener('click', hidePanel); getEl('tm-close-panel')?.addEventListener('click', hidePanel); getEl('tm-center-panel')?.addEventListener('click', centerPanel); getEl('tm-coupon-select')?.addEventListener('change', refreshCouponHint); getEl('tm-apply-custom-promo')?.addEventListener('click', applyCustomPromo); getEl('tm-generate')?.addEventListener('click', generateLink); getEl('tm-copy')?.addEventListener('click', copyResult); getEl('tm-open')?.addEventListener('click', openResult); getEl('tm-clear')?.addEventListener('click', clearResult); getEl('tm-fill-session')?.addEventListener('click', fillSessionManually); getEl('tm-clear-session')?.addEventListener('click', () => { const ta = getEl('tm-manual-session'); if (ta) ta.value = ''; setStatus('已清空手动 Session 内容。'); }); enablePanelDrag(getEl(PANEL_ID), getEl(HEADER_ID)); } function refreshCouponHint() { const select = getEl('tm-coupon-select'); const hint = getEl('tm-coupon-hint'); if (!select || !hint) return; const cfg = getCouponByCode(select.value); const promoOverride = (getEl('tm-promo-override')?.value || '').trim(); const countryOverride = (getEl('tm-country-override')?.value || '').trim().toUpperCase(); const currencyOverride = (getEl('tm-currency-override')?.value || '').trim().toUpperCase(); if (promoOverride) { hint.textContent = `当前使用自定义/覆盖:country=${countryOverride || cfg.country},currency=${currencyOverride || cfg.currency},promo_code=${promoOverride}`; } else { hint.textContent = `当前匹配:country=${cfg.country},currency=${cfg.currency},promo_code=${cfg.promoCode}`; } } function applyCustomPromo() { const code = (getEl('tm-custom-promo')?.value || '').trim(); if (!code) { setStatus('请先填写自定义优惠码。', 'warn'); return; } const selectedCode = getEl('tm-coupon-select')?.value || COUPONS[0].promoCode; const cfg = getCouponByCode(selectedCode); const country = (getEl('tm-custom-country')?.value || cfg.country || '').trim().toUpperCase(); const currency = (getEl('tm-custom-currency')?.value || cfg.currency || '').trim().toUpperCase(); const promoOverride = getEl('tm-promo-override'); const countryOverride = getEl('tm-country-override'); const currencyOverride = getEl('tm-currency-override'); if (promoOverride) promoOverride.value = code; if (countryOverride) countryOverride.value = country; if (currencyOverride) currencyOverride.value = currency; refreshCouponHint(); setStatus(`已应用自定义优惠码:${code},国家/币种:${country}/${currency}。现在点击“生成长链接”即可。`, 'ok'); } function enablePanelDrag(panel, handle) { if (!panel || !handle || panel.dataset.dragBound === '1') return; panel.dataset.dragBound = '1'; let dragging = false; let startX = 0; let startY = 0; let startLeft = 0; let startTop = 0; handle.addEventListener('mousedown', (event) => { if (event.target.closest('button,input,textarea,select,summary')) return; dragging = true; state.dragged = true; const rect = panel.getBoundingClientRect(); startX = event.clientX; startY = event.clientY; startLeft = rect.left; startTop = rect.top; panel.style.transform = 'none'; document.body.style.userSelect = 'none'; event.preventDefault(); }); window.addEventListener('mousemove', (event) => { if (!dragging) return; const rect = panel.getBoundingClientRect(); let left = startLeft + event.clientX - startX; let top = startTop + event.clientY - startY; left = Math.max(8, Math.min(left, window.innerWidth - rect.width - 8)); top = Math.max(8, Math.min(top, window.innerHeight - rect.height - 8)); panel.style.left = `${left}px`; panel.style.top = `${top}px`; }); window.addEventListener('mouseup', () => { if (!dragging) return; dragging = false; document.body.style.userSelect = ''; }); } function normalizeToken(raw) { return String(raw || '') .trim() .replace(/^Bearer\s+/i, '') .replace(/^['"]|['"]$/g, '') .trim(); } function extractAccessTokenFromManualInput(input) { const text = String(input || '').trim(); if (!text) throw new Error('手动模式已开启,但没有填写 Session JSON 或 accessToken。'); try { const obj = JSON.parse(text); const token = normalizeToken(obj?.accessToken || obj?.token || obj?.session?.accessToken); if (token) return token; } catch (_) { // 不是标准 JSON 时,继续尝试正则或纯 token。 } const match = text.match(/["']accessToken["']\s*:\s*["']([^"']+)["']/); if (match?.[1]) return normalizeToken(match[1]); const direct = normalizeToken(text); if (direct.length > 20) return direct; throw new Error('无法从手动内容里解析 accessToken,请粘贴完整 JSON 或纯 accessToken。'); } async function getAutoAccessToken() { const resp = await window.fetch(SESSION_URL, { method: 'GET', credentials: 'include', headers: { Accept: 'application/json' }, }); const text = await resp.text(); let data; try { data = JSON.parse(text); } catch (_) { throw new Error(`Session 响应不是 JSON,HTTP ${resp.status}`); } if (!resp.ok) { throw new Error(`获取 Session 失败,HTTP ${resp.status}:${JSON.stringify(data).slice(0, 300)}`); } const token = normalizeToken(data?.accessToken); if (!token) throw new Error('Session 里没有 accessToken,请确认已经登录 ChatGPT。'); return { token, data }; } async function getAccessToken() { const mode = getEl('tm-token-mode')?.value || 'auto'; if (mode === 'manual') { const token = extractAccessTokenFromManualInput(getEl('tm-manual-session')?.value || ''); return { token, data: null, source: 'manual' }; } const { token, data } = await getAutoAccessToken(); return { token, data, source: 'auto' }; } async function fillSessionManually() { const btn = getEl('tm-fill-session'); try { if (btn) btn.disabled = true; setStatus('正在读取 /api/auth/session ...'); const { data } = await getAutoAccessToken(); const ta = getEl('tm-manual-session'); const mode = getEl('tm-token-mode'); if (ta) ta.value = JSON.stringify(data, null, 2); if (mode) mode.value = 'manual'; setStatus('已自动读取并填入 Session JSON,当前已切换为手动模式。', 'ok'); } catch (err) { setStatus(`读取 Session 失败:${err.message}`, 'err'); } finally { if (btn) btn.disabled = false; } } function buildPayload() { const selectedCode = getEl('tm-coupon-select')?.value || COUPONS[0].promoCode; const cfg = getCouponByCode(selectedCode); const workspaceName = (getEl('tm-workspace')?.value || 'MyTeam').trim() || 'MyTeam'; const seatsRaw = Number.parseInt(getEl('tm-seats')?.value || '2', 10); const seatQuantity = Math.max(2, Number.isFinite(seatsRaw) ? seatsRaw : 2); const priceInterval = getEl('tm-interval')?.value === 'year' ? 'year' : 'month'; const planName = (getEl('tm-plan')?.value || 'chatgptteamplan').trim() || 'chatgptteamplan'; const promoOverride = (getEl('tm-promo-override')?.value || '').trim(); const countryOverride = (getEl('tm-country-override')?.value || '').trim().toUpperCase(); const currencyOverride = (getEl('tm-currency-override')?.value || '').trim().toUpperCase(); const promoCode = promoOverride || cfg.promoCode; const country = countryOverride || cfg.country; const currency = currencyOverride || cfg.currency; const payload = { plan_name: planName, team_plan_data: { workspace_name: workspaceName, price_interval: priceInterval, seat_quantity: seatQuantity, }, billing_details: { country, currency, }, cancel_url: `https://chatgpt.com/?promoCode=${encodeURIComponent(promoCode)}`, promo_code: promoCode, checkout_ui_mode: 'hosted', }; return { payload, cfg, promoCode, country, currency }; } async function generateLink() { const btn = getEl('tm-generate'); const output = getEl('tm-output'); try { if (btn) btn.disabled = true; if (output) output.value = ''; setStatus('正在准备 payload 并获取 accessToken ...'); const { payload, promoCode, country, currency } = buildPayload(); const { token, source } = await getAccessToken(); setStatus(`Token 来源:${source === 'manual' ? '手动填入' : '自动获取'}。正在请求 Stripe hosted checkout 长链接 ...`); const resp = await window.fetch(CHECKOUT_URL, { method: 'POST', credentials: 'include', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', Accept: 'application/json', }, body: JSON.stringify(payload), }); const text = await resp.text(); let data; try { data = JSON.parse(text); } catch (_) { throw new Error(`checkout 响应不是 JSON,HTTP ${resp.status}:${text.slice(0, 300)}`); } state.lastData = data; state.lastPayload = payload; if (!resp.ok) { const detail = data?.detail || data?.message || data?.error || JSON.stringify(data).slice(0, 600); throw new Error(`请求失败,HTTP ${resp.status}:${detail}`); } const hostedUrl = data?.url || data?.stripe_hosted_url || data?.checkout_url; if (!hostedUrl) { if (output) output.value = JSON.stringify(data, null, 2); setStatus('请求成功,但没有找到 url / stripe_hosted_url / checkout_url。已把原始响应输出到结果框。', 'warn'); return; } state.lastUrl = hostedUrl; if (output) output.value = hostedUrl; const sessionId = data?.checkout_session_id || data?.id || '未知'; setStatus( `生成成功!\n优惠码:${promoCode}\n国家/币种:${country}/${currency}\n席位:${payload.team_plan_data.seat_quantity}\n周期:${payload.team_plan_data.price_interval}\nCheckout Session:${sessionId}`, 'ok' ); } catch (err) { console.error('[team-link] 生成失败:', err); setStatus(`生成失败:${err.message}`, 'err'); } finally { if (btn) btn.disabled = false; } } async function copyResult() { const value = (getEl('tm-output')?.value || state.lastUrl || '').trim(); if (!value) { setStatus('没有可复制的结果,请先生成长链接。', 'warn'); return; } try { if (typeof GM_setClipboard === 'function') { GM_setClipboard(value); } else if (navigator.clipboard?.writeText) { await navigator.clipboard.writeText(value); } else { const ta = getEl('tm-output'); ta?.focus(); ta?.select(); document.execCommand('copy'); } setStatus('已复制结果到剪贴板。', 'ok'); } catch (err) { setStatus(`复制失败:${err.message}`, 'err'); } } function openResult() { const value = (getEl('tm-output')?.value || state.lastUrl || '').trim(); if (!value || !/^https?:\/\//i.test(value)) { setStatus('没有可打开的长链接,请先生成。', 'warn'); return; } window.open(value, '_blank', 'noopener,noreferrer'); } function clearResult() { state.lastUrl = ''; state.lastData = null; state.lastPayload = null; const output = getEl('tm-output'); if (output) output.value = ''; setStatus('已清空输出结果。'); } function boot() { waitForBody(() => { ensureUi(); const observer = new MutationObserver(() => { if (!getEl(LAUNCHER_ID) || !getEl(PANEL_ID)) { ensureUi(); } }); observer.observe(document.body, { childList: true, subtree: true }); }); } window.addEventListener('resize', () => { const panel = getEl(PANEL_ID); if (!panel || panel.style.display === 'none') return; if (state.dragged) clampPanelIntoView(); else centerPanel(); }); window.addEventListener('keydown', (event) => { if (event.key === 'Escape') hidePanel(); }); boot(); })();