This commit is contained in:
wood 2023-07-22 00:47:43 +08:00
parent 26f74fc1e3
commit f2e9b2dd2d
4 changed files with 547 additions and 3 deletions

View File

@ -3,6 +3,12 @@
## [English](README_EN.md)
# 7月22日更新后需登录一次账号F12查看sess码使用sess码进行查询
7月22日更新后已删除key校验规则需使用sess码进行查询。使用key只能查询总额度、绑卡、GPT4和组织ID。
![1689957580942.png](https://cdn-img.czl.net/2023/07/22/64bab4daba587.png)
# 查询示例图
![1688789680187.png](https://cdn-img.czl.net/2023/07/08/64a8e2b180068.png)

View File

@ -1,6 +1,12 @@
# openai-billing-query
Batch visualization query for openai (chatgpt) balance, supporting display of total amount, used amount, remaining amount, usage ratio, expiration time, GPT-4, and whether it is bound with a card.
# After the update on July 22nd, you need to log in to your account once. Use F12 to view the session code and use the session code for queries.
After the update on July 22nd, the key verification rule has been removed. You need to use the session code for queries. The key can only be used to query the total amount, binding of cards, GPT4, and organization ID.
![1689957580942.png](https://cdn-img.czl.net/2023/07/22/64bab4daba587.png)
# Query example image
![1688789680187.png](https://cdn-img.czl.net/2023/07/08/64a8e2b180068.png)

View File

@ -341,6 +341,8 @@
<body>
<header>
<h1 class="text-3xl font-semibold text-center mb-8 text-gradient">OpenAI API 信息查询</h1>
<p class=" text-center text-gradient" style="text-align: center;">7月22日更新后需使用sess码进行查询。使用key只能查询总额度、绑卡、GPT4和组织ID。</p>
<a href="oapi.html" style="text-align: center;color:antiquewhite;text-decoration: none;font-weight: 700;"><p>Oapi的余额查询</p></a>
</header>
<div class="query">
@ -348,7 +350,7 @@
<h2 style="display: inline-block;">输入 API KEY</h2>
<p style="display: inline-block; font-size: smaller;">本站不保存 KEY 信息,查询后请自行保存</p>
</div>
<textarea id="api-key-input" placeholder="请输入 API-KEY,必须包含 sk-,多个可直接粘贴"></textarea></p>
<textarea id="api-key-input" placeholder="请输入 API-KEY或sessID多个可直接粘贴"></textarea></p>
<!-- API链接选择框 -->
<div class="api-url-container"></div>
<!-- API链接选择 -->
@ -505,7 +507,7 @@
apiUrl = apiUrlSelect.value;
}
let apiKeys = apiKeyInput.value.split(/[,\s\n]+/).filter(key => key.startsWith("sk-"));
let apiKeys = apiKeyInput.value.split(/[,\s\n]+/);
if (apiKeys.length === 0) {
alert("未匹配到 API-KEY请检查输入内容");
@ -532,7 +534,7 @@
row.appendChild(serialNumberCell); // 将序列号单元格添加到行中
let apiKeyCell = document.createElement("td");
apiKeyCell.textContent = apiKey.replace(/^(sk-[a-zA-Z0-9]{4})[a-zA-Z0-9]*(.{6})$/, "$1***$2");
apiKeyCell.textContent = apiKey;
row.appendChild(apiKeyCell);
console.log(data); // 添加 console.log 以查看 data 的值

530
oapi.html Normal file
View File

@ -0,0 +1,530 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Oapi余额查询</title>
<meta name="description" content="查询Oapi的余额">
<style>
:root {
--color-primary: #1E3B7A;
--color-primary-dark: #1E3B7A;
--color-primary-alpha: #01847f;
--body-color: #dadada;
--body-bg: #000000;
--border-color: #f8f8f800;
}
body {
background: url('https://cdn-img.czl.net/2023/05/23/pjbczr.webp') no-repeat center center fixed;
/* 自定义背景图 */
background-size: cover;
width: 90%;
max-width: 980px;
margin-left: auto;
margin-right: auto;
padding-left: 2rem;
padding-right: 2rem;
color: var(--body-color);
/* background: var(--body-bg); */
font-family: system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;
line-height: 1.5;
-webkit-tap-highlight-color: rgb(252, 247, 247);
text-rendering: optimizelegibility;
-webkit-font-smoothing: antialiased;
}
.query {
max-width: 50rem;
margin: auto;
}
a {
color: var(--color-primary);
text-decoration: none;
transition: color .3s;
}
a:hover {
color: var(--color-primary);
text-decoration: underline;
}
h1 {
font-size: 2.5rem;
text-align: center;
}
main[x-cloak] {
opacity: 0;
}
main:not([x-cloak]) {
opacity: 1;
transition: opacity .3s;
}
textarea {
-webkit-appearance: none;
appearance: none;
display: block;
width: 100%;
padding: .5rem 1rem;
border: 1px solid var(--border-color);
border-radius: .25rem;
box-sizing: border-box;
color: #33404d;
line-height: inherit;
font-size: 1rem;
transition: border .3s, box-shadow .3s;
}
textarea:focus {
box-shadow: 0 0 0 .25rem var(--color-primary-alpha);
border-color: var(--color-primary);
outline: 0;
}
input {
-webkit-appearance: none;
appearance: none;
display: block;
width: 100%;
padding: .5rem 1rem;
border: 1px solid var(--border-color);
border-radius: .25rem;
box-sizing: border-box;
color: #33404d;
line-height: inherit;
font-size: 1rem;
transition: border .3s, box-shadow .3s;
}
input:focus {
box-shadow: 0 0 0 .25rem var(--color-primary-alpha);
border-color: var(--color-primary);
outline: 0;
}
details {
margin: 1rem 0 2rem;
border: 1px solid var(--border-color);
border-radius: .25rem;
transition: background .3s;
}
details[open] {
background: #fff;
}
details summary {
padding: .5rem 1rem;
font-weight: 500;
user-select: none;
cursor: pointer;
opacity: .8;
outline: 0;
}
details div {
padding: 1rem;
border-top: 1px solid var(--border-color);
}
details small {
margin: 0;
font-size: .875rem;
line-height: 2;
}
button {
appearance: none;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: auto;
max-width: 50rem;
margin-bottom: 1rem;
padding: .5rem .75rem;
border: 1px solid var(--color-primary);
border-radius: .25rem;
background: var(--color-primary);
color: #fff;
font-size: 1rem;
font-weight: 500;
line-height: inherit;
cursor: pointer;
user-select: none;
transition: border .3s, background .3s, ;
}
button:hover {
border-color: var(--color-primary-dark);
background: var(--color-primary-dark);
}
button:focus {
box-shadow: 0 0 0 .25rem var(--color-primary-alpha);
border-color: var(--color-primary);
outline: 0;
}
button:disabled {
background: var(--color-primary);
border-color: var(--color-primary);
opacity: .6;
cursor: not-allowed;
}
button.loading::before {
content: '';
display: inline-block;
margin-right: .5rem;
border: 2px solid #fff;
border-top-color: transparent;
border-bottom-color: transparent;
border-radius: 50%;
width: .75rem;
height: .75rem;
animation: rotate .5s linear infinite;
}
footer {
padding: 1rem;
border-top: 1px solid var(--border-color);
text-align: center;
opacity: .5;
}
footer i {
font-style: normal;
color: #b32142;
}
.success,
.error {
margin-bottom: 1rem;
padding: .5rem 1rem;
border-radius: .25rem;
color: #fff;
text-align: center;
opacity: 1;
transition: opacity .3s;
}
.success {
/* border: 1px solid #12b886; */
background: #38d9a9;
}
.error {
/* border: 1px solid #b32142; */
background: #b32142;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
/* 设置API URL选择器和自定义URL输入框的样式 */
select#api-url-select,
input#custom-url-input {
width: 100%;
height: 2.4rem;
font-size: 1rem;
background-color: #212121;
margin-bottom: .5rem;
padding: .5rem .75rem;
color: #ffffff;
border: none;
padding: .5rem .75rem;
border-radius: .25rem;
margin-right: 10px;
margin-top: 2vhpx;
/* 添加了顶部边距 */
}
/* 下面的代码定义了结果表格的样式 */
#result-table {
border-collapse: collapse;
width: 100%;
margin-top: 20px;
font-size: 14px;
border-radius: 5px;
border: none;
overflow: hidden;
}
/* 下面的代码定义了结果表格表头的样式 */
#result-table th {
background-color: var(--color-primary);
color: #ffffff;
font-weight: 400;
height: 20px;
padding: 10px 25px;
text-align: left;
/* border: 1px solid #dcdcdc; */
}
/* 下面的代码定义了结果表格数据单元格的样式 */
#result-table td {
height: 20px;
text-align: left;
padding: 10px 25px;
/* border: 1px solid #d3d3d3; */
}
/* 下面的代码定义了结果表格奇数行的背景颜色 */
#result-table tbody tr:nth-child(odd) {
background-color: #252422;
}
/* 下面的代码定义了结果表格偶数行的背景颜色 */
#result-table tbody tr:nth-child(even) {
background-color: #31302d;
}
/* 下面的代码定义了结果表格表头的宽度 */
#result-table .table-header {
width: 25%;
}
/* 下面的代码定义了结果表格数据单元格的宽度、字体加粗和颜色 */
#result-table .table-data {
width: 25%;
font-weight: bold;
color: #1c248b;
}
/* 下面的代码定义了结果表格数据单元格的样式 */
#result-table .status-error {
height: 20px;
text-align: center;
padding: 10px 25px;
/* border: 1px solid #dcdcdc; */
}
/* 下面的代码定义了一个类名为status-ok的样式用于设置成功状态的文本颜色 */
.status-ok {
color: #2d8d2d;
}
/* 下面的代码定义了一个类名为status-error的样式用于设置错误状态的文本颜色 */
.status-error {
color: #ed0808;
}
#api-key-input {
width: 100%;
height: 100px;
/* border: 1px solid #999; */
background-color: #fff;
}
</style>
</head>
<body>
<header>
<h1 class="text-3xl font-semibold text-center mb-8 text-gradient">Oapi余额查询</h1>
<a href="index.html" style="text-align: center;color:antiquewhite;text-decoration: none;font-weight: 700;"><p>OPENAI的余额查询</p></a>
</header>
<div class="query">
<div>
<h2 style="display: inline-block;">输入 API KEY</h2>
<p style="display: inline-block; font-size: smaller;">本站不保存 KEY 信息,查询后请自行保存</p>
</div>
<textarea id="api-key-input" placeholder="请输入 API-KEY必须包含 sk-,多个可直接粘贴"></textarea></p>
<!-- API链接选择框 -->
<div class="api-url-container"></div>
<!-- API链接选择 -->
<div>
<h2 style="display: inline-block;">选择查询线路</h2>
<p style="display: inline-block; font-size: smaller;">只支持Oapi的余额查询</p>
</div>
<select id="api-url-select" style="width:100%;">
<option value="https://oapi.czl.net">【Oapi】余额查询</option>
</select>
</div>
<button :class="{ loading }" :disabled="loading" onclick="sendRequest()">查询</button> </p>
</div>
<h2 id="result-head" style="visibility:hidden">查询结果</h2>
<table id="result-table" style="visibility:hidden">
<thead>
<tr>
<th style="width:35px">序号</th>
<th>API KEY</th>
<th style="width: 50px">余额</th>
</tr>
</thead>
<tbody></tbody>
</table>
<footer>
<a href="https://chat.czl.net" style="color: white;" target="_blank">CZLChat</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://oapi.czl.net" style="color: white;" target="_blank">CZLOapi</a>
</footer>
<script>
let queriedApiKeys = [];
let serialNumber = 1;
async function checkBilling(apiKey, apiUrl) {
const headers = {
"Authorization": "Bearer " + apiKey,
"Content-Type": "application/json"
};
const urlSubscription = `${apiUrl}/v1/dashboard/billing/subscription`;
try {
let response = await fetch(urlSubscription, { headers });
if (!response.ok) {
console.log("APIKEY 错误或账号被封,请登录 OpenAI 查看。");
return;
}
const subscriptionData = await response.json();
const totalAmount = subscriptionData.system_hard_limit_usd;
return [totalAmount];
} catch (error) {
console.error(error);
return ["Error"];
}
}
function sendRequest() {
let apiKeyInput = document.getElementById("api-key-input");
let apiUrlSelect = document.getElementById("api-url-select");
let customUrlInput = document.getElementById("custom-url-input");
let table = document.getElementById("result-table");
let h2 = document.getElementById("result-head");
h2.style.visibility = "visible";
table.style.visibility = "visible";
if (apiKeyInput.value.trim() === "") {
alert("请填写API KEY");
return;
}
document.getElementById("result-table").getElementsByTagName('tbody')[0].innerHTML = "";
let apiUrl = "";
if (apiUrlSelect.value === "custom") {
if (customUrlInput.value.trim() === "") {
alert("请设置API链接");
return;
} else {
apiUrl = customUrlInput.value.trim();
if (!apiUrl.startsWith("http://") && !apiUrl.startsWith("https://")) {
apiUrl = "https://" + apiUrl;
}
}
} else {
apiUrl = apiUrlSelect.value;
}
let apiKeys = apiKeyInput.value.split(/[,\s\n]+/).filter(key => key.startsWith("sk-"));
if (apiKeys.length === 0) {
alert("未匹配到 API-KEY请检查输入内容");
return;
}
alert("成功匹配到 API Key确认后开始查询" + apiKeys);
let tableBody = document.querySelector("#result-table tbody");
for (let i = 0; i < apiKeys.length; i++) {
let apiKey = apiKeys[i].trim();
if (queriedApiKeys.includes(apiKey)) {
console.log(`API KEY ${apiKey} 已查询过,跳过此次查询`);
continue;
}
queriedApiKeys.push(apiKey);
checkBilling(apiKey, apiUrl).then((data) => {
let row = document.createElement("tr");
let serialNumberCell = document.createElement("td");
serialNumberCell.textContent = serialNumber;
row.appendChild(serialNumberCell);
let apiKeyCell = document.createElement("td");
apiKeyCell.textContent = apiKey;
row.appendChild(apiKeyCell);
console.log(data);
if (data?.[0] === "Error" || data === undefined) {
let errorMessageCell = document.createElement("td");
errorMessageCell.colSpan = "3";
errorMessageCell.classList.add("status-error");
errorMessageCell.textContent = "不正确或已失效的API-KEY";
row.appendChild(errorMessageCell);
} else {
let totalGrantedCell = document.createElement("td");
totalGrantedCell.textContent = data[0].toFixed(2);
row.appendChild(totalGrantedCell);
}
tableBody.appendChild(row);
if (i === apiKeys.length - 1) {
queriedApiKeys = [];
}
serialNumber++;
h2.style.display = 'block';
table.style.display = 'table';
}).catch((error) => {
console.error(error);
let row = document.createElement("tr");
let serialNumberCell = document.createElement("td");
serialNumberCell.textContent = serialNumber;
row.appendChild(serialNumberCell);
let apiKeyCell = document.createElement("td");
apiKeyCell.textContent = apiKey;
row.appendChild(apiKeyCell);
let errorMessageCell = document.createElement("td");
errorMessageCell.colSpan = "3";
errorMessageCell.style.width = "90px";
errorMessageCell.classList.add("status-error");
errorMessageCell.textContent = "账户疑似被封禁请登录Oapi官网确认";
row.appendChild(errorMessageCell);
tableBody.appendChild(row);
if (i === apiKeys.length - 1) {
queriedApiKeys = [];
}
serialNumber++;
});
}
}
let apiUrlSelect = document.getElementById("api-url-select");
let customUrlInput = document.getElementById("custom-url-input");
apiUrlSelect.addEventListener("change", function () {
if (apiUrlSelect.value === "custom") {
customUrlInput.style.display = "inline-block";
customUrlInput.style.marginTop = "5px";
} else {
customUrlInput.style.display = "none";
}
});
</script>
</body>
</html>