File size: 10,452 Bytes
71c9f40
 
 
 
 
 
 
 
7f9ecf5
71c9f40
 
 
 
 
8933026
 
 
 
 
 
 
 
 
 
 
 
71c9f40
 
b4cdd98
 
 
 
 
8933026
b4cdd98
 
 
 
 
 
 
 
 
71c9f40
 
 
 
 
 
 
 
 
81d8f02
 
 
 
 
 
71c9f40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b4cdd98
71c9f40
18bbaa5
71c9f40
 
 
 
 
 
18bbaa5
 
 
71c9f40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e26975
 
 
71c9f40
7e26975
71c9f40
7e26975
71c9f40
 
 
 
 
 
 
 
 
 
 
 
 
7e26975
71c9f40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
  <meta name="apple-mobile-web-app-title" content="Skills">
  <meta name="theme-color" content="#0B0D12">
  <title>Skills</title>
  <link rel="manifest" href="./manifest.json">
  <link rel="icon" type="image/svg+xml" href="./icon.svg">
  <link rel="apple-touch-icon" href="./icon.svg">
  <link rel="stylesheet" href="./style.css">
  <script>

    // 预判登录态,防刷新闪烁

    if (sessionStorage.getItem('skill_auth') === '1') {

      document.documentElement.classList.add('authed');

    }

  </script>
  <style>

    /* 未登录时隐藏主界面,防闪烁 */

    html:not(.authed) body > :not(#loginGate):not(script):not(style) { display: none !important; }

    #loginGate { display: flex; }

    html.authed #loginGate { display: none; }

  </style>
</head>
<body>
  <!-- 商用登录页 -->
  <div id="loginGate" class="login-gate" hidden>
    <div class="login-card">
      <div class="login-logo">💬</div>
      <h2 class="login-title">Skills</h2>
      <p class="login-sub">AI 助手 · 请输入访问密码</p>
      <div class="login-input-wrap">
        <input id="loginPwd" type="password" placeholder="访问密码" autocomplete="off" autocapitalize="off">
        <button id="loginBtn" class="login-btn">进入</button>
      </div>
      <p id="loginErr" class="login-err" hidden>密码错误,请重试</p>
      <p class="login-foot">© 2026 Skills · 仅授权用户可用</p>
    </div>
  </div>

  <header class="header">
    <div class="header-left">
      <img src="./icon.svg" alt="S" class="avatar">
      <div class="title-wrap">
        <h1>Skills</h1>
        <span class="subtitle">AI Assistant</span>
      </div>
    </div>
    <span id="relayStatus" class="relay-status" title="本地中继状态">●</span>
    <!-- 模型提供商快速切换:一键切智谱/CHATSAM/ChatGPT -->
    <select id="quickProvSwitch" class="quick-prov-switch" aria-label="切换模型提供商" title="一键切换模型提供商">
      <option value="zhipu">🧠 智谱 GLM</option>
      <option value="chatsam">🪙 CHATSAM 免Key</option>
      <option value="chatgpt">🤖 ChatGPT 官网</option>
    </select>
    <button id="settingsBtn" class="icon-btn" aria-label="设置">
      <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2">
        <circle cx="12" cy="12" r="3"/>
        <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
      </svg>
    </button>
  </header>

  <main id="messages" class="messages">
    <div class="welcome" id="welcome">
      <div class="welcome-icon">把对方的话发过来</div>
      <p class="welcome-text">截图、文字、对方资料都行。<br>帮你看清局面,给你能直接发回去的话。</p>
      <div class="suggest">
        <button class="chip" data-q="她突然说「最近好累啊」,我该回什么">她喊累</button>
        <button class="chip" data-q="她回我消息越来越慢了,咋办">回得越来越慢</button>
        <button class="chip" data-q="她问我「你是不是对谁都这么好」怎么回">送命题</button>
        <button class="chip" data-q="刚加的女生,第一句怎么开口不尴尬">开场白</button>
      </div>
    </div>
  </main>

  <div id="imgPreview" class="img-preview" style="display:none"></div>

  <footer class="input-bar">
    <button id="imgBtn" class="img-btn" type="button" aria-label="发截图">
      <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2">
        <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
        <circle cx="12" cy="13" r="4"/>
      </svg>
    </button>
    <input id="imgInput" type="file" accept="image/*" hidden>
    <textarea id="input" rows="1" placeholder="把对方发的话贴过来,或点左边发截图..." autocomplete="off"></textarea>
    <button id="sendBtn" class="send-btn" aria-label="发送">
      <svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
        <path d="M3.4 20.4l17.45-7.48a1 1 0 0 0 0-1.84L3.4 3.6a.993.993 0 0 0-1.39.91L2 9.12c0 .5.37.93.87.99L17 12 2.87 13.88c-.5.07-.87.5-.87 1l.01 4.61c0 .71.73 1.2 1.39.91z"/>
      </svg>
    </button>
  </footer>

  <!-- 设置面板 -->
  <div id="settingsModal" class="modal" hidden>
    <div class="modal-mask"></div>
    <div class="modal-body">
      <div class="modal-head">
        <h2>设置</h2>
        <button id="closeSettings" class="icon-btn" aria-label="关闭">
          <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12"/></svg>
        </button>
      </div>

      <div class="field">
        <label>女生档案(自动记录,互不混淆)</label>
        <div id="girlsList" class="girls-list"></div>
      </div>

      <div class="field">
        <label for="provider">AI 模型</label>
        <select id="provider">
          <option value="chatsam" selected>CHATSAM(内置号池·免配置·推荐)</option>
          <option value="chatgpt">ChatGPT 官网多账号池(免费·无限制)</option>
          <option value="zhipu">智谱 AI(GLM 系列模型轮调)</option>
        </select>
      </div>

      <!-- 各平台 API 获取说明(切换 provider 自动更新) -->
      <div id="providerGuide" class="provider-guide"></div>

      <!-- 智谱模型余额显示 -->
      <div id="zhipuQuotas" class="field" style="display:none"></div>

      <!-- 本地中继状态 -->
      <div id="relayBox" class="field relay-box">
        <label>本地中继 <span id="relayDot" class="relay-dot offline">●</span></label>
        <p class="hint" id="relayHint">本地中继未启动,正在使用 Cloudflare Worker(可能遇到 403 限流)</p>
        <div class="relay-cmd">
          <code id="relayCmd">node relay.mjs</code>
          <button id="copyRelayCmd" class="ghost-btn" type="button" title="复制启动命令">复制</button>
        </div>
      </div>

      <!-- 自定义 Base URL(不再需要,Worker 已硬编码) -->
      <div class="field" id="baseUrlField" style="display:none">
        <label for="baseUrl">Base URL(自定义后端)</label>
        <input type="text" id="baseUrl" placeholder="http://localhost:3000/v1/chat/completions" autocomplete="off">
        <p class="hint hint-tip">只对需要本机后端的平台显示(如 chatsam-hf)。填完整 endpoint,不要带 Bearer。</p>
      </div>

      <!-- 账号池(access_token / API Key,根据 provider 动态切换) -->
      <div class="field" id="keyField">
        <label id="keyFieldLabel">账号池(多 Key 自动轮询)</label>
        <div id="keyList" class="key-list"></div>
        <div class="key-add-row" id="keyAddRow">
          <input type="text" id="newKeyName" placeholder="备注(如:账号1)" autocomplete="off">
          <input type="password" id="newKeyVal" placeholder="粘贴 API Key / access_token" autocomplete="off">
          <select id="newKeyModel" class="model-select">
            <option value="gpt-4o" selected>GPT-4o</option>
            <option value="gpt-4o-mini">GPT-4o mini</option>
            <option value="gpt-4.1">GPT-4.1</option>
            <option value="gpt-4.1-mini">GPT-4.1 mini</option>
            <option value="o1">o1</option>
            <option value="o1-mini">o1 mini</option>
            <option value="o3">o3</option>
            <option value="o3-mini">o3 mini</option>
          </select>
          <button id="addKeyBtn" class="add-key-btn" type="button">添加</button>
        </div>
        <a id="getKeyLink" class="hint" href="https://chatgpt.com" target="_blank" rel="noopener">→ 打开 chatgpt.com 登录账号拿 access_token</a>
        <p class="hint hint-tip" id="keyHintTip">多账号 token 都加进来,自动轮询;单账号限流/失效不影响其他。Token 存本机 localStorage,不上传服务器。</p>
      </div>

      <!-- 账号仪表盘(类型 / 状态 / 账号信息 / 额度 / 恢复时间 / 成功 / 失败 / 操作) -->
      <div class="field">
        <div class="dash-head">
          <label>账号仪表盘</label>
          <button id="refreshAllBtn" class="ghost-btn" type="button" title="批量刷新所有账号的 session/探测">🔄 全部刷新</button>
        </div>
        <div class="dash-wrap">
          <table class="dash-table">
            <thead>
              <tr>
                <th>类型</th>
                <th>状态</th>
                <th>账号信息</th>
                <th>额度</th>
                <th>恢复时间</th>
                <th>成功</th>
                <th>失败</th>
                <th>操作</th>
              </tr>
            </thead>
            <tbody id="dashBody">
              <tr><td colspan="8" class="empty-tip">还没添加账号</td></tr>
            </tbody>
          </table>
        </div>
      </div>

      <div class="field-row">
        <button id="clearChat" class="ghost-btn">清空对话</button>
        <button id="saveSettings" class="primary-btn">保存</button>
      </div>

      <p class="footnote">Key 只存在你本机浏览器,不会上传。换设备需重新填。</p>
    </div>
  </div>

  <script src="./app.js"></script>
</body>
</html>