recticode 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
recticode-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: recticode
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author: VulcanWM
|
|
6
|
+
Author-email: VulcanWM <vulcanwmemail@gmail.com>
|
|
7
|
+
Requires-Dist: pytest>=9.0.2
|
|
8
|
+
Requires-Dist: requests>=2.32.5
|
|
9
|
+
Requires-Dist: typer>=0.24.1
|
|
10
|
+
Requires-Python: >=3.13
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# Recticode
|
|
14
|
+
|
|
15
|
+
It's cool
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "recticode"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "VulcanWM", email = "vulcanwmemail@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"pytest>=9.0.2",
|
|
12
|
+
"requests>=2.32.5",
|
|
13
|
+
"typer>=0.24.1",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
recticode = "recticode.main:app"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["uv_build>=0.10.11,<0.11.0"]
|
|
21
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
def get_user_code():
|
|
7
|
+
url = "https://github.com/login/device/code"
|
|
8
|
+
|
|
9
|
+
values = {
|
|
10
|
+
"client_id": "Ov23lizX5wFSpnR89gKJ",
|
|
11
|
+
"scope": "read:user repo"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
headers = {
|
|
15
|
+
"Accept": "application/json"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
r = requests.post(url, data=values, headers=headers)
|
|
19
|
+
|
|
20
|
+
r_json = r.json()
|
|
21
|
+
|
|
22
|
+
device_code = r_json["device_code"]
|
|
23
|
+
verification_uri = r_json['verification_uri']
|
|
24
|
+
expires_in = r_json['expires_in']
|
|
25
|
+
user_code = r_json['user_code']
|
|
26
|
+
interval = r_json['interval']
|
|
27
|
+
|
|
28
|
+
return device_code, verification_uri, expires_in, user_code, interval
|
|
29
|
+
|
|
30
|
+
def get_access_token():
|
|
31
|
+
try:
|
|
32
|
+
config_dir = os.path.expanduser("~/.config/recticode")
|
|
33
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
34
|
+
|
|
35
|
+
token_path = os.path.join(config_dir, "token.json")
|
|
36
|
+
with open(token_path) as f:
|
|
37
|
+
token = json.load(f)["access_token"]
|
|
38
|
+
return token
|
|
39
|
+
except:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def save_access_token(access_token):
|
|
44
|
+
try:
|
|
45
|
+
config_dir = os.path.expanduser("~/.config/recticode")
|
|
46
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
47
|
+
|
|
48
|
+
token_path = os.path.join(config_dir, "token.json")
|
|
49
|
+
|
|
50
|
+
data = {"access_token": access_token}
|
|
51
|
+
|
|
52
|
+
with open(token_path, "w") as f:
|
|
53
|
+
json.dump(data, f)
|
|
54
|
+
|
|
55
|
+
return True
|
|
56
|
+
except:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def clone_repo(url):
|
|
61
|
+
cmd = ["git", "clone", url]
|
|
62
|
+
subprocess.run(cmd, check=True)
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
def get_user_data(access_token):
|
|
66
|
+
check_headers = {
|
|
67
|
+
"Authorization": f"Bearer {access_token}",
|
|
68
|
+
"Accept": "application/vnd.github+json"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
check_post = requests.get("https://api.github.com/user", headers=check_headers)
|
|
72
|
+
|
|
73
|
+
user_json = check_post.json()
|
|
74
|
+
username = user_json['login']
|
|
75
|
+
email = user_json['email']
|
|
76
|
+
name = user_json['name']
|
|
77
|
+
|
|
78
|
+
return username, email, name
|
|
79
|
+
|
|
80
|
+
def remove_access_token():
|
|
81
|
+
try:
|
|
82
|
+
config_dir = os.path.expanduser("~/.config/recticode")
|
|
83
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
84
|
+
|
|
85
|
+
token_path = os.path.join(config_dir, "token.json")
|
|
86
|
+
|
|
87
|
+
os.remove(token_path)
|
|
88
|
+
|
|
89
|
+
return True
|
|
90
|
+
except:
|
|
91
|
+
return False
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich import print
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from time import sleep
|
|
5
|
+
from recticode.git_code import clone_repo, save_access_token, get_access_token, get_user_data, remove_access_token, get_user_code
|
|
6
|
+
import requests
|
|
7
|
+
import os.path
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
app = typer.Typer()
|
|
12
|
+
|
|
13
|
+
def require_login(func):
|
|
14
|
+
@wraps(func)
|
|
15
|
+
def wrapper(*args, **kwargs):
|
|
16
|
+
access_token = get_access_token()
|
|
17
|
+
if not access_token:
|
|
18
|
+
print("[red]You must login first[/red]")
|
|
19
|
+
raise typer.Exit()
|
|
20
|
+
return func(*args, **kwargs)
|
|
21
|
+
return wrapper
|
|
22
|
+
|
|
23
|
+
@app.command()
|
|
24
|
+
def login():
|
|
25
|
+
access_token = get_access_token()
|
|
26
|
+
if not access_token:
|
|
27
|
+
headers = {
|
|
28
|
+
"Accept": "application/json"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
device_code, verification_uri, expires_in, user_code, interval = get_user_code()
|
|
32
|
+
|
|
33
|
+
print(f"Go to {verification_uri} and enter code [bold]{user_code}[/bold]")
|
|
34
|
+
|
|
35
|
+
for i in range(expires_in // interval):
|
|
36
|
+
poll_values = {
|
|
37
|
+
"client_id": "Ov23lizX5wFSpnR89gKJ",
|
|
38
|
+
"device_code": device_code,
|
|
39
|
+
"grant_type": "urn:ietf:params:oauth:grant-type:device_code"
|
|
40
|
+
}
|
|
41
|
+
poll_post = requests.post("https://github.com/login/oauth/access_token", data=poll_values, headers=headers)
|
|
42
|
+
|
|
43
|
+
poll_json = poll_post.json()
|
|
44
|
+
|
|
45
|
+
if "access_token" in poll_json:
|
|
46
|
+
access_token = poll_json['access_token']
|
|
47
|
+
|
|
48
|
+
username, email, name = get_user_data(access_token=access_token)
|
|
49
|
+
|
|
50
|
+
save_access_token(access_token=access_token)
|
|
51
|
+
print(f"[green]✓ Logged in as {name}[/green]")
|
|
52
|
+
break
|
|
53
|
+
|
|
54
|
+
if "error" in poll_json:
|
|
55
|
+
if poll_json["error"] == "authorization_pending":
|
|
56
|
+
sleep(interval)
|
|
57
|
+
continue
|
|
58
|
+
elif poll_json["error"] == "slow_down":
|
|
59
|
+
interval += 5
|
|
60
|
+
sleep(interval)
|
|
61
|
+
elif poll_json["error"] == "expired_token":
|
|
62
|
+
print("[red]Device code expired, try login again[/red]")
|
|
63
|
+
break
|
|
64
|
+
else:
|
|
65
|
+
username, email, name = get_user_data(access_token=access_token)
|
|
66
|
+
|
|
67
|
+
print("You are already logged in")
|
|
68
|
+
|
|
69
|
+
@app.command()
|
|
70
|
+
@require_login
|
|
71
|
+
def whoami():
|
|
72
|
+
access_token = get_access_token()
|
|
73
|
+
username, email, name = get_user_data(access_token=access_token)
|
|
74
|
+
print("Hi", name)
|
|
75
|
+
|
|
76
|
+
@app.command()
|
|
77
|
+
def logout():
|
|
78
|
+
access_token = get_access_token()
|
|
79
|
+
if not access_token:
|
|
80
|
+
print("You are not logged in")
|
|
81
|
+
else:
|
|
82
|
+
remove_access_token()
|
|
83
|
+
|
|
84
|
+
print("[yellow]✓ Logged out[/yellow]")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@app.command()
|
|
88
|
+
@require_login
|
|
89
|
+
def start(challenge_name):
|
|
90
|
+
access_token = get_access_token()
|
|
91
|
+
|
|
92
|
+
request_url = "https://api.recticode.com/challenge_repo/"
|
|
93
|
+
|
|
94
|
+
response = requests.get(request_url + challenge_name + "?token=" + access_token)
|
|
95
|
+
if response.status_code == 200:
|
|
96
|
+
if "error" in response.json():
|
|
97
|
+
print(response.json()['error'])
|
|
98
|
+
else:
|
|
99
|
+
clone_repo(response.json()['repo_name'])
|
|
100
|
+
print(f"[green]Challenge cloned![/green] cd {challenge_name} to start")
|
|
101
|
+
else:
|
|
102
|
+
print("Error occurred")
|
|
103
|
+
|
|
104
|
+
@app.command()
|
|
105
|
+
@require_login
|
|
106
|
+
def list_challenges():
|
|
107
|
+
access_token = get_access_token()
|
|
108
|
+
|
|
109
|
+
request_url = "https://api.recticode.com/list_challenges?token=" + access_token
|
|
110
|
+
|
|
111
|
+
response = requests.get(request_url)
|
|
112
|
+
if response.status_code == 200:
|
|
113
|
+
challenges = response.json()['challenges']
|
|
114
|
+
|
|
115
|
+
print("[bold][yellow]All Challenges[/yellow][/bold]")
|
|
116
|
+
for challenge in challenges:
|
|
117
|
+
print(f"[bold]{challenge['name']}[/bold]: {challenge['description']} ({challenge['language']})")
|
|
118
|
+
else:
|
|
119
|
+
print("Error occurred")
|
|
120
|
+
|
|
121
|
+
@app.command()
|
|
122
|
+
@require_login
|
|
123
|
+
def check():
|
|
124
|
+
if os.path.exists("challenge.json"):
|
|
125
|
+
env = os.environ.copy()
|
|
126
|
+
env["PYTHONPATH"] = "."
|
|
127
|
+
subprocess.run(
|
|
128
|
+
[sys.executable, "-m", "pytest", "tests/"],
|
|
129
|
+
env=env
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
print("This is not a valid challenge")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
app()
|