otpilot 2.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.
otpilot-2.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OTPilot Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
otpilot-2.0.0/PKG-INFO ADDED
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.4
2
+ Name: otpilot
3
+ Version: 2.0.0
4
+ Summary: Background CLI utility that copies OTPs from Gmail to clipboard on hotkey trigger
5
+ Author: Jenil
6
+ Author-email: Jenil <mail2jenil.pokar19@gmail.com>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2026 OTPilot Contributors
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+
29
+ Project-URL: Homepage, https://github.com/CodeWithJenil/otpilot
30
+ Project-URL: Documentation, https://github.com/CodeWithJenil/otpilot/tree/main/docs
31
+ Project-URL: Repository, https://github.com/CodeWithJenil/otpilot
32
+ Project-URL: Issues, https://github.com/CodeWithJenil/otpilot/issues
33
+ Keywords: otp,gmail,clipboard,hotkey,automation,2fa
34
+ Classifier: Development Status :: 4 - Beta
35
+ Classifier: Intended Audience :: End Users/Desktop
36
+ Classifier: License :: OSI Approved :: MIT License
37
+ Classifier: Operating System :: OS Independent
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Programming Language :: Python :: 3.8
40
+ Classifier: Programming Language :: Python :: 3.9
41
+ Classifier: Programming Language :: Python :: 3.10
42
+ Classifier: Programming Language :: Python :: 3.11
43
+ Classifier: Programming Language :: Python :: 3.12
44
+ Classifier: Topic :: Communications :: Email
45
+ Classifier: Topic :: Utilities
46
+ Requires-Python: >=3.8
47
+ Description-Content-Type: text/markdown
48
+ License-File: LICENSE
49
+ Requires-Dist: google-api-python-client>=2.100.0
50
+ Requires-Dist: google-auth-oauthlib>=1.1.0
51
+ Requires-Dist: google-auth>=2.23.0
52
+ Requires-Dist: pynput>=1.7.6
53
+ Requires-Dist: pystray>=0.19.5
54
+ Requires-Dist: Pillow>=10.0.0
55
+ Requires-Dist: pyperclip>=1.8.2
56
+ Requires-Dist: plyer>=2.1.0
57
+ Requires-Dist: click>=8.1.0
58
+ Requires-Dist: rich>=13.0.0
59
+ Provides-Extra: dev
60
+ Requires-Dist: pytest; extra == "dev"
61
+ Requires-Dist: build; extra == "dev"
62
+ Requires-Dist: twine; extra == "dev"
63
+ Dynamic: author
64
+ Dynamic: license-file
65
+ Dynamic: requires-python
66
+
67
+ ![OTPilot Demo](assets/demo.gif)
68
+
69
+ # OTPilot
70
+
71
+ > Press a hotkey. Your OTP is already copied.
72
+ ```bash
73
+ pip install otpilot
74
+ ```
75
+
76
+ ---
77
+
78
+ <p align="center">
79
+ <strong>✈ OTPilot v2.0</strong>
80
+ </p>
81
+
82
+ <p align="center">
83
+ <em>The new era of private OTP fetching. Background utility that copies OTPs from Gmail to your clipboard with a single hotkey.</em>
84
+ </p>
85
+
86
+ <p align="center">
87
+ <a href="https://github.com/codewithjenil/otpilot">GitHub</a> •
88
+ <a href="#installation">Install</a> •
89
+ <a href="#quickstart">Quickstart</a> •
90
+ <a href="SETUP.md">OAuth Setup</a> •
91
+ <a href="CONTRIBUTING.md">Contributing</a>
92
+ </p>
93
+
94
+ ---
95
+
96
+ ## How It Works
97
+
98
+ 1. **OTPilot sits silently in your system tray** — no windows, no polling, zero CPU usage.
99
+ 2. **Press your hotkey** (e.g. `Ctrl+Shift+O`) — OTPilot fetches your last 10 emails from Gmail and extracts the OTP.
100
+ 3. **OTP is copied to your clipboard** — just paste it. A desktop notification confirms the action.
101
+
102
+ ## Installation
103
+
104
+ ```bash
105
+ pip install otpilot
106
+ ```
107
+
108
+ ### Requirements
109
+
110
+ - **Python 3.8+**
111
+ - **Gmail account**
112
+ - **Google Cloud project** with Gmail API enabled (free — [setup guide](SETUP.md))
113
+ - **OS**: macOS, Windows, or Linux
114
+
115
+ > **Linux users**: Make sure `xclip` or `xsel` is installed for clipboard support.
116
+ > ```bash
117
+ > sudo apt install xclip # Debian/Ubuntu
118
+ > ```
119
+
120
+ ## Quickstart
121
+
122
+ ### 1. Get Your Credentials
123
+
124
+ Follow the steps above (or the [full guide](SETUP.md)) to download your `credentials.json` file.
125
+
126
+ ### 2. Run Setup
127
+
128
+ ```bash
129
+ otpilot setup
130
+ ```
131
+
132
+ The wizard will:
133
+ - Ask for the path to your `credentials.json` file
134
+ - Open your browser for Google sign-in (one-time authorization)
135
+ - Let you set your preferred hotkey
136
+ - Save your configuration
137
+
138
+ ### 3. Start OTPilot
139
+
140
+ ```bash
141
+ otpilot start
142
+ ```
143
+
144
+ OTPilot runs in the background with a system tray icon. Press your hotkey whenever you need an OTP.
145
+
146
+ ### 4. Daily Use
147
+
148
+ 1. Receive an OTP email in Gmail
149
+ 2. Press your hotkey (default: `Ctrl+Shift+O`)
150
+ 3. Paste the OTP — done!
151
+
152
+ ## Getting Your credentials.json
153
+
154
+ Before using OTPilot, you need to download a `credentials.json` file from Google Cloud Console. This is a one-time, ~5 minute process.
155
+
156
+ 1. Go to [console.cloud.google.com](https://console.cloud.google.com)
157
+ 2. Create a new project (or use an existing one)
158
+ 3. Enable the **Gmail API**
159
+ 4. Go to **APIs & Services → Credentials**
160
+ 5. Create an **OAuth 2.0 Client ID** (Application type: **Desktop app**)
161
+ 6. Click **Download JSON** to save the `credentials.json` file
162
+
163
+ > **Your credentials never leave your machine.** The file is stored locally at `~/.otpilot/credentials.json`.
164
+
165
+ For detailed, step-by-step instructions with screenshots, see [**SETUP.md**](SETUP.md).
166
+
167
+ ## CLI Commands
168
+
169
+ | Command | Description |
170
+ | ------------------ | ---------------------------------------- |
171
+ | `otpilot setup` | Run or re-run the interactive setup |
172
+ | `otpilot start` | Start the background service |
173
+ | `otpilot status` | Show auth state, hotkey, and config path |
174
+ | `otpilot version` | Print the version number |
175
+
176
+ ## Configuration
177
+
178
+ OTPilot stores its configuration at `~/.otpilot/config.json`:
179
+
180
+ ```json
181
+ {
182
+ "hotkey": "ctrl+shift+o",
183
+ "notify_on_copy": true,
184
+ "otp_max_age_minutes": 10,
185
+ "email_fetch_count": 10
186
+ }
187
+ ```
188
+
189
+ | Field | Type | Default | Description |
190
+ | --------------------- | ------ | ---------------- | ---------------------------------------------- |
191
+ | `hotkey` | string | `ctrl+shift+o` | Global hotkey combination |
192
+ | `notify_on_copy` | bool | `true` | Show desktop notification when OTP is copied |
193
+ | `otp_max_age_minutes` | int | `10` | Ignore emails older than this (minutes) |
194
+ | `email_fetch_count` | int | `10` | Number of recent emails to scan |
195
+
196
+ ### Files Stored Locally
197
+
198
+ | File | Purpose |
199
+ | -------------------------------- | ------------------------------------ |
200
+ | `~/.otpilot/config.json` | Your hotkey and settings |
201
+ | `~/.otpilot/credentials.json` | Your Google OAuth credentials |
202
+ | `~/.otpilot/token.json` | OAuth session token (auto-generated) |
203
+
204
+ ## Platform Support
205
+
206
+ | Platform | Status | Notes |
207
+ | -------- | ------ | -------------------------------------- |
208
+ | macOS | ✅ | Full support |
209
+ | Windows | ✅ | Full support |
210
+ | Linux | ✅ | Requires `xclip`/`xsel` for clipboard |
211
+
212
+ ## How OTP Extraction Works
213
+
214
+ OTPilot scans the subject line and body of your recent emails for:
215
+
216
+ - **4–8 digit standalone numbers** near context words like *OTP*, *code*, *verify*, *verification*, *one-time*, *passcode*, *authentication*
217
+ - Only emails within the configured time window are considered
218
+ - If multiple OTPs are found, the most recent one wins
219
+
220
+ ## Security & Privacy
221
+
222
+ - **Your credentials stay local**: `credentials.json` is stored on your machine at `~/.otpilot/` and is never uploaded anywhere.
223
+ - **Read-only access**: OTPilot only reads your emails — it cannot send, delete, or modify anything.
224
+ - **Local storage only**: Your OAuth token is stored locally at `~/.otpilot/token.json`. Nothing is sent to any third-party server.
225
+ - **On-demand only**: Emails are fetched only when you press the hotkey. There is no background polling.
226
+
227
+ ## Troubleshooting
228
+
229
+ | Issue | Solution |
230
+ | --------------------------------- | -------------------------------------------------------------- |
231
+ | "credentials.json not found" | Run `otpilot setup` and provide your credentials file |
232
+ | "Invalid credentials file" | Re-download credentials.json from Google Cloud Console |
233
+ | "Not authenticated" error | Run `otpilot setup` to re-authenticate |
234
+ | No OTP found | Check `otp_max_age_minutes` — the email might be too old |
235
+ | Clipboard not working (Linux) | Install `xclip`: `sudo apt install xclip` |
236
+ | Hotkey not working | Run `otpilot setup` to reconfigure the hotkey |
237
+ | Tray icon not visible | Check your system tray / menu bar settings |
238
+
239
+ ## Contributing
240
+
241
+ Contributions are welcome! See [**CONTRIBUTING.md**](CONTRIBUTING.md) for guidelines on setting up the development environment, running tests, and submitting pull requests.
242
+
243
+ ## License
244
+
245
+ [MIT](../LICENSE) — use it freely.
@@ -0,0 +1,179 @@
1
+ ![OTPilot Demo](assets/demo.gif)
2
+
3
+ # OTPilot
4
+
5
+ > Press a hotkey. Your OTP is already copied.
6
+ ```bash
7
+ pip install otpilot
8
+ ```
9
+
10
+ ---
11
+
12
+ <p align="center">
13
+ <strong>✈ OTPilot v2.0</strong>
14
+ </p>
15
+
16
+ <p align="center">
17
+ <em>The new era of private OTP fetching. Background utility that copies OTPs from Gmail to your clipboard with a single hotkey.</em>
18
+ </p>
19
+
20
+ <p align="center">
21
+ <a href="https://github.com/codewithjenil/otpilot">GitHub</a> •
22
+ <a href="#installation">Install</a> •
23
+ <a href="#quickstart">Quickstart</a> •
24
+ <a href="SETUP.md">OAuth Setup</a> •
25
+ <a href="CONTRIBUTING.md">Contributing</a>
26
+ </p>
27
+
28
+ ---
29
+
30
+ ## How It Works
31
+
32
+ 1. **OTPilot sits silently in your system tray** — no windows, no polling, zero CPU usage.
33
+ 2. **Press your hotkey** (e.g. `Ctrl+Shift+O`) — OTPilot fetches your last 10 emails from Gmail and extracts the OTP.
34
+ 3. **OTP is copied to your clipboard** — just paste it. A desktop notification confirms the action.
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install otpilot
40
+ ```
41
+
42
+ ### Requirements
43
+
44
+ - **Python 3.8+**
45
+ - **Gmail account**
46
+ - **Google Cloud project** with Gmail API enabled (free — [setup guide](SETUP.md))
47
+ - **OS**: macOS, Windows, or Linux
48
+
49
+ > **Linux users**: Make sure `xclip` or `xsel` is installed for clipboard support.
50
+ > ```bash
51
+ > sudo apt install xclip # Debian/Ubuntu
52
+ > ```
53
+
54
+ ## Quickstart
55
+
56
+ ### 1. Get Your Credentials
57
+
58
+ Follow the steps above (or the [full guide](SETUP.md)) to download your `credentials.json` file.
59
+
60
+ ### 2. Run Setup
61
+
62
+ ```bash
63
+ otpilot setup
64
+ ```
65
+
66
+ The wizard will:
67
+ - Ask for the path to your `credentials.json` file
68
+ - Open your browser for Google sign-in (one-time authorization)
69
+ - Let you set your preferred hotkey
70
+ - Save your configuration
71
+
72
+ ### 3. Start OTPilot
73
+
74
+ ```bash
75
+ otpilot start
76
+ ```
77
+
78
+ OTPilot runs in the background with a system tray icon. Press your hotkey whenever you need an OTP.
79
+
80
+ ### 4. Daily Use
81
+
82
+ 1. Receive an OTP email in Gmail
83
+ 2. Press your hotkey (default: `Ctrl+Shift+O`)
84
+ 3. Paste the OTP — done!
85
+
86
+ ## Getting Your credentials.json
87
+
88
+ Before using OTPilot, you need to download a `credentials.json` file from Google Cloud Console. This is a one-time, ~5 minute process.
89
+
90
+ 1. Go to [console.cloud.google.com](https://console.cloud.google.com)
91
+ 2. Create a new project (or use an existing one)
92
+ 3. Enable the **Gmail API**
93
+ 4. Go to **APIs & Services → Credentials**
94
+ 5. Create an **OAuth 2.0 Client ID** (Application type: **Desktop app**)
95
+ 6. Click **Download JSON** to save the `credentials.json` file
96
+
97
+ > **Your credentials never leave your machine.** The file is stored locally at `~/.otpilot/credentials.json`.
98
+
99
+ For detailed, step-by-step instructions with screenshots, see [**SETUP.md**](SETUP.md).
100
+
101
+ ## CLI Commands
102
+
103
+ | Command | Description |
104
+ | ------------------ | ---------------------------------------- |
105
+ | `otpilot setup` | Run or re-run the interactive setup |
106
+ | `otpilot start` | Start the background service |
107
+ | `otpilot status` | Show auth state, hotkey, and config path |
108
+ | `otpilot version` | Print the version number |
109
+
110
+ ## Configuration
111
+
112
+ OTPilot stores its configuration at `~/.otpilot/config.json`:
113
+
114
+ ```json
115
+ {
116
+ "hotkey": "ctrl+shift+o",
117
+ "notify_on_copy": true,
118
+ "otp_max_age_minutes": 10,
119
+ "email_fetch_count": 10
120
+ }
121
+ ```
122
+
123
+ | Field | Type | Default | Description |
124
+ | --------------------- | ------ | ---------------- | ---------------------------------------------- |
125
+ | `hotkey` | string | `ctrl+shift+o` | Global hotkey combination |
126
+ | `notify_on_copy` | bool | `true` | Show desktop notification when OTP is copied |
127
+ | `otp_max_age_minutes` | int | `10` | Ignore emails older than this (minutes) |
128
+ | `email_fetch_count` | int | `10` | Number of recent emails to scan |
129
+
130
+ ### Files Stored Locally
131
+
132
+ | File | Purpose |
133
+ | -------------------------------- | ------------------------------------ |
134
+ | `~/.otpilot/config.json` | Your hotkey and settings |
135
+ | `~/.otpilot/credentials.json` | Your Google OAuth credentials |
136
+ | `~/.otpilot/token.json` | OAuth session token (auto-generated) |
137
+
138
+ ## Platform Support
139
+
140
+ | Platform | Status | Notes |
141
+ | -------- | ------ | -------------------------------------- |
142
+ | macOS | ✅ | Full support |
143
+ | Windows | ✅ | Full support |
144
+ | Linux | ✅ | Requires `xclip`/`xsel` for clipboard |
145
+
146
+ ## How OTP Extraction Works
147
+
148
+ OTPilot scans the subject line and body of your recent emails for:
149
+
150
+ - **4–8 digit standalone numbers** near context words like *OTP*, *code*, *verify*, *verification*, *one-time*, *passcode*, *authentication*
151
+ - Only emails within the configured time window are considered
152
+ - If multiple OTPs are found, the most recent one wins
153
+
154
+ ## Security & Privacy
155
+
156
+ - **Your credentials stay local**: `credentials.json` is stored on your machine at `~/.otpilot/` and is never uploaded anywhere.
157
+ - **Read-only access**: OTPilot only reads your emails — it cannot send, delete, or modify anything.
158
+ - **Local storage only**: Your OAuth token is stored locally at `~/.otpilot/token.json`. Nothing is sent to any third-party server.
159
+ - **On-demand only**: Emails are fetched only when you press the hotkey. There is no background polling.
160
+
161
+ ## Troubleshooting
162
+
163
+ | Issue | Solution |
164
+ | --------------------------------- | -------------------------------------------------------------- |
165
+ | "credentials.json not found" | Run `otpilot setup` and provide your credentials file |
166
+ | "Invalid credentials file" | Re-download credentials.json from Google Cloud Console |
167
+ | "Not authenticated" error | Run `otpilot setup` to re-authenticate |
168
+ | No OTP found | Check `otp_max_age_minutes` — the email might be too old |
169
+ | Clipboard not working (Linux) | Install `xclip`: `sudo apt install xclip` |
170
+ | Hotkey not working | Run `otpilot setup` to reconfigure the hotkey |
171
+ | Tray icon not visible | Check your system tray / menu bar settings |
172
+
173
+ ## Contributing
174
+
175
+ Contributions are welcome! See [**CONTRIBUTING.md**](CONTRIBUTING.md) for guidelines on setting up the development environment, running tests, and submitting pull requests.
176
+
177
+ ## License
178
+
179
+ [MIT](../LICENSE) — use it freely.
@@ -0,0 +1,4 @@
1
+ """OTPilot — Background desktop utility that copies OTPs from Gmail to clipboard on hotkey trigger."""
2
+
3
+ __version__ = "2.0.0"
4
+ __app_name__ = "OTPilot"
@@ -0,0 +1,38 @@
1
+ """Clipboard operations for OTPilot.
2
+
3
+ Wraps ``pyperclip`` with error handling so clipboard failures never crash
4
+ the application.
5
+ """
6
+
7
+ import pyperclip
8
+
9
+
10
+ class ClipboardError(Exception):
11
+ """Raised when a clipboard operation fails."""
12
+
13
+ def __init__(self, message: str = "Could not access the clipboard.") -> None:
14
+ super().__init__(message)
15
+
16
+
17
+ def copy_to_clipboard(text: str) -> None:
18
+ """Copy text to the system clipboard.
19
+
20
+ Args:
21
+ text: The string to copy.
22
+
23
+ Raises:
24
+ ClipboardError: If the clipboard is unavailable (e.g. no display
25
+ server on Linux, or missing clipboard utilities).
26
+ """
27
+ try:
28
+ pyperclip.copy(text)
29
+ except pyperclip.PyperclipException as exc:
30
+ raise ClipboardError(
31
+ "Could not copy to clipboard. "
32
+ "On Linux, make sure xclip or xsel is installed. "
33
+ f"Details: {exc}"
34
+ ) from exc
35
+ except Exception as exc:
36
+ raise ClipboardError(
37
+ f"Unexpected clipboard error: {exc}"
38
+ ) from exc
@@ -0,0 +1,111 @@
1
+ """Configuration management for OTPilot.
2
+
3
+ Handles reading and writing of ~/.otpilot/config.json.
4
+ """
5
+
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any, Dict, Optional
9
+
10
+
11
+ # Default configuration directory and file paths
12
+ CONFIG_DIR: Path = Path.home() / ".otpilot"
13
+ CONFIG_FILE: Path = CONFIG_DIR / "config.json"
14
+ TOKEN_FILE: Path = CONFIG_DIR / "token.json"
15
+
16
+ # Default configuration values
17
+ DEFAULT_CONFIG: Dict[str, Any] = {
18
+ "hotkey": "ctrl+shift+o",
19
+ "notify_on_copy": True,
20
+ "otp_max_age_minutes": 10,
21
+ "email_fetch_count": 10,
22
+ }
23
+
24
+
25
+ def _ensure_config_dir() -> None:
26
+ """Create the configuration directory if it doesn't exist."""
27
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
28
+
29
+
30
+ def get_config() -> Dict[str, Any]:
31
+ """Load and return the current configuration.
32
+
33
+ If the config file doesn't exist, creates it with default values.
34
+
35
+ Returns:
36
+ Dictionary containing all configuration values.
37
+ """
38
+ _ensure_config_dir()
39
+
40
+ if not CONFIG_FILE.exists():
41
+ save_config(DEFAULT_CONFIG)
42
+ return DEFAULT_CONFIG.copy()
43
+
44
+ try:
45
+ with open(CONFIG_FILE, "r", encoding="utf-8") as f:
46
+ stored_config: Dict[str, Any] = json.load(f)
47
+ except (json.JSONDecodeError, OSError):
48
+ # If config is corrupted, reset to defaults
49
+ save_config(DEFAULT_CONFIG)
50
+ return DEFAULT_CONFIG.copy()
51
+
52
+ # Merge with defaults so new keys are always present
53
+ merged = DEFAULT_CONFIG.copy()
54
+ merged.update(stored_config)
55
+ return merged
56
+
57
+
58
+ def save_config(data: Dict[str, Any]) -> None:
59
+ """Save configuration data to disk.
60
+
61
+ Args:
62
+ data: Dictionary of configuration values to persist.
63
+ """
64
+ _ensure_config_dir()
65
+
66
+ with open(CONFIG_FILE, "w", encoding="utf-8") as f:
67
+ json.dump(data, f, indent=2, sort_keys=True)
68
+
69
+
70
+ def config_exists() -> bool:
71
+ """Check whether a configuration file already exists.
72
+
73
+ Returns:
74
+ True if config.json is present, False otherwise.
75
+ """
76
+ return CONFIG_FILE.exists()
77
+
78
+
79
+ def token_exists() -> bool:
80
+ """Check whether an OAuth token file already exists.
81
+
82
+ Returns:
83
+ True if token.json is present, False otherwise.
84
+ """
85
+ return TOKEN_FILE.exists()
86
+
87
+
88
+ def get_value(key: str, default: Optional[Any] = None) -> Any:
89
+ """Retrieve a single configuration value.
90
+
91
+ Args:
92
+ key: The config key to look up.
93
+ default: Fallback value if the key is missing.
94
+
95
+ Returns:
96
+ The configuration value, or *default* if not found.
97
+ """
98
+ config = get_config()
99
+ return config.get(key, default)
100
+
101
+
102
+ def set_value(key: str, value: Any) -> None:
103
+ """Update a single configuration value and persist to disk.
104
+
105
+ Args:
106
+ key: The config key to update.
107
+ value: The new value.
108
+ """
109
+ config = get_config()
110
+ config[key] = value
111
+ save_config(config)
@@ -0,0 +1,53 @@
1
+ """OAuth2 credential paths and scopes for OTPilot.
2
+
3
+ Users provide their own ``credentials.json`` downloaded from the
4
+ Google Cloud Console. The file is stored at ``~/.otpilot/credentials.json``
5
+ and never leaves the user's machine.
6
+ """
7
+
8
+ from pathlib import Path
9
+
10
+
11
+ # OAuth2 configuration
12
+ SCOPES: list = ["https://www.googleapis.com/auth/gmail.readonly"]
13
+
14
+ # Paths
15
+ CREDENTIALS_FILE: Path = Path.home() / ".otpilot" / "credentials.json"
16
+
17
+
18
+ def credentials_exist() -> bool:
19
+ """Check whether a user-provided credentials.json file exists.
20
+
21
+ Returns:
22
+ True if ``~/.otpilot/credentials.json`` is present, False otherwise.
23
+ """
24
+ return CREDENTIALS_FILE.exists()
25
+
26
+
27
+ def validate_credentials_file(path: Path) -> bool:
28
+ """Validate that a file looks like a Google OAuth2 credentials JSON.
29
+
30
+ Performs a basic structural check — verifies the file is valid JSON
31
+ and contains either an ``installed`` or ``web`` top-level key with
32
+ a ``client_id`` field.
33
+
34
+ Args:
35
+ path: Path to the credentials JSON file.
36
+
37
+ Returns:
38
+ True if the file appears valid, False otherwise.
39
+ """
40
+ import json
41
+
42
+ try:
43
+ with open(path, "r", encoding="utf-8") as f:
44
+ data = json.load(f)
45
+ except (json.JSONDecodeError, OSError):
46
+ return False
47
+
48
+ # Google credentials JSON has either "installed" or "web" as a top key
49
+ for key in ("installed", "web"):
50
+ if key in data and "client_id" in data[key]:
51
+ return True
52
+
53
+ return False