seventeen 1.0.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.
- seventeen-1.0.0/PKG-INFO +40 -0
- seventeen-1.0.0/README.md +24 -0
- seventeen-1.0.0/pyproject.toml +27 -0
- seventeen-1.0.0/setup.cfg +4 -0
- seventeen-1.0.0/seventeen/__init__.py +2 -0
- seventeen-1.0.0/seventeen/cli.py +30 -0
- seventeen-1.0.0/seventeen/main.py +214 -0
- seventeen-1.0.0/seventeen.egg-info/PKG-INFO +40 -0
- seventeen-1.0.0/seventeen.egg-info/SOURCES.txt +11 -0
- seventeen-1.0.0/seventeen.egg-info/dependency_links.txt +1 -0
- seventeen-1.0.0/seventeen.egg-info/entry_points.txt +2 -0
- seventeen-1.0.0/seventeen.egg-info/requires.txt +7 -0
- seventeen-1.0.0/seventeen.egg-info/top_level.txt +2 -0
seventeen-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: seventeen
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AutoTyper + AI Screen Answer Tool — type clipboard text and get AI answers from your screen
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: autotyper,ai,screen,ocr,hotkey,automation
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: keyboard
|
|
10
|
+
Requires-Dist: pyperclip
|
|
11
|
+
Requires-Dist: pyautogui
|
|
12
|
+
Requires-Dist: pytesseract
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: pillow
|
|
15
|
+
Requires-Dist: pystray
|
|
16
|
+
|
|
17
|
+
# seventeen
|
|
18
|
+
|
|
19
|
+
AutoTyper + AI Screen Answer Tool for Windows.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pip install seventeen
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> Also install Tesseract OCR: https://github.com/UB-Mannheim/tesseract/wiki
|
|
28
|
+
|
|
29
|
+
## Run
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
seventeen
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **F9** — Start / Pause / Resume typing clipboard text
|
|
36
|
+
- **F10** — Scan screen, get AI answer, copy to clipboard
|
|
37
|
+
- **F8** — Auto-type the AI answer at cursor
|
|
38
|
+
- **Ctrl+F10** — Quit
|
|
39
|
+
|
|
40
|
+
A green icon appears in your system tray when running.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# seventeen
|
|
2
|
+
|
|
3
|
+
AutoTyper + AI Screen Answer Tool for Windows.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install seventeen
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> Also install Tesseract OCR: https://github.com/UB-Mannheim/tesseract/wiki
|
|
12
|
+
|
|
13
|
+
## Run
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
seventeen
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **F9** — Start / Pause / Resume typing clipboard text
|
|
20
|
+
- **F10** — Scan screen, get AI answer, copy to clipboard
|
|
21
|
+
- **F8** — Auto-type the AI answer at cursor
|
|
22
|
+
- **Ctrl+F10** — Quit
|
|
23
|
+
|
|
24
|
+
A green icon appears in your system tray when running.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "seventeen"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "AutoTyper + AI Screen Answer Tool — type clipboard text and get AI answers from your screen"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
keywords = ["autotyper", "ai", "screen", "ocr", "hotkey", "automation"]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"keyboard",
|
|
15
|
+
"pyperclip",
|
|
16
|
+
"pyautogui",
|
|
17
|
+
"pytesseract",
|
|
18
|
+
"requests",
|
|
19
|
+
"pillow",
|
|
20
|
+
"pystray",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
seventeen = "seventeen.cli:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
where = ["."]
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
"""Entry point: runs seventeen as admin with no terminal window."""
|
|
8
|
+
script = os.path.join(os.path.dirname(__file__), "main.py")
|
|
9
|
+
|
|
10
|
+
# Check if already admin
|
|
11
|
+
try:
|
|
12
|
+
import ctypes
|
|
13
|
+
is_admin = ctypes.windll.shell32.IsUserAnAdmin()
|
|
14
|
+
except Exception:
|
|
15
|
+
is_admin = False
|
|
16
|
+
|
|
17
|
+
if not is_admin:
|
|
18
|
+
# Re-launch as admin
|
|
19
|
+
import ctypes
|
|
20
|
+
ctypes.windll.shell32.ShellExecuteW(
|
|
21
|
+
None, "runas", sys.executable, f'"{script}"', None, 0
|
|
22
|
+
)
|
|
23
|
+
else:
|
|
24
|
+
# Already admin — run directly
|
|
25
|
+
from seventeen.main import run
|
|
26
|
+
run()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
main()
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import keyboard
|
|
2
|
+
import pyperclip
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
import requests
|
|
6
|
+
import pyautogui
|
|
7
|
+
import pytesseract
|
|
8
|
+
import subprocess
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import traceback
|
|
12
|
+
import re
|
|
13
|
+
import pystray
|
|
14
|
+
from PIL import Image, ImageDraw
|
|
15
|
+
|
|
16
|
+
# ===== settings =====
|
|
17
|
+
TYPE_TOGGLE_KEY = "f9"
|
|
18
|
+
AI_KEY = "f10"
|
|
19
|
+
TYPE_ANSWER_KEY = "f8"
|
|
20
|
+
QUIT_KEY = "ctrl+f10"
|
|
21
|
+
CHAR_DELAY = 0.0
|
|
22
|
+
START_DELAY = 2.0
|
|
23
|
+
GROQ_API_KEY = "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"
|
|
24
|
+
GROQ_MODEL = "openai/gpt-oss-20b"
|
|
25
|
+
GROQ_URL = "https://api.groq.com/openai/v1/chat/completions"
|
|
26
|
+
# ====================
|
|
27
|
+
|
|
28
|
+
# Try to find tesseract
|
|
29
|
+
TESS_PATHS = [
|
|
30
|
+
r"C:\Program Files\Tesseract-OCR\tesseract.exe",
|
|
31
|
+
r"C:\Program Files (x86)\Tesseract-OCR\tesseract.exe",
|
|
32
|
+
r"C:\Users\%s\AppData\Local\Programs\Tesseract-OCR\tesseract.exe" % os.getenv("USERNAME",""),
|
|
33
|
+
]
|
|
34
|
+
for p in TESS_PATHS:
|
|
35
|
+
if os.path.exists(p):
|
|
36
|
+
pytesseract.pytesseract.tesseract_cmd = p
|
|
37
|
+
break
|
|
38
|
+
|
|
39
|
+
LOG_FILE = os.path.join(os.path.expanduser("~"), "seventeen_log.txt")
|
|
40
|
+
|
|
41
|
+
stop_event = threading.Event()
|
|
42
|
+
typing_lock = threading.Lock()
|
|
43
|
+
ai_lock = threading.Lock()
|
|
44
|
+
typing_active = False
|
|
45
|
+
current_text = ""
|
|
46
|
+
current_pos = 0
|
|
47
|
+
latest_answer = ""
|
|
48
|
+
tray_icon = None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def log(msg):
|
|
52
|
+
line = f"[{time.strftime('%H:%M:%S')}] {msg}"
|
|
53
|
+
print(line, flush=True)
|
|
54
|
+
try:
|
|
55
|
+
with open(LOG_FILE, "a", encoding="utf-8") as f:
|
|
56
|
+
f.write(line + "\n")
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def make_icon(color="green"):
|
|
62
|
+
img = Image.new("RGB", (64, 64), color=(30, 30, 30))
|
|
63
|
+
draw = ImageDraw.Draw(img)
|
|
64
|
+
c = (0, 200, 0) if color == "green" else (200, 200, 0) if color == "yellow" else (200, 0, 0)
|
|
65
|
+
draw.ellipse([8, 8, 56, 56], fill=c)
|
|
66
|
+
return img
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def toast(message):
|
|
70
|
+
log(f"TOAST: {message}")
|
|
71
|
+
if tray_icon:
|
|
72
|
+
def _n():
|
|
73
|
+
try:
|
|
74
|
+
tray_icon.notify(message, "AutoTyper")
|
|
75
|
+
except Exception as e:
|
|
76
|
+
log(f"Toast error: {e}")
|
|
77
|
+
threading.Thread(target=_n, daemon=True).start()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def type_character(char):
|
|
81
|
+
if char == '\n':
|
|
82
|
+
keyboard.press_and_release('shift+enter')
|
|
83
|
+
elif char == '\t':
|
|
84
|
+
keyboard.press_and_release('tab')
|
|
85
|
+
else:
|
|
86
|
+
keyboard.write(char, delay=0)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def auto_type(text_override=None):
|
|
90
|
+
global typing_active, current_text, current_pos
|
|
91
|
+
if not typing_lock.acquire(blocking=False):
|
|
92
|
+
return
|
|
93
|
+
typing_active = True
|
|
94
|
+
stop_event.clear()
|
|
95
|
+
try:
|
|
96
|
+
if text_override is not None:
|
|
97
|
+
current_text = text_override
|
|
98
|
+
current_pos = 0
|
|
99
|
+
elif current_pos == 0 or current_pos >= len(current_text):
|
|
100
|
+
current_text = pyperclip.paste()
|
|
101
|
+
current_pos = 0
|
|
102
|
+
if not current_text:
|
|
103
|
+
toast("Clipboard empty!")
|
|
104
|
+
return
|
|
105
|
+
time.sleep(START_DELAY)
|
|
106
|
+
while current_pos < len(current_text):
|
|
107
|
+
if stop_event.is_set():
|
|
108
|
+
return
|
|
109
|
+
type_character(current_text[current_pos])
|
|
110
|
+
current_pos += 1
|
|
111
|
+
toast("✅ Done")
|
|
112
|
+
current_text = ""
|
|
113
|
+
current_pos = 0
|
|
114
|
+
except Exception:
|
|
115
|
+
log(traceback.format_exc())
|
|
116
|
+
finally:
|
|
117
|
+
typing_active = False
|
|
118
|
+
typing_lock.release()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def toggle_typing():
|
|
122
|
+
global typing_active
|
|
123
|
+
if typing_active:
|
|
124
|
+
stop_event.set()
|
|
125
|
+
toast("⏸ Paused")
|
|
126
|
+
else:
|
|
127
|
+
remaining = len(current_text) - current_pos if current_text else 0
|
|
128
|
+
toast("▶ Resuming..." if remaining > 0 else "▶ Typing...")
|
|
129
|
+
threading.Thread(target=auto_type, daemon=True).start()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def extract_and_answer():
|
|
133
|
+
if not ai_lock.acquire(blocking=False):
|
|
134
|
+
return
|
|
135
|
+
toast("🔍 Scanning...")
|
|
136
|
+
threading.Thread(target=_do_ai, daemon=True).start()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _do_ai():
|
|
140
|
+
global latest_answer
|
|
141
|
+
try:
|
|
142
|
+
image = pyautogui.screenshot()
|
|
143
|
+
screen_text = pytesseract.image_to_string(image).strip()[:3000]
|
|
144
|
+
if not screen_text:
|
|
145
|
+
toast("No text found")
|
|
146
|
+
return
|
|
147
|
+
resp = requests.post(
|
|
148
|
+
GROQ_URL,
|
|
149
|
+
headers={"Authorization": f"Bearer {GROQ_API_KEY}"},
|
|
150
|
+
json={
|
|
151
|
+
"model": GROQ_MODEL,
|
|
152
|
+
"messages": [
|
|
153
|
+
{
|
|
154
|
+
"role": "system",
|
|
155
|
+
"content": (
|
|
156
|
+
"You are a direct answer engine. "
|
|
157
|
+
"If the question has multiple choice options (A, B, C, D), reply with ONLY the correct option letter. "
|
|
158
|
+
"If it is a coding or text question, reply with ONLY the answer, no explanation. "
|
|
159
|
+
"Never add extra text."
|
|
160
|
+
)
|
|
161
|
+
},
|
|
162
|
+
{"role": "user", "content": screen_text}
|
|
163
|
+
],
|
|
164
|
+
"temperature": 0.1
|
|
165
|
+
},
|
|
166
|
+
timeout=60
|
|
167
|
+
)
|
|
168
|
+
resp.raise_for_status()
|
|
169
|
+
latest_answer = resp.json()["choices"][0]["message"]["content"].strip()
|
|
170
|
+
pyperclip.copy(latest_answer)
|
|
171
|
+
mcq = re.match(r'^([A-Da-d])[\.\):]?$', latest_answer.strip())
|
|
172
|
+
toast(mcq.group(1).upper() if mcq else "✅")
|
|
173
|
+
except Exception as e:
|
|
174
|
+
log(traceback.format_exc())
|
|
175
|
+
toast(f"❌ {str(e)[:50]}")
|
|
176
|
+
finally:
|
|
177
|
+
ai_lock.release()
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def type_answer():
|
|
181
|
+
if latest_answer:
|
|
182
|
+
toast("⌨ Typing...")
|
|
183
|
+
threading.Thread(target=auto_type, args=(latest_answer,), daemon=True).start()
|
|
184
|
+
else:
|
|
185
|
+
toast("Press F10 first!")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def quit_app(icon, item):
|
|
189
|
+
icon.stop()
|
|
190
|
+
os._exit(0)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def run():
|
|
194
|
+
global tray_icon
|
|
195
|
+
log("seventeen AutoTyper starting...")
|
|
196
|
+
keyboard.add_hotkey(TYPE_TOGGLE_KEY, toggle_typing)
|
|
197
|
+
keyboard.add_hotkey(AI_KEY, extract_and_answer)
|
|
198
|
+
keyboard.add_hotkey(TYPE_ANSWER_KEY, type_answer)
|
|
199
|
+
|
|
200
|
+
icon_img = make_icon("green")
|
|
201
|
+
menu = pystray.Menu(
|
|
202
|
+
pystray.MenuItem("seventeen AutoTyper", None, enabled=False),
|
|
203
|
+
pystray.MenuItem("F9=Type | F10=AI | F8=Answer", None, enabled=False),
|
|
204
|
+
pystray.Menu.SEPARATOR,
|
|
205
|
+
pystray.MenuItem("Quit", quit_app)
|
|
206
|
+
)
|
|
207
|
+
tray_icon = pystray.Icon("seventeen", icon_img, "seventeen 🟢", menu)
|
|
208
|
+
|
|
209
|
+
def _startup():
|
|
210
|
+
time.sleep(2)
|
|
211
|
+
toast("🟢 Running! F9=Type F10=AI F8=Answer")
|
|
212
|
+
threading.Thread(target=_startup, daemon=True).start()
|
|
213
|
+
|
|
214
|
+
tray_icon.run()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: seventeen
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AutoTyper + AI Screen Answer Tool — type clipboard text and get AI answers from your screen
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: autotyper,ai,screen,ocr,hotkey,automation
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: keyboard
|
|
10
|
+
Requires-Dist: pyperclip
|
|
11
|
+
Requires-Dist: pyautogui
|
|
12
|
+
Requires-Dist: pytesseract
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: pillow
|
|
15
|
+
Requires-Dist: pystray
|
|
16
|
+
|
|
17
|
+
# seventeen
|
|
18
|
+
|
|
19
|
+
AutoTyper + AI Screen Answer Tool for Windows.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pip install seventeen
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> Also install Tesseract OCR: https://github.com/UB-Mannheim/tesseract/wiki
|
|
28
|
+
|
|
29
|
+
## Run
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
seventeen
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **F9** — Start / Pause / Resume typing clipboard text
|
|
36
|
+
- **F10** — Scan screen, get AI answer, copy to clipboard
|
|
37
|
+
- **F8** — Auto-type the AI answer at cursor
|
|
38
|
+
- **Ctrl+F10** — Quit
|
|
39
|
+
|
|
40
|
+
A green icon appears in your system tray when running.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
seventeen/__init__.py
|
|
4
|
+
seventeen/cli.py
|
|
5
|
+
seventeen/main.py
|
|
6
|
+
seventeen.egg-info/PKG-INFO
|
|
7
|
+
seventeen.egg-info/SOURCES.txt
|
|
8
|
+
seventeen.egg-info/dependency_links.txt
|
|
9
|
+
seventeen.egg-info/entry_points.txt
|
|
10
|
+
seventeen.egg-info/requires.txt
|
|
11
|
+
seventeen.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|