当你拥有一个github copilot,就可以免费使用gpt-4了
文章目录
前提条件
- 拥有一个github账号且账号已经购买了github copilot
- 在cloudflare有一个账号,最好已经有一个域名
步骤
-
新增一个KV namespace,名称随意,但是要记住,后面会用到

-
在cloudflare中添加一个worker,代码如下:
const GithubCopilotChat = GITHUB_COPILOT_CHAT;
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': '*',
}
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 200,
headers: corsHeaders,
})
}
if (request.method === 'GET') {
let data = {
object: "list",
data: [
{ "id": "gpt-4", "object": "model", "created": 1687882411, "owned_by": "openai" },
{ "id": "gpt-3.5-turbo", "object": "model", "created": 1677610602, "owned_by": "openai" },
],
}
return new Response(JSON.stringify(data), {
status: 200,
headers: corsHeaders,
})
}
if (request.method !== 'POST') {
return new Response('Method Not Allowed', {
status: 405,
headers: corsHeaders,
})
}
try {
const authorizationHeader = request.headers.get('Authorization') || ''
const match = authorizationHeader.match(/^Bearer\s+(.*)$/)
if (!match) {
throw new Error('Missing or malformed Authorization header')
}
const githubToken = match[1]
const copilotToken = await getCopilotToken(githubToken)
const headers = await createHeaders(copilotToken);
const requestData = await request.json()
const openAIResponse = await fetch('https://api.githubcopilot.com/chat/completions', {
method: 'POST',
headers: {
...headers,
},
body: typeof requestData === 'object' ? JSON.stringify(requestData) : '{}',
})
const { readable, writable } = new TransformStream();
streamResponse(openAIResponse, writable, requestData);
return new Response(readable, {
headers: {
...corsHeaders,
'Content-Type': 'text/event-stream; charset=utf-8',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
}
});
} catch (error) {
return new Response(error.message, {
status: 500,
headers: corsHeaders,
});
}
}
async function streamResponse(openAIResponse, writable, requestData) {
const reader = openAIResponse.body.getReader();
const writer = writable.getWriter();
const encoder = new TextEncoder();
const decoder = new TextDecoder("utf-8");
let buffer = "";
function push() {
reader.read().then(({ done, value }) => {
if (done) {
writer.close();
return;
}
const chunk = decoder.decode(value, { stream: true });
let to_send = "";
(buffer + chunk).split("data: ").forEach((raw) => {
if (raw === "")
return;
else if (!raw.endsWith("\n\n"))
buffer = raw;
else if (raw.startsWith("[DONE]"))
to_send += "data: [DONE]\n\n";
else {
let data = JSON.parse(raw);
if (data.choices[0].delta?.content === null)
data.choices[0].delta.content = "";
if (data.choices[0].finish_reason === undefined)
data.choices[0].finish_reason = null;
if (data.model === undefined && requestData.model !== undefined)
data.model = requestData.model;
if (data.object === undefined)
data.object = "chat.completion.chunk";
to_send += `data: ${JSON.stringify(data)}\n\n`;
}
});
writer.write(encoder.encode(to_send));
push();
}).catch(error => {
console.error(error);
writer.close();
});
}
push();
}
async function getCopilotToken(githubToken) {
let tokenData = await GithubCopilotChat.get(githubToken, "json");
if (tokenData && tokenData.expires_at * 1000 > Date.now()) {
return tokenData.token;
}
const getTokenUrl = 'https://api.github.com/copilot_internal/v2/token';
const response = await fetch(getTokenUrl, {
headers: {
'Authorization': `token ${githubToken}`,
'User-Agent': 'GitHubCopilotChat/0.11.1',
}
});
if (!response.ok) {
const errorResponse = await response.text();
console.error('Failed to get Copilot token from GitHub:', errorResponse);
throw new Error('Failed to get Copilot token from GitHub:');
}
const data = await response.json();
await GithubCopilotChat.put(githubToken, JSON.stringify({ token: data.token, expires_at: data.expires_at }), {
expirationTtl: data.expires_at
});
return data.token;
}
async function createHeaders(copilotToken) {
function genHexStr(length) {
const arr = new Uint8Array(length / 2);
crypto.getRandomValues(arr);
return Array.from(arr, (byte) => byte.toString(16).padStart(2, '0')).join('');
}
return {
'Authorization': `Bearer ${copilotToken}`,
'X-Request-Id': `${genHexStr(8)}-${genHexStr(4)}-${genHexStr(4)}-${genHexStr(4)}-${genHexStr(12)}`,
'X-Github-Api-Version': "2023-07-07",
'Vscode-Sessionid': `${genHexStr(8)}-${genHexStr(4)}-${genHexStr(4)}-${genHexStr(4)}-${genHexStr(25)}`,
'Vscode-Machineid': genHexStr(64),
'Editor-Version': 'vscode/1.85.1',
'Editor-Plugin-Version': 'copilot-chat/0.11.1',
'Openai-Organization': 'github-copilot',
'Openai-Intent': 'conversation-panel',
'Content-Type': 'text/event-stream; charset=utf-8',
'User-Agent': 'GitHubCopilotChat/0.11.1',
'Accept': '*/*',
'Accept-Encoding': 'gzip,deflate,br',
'Connection': 'close'
};
}
-
给worker添加一个变量,名称为GITHUB_COPILOT_CHAT,并绑定刚刚创建的KV namespace的名称

-
绑定域名

-
打开 https://cocopilot.org/copilot/token,按提示操作获取token
-
按照教程部署一个 ChatGPT-Next-Web到vercel,并绑定域名
-
打开自己部署的ChatGPT-Next-Web,API地址填写刚刚绑定的域名,token填写刚刚获取的token

参考资料
- https://github.com/wpv-chan/cf-copilot-service
- https://github.com/aaamoon/copilot-gpt4-service
文章作者 pengxiaochao
上次更新 2024-01-05
许可协议 不允许任何形式转载。



