xiaoyukkkk commited on
Commit
4dde5ce
·
verified ·
1 Parent(s): fa8692e

Delete gemini-business2api helper.js

Browse files
Files changed (1) hide show
  1. gemini-business2api helper.js +0 -425
gemini-business2api helper.js DELETED
@@ -1,425 +0,0 @@
1
- // ==UserScript==
2
- // @name Gemini Business 2API Helper (JSON Format + Expiration)
3
- // @namespace https://linux.do/u/f-droid
4
- // @version 1.6
5
- // @icon https://cdn.inviter.co/community/b5c3dc29-b7e3-49f9-a18d-819398ba4fe6.png
6
- // @description 提取配置,支持 JSON 格式,过期时间逻辑为:Cookie过期时间戳 - 12小时。
7
- // @author Gemini Business
8
- // @match https://business.gemini.google/*
9
- // @grant GM_setClipboard
10
- // @grant GM_addStyle
11
- // @grant GM_cookie
12
- // @connect business.gemini.google
13
- // ==/UserScript==
14
-
15
- (function() {
16
- 'use strict';
17
-
18
- const getFavicon = () => {
19
- const link = document.querySelector("link[rel*='icon']") || document.querySelector("link[rel='shortcut icon']");
20
- return link ? link.href : 'https://cdn.inviter.co/community/b5c3dc29-b7e3-49f9-a18d-819398ba4fe6.png';
21
- };
22
-
23
- GM_addStyle(`
24
- :root {
25
- --gb-primary: #1a73e8;
26
- --gb-primary-hover: #1557b0;
27
- --gb-success: #1e8e3e;
28
- --gb-success-hover: #137333;
29
- --gb-surface: #ffffff;
30
- --gb-bg: #f8f9fa;
31
- --gb-text-main: #202124;
32
- --gb-text-sub: #5f6368;
33
- --gb-border: #dadce0;
34
- --gb-shadow-sm: 0 1px 2px 0 rgba(60,64,67,0.3), 0 1px 3px 1px rgba(60,64,67,0.15);
35
- --gb-shadow-md: 0 4px 8px 3px rgba(60,64,67,0.15), 0 1px 3px rgba(60,64,67,0.3);
36
- --gb-shadow-lg: 0 8px 24px rgba(60,64,67,0.2);
37
- --gb-font: 'Google Sans', 'Roboto', Arial, sans-serif;
38
- --gb-mono: 'Roboto Mono', 'Menlo', monospace;
39
- --transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
40
- }
41
-
42
- #gb-float-ball {
43
- position: fixed;
44
- bottom: 32px;
45
- right: 32px;
46
- width: 60px;
47
- height: 60px;
48
- background: white;
49
- border-radius: 50%;
50
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
51
- cursor: pointer;
52
- z-index: 9998;
53
- border: 1px solid var(--gb-border);
54
- display: flex;
55
- align-items: center;
56
- justify-content: center;
57
- transition: var(--transition);
58
- transform: scale(1);
59
- }
60
-
61
- #gb-float-ball:hover {
62
- transform: scale(1.1) rotate(10deg);
63
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
64
- }
65
-
66
- #gb-float-ball img {
67
- width: 36px;
68
- height: 36px;
69
- border-radius: 8px;
70
- object-fit: contain;
71
- pointer-events: none;
72
- }
73
-
74
- #gb-float-ball::after {
75
- content: 'JSON';
76
- position: absolute;
77
- bottom: -4px;
78
- right: -4px;
79
- font-size: 8px;
80
- font-weight: bold;
81
- background: var(--gb-success);
82
- color: white;
83
- padding: 2px 6px;
84
- border-radius: 8px;
85
- transform: rotate(-15deg);
86
- }
87
-
88
- #gb-overlay {
89
- position: fixed; inset: 0;
90
- background: rgba(32, 33, 36, 0.6);
91
- backdrop-filter: blur(3px);
92
- z-index: 9999;
93
- display: flex; align-items: center; justify-content: center;
94
- opacity: 0; pointer-events: none;
95
- transition: opacity 0.25s ease;
96
- }
97
- #gb-overlay.active { opacity: 1; pointer-events: auto; }
98
-
99
- #gb-panel {
100
- width: 550px; max-width: 90vw;
101
- background: var(--gb-surface);
102
- border-radius: 24px;
103
- box-shadow: var(--gb-shadow-lg);
104
- overflow: hidden;
105
- display: flex; flex-direction: column;
106
- transform: scale(0.92) translateY(20px);
107
- transition: transform 0.3s cubic-bezier(0.2, 0.0, 0, 1);
108
- font-family: var(--gb-font);
109
- }
110
- #gb-overlay.active #gb-panel { transform: scale(1) translateY(0); }
111
-
112
- .gb-header {
113
- background: linear-gradient(135deg, #4285f4 0%, #34a853 100%);
114
- padding: 24px 32px;
115
- color: white;
116
- }
117
- .gb-title { font-size: 22px; font-weight: 500; margin: 0; letter-spacing: 0.5px; }
118
- .gb-subtitle { font-size: 13px; opacity: 0.9; margin-top: 6px; font-weight: 400; }
119
-
120
- .gb-body { padding: 24px 32px 16px; background: var(--gb-surface); }
121
- .gb-label {
122
- font-size: 14px; color: var(--gb-text-sub);
123
- margin-bottom: 12px; font-weight: 500;
124
- display: flex; justify-content: space-between; align-items: center;
125
- }
126
-
127
- .gb-textarea-wrapper {
128
- position: relative;
129
- border: 1px solid var(--gb-border);
130
- border-radius: 12px;
131
- background: var(--gb-bg);
132
- transition: border-color 0.2s, background 0.2s;
133
- }
134
- .gb-textarea-wrapper.editing {
135
- background: white;
136
- border-color: var(--gb-primary);
137
- box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2);
138
- }
139
- .gb-textarea {
140
- width: 100%; height: 220px;
141
- border: none; background: transparent;
142
- padding: 16px;
143
- font-family: var(--gb-mono);
144
- font-size: 13px; line-height: 1.6;
145
- color: var(--gb-text-main);
146
- resize: none; outline: none;
147
- box-sizing: border-box;
148
- white-space: pre;
149
- }
150
-
151
- .gb-status {
152
- margin-top: 12px; font-size: 13px;
153
- display: flex; align-items: center; gap: 8px;
154
- color: var(--gb-text-sub);
155
- height: 20px;
156
- }
157
- .gb-dot { width: 8px; height: 8px; border-radius: 50%; background: #ccc; transition: background 0.3s; }
158
- .gb-dot.success { background: var(--gb-success); }
159
- .gb-dot.error { background: #ea4335; }
160
-
161
- .gb-footer {
162
- padding: 16px 32px 24px;
163
- display: flex; justify-content: flex-end; gap: 12px;
164
- border-top: 1px solid #f1f3f4;
165
- background: var(--gb-surface);
166
- }
167
-
168
- .gb-btn {
169
- border: none; outline: none;
170
- padding: 0 24px; height: 40px;
171
- border-radius: 20px;
172
- font-family: var(--gb-font);
173
- font-size: 14px; font-weight: 500;
174
- cursor: pointer;
175
- transition: all 0.2s;
176
- display: flex; align-items: center; justify-content: center;
177
- }
178
- .gb-btn-text {
179
- background: transparent; color: var(--gb-text-sub);
180
- }
181
- .gb-btn-text:hover { background: rgba(0,0,0,0.05); color: var(--gb-text-main); }
182
-
183
- .gb-btn-primary {
184
- background: var(--gb-primary); color: white;
185
- box-shadow: var(--gb-shadow-sm);
186
- }
187
- .gb-btn-primary:hover {
188
- background: var(--gb-primary-hover);
189
- box-shadow: var(--gb-shadow-md);
190
- }
191
- .gb-btn-primary:active { transform: scale(0.98); }
192
-
193
- .gb-btn-success {
194
- background: var(--gb-success); color: white;
195
- }
196
- .gb-btn-success:hover { background: var(--gb-success-hover); }
197
-
198
- .gb-hidden { display: none !important; }
199
- `);
200
-
201
- // Helper: 格式化 Unix 时间戳 (秒)
202
- const formatTimestamp = (ts) => {
203
- if (!ts) return "Session (Browser Close)";
204
- // ts 是秒,需要转毫秒
205
- const date = new Date(ts * 1000);
206
- const y = date.getFullYear();
207
- const m = String(date.getMonth() + 1).padStart(2, '0');
208
- const d = String(date.getDate()).padStart(2, '0');
209
- const h = String(date.getHours()).padStart(2, '0');
210
- const min = String(date.getMinutes()).padStart(2, '0');
211
- const s = String(date.getSeconds()).padStart(2, '0');
212
- return `${y}-${m}-${d} ${h}:${min}:${s}`;
213
- };
214
-
215
- const floatBall = document.createElement('div');
216
- floatBall.id = 'gb-float-ball';
217
- floatBall.title = "提取配置";
218
- const ballIcon = document.createElement('img');
219
- ballIcon.src = getFavicon();
220
- floatBall.appendChild(ballIcon);
221
- document.body.appendChild(floatBall);
222
-
223
- const overlay = document.createElement('div');
224
- overlay.id = 'gb-overlay';
225
- const panel = document.createElement('div');
226
- panel.id = 'gb-panel';
227
- overlay.appendChild(panel);
228
- document.body.appendChild(overlay);
229
-
230
- const header = document.createElement('div');
231
- header.className = 'gb-header';
232
- const title = document.createElement('h2');
233
- title.className = 'gb-title';
234
- title.textContent = 'Gemini Business 2API Helper';
235
- const subtitle = document.createElement('div');
236
- subtitle.className = 'gb-subtitle';
237
- subtitle.textContent = '配置提取与管理';
238
- header.appendChild(title);
239
- header.appendChild(subtitle);
240
- panel.appendChild(header);
241
-
242
- const body = document.createElement('div');
243
- body.className = 'gb-body';
244
-
245
- const label = document.createElement('div');
246
- label.className = 'gb-label';
247
- label.textContent = '提取的配置 (JSON):';
248
- body.appendChild(label);
249
-
250
- const textWrapper = document.createElement('div');
251
- textWrapper.className = 'gb-textarea-wrapper';
252
- const textarea = document.createElement('textarea');
253
- textarea.className = 'gb-textarea';
254
- textarea.readOnly = true;
255
- textarea.spellcheck = false;
256
- textWrapper.appendChild(textarea);
257
- body.appendChild(textWrapper);
258
-
259
- const statusDiv = document.createElement('div');
260
- statusDiv.className = 'gb-status';
261
- const statusDot = document.createElement('div');
262
- statusDot.className = 'gb-dot';
263
- const statusText = document.createElement('span');
264
- statusText.textContent = '等待操作...';
265
- statusDiv.appendChild(statusDot);
266
- statusDiv.appendChild(statusText);
267
- body.appendChild(statusDiv);
268
- panel.appendChild(body);
269
-
270
- const footer = document.createElement('div');
271
- footer.className = 'gb-footer';
272
-
273
- const btnClose = document.createElement('button');
274
- btnClose.className = 'gb-btn gb-btn-text';
275
- btnClose.textContent = '关闭';
276
-
277
- const btnEdit = document.createElement('button');
278
- btnEdit.className = 'gb-btn gb-btn-text';
279
- btnEdit.textContent = '编辑';
280
-
281
- const btnSave = document.createElement('button');
282
- btnSave.className = 'gb-btn gb-btn-success gb-hidden';
283
- btnSave.textContent = '保存修改';
284
-
285
- const btnCopy = document.createElement('button');
286
- btnCopy.className = 'gb-btn gb-btn-primary';
287
- btnCopy.textContent = '复制配置';
288
-
289
- footer.appendChild(btnClose);
290
- footer.appendChild(btnEdit);
291
- footer.appendChild(btnSave);
292
- footer.appendChild(btnCopy);
293
- panel.appendChild(footer);
294
-
295
- let isEditing = false;
296
- let extractedData = "";
297
-
298
- const setStatus = (type, msg) => {
299
- statusDot.className = 'gb-dot ' + (type === 'success' ? 'success' : type === 'error' ? 'error' : '');
300
- statusText.textContent = msg;
301
- };
302
-
303
- const toggleEditMode = () => {
304
- isEditing = !isEditing;
305
- textarea.readOnly = !isEditing;
306
-
307
- if (isEditing) {
308
- textWrapper.classList.add('editing');
309
- btnEdit.classList.add('gb-hidden');
310
- btnCopy.classList.add('gb-hidden');
311
- btnSave.classList.remove('gb-hidden');
312
- setStatus('normal', '正在编辑...');
313
- textarea.focus();
314
- } else {
315
- textWrapper.classList.remove('editing');
316
- btnEdit.classList.remove('gb-hidden');
317
- btnCopy.classList.remove('gb-hidden');
318
- btnSave.classList.add('gb-hidden');
319
- }
320
- };
321
-
322
- const saveContent = () => {
323
- extractedData = textarea.value;
324
- toggleEditMode();
325
- setStatus('success', '修改已保存');
326
- };
327
-
328
- const openPanel = () => {
329
- overlay.classList.add('active');
330
-
331
- textarea.value = "正在读取环境数据...";
332
- textarea.style.color = "#9aa0a6";
333
- btnCopy.disabled = true;
334
- btnEdit.style.display = 'none';
335
- setStatus('normal', '分析中...');
336
-
337
- const pathParts = window.location.pathname.split('/');
338
- const cidIndex = pathParts.indexOf('cid');
339
- const config_id = (cidIndex !== -1 && pathParts.length > cidIndex + 1) ? pathParts[cidIndex + 1] : null;
340
- const urlParams = new URLSearchParams(window.location.search);
341
- const csesidx = urlParams.get('csesidx');
342
-
343
- GM_cookie('list', {}, (cookies, error) => {
344
- if (error) {
345
- textarea.value = "错误:无法读取 Cookie。\n请检查 Tampermonkey 权限。";
346
- textarea.style.color = "#ea4335";
347
- setStatus('error', '读取失败');
348
- return;
349
- }
350
-
351
- const host_c_oses_raw = (cookies.find(c => c.name === '__Host-C_OSES' && c.domain === window.location.hostname) || {}).value;
352
- const host_c_oses_formatted = host_c_oses_raw ? `"${host_c_oses_raw}"` : "null";
353
-
354
- // 获取 SES Cookie 对象
355
- const sesCookieObj = cookies.find(c => c.name === '__Secure-C_SES') || {};
356
- const secure_c_ses = sesCookieObj.value || null;
357
-
358
- // 修正过期时间逻辑:Cookie 过期时间戳 - 12小时 (12 * 60 * 60 = 43200秒)
359
- let expireTime = "Session (Unknown)";
360
- if (sesCookieObj.expirationDate) {
361
- // expirationDate 是秒
362
- const adjustedTimestamp = sesCookieObj.expirationDate - 43200;
363
- expireTime = formatTimestamp(adjustedTimestamp);
364
- } else {
365
- // 如果没有过期时间(浏览器会话结束),显示 Session
366
- expireTime = "Session (Browser Close)";
367
- }
368
-
369
- if (!config_id || !csesidx || !secure_c_ses) {
370
- textarea.value = "⚠️ 数据不完整。\n请确保您在 Gemini Business 聊天界面,且 URL 包含 /cid/ 和 ?csesidx=";
371
- textarea.style.color = "#f9ab00";
372
- setStatus('error', '数据缺失');
373
- return;
374
- }
375
-
376
- extractedData = `"csesidx": "${csesidx}",
377
- "config_id": "${config_id}",
378
- "secure_c_ses": "${secure_c_ses}",
379
- "host_c_oses": ${host_c_oses_formatted},
380
- "expires_at": "${expireTime}"`;
381
-
382
- textarea.value = extractedData;
383
- textarea.style.color = "var(--gb-text-main)";
384
- btnCopy.disabled = false;
385
- btnEdit.style.display = 'block';
386
- setStatus('success', '提取成功');
387
- });
388
- };
389
-
390
- const closePanel = () => {
391
- overlay.classList.remove('active');
392
- if (isEditing) {
393
- textarea.value = extractedData;
394
- toggleEditMode();
395
- }
396
- };
397
-
398
- const copyToClipboard = () => {
399
- if (!textarea.value) return;
400
- GM_setClipboard(textarea.value);
401
-
402
- const originalText = btnCopy.textContent;
403
- btnCopy.textContent = "已复制";
404
- btnCopy.classList.remove('gb-btn-primary');
405
- btnCopy.classList.add('gb-btn-success');
406
-
407
- setTimeout(() => {
408
- btnCopy.textContent = originalText;
409
- btnCopy.classList.remove('gb-btn-success');
410
- btnCopy.classList.add('gb-btn-primary');
411
- closePanel();
412
- }, 1200);
413
- };
414
-
415
- floatBall.addEventListener('click', openPanel);
416
- btnClose.addEventListener('click', closePanel);
417
- btnCopy.addEventListener('click', copyToClipboard);
418
- btnEdit.addEventListener('click', toggleEditMode);
419
- btnSave.addEventListener('click', saveContent);
420
-
421
- overlay.addEventListener('click', (e) => {
422
- if (e.target === overlay) closePanel();
423
- });
424
-
425
- })();