Chave commited on
Commit
b54b038
·
verified ·
1 Parent(s): d6c1f9b

Upload 10 files

Browse files
Files changed (10) hide show
  1. .env.example +1 -0
  2. .gitignore +2 -0
  3. Dockerfile +9 -0
  4. Makefile +45 -0
  5. assets.go +28 -0
  6. build.sh +28 -0
  7. go.mod +39 -0
  8. go.sum +99 -0
  9. html/auth.tmpl +110 -0
  10. main.go +587 -0
.env.example ADDED
@@ -0,0 +1 @@
 
 
1
+ PORT=8081
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ copilot2gpt
2
+ .env
Dockerfile ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ FROM alpine:latest
2
+
3
+ WORKDIR /app
4
+
5
+ COPY ./dist/copilot2gpt-linux-386-v0.6.1.tar.gz .
6
+
7
+ RUN tar -xf copilot2gpt-linux-386-v0.6.1.tar.gz
8
+
9
+ CMD ["./copilot2gpt"]
Makefile ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Binary name
2
+ BINARY=copilot2gpt
3
+ # 构建目录
4
+ DIST_DIR := ./dist
5
+ # Builds the project
6
+ build:
7
+ GO111MODULE=on go build -o ${BINARY} -ldflags "-X main.Version=${VERSION}"
8
+ # GO111MODULE=on go test -v
9
+ # Installs our project: copies binaries
10
+ install:
11
+ GO111MODULE=on go install
12
+ release:
13
+ # Clean
14
+ go clean
15
+ rm -rf ${DIST_DIR}/*.gz
16
+ # Build for mac
17
+ GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
18
+ tar czvf ${DIST_DIR}/${BINARY}-mac64-${VERSION}.tar.gz ./${BINARY} .env.example
19
+ # Build for arm
20
+ go clean
21
+ CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
22
+ tar czvf ${DIST_DIR}/${BINARY}-linux-arm64-${VERSION}.tar.gz ./${BINARY} .env.example
23
+ # Build for linux386
24
+ go clean
25
+ CGO_ENABLED=0 GOOS=linux GOARCH=386 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
26
+ tar czvf ${DIST_DIR}/${BINARY}-linux-386-${VERSION}.tar.gz ./${BINARY} .env.example
27
+ # Build for linux
28
+ go clean
29
+ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
30
+ tar czvf ${DIST_DIR}/${BINARY}-linux-64-${VERSION}.tar.gz ./${BINARY} .env.example
31
+ # Build for win386
32
+ go clean
33
+ CGO_ENABLED=0 GOOS=windows GOARCH=386 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
34
+ tar czvf ${DIST_DIR}/${BINARY}-win-386-${VERSION}.tar.gz ./${BINARY}.exe .env.example
35
+ # Build for win
36
+ go clean
37
+ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 GO111MODULE=on go build -ldflags "-s -w -X main.Version=${VERSION}"
38
+ tar czvf ${DIST_DIR}/${BINARY}-win-64-${VERSION}.tar.gz ./${BINARY}.exe .env.example
39
+ go clean
40
+ # Cleans our projects: deletes binaries
41
+ clean:
42
+ go clean
43
+ rm -rf ${DIST_DIR}/*.gz
44
+
45
+ .PHONY: clean build
assets.go ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "time"
5
+
6
+ "github.com/jessevdk/go-assets"
7
+ )
8
+
9
+ var _Assetsf86c9bbb3d0fc4401249716089ed36f50263323a = "<!doctype html>\n<html>\n<h1>\n {{ .title }}\n</h1>\n打开链接 <a href=\"https://github.com/login/device\" target=\"__blank\">https://github.com/login/device</a>\n<!-- <button onclick=\"copyToClipboard()\">复制</button> -->\n<p>输入:<input type=\"text\" value=\"{{ .userCode }}\" disabled=\"disabled\" id=\"code\" size=\"10\"></p>\n<p>获取有延迟,填写完毕请勿刷新页面</p>\n<p>ghu: <input type=\"text\" value=\"获取中\" id=\"ghu\" size=\"50\"> &nbsp <button onclick=\"checkGhu(this)\">检测</button></p>\n<script>\n var deviceCode = \"{{ .deviceCode }}\"\n\n let intervalId = null;\n let count = 0;\n\n // 每5秒执行一次的函数\n function polling() {\n count++;\n console.log('Polling: ' + count);\n var xhr = new XMLHttpRequest();\n xhr.open(\"POST\", \"/auth/check\", true);\n var formData = new FormData();\n formData.append(\"deviceCode\", deviceCode);\n\n xhr.onreadystatechange = function () {\n if (xhr.status >= 200 && xhr.status < 300) {\n if (xhr.responseText.length > 0) {\n try {\n var data = JSON.parse(xhr.responseText);\n // 现在你可以使用解析后的数据了\n if (data.code == \"0\") {\n stopPolling();\n var inputElement = document.getElementById('ghu');\n inputElement.value = data.data; // 将值更改为你想要的任何字符串\n }\n } catch (e) {\n console.error(\"解析JSON数据时出错: \", e);\n }\n }\n } else {\n console.error(\"请求失败,HTTP状态码: \", xhr.status);\n }\n };\n xhr.send(formData);\n }\n\n // 15分钟后停止轮询的函数\n function stopPolling() {\n clearInterval(intervalId);\n console.log('Polling stopped');\n }\n\n // 开始轮询\n intervalId = setInterval(polling, 6 * 1000); // 5秒\n\n // 15分钟后停止轮询\n setTimeout(stopPolling, 15 * 60 * 1000); // 15分钟\n\n function copyToClipboard() {\n let text = document.getElementById('code').innerHTML;\n const copyContent = async () => {\n try {\n await navigator.clipboard.writeText(text);\n console.log('Content copied to clipboard');\n } catch (err) {\n console.error('Failed to copy: ', err);\n }\n }\n }\n\n function checkGhu(btn) {\n btn.disabled = true;\n\n var ghu = document.getElementById('ghu').value;\n var xhr = new XMLHttpRequest();\n xhr.open(\"POST\", \"/auth/checkGhu\", true);\n var formData = new FormData();\n formData.append(\"ghu\", ghu);\n\n xhr.onreadystatechange = function () {\n // 请求结束,无论成功还是失败,都重新启用按钮\n if (xhr.readyState == 4) {\n btn.disabled = false;\n if (xhr.status >= 200 && xhr.status < 300) {\n if (xhr.responseText.length > 0) {\n try {\n var data = JSON.parse(xhr.responseText);\n // 现在你可以使用解析后的数据了\n if (data.code == \"0\") {\n alert(data.data);\n } else {\n alert(data.msg);\n }\n } catch (e) {\n console.error(\"解析JSON数据时出错: \", e);\n }\n }\n } else {\n console.error(\"请求失败,HTTP状态码: \", xhr.status);\n }\n }\n\n };\n xhr.send(formData);\n }\n\n</script>\n\n</html>"
10
+
11
+ // Assets returns go-assets FileSystem
12
+ var Assets = assets.NewFileSystem(map[string][]string{"/": []string{"html"}, "/html": []string{"auth.tmpl"}}, map[string]*assets.File{
13
+ "/": &assets.File{
14
+ Path: "/",
15
+ FileMode: 0x800001ed,
16
+ Mtime: time.Unix(1706450942, 1706450942218803764),
17
+ Data: nil,
18
+ }, "/html": &assets.File{
19
+ Path: "/html",
20
+ FileMode: 0x800001ed,
21
+ Mtime: time.Unix(1706329545, 1706329545851955289),
22
+ Data: nil,
23
+ }, "/html/auth.tmpl": &assets.File{
24
+ Path: "/html/auth.tmpl",
25
+ FileMode: 0x1a4,
26
+ Mtime: time.Unix(1706451252, 1706451252248792005),
27
+ Data: []byte(_Assetsf86c9bbb3d0fc4401249716089ed36f50263323a),
28
+ }}, "")
build.sh ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ source .env
3
+ if [ -z "$VERSION" ]
4
+ then
5
+ echo "VERSION is empty"
6
+ exit 1
7
+ else
8
+ echo "VERSION is $VERSION"
9
+ fi
10
+
11
+
12
+ # Run make release with the specified version
13
+ make release VERSION="$VERSION"
14
+
15
+ # Build the docker image
16
+ sudo docker build -t copilot2gpt .
17
+
18
+ # Tag the docker image
19
+ sudo docker tag copilot2gpt ersichub/copilot2gpt:"$VERSION"
20
+
21
+ # Push the docker image to Docker Hub
22
+ sudo docker push ersichub/copilot2gpt:"$VERSION"
23
+
24
+ # Tag the docker image
25
+ sudo docker tag copilot2gpt ersichub/copilot2gpt:latest
26
+
27
+ # Push the docker image to Docker Hub
28
+ sudo docker push ersichub/copilot2gpt:latest
go.mod ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module copilot2gpt
2
+
3
+ go 1.21.0
4
+
5
+ require (
6
+ github.com/bytedance/sonic v1.10.2 // indirect
7
+ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
8
+ github.com/chenzhuoyu/iasm v0.9.1 // indirect
9
+ github.com/gabriel-vasile/mimetype v1.4.3 // indirect
10
+ github.com/gin-contrib/sse v0.1.0 // indirect
11
+ github.com/gin-gonic/gin v1.9.1 // indirect
12
+ github.com/go-playground/locales v0.14.1 // indirect
13
+ github.com/go-playground/universal-translator v0.18.1 // indirect
14
+ github.com/go-playground/validator/v10 v10.16.0 // indirect
15
+ github.com/goccy/go-json v0.10.2 // indirect
16
+ github.com/google/uuid v1.5.0 // indirect
17
+ github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 // indirect
18
+ github.com/joho/godotenv v1.5.1 // indirect
19
+ github.com/json-iterator/go v1.1.12 // indirect
20
+ github.com/klauspost/cpuid/v2 v2.2.6 // indirect
21
+ github.com/leodido/go-urn v1.2.4 // indirect
22
+ github.com/mattn/go-isatty v0.0.20 // indirect
23
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
24
+ github.com/modern-go/reflect2 v1.0.2 // indirect
25
+ github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
26
+ github.com/pelletier/go-toml/v2 v2.1.1 // indirect
27
+ github.com/tidwall/gjson v1.17.0 // indirect
28
+ github.com/tidwall/match v1.1.1 // indirect
29
+ github.com/tidwall/pretty v1.2.0 // indirect
30
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
31
+ github.com/ugorji/go/codec v1.2.12 // indirect
32
+ golang.org/x/arch v0.6.0 // indirect
33
+ golang.org/x/crypto v0.16.0 // indirect
34
+ golang.org/x/net v0.19.0 // indirect
35
+ golang.org/x/sys v0.15.0 // indirect
36
+ golang.org/x/text v0.14.0 // indirect
37
+ google.golang.org/protobuf v1.31.0 // indirect
38
+ gopkg.in/yaml.v3 v3.0.1 // indirect
39
+ )
go.sum ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
2
+ github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
3
+ github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
4
+ github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
5
+ github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
6
+ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
7
+ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
8
+ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
9
+ github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
10
+ github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
11
+ github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
12
+ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
13
+ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
14
+ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
15
+ github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
16
+ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
17
+ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
18
+ github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
19
+ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
20
+ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
21
+ github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
22
+ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
23
+ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
24
+ github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
25
+ github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
26
+ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
27
+ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
28
+ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
29
+ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
30
+ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
31
+ github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
32
+ github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
33
+ github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15 h1:cW/amwGEJK5MSKntPXRjX4dxs/nGxGT8gXKIsKFmHGc=
34
+ github.com/jessevdk/go-assets v0.0.0-20160921144138-4f4301a06e15/go.mod h1:Fdm/oWRW+CH8PRbLntksCNtmcCBximKPkVQYvmMl80k=
35
+ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
36
+ github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
37
+ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
38
+ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
39
+ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
40
+ github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
41
+ github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
42
+ github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
43
+ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
44
+ github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
45
+ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
46
+ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
47
+ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
48
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
49
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
50
+ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
51
+ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
52
+ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
53
+ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
54
+ github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
55
+ github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
56
+ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
57
+ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
58
+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
59
+ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
60
+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
61
+ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
62
+ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
63
+ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
64
+ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
65
+ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
66
+ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
67
+ github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
68
+ github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
69
+ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
70
+ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
71
+ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
72
+ github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
73
+ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
74
+ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
75
+ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
76
+ github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
77
+ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
78
+ golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
79
+ golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
80
+ golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
81
+ golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
82
+ golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
83
+ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
84
+ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
85
+ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
86
+ golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
87
+ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
88
+ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
89
+ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
90
+ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
91
+ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
92
+ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
93
+ google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
94
+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
95
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
96
+ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
97
+ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
98
+ nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
99
+ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
html/auth.tmpl ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html>
3
+ <h1>
4
+ {{ .title }}
5
+ </h1>
6
+ 打开链接 <a href="https://github.com/login/device" target="__blank">https://github.com/login/device</a>
7
+ <!-- <button onclick="copyToClipboard()">复制</button> -->
8
+ <p>输入:<input type="text" value="{{ .userCode }}" disabled="disabled" id="code" size="10"></p>
9
+ <p>获取有延迟,填写完毕请勿刷新页面</p>
10
+ <p>ghu: <input type="text" value="获取中" id="ghu" size="50"> &nbsp <button onclick="checkGhu(this)">检测</button></p>
11
+ <script>
12
+ var deviceCode = "{{ .deviceCode }}"
13
+
14
+ let intervalId = null;
15
+ let count = 0;
16
+
17
+ // 每5秒执行一次的函数
18
+ function polling() {
19
+ count++;
20
+ console.log('Polling: ' + count);
21
+ var xhr = new XMLHttpRequest();
22
+ xhr.open("POST", "/auth/check", true);
23
+ var formData = new FormData();
24
+ formData.append("deviceCode", deviceCode);
25
+
26
+ xhr.onreadystatechange = function () {
27
+ if (xhr.status >= 200 && xhr.status < 300) {
28
+ if (xhr.responseText.length > 0) {
29
+ try {
30
+ var data = JSON.parse(xhr.responseText);
31
+ // 现在你可以使用解析后的数据了
32
+ if (data.code == "0") {
33
+ stopPolling();
34
+ var inputElement = document.getElementById('ghu');
35
+ inputElement.value = data.data; // 将值更改为你想要的任何字符串
36
+ }
37
+ } catch (e) {
38
+ console.error("解析JSON数据时出错: ", e);
39
+ }
40
+ }
41
+ } else {
42
+ console.error("请求失败,HTTP状态码: ", xhr.status);
43
+ }
44
+ };
45
+ xhr.send(formData);
46
+ }
47
+
48
+ // 15分钟后停止轮询的函数
49
+ function stopPolling() {
50
+ clearInterval(intervalId);
51
+ console.log('Polling stopped');
52
+ }
53
+
54
+ // 开始轮询
55
+ intervalId = setInterval(polling, 6 * 1000); // 5秒
56
+
57
+ // 15分钟后停止轮询
58
+ setTimeout(stopPolling, 15 * 60 * 1000); // 15分钟
59
+
60
+ function copyToClipboard() {
61
+ let text = document.getElementById('code').innerHTML;
62
+ const copyContent = async () => {
63
+ try {
64
+ await navigator.clipboard.writeText(text);
65
+ console.log('Content copied to clipboard');
66
+ } catch (err) {
67
+ console.error('Failed to copy: ', err);
68
+ }
69
+ }
70
+ }
71
+
72
+ function checkGhu(btn) {
73
+ btn.disabled = true;
74
+
75
+ var ghu = document.getElementById('ghu').value;
76
+ var xhr = new XMLHttpRequest();
77
+ xhr.open("POST", "/auth/checkGhu", true);
78
+ var formData = new FormData();
79
+ formData.append("ghu", ghu);
80
+
81
+ xhr.onreadystatechange = function () {
82
+ // 请求结束,无论成功还是失败,都重新启用按钮
83
+ if (xhr.readyState == 4) {
84
+ btn.disabled = false;
85
+ if (xhr.status >= 200 && xhr.status < 300) {
86
+ if (xhr.responseText.length > 0) {
87
+ try {
88
+ var data = JSON.parse(xhr.responseText);
89
+ // 现在你可以使用解析后的数据了
90
+ if (data.code == "0") {
91
+ alert(data.data);
92
+ } else {
93
+ alert(data.msg);
94
+ }
95
+ } catch (e) {
96
+ console.error("解析JSON数据时出错: ", e);
97
+ }
98
+ }
99
+ } else {
100
+ console.error("请求失败,HTTP状态码: ", xhr.status);
101
+ }
102
+ }
103
+
104
+ };
105
+ xhr.send(formData);
106
+ }
107
+
108
+ </script>
109
+
110
+ </html>
main.go ADDED
@@ -0,0 +1,587 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "bufio"
5
+ "bytes"
6
+ "compress/gzip"
7
+ "crypto/sha256"
8
+ "encoding/hex"
9
+ "encoding/json"
10
+ "fmt"
11
+ "html/template"
12
+ "io"
13
+ "log"
14
+ "net/http"
15
+ "net/url"
16
+ "os"
17
+ "strings"
18
+ "time"
19
+
20
+ "github.com/gin-gonic/gin"
21
+ "github.com/google/uuid"
22
+ "github.com/joho/godotenv"
23
+ "github.com/patrickmn/go-cache"
24
+ "github.com/tidwall/gjson"
25
+ )
26
+
27
+ const tokenUrl = "https://api.github.com/copilot_internal/v2/token"
28
+ const completionsUrl = "https://api.githubcopilot.com/chat/completions"
29
+ const embeddingsUrl = "https://api.githubcopilot.com/embeddings"
30
+
31
+ var requestUrl = ""
32
+
33
+ type Model struct {
34
+ ID string `json:"id"`
35
+ Object string `json:"object"`
36
+ Created int `json:"created"`
37
+ OwnedBy string `json:"owned_by"`
38
+ Root string `json:"root"`
39
+ Parent *string `json:"parent"`
40
+ }
41
+
42
+ type ModelList struct {
43
+ Object string `json:"object"`
44
+ Data []Model `json:"data"`
45
+ }
46
+
47
+ var version = "v0.6.1"
48
+ var port = "8081"
49
+ var client_id = "Iv1.b507a08c87ecfe98"
50
+
51
+ func main() {
52
+ err := godotenv.Load()
53
+ if err == nil {
54
+ // 从环境变量中获取配置值
55
+ portEnv := os.Getenv("PORT")
56
+ if portEnv != "" {
57
+ port = portEnv
58
+ version = os.Getenv("VERSION")
59
+ }
60
+ }
61
+
62
+ log.Printf("Server is running on port %s, version: %s\n", port, version)
63
+ log.Printf("client_id: %s\n", client_id)
64
+
65
+ gin.SetMode(gin.ReleaseMode)
66
+
67
+ r := gin.Default()
68
+
69
+ // CORS 中间件
70
+ r.Use(func(c *gin.Context) {
71
+ c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
72
+ c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
73
+ c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
74
+ c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
75
+
76
+ if c.Request.Method == "OPTIONS" {
77
+ c.AbortWithStatus(204)
78
+ return
79
+ }
80
+
81
+ c.Next()
82
+ })
83
+
84
+ r.GET("/", func(c *gin.Context) {
85
+ c.String(http.StatusOK, `
86
+ curl --location 'http://127.0.0.1:8081/v1/chat/completions' \
87
+ --header 'Content-Type: application/json' \
88
+ --header 'Authorization: Bearer ghu_xxx' \
89
+ --data '{
90
+ "model": "gpt-4",
91
+ "messages": [{"role": "user", "content": "hi"}]
92
+ }'`)
93
+ })
94
+
95
+ r.GET("/v1/models", func(c *gin.Context) {
96
+ c.JSON(http.StatusOK, models())
97
+ })
98
+
99
+ r.POST("/v1/chat/completions", func(c *gin.Context) {
100
+ c.Header("Cache-Control", "no-cache, must-revalidate")
101
+ c.Header("Connection", "keep-alive")
102
+
103
+ requestUrl = completionsUrl
104
+ forwardRequest(c)
105
+ })
106
+
107
+ r.POST("/v1/embeddings", func(c *gin.Context) {
108
+ c.Header("Cache-Control", "no-cache, must-revalidate")
109
+ c.Header("Connection", "keep-alive")
110
+
111
+ requestUrl = embeddingsUrl
112
+ forwardRequest(c)
113
+ })
114
+
115
+ // 获取ghu
116
+
117
+ t, err := loadTemplate()
118
+ if err != nil {
119
+ panic(err)
120
+ }
121
+ r.SetHTMLTemplate(t)
122
+
123
+ r.GET("/auth", func(c *gin.Context) {
124
+ // 获取设备授权码
125
+ deviceCode, userCode, err := getDeviceCode()
126
+ if err != nil {
127
+ c.String(http.StatusOK, "获取设备码失败:"+err.Error())
128
+ return
129
+ }
130
+
131
+ // 使用 deviceCode 和 userCode
132
+ fmt.Println("Device Code: ", deviceCode)
133
+ fmt.Println("User Code: ", userCode)
134
+
135
+ c.HTML(http.StatusOK, "/html/auth.tmpl", gin.H{
136
+ "title": "Get Copilot Token",
137
+ "deviceCode": deviceCode,
138
+ "userCode": userCode,
139
+ })
140
+ })
141
+
142
+ r.POST("/auth/check", func(c *gin.Context) {
143
+ returnData := map[string]string{
144
+ "code": "1",
145
+ "msg": "",
146
+ "data": "",
147
+ }
148
+
149
+ deviceCode := c.PostForm("deviceCode")
150
+ if deviceCode == "" {
151
+ returnData["msg"] = "device code null"
152
+ c.JSON(http.StatusOK, returnData)
153
+ return
154
+ }
155
+ token, err := checkUserCode(deviceCode)
156
+ if err != nil {
157
+ returnData["msg"] = err.Error()
158
+ c.JSON(http.StatusOK, returnData)
159
+ return
160
+ }
161
+ if token == "" {
162
+ returnData["msg"] = "token null"
163
+ c.JSON(http.StatusOK, returnData)
164
+ return
165
+ }
166
+ returnData["code"] = "0"
167
+ returnData["msg"] = "success"
168
+ returnData["data"] = token
169
+ c.JSON(http.StatusOK, returnData)
170
+ return
171
+ })
172
+
173
+ r.POST("/auth/checkGhu", func(c *gin.Context) {
174
+ returnData := map[string]string{
175
+ "code": "1",
176
+ "msg": "",
177
+ "data": "",
178
+ }
179
+
180
+ ghu := c.PostForm("ghu")
181
+ if ghu == "" {
182
+ returnData["msg"] = "ghu null"
183
+ c.JSON(http.StatusOK, returnData)
184
+ return
185
+ }
186
+ if !strings.HasPrefix(ghu, "gh") {
187
+ returnData["msg"] = "ghu 格式错误"
188
+ c.JSON(http.StatusOK, returnData)
189
+ return
190
+ }
191
+
192
+ info := checkGhuToken(ghu)
193
+
194
+ returnData["code"] = "0"
195
+ returnData["msg"] = "success"
196
+ returnData["data"] = info
197
+ c.JSON(http.StatusOK, returnData)
198
+ return
199
+ })
200
+
201
+ r.Run(":" + port)
202
+ }
203
+
204
+ func forwardRequest(c *gin.Context) {
205
+ var jsonBody map[string]interface{}
206
+ if err := c.ShouldBindJSON(&jsonBody); err != nil {
207
+ c.JSON(http.StatusBadRequest, gin.H{"error": "Request body is missing or not in JSON format"})
208
+ return
209
+ }
210
+
211
+ ghuToken := strings.Split(c.GetHeader("Authorization"), " ")[1]
212
+
213
+ if !strings.HasPrefix(ghuToken, "gh") {
214
+ c.JSON(http.StatusBadRequest, gin.H{"error": "auth token not found"})
215
+ log.Printf("token 格式错误:%s\n", ghuToken)
216
+ return
217
+ }
218
+
219
+ // 检查 token 是否有效
220
+ if !checkToken(ghuToken) {
221
+ c.JSON(http.StatusBadRequest, gin.H{"error": "auth token is invalid"})
222
+ log.Printf("token 无效:%s\n", ghuToken)
223
+ return
224
+ }
225
+ accToken, err := getAccToken(ghuToken)
226
+ if accToken == "" {
227
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
228
+ return
229
+ }
230
+
231
+ sessionId := fmt.Sprintf("%s%d", uuid.New().String(), time.Now().UnixNano()/int64(time.Millisecond))
232
+ machineID := sha256.Sum256([]byte(uuid.New().String()))
233
+ machineIDStr := hex.EncodeToString(machineID[:])
234
+ accHeaders := getAccHeaders(accToken, uuid.New().String(), sessionId, machineIDStr)
235
+ client := &http.Client{}
236
+
237
+ jsonData, err := json.Marshal(jsonBody)
238
+ if err != nil {
239
+ c.AbortWithError(http.StatusInternalServerError, err)
240
+ return
241
+ }
242
+
243
+ isStream := gjson.GetBytes(jsonData, "stream").String() == "true"
244
+
245
+ req, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(jsonData))
246
+ if err != nil {
247
+ c.AbortWithError(http.StatusInternalServerError, err)
248
+ }
249
+
250
+ for key, value := range accHeaders {
251
+ req.Header.Add(key, value)
252
+ }
253
+
254
+ resp, err := client.Do(req)
255
+ if err != nil {
256
+ return
257
+ }
258
+ defer resp.Body.Close()
259
+
260
+ if resp.StatusCode != http.StatusOK {
261
+ bodyBytes, err := io.ReadAll(resp.Body)
262
+ if err != nil {
263
+ log.Fatal(err)
264
+ }
265
+ bodyString := string(bodyBytes)
266
+ log.Printf("对话失败:%d, %s ", resp.StatusCode, bodyString)
267
+ cache := cache.New(5*time.Minute, 10*time.Minute)
268
+ cache.Delete(ghuToken)
269
+ c.AbortWithError(resp.StatusCode, fmt.Errorf(bodyString))
270
+ return
271
+ }
272
+
273
+ c.Header("Content-Type", "application/json; charset=utf-8")
274
+
275
+ if isStream {
276
+ returnStream(c, resp)
277
+ } else {
278
+ returnJson(c, resp)
279
+ }
280
+ return
281
+ }
282
+
283
+ func returnJson(c *gin.Context, resp *http.Response) {
284
+ c.Header("Content-Type", "application/json; charset=utf-8")
285
+
286
+ body, err := io.ReadAll(resp.Body.(io.Reader))
287
+ if err != nil {
288
+ c.AbortWithError(http.StatusInternalServerError, err)
289
+ return
290
+ }
291
+
292
+ c.Writer.Write(body)
293
+ return
294
+ }
295
+
296
+ func returnStream(c *gin.Context, resp *http.Response) {
297
+ c.Header("Content-Type", "text/event-stream; charset=utf-8")
298
+
299
+ // 创建一个新的扫描器
300
+ scanner := bufio.NewScanner(resp.Body)
301
+
302
+ // 使用Scan方法来读取流
303
+ for scanner.Scan() {
304
+ line := scanner.Bytes()
305
+
306
+ // 替换 "content":null 为 "content":""
307
+ modifiedLine := bytes.Replace(line, []byte(`"content":null`), []byte(`"content":""`), -1)
308
+
309
+ // 将修改后的数据写入响应体
310
+ if _, err := c.Writer.Write(modifiedLine); err != nil {
311
+ c.AbortWithError(http.StatusInternalServerError, err)
312
+ return
313
+ }
314
+
315
+ // 添加一个换行符
316
+ if _, err := c.Writer.Write([]byte("\n")); err != nil {
317
+ c.AbortWithError(http.StatusInternalServerError, err)
318
+ return
319
+ }
320
+ }
321
+
322
+ if scanner.Err() != nil {
323
+ // 处理来自扫描器的任何错误
324
+ c.AbortWithError(http.StatusInternalServerError, scanner.Err())
325
+ return
326
+ }
327
+ return
328
+ }
329
+
330
+ func models() ModelList {
331
+ jsonStr := `{
332
+ "object": "list",
333
+ "data": [
334
+ {"id": "text-search-babbage-doc-001","object": "model","created": 1651172509,"owned_by": "openai-dev"},
335
+ {"id": "gpt-4-0613","object": "model","created": 1686588896,"owned_by": "openai"},
336
+ {"id": "gpt-4", "object": "model", "created": 1687882411, "owned_by": "openai"},
337
+ {"id": "babbage", "object": "model", "created": 1649358449, "owned_by": "openai"},
338
+ {"id": "gpt-3.5-turbo-0613", "object": "model", "created": 1686587434, "owned_by": "openai"},
339
+ {"id": "text-babbage-001", "object": "model", "created": 1649364043, "owned_by": "openai"},
340
+ {"id": "gpt-3.5-turbo", "object": "model", "created": 1677610602, "owned_by": "openai"},
341
+ {"id": "gpt-3.5-turbo-1106", "object": "model", "created": 1698959748, "owned_by": "system"},
342
+ {"id": "curie-instruct-beta", "object": "model", "created": 1649364042, "owned_by": "openai"},
343
+ {"id": "gpt-3.5-turbo-0301", "object": "model", "created": 1677649963, "owned_by": "openai"},
344
+ {"id": "gpt-3.5-turbo-16k-0613", "object": "model", "created": 1685474247, "owned_by": "openai"},
345
+ {"id": "text-embedding-ada-002", "object": "model", "created": 1671217299, "owned_by": "openai-internal"},
346
+ {"id": "davinci-similarity", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
347
+ {"id": "curie-similarity", "object": "model", "created": 1651172510, "owned_by": "openai-dev"},
348
+ {"id": "babbage-search-document", "object": "model", "created": 1651172510, "owned_by": "openai-dev"},
349
+ {"id": "curie-search-document", "object": "model", "created": 1651172508, "owned_by": "openai-dev"},
350
+ {"id": "babbage-code-search-code", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
351
+ {"id": "ada-code-search-text", "object": "model", "created": 1651172510, "owned_by": "openai-dev"},
352
+ {"id": "text-search-curie-query-001", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
353
+ {"id": "text-davinci-002", "object": "model", "created": 1649880484, "owned_by": "openai"},
354
+ {"id": "ada", "object": "model", "created": 1649357491, "owned_by": "openai"},
355
+ {"id": "text-ada-001", "object": "model", "created": 1649364042, "owned_by": "openai"},
356
+ {"id": "ada-similarity", "object": "model", "created": 1651172507, "owned_by": "openai-dev"},
357
+ {"id": "code-search-ada-code-001", "object": "model", "created": 1651172507, "owned_by": "openai-dev"},
358
+ {"id": "text-similarity-ada-001", "object": "model", "created": 1651172505, "owned_by": "openai-dev"},
359
+ {"id": "text-davinci-edit-001", "object": "model", "created": 1649809179, "owned_by": "openai"},
360
+ {"id": "code-davinci-edit-001", "object": "model", "created": 1649880484, "owned_by": "openai"},
361
+ {"id": "text-search-curie-doc-001", "object": "model", "created": 1651172509, "owned_by": "openai-dev"},
362
+ {"id": "text-curie-001", "object": "model", "created": 1649364043, "owned_by": "openai"},
363
+ {"id": "curie", "object": "model", "created": 1649359874, "owned_by": "openai"},
364
+ {"id": "davinci", "object": "model", "created": 1649359874, "owned_by": "openai"},
365
+ {"id": "gpt-4-0314", "object": "model", "created": 1687882410, "owned_by": "openai"}
366
+ ]
367
+ }`
368
+
369
+ var modelList ModelList
370
+ json.Unmarshal([]byte(jsonStr), &modelList)
371
+ return modelList
372
+ }
373
+
374
+ func getAccToken(ghuToken string) (string, error) {
375
+ var accToken = ""
376
+
377
+ cache := cache.New(15*time.Minute, 60*time.Minute)
378
+ cacheToken, found := cache.Get(ghuToken)
379
+ if found {
380
+ accToken = cacheToken.(string)
381
+ } else {
382
+ client := &http.Client{}
383
+ req, err := http.NewRequest("GET", tokenUrl, nil)
384
+ if err != nil {
385
+ return accToken, err
386
+ }
387
+
388
+ headers := getHeaders(ghuToken)
389
+
390
+ for key, value := range headers {
391
+ req.Header.Add(key, value)
392
+ }
393
+
394
+ resp, err := client.Do(req)
395
+ if err != nil {
396
+ return accToken, err
397
+ }
398
+ defer resp.Body.Close()
399
+
400
+ var reader interface{}
401
+ switch resp.Header.Get("Content-Encoding") {
402
+ case "gzip":
403
+ reader, err = gzip.NewReader(resp.Body)
404
+ if err != nil {
405
+ return accToken, fmt.Errorf("数据解压失败")
406
+ }
407
+ default:
408
+ reader = resp.Body
409
+ }
410
+
411
+ body, err := io.ReadAll(reader.(io.Reader))
412
+ if err != nil {
413
+ return accToken, fmt.Errorf("数据读取失败")
414
+ }
415
+ if resp.StatusCode == http.StatusOK {
416
+ accToken = gjson.GetBytes(body, "token").String()
417
+ if accToken == "" {
418
+ return accToken, fmt.Errorf("acc_token 未返回")
419
+ }
420
+ cache.Set(ghuToken, accToken, 14*time.Minute)
421
+ } else {
422
+ log.Printf("获取 acc_token 请求失败:%d, %s ", resp.StatusCode, string(body))
423
+ return accToken, fmt.Errorf("获取 acc_token 请求失败: %d", resp.StatusCode)
424
+ }
425
+ }
426
+ return accToken, nil
427
+ }
428
+
429
+ func checkToken(ghuToken string) bool {
430
+ client := &http.Client{}
431
+
432
+ url := "https://api.github.com/user"
433
+ req, err := http.NewRequest("GET", url, nil)
434
+ if err != nil {
435
+ fmt.Println(err)
436
+ return false
437
+ }
438
+ req.Header.Add("Accept", "application/vnd.github+json")
439
+ req.Header.Add("Authorization", "Bearer "+ghuToken)
440
+ req.Header.Add("X-GitHub-Api-Version", "2022-11-28")
441
+
442
+ resp, err := client.Do(req)
443
+ if err != nil {
444
+ fmt.Println(err)
445
+ return false
446
+ }
447
+ defer resp.Body.Close()
448
+ return resp.StatusCode == http.StatusOK
449
+ }
450
+
451
+ func getHeaders(ghoToken string) map[string]string {
452
+ return map[string]string{
453
+ "Host": "api.github.com",
454
+ "Authorization": "token " + ghoToken,
455
+
456
+ "Editor-Version": "vscode/1.85.1",
457
+ "Editor-Plugin-Version": "copilot-chat/0.11.1",
458
+ "User-Agent": "GitHubCopilotChat/0.11.1",
459
+ "Accept": "*/*",
460
+ "Accept-Encoding": "gzip, deflate, br",
461
+ }
462
+ }
463
+
464
+ func getAccHeaders(accessToken, uuid string, sessionId string, machineId string) map[string]string {
465
+ return map[string]string{
466
+ "Host": "api.githubcopilot.com",
467
+ "Authorization": "Bearer " + accessToken,
468
+ "X-Request-Id": uuid,
469
+ "X-Github-Api-Version": "2023-07-07",
470
+ "Vscode-Sessionid": sessionId,
471
+ "Vscode-machineid": machineId,
472
+ "Editor-Version": "vscode/1.85.1",
473
+ "Editor-Plugin-Version": "copilot-chat/0.11.1",
474
+ "Openai-Organization": "github-copilot",
475
+ "Openai-Intent": "conversation-panel",
476
+ "Content-Type": "application/json",
477
+ "User-Agent": "GitHubCopilotChat/0.11.1",
478
+ "Copilot-Integration-Id": "vscode-chat",
479
+ "Accept": "*/*",
480
+ "Accept-Encoding": "gzip, deflate, br",
481
+ }
482
+ }
483
+
484
+ func getDeviceCode() (string, string, error) {
485
+ requestUrl := "https://github.com/login/device/code"
486
+
487
+ body := url.Values{}
488
+ headers := map[string]string{
489
+ "Accept": "application/json",
490
+ }
491
+
492
+ body.Set("client_id", client_id)
493
+ res, err := handleRequest("POST", body, requestUrl, headers)
494
+ deviceCode := gjson.Get(res, "device_code").String()
495
+ userCode := gjson.Get(res, "user_code").String()
496
+
497
+ if deviceCode == "" {
498
+ return "", "", fmt.Errorf("device code null")
499
+ }
500
+ if userCode == "" {
501
+ return "", "", fmt.Errorf("user code null")
502
+ }
503
+ return deviceCode, userCode, err
504
+ }
505
+
506
+ func checkUserCode(deviceCode string) (string, error) {
507
+ requestUrl := "https://github.com/login/oauth/access_token"
508
+ body := url.Values{}
509
+ headers := map[string]string{
510
+ "Accept": "application/json",
511
+ }
512
+
513
+ body.Set("client_id", client_id)
514
+ body.Set("device_code", deviceCode)
515
+ body.Set("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
516
+ res, err := handleRequest("POST", body, requestUrl, headers)
517
+ if err != nil {
518
+ return "", err
519
+ }
520
+ token := gjson.Get(res, "access_token").String()
521
+ return token, nil
522
+ }
523
+
524
+ func checkGhuToken(ghuToken string) string {
525
+ requestUrl := "https://api.github.com/copilot_internal/v2/token"
526
+ body := url.Values{}
527
+ headers := map[string]string{
528
+ "Authorization": "Bearer " + ghuToken,
529
+ "editor-version": "JetBrains-IU/232.10203.10",
530
+ "editor-plugin-version": "copilot-intellij/1.3.3.3572",
531
+ "User-Agent": "GithubCopilot/1.129.0",
532
+ "Host": "api.github.com",
533
+ }
534
+
535
+ res, err := handleRequest("GET", body, requestUrl, headers)
536
+ if err != nil {
537
+ return "查询失败"
538
+ }
539
+ info := gjson.Get(res, "sku").String()
540
+ if info != "" {
541
+ return info
542
+ } else {
543
+ return "未订阅"
544
+ }
545
+ }
546
+
547
+ func handleRequest(method string, body url.Values, requestUrl string, headers map[string]string) (string, error) {
548
+ client := &http.Client{}
549
+
550
+ req, err := http.NewRequest(method, requestUrl, bytes.NewBuffer([]byte(body.Encode())))
551
+ if err != nil {
552
+ return "", err
553
+ }
554
+ for key, value := range headers {
555
+ req.Header.Add(key, value)
556
+ }
557
+ resp, err := client.Do(req)
558
+ if err != nil {
559
+ return "", err
560
+ }
561
+ defer resp.Body.Close()
562
+
563
+ respBody, err := io.ReadAll(resp.Body)
564
+ if err != nil {
565
+ return "", fmt.Errorf("status code: %d, read body error", resp.StatusCode)
566
+ }
567
+
568
+ return string(respBody), nil
569
+ }
570
+
571
+ func loadTemplate() (*template.Template, error) {
572
+ t := template.New("")
573
+ for name, file := range Assets.Files {
574
+ if file.IsDir() || !strings.HasSuffix(name, ".tmpl") {
575
+ continue
576
+ }
577
+ h, err := io.ReadAll(file)
578
+ if err != nil {
579
+ return nil, err
580
+ }
581
+ t, err = t.New(name).Parse(string(h))
582
+ if err != nil {
583
+ return nil, err
584
+ }
585
+ }
586
+ return t, nil
587
+ }