L1B3RT4S/main.py
2025-02-12 11:33:59 -08:00

177 lines
5.5 KiB
Python

import threading
import requests
import json
import time
import sys
import http.server
token = None
def setup():
resp = requests.post('https://github.com/login/device/code', headers={
'accept': 'application/json',
'editor-version': 'Neovim/0.6.1',
'editor-plugin-version': 'copilot.vim/1.16.0',
'content-type': 'application/json',
'user-agent': 'GithubCopilot/1.155.0',
'accept-encoding': 'gzip,deflate,br'
}, data='{"client_id":"Iv1.b507a08c87ecfe98","scope":"read:user"}')
# Parse the response json, isolating the device_code, user_code, and verification_uri
resp_json = resp.json()
device_code = resp_json.get('device_code')
user_code = resp_json.get('user_code')
verification_uri = resp_json.get('verification_uri')
# Print the user code and verification uri
print(f'Please visit {verification_uri} and enter code {user_code} to authenticate.')
while True:
time.sleep(5)
resp = requests.post('https://github.com/login/oauth/access_token', headers={
'accept': 'application/json',
'editor-version': 'Neovim/0.6.1',
'editor-plugin-version': 'copilot.vim/1.16.0',
'content-type': 'application/json',
'user-agent': 'GithubCopilot/1.155.0',
'accept-encoding': 'gzip,deflate,br'
}, data=f'{{"client_id":"Iv1.b507a08c87ecfe98","device_code":"{device_code}","grant_type":"urn:ietf:params:oauth:grant-type:device_code"}}')
# Parse the response json, isolating the access_token
resp_json = resp.json()
access_token = resp_json.get('access_token')
if access_token:
break
# Save the access token to a file
with open('.copilot_token', 'w') as f:
f.write(access_token)
print('Authentication success!')
def get_token():
global token
# Check if the .copilot_token file exists
while True:
try:
with open('.copilot_token', 'r') as f:
access_token = f.read()
break
except FileNotFoundError:
setup()
# Get a session with the access token
resp = requests.get('https://api.github.com/copilot_internal/v2/token', headers={
'authorization': f'token {access_token}',
'editor-version': 'Neovim/0.6.1',
'editor-plugin-version': 'copilot.vim/1.16.0',
'user-agent': 'GithubCopilot/1.155.0'
})
# Parse the response json, isolating the token
resp_json = resp.json()
token = resp_json.get('token')
def token_thread():
global token
while True:
get_token()
time.sleep(25 * 60)
def copilot(prompt, language='python'):
global token
# If the token is None, get a new one
if token is None or is_token_invalid(token):
get_token()
try:
resp = requests.post('https://copilot-proxy.githubusercontent.com/v1/engines/copilot-codex/completions', headers={'authorization': f'Bearer {token}'}, json={
'prompt': prompt,
'suffix': '',
'max_tokens': 1000,
'temperature': 0,
'top_p': 1,
'n': 1,
'stop': ['\n'],
'nwo': 'github/copilot.vim',
'stream': True,
'extra': {
'language': language
}
})
except requests.exceptions.ConnectionError:
return ''
result = ''
# Parse the response text, splitting it by newlines
resp_text = resp.text.split('\n')
for line in resp_text:
# If the line contains a completion, print it
if line.startswith('data: {'):
# Parse the completion from the line as json
json_completion = json.loads(line[6:])
completion = json_completion.get('choices')[0].get('text')
if completion:
result += completion
else:
result += '\n'
return result
# Check if the token is invalid through the exp field
def is_token_invalid(token):
if token is None or 'exp' not in token or extract_exp_value(token) <= time.time():
return True
return False
def extract_exp_value(token):
pairs = token.split(';')
for pair in pairs:
key, value = pair.split('=')
if key.strip() == 'exp':
return int(value.strip())
return None
class HTTPRequestHandler(http.server.BaseHTTPRequestHandler):
def do_POST(self):
# Get the request body
content_length = int(self.headers['Content-Length'])
body = self.rfile.read(content_length)
# Parse the request body as json
body_json = json.loads(body)
# Get the prompt from the request body
prompt = body_json.get('prompt')
language = body_json.get('language', 'python')
# Get the completion from the copilot function
completion = copilot(prompt, language)
# Send the completion as the response
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(completion.encode())
def main():
# Every 25 minutes, get a new token
threading.Thread(target=token_thread).start()
# Get the port to listen on from the command line
if len(sys.argv) < 2:
port = 8080
else:
port = int(sys.argv[1])
# Start the http server
httpd = http.server.HTTPServer(('0.0.0.0', port), HTTPRequestHandler)
print(f'Listening on port 0.0.0.0:{port}...')
httpd.serve_forever()
if __name__ == '__main__':
main()