signbuddy 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.
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: signbuddy
3
+ Version: 1.0.0
4
+ Summary: CLI for verifying SignBuddy-signed PDF documents
5
+ Author: SignBuddy
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: requests>=2.31.0
9
+
10
+ # SignBuddy CLI
11
+
12
+ Verify whether a PDF was signed and recorded through [SignBuddy](https://signbuddy.eu).
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install signbuddy
18
+ ```
19
+
20
+ Or from local source:
21
+
22
+ ```bash
23
+ git clone <your-repo-url>
24
+ cd signbuddy
25
+ pip install .
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ No setup required — the CLI talks to the SignBuddy API out of the box.
31
+
32
+ ```bash
33
+ signbuddy --verify AGR-1234567890 /path/to/document.pdf
34
+ ```
35
+
36
+ The contract number is printed on the signed document (format `AGR-` followed by 10 digits).
37
+
38
+ ### Exit codes
39
+
40
+ | Code | Meaning |
41
+ | ---- | ---------------------------------------- |
42
+ | `0` | Document verified |
43
+ | `1` | Document not verified |
44
+ | `2` | Invalid input (file missing / not a PDF) |
45
+ | `3` | Could not reach the API / API error |
46
+
47
+ ## Options
48
+
49
+ | Option | Description |
50
+ | -------------------- | ------------------------------------ |
51
+ | `--timeout <secs>` | HTTP timeout in seconds (default 30) |
52
+ | `--version` | Print the CLI version |
53
+ | `-h`, `--help` | Show help |
@@ -0,0 +1,44 @@
1
+ # SignBuddy CLI
2
+
3
+ Verify whether a PDF was signed and recorded through [SignBuddy](https://signbuddy.eu).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install signbuddy
9
+ ```
10
+
11
+ Or from local source:
12
+
13
+ ```bash
14
+ git clone <your-repo-url>
15
+ cd signbuddy
16
+ pip install .
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ No setup required — the CLI talks to the SignBuddy API out of the box.
22
+
23
+ ```bash
24
+ signbuddy --verify AGR-1234567890 /path/to/document.pdf
25
+ ```
26
+
27
+ The contract number is printed on the signed document (format `AGR-` followed by 10 digits).
28
+
29
+ ### Exit codes
30
+
31
+ | Code | Meaning |
32
+ | ---- | ---------------------------------------- |
33
+ | `0` | Document verified |
34
+ | `1` | Document not verified |
35
+ | `2` | Invalid input (file missing / not a PDF) |
36
+ | `3` | Could not reach the API / API error |
37
+
38
+ ## Options
39
+
40
+ | Option | Description |
41
+ | -------------------- | ------------------------------------ |
42
+ | `--timeout <secs>` | HTTP timeout in seconds (default 30) |
43
+ | `--version` | Print the CLI version |
44
+ | `-h`, `--help` | Show help |
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "signbuddy"
7
+ version = "1.0.0"
8
+ description = "CLI for verifying SignBuddy-signed PDF documents"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ authors = [
12
+ { name = "SignBuddy" }
13
+ ]
14
+ dependencies = [
15
+ "requests>=2.31.0",
16
+ ]
17
+
18
+ [project.scripts]
19
+ signbuddy = "signbuddy.cli:main"
20
+
21
+ [tool.setuptools]
22
+ package-dir = {"" = "src"}
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ __all__ = ["__version__"]
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1,5 @@
1
+ from signbuddy.cli import main
2
+
3
+
4
+ if __name__ == "__main__":
5
+ raise SystemExit(main())
@@ -0,0 +1,138 @@
1
+ import argparse
2
+ import json
3
+ from pathlib import Path
4
+
5
+ import requests
6
+
7
+ from signbuddy import __version__
8
+
9
+ API_BASE_URL = "https://api.signbuddy.eu"
10
+
11
+
12
+ def build_parser() -> argparse.ArgumentParser:
13
+ parser = argparse.ArgumentParser(
14
+ prog="signbuddy",
15
+ description="Verify whether a PDF was signed and recorded through SignBuddy.",
16
+ )
17
+ parser.add_argument(
18
+ "--version",
19
+ action="version",
20
+ version=f"%(prog)s {__version__}",
21
+ )
22
+ parser.add_argument(
23
+ "--timeout",
24
+ type=float,
25
+ default=30.0,
26
+ help="HTTP timeout in seconds. Default: 30",
27
+ )
28
+ parser.add_argument(
29
+ "--verify",
30
+ nargs=2,
31
+ metavar=("CONTRACT_NUMBER", "FILEPATH"),
32
+ help="Verify a PDF by contract number and file path.",
33
+ )
34
+ return parser
35
+
36
+
37
+ def main() -> int:
38
+ parser = build_parser()
39
+ args = parser.parse_args()
40
+
41
+ if not args.verify:
42
+ parser.error("the following argument is required: --verify")
43
+
44
+ contract_number, file_path = args.verify
45
+ pdf_path = Path(file_path).expanduser().resolve()
46
+
47
+ if not pdf_path.exists():
48
+ print(f"Error: file not found: {pdf_path}")
49
+ return 2
50
+ if not pdf_path.is_file():
51
+ print(f"Error: not a file: {pdf_path}")
52
+ return 2
53
+ if pdf_path.suffix.lower() != ".pdf":
54
+ print(f"Error: expected a PDF file, got: {pdf_path.name}")
55
+ return 2
56
+
57
+ verify_url = f"{API_BASE_URL}/api/v1/verify"
58
+
59
+ try:
60
+ with pdf_path.open("rb") as handle:
61
+ response = requests.post(
62
+ verify_url,
63
+ data={"contract_number": contract_number},
64
+ files={"pdf_file": (pdf_path.name, handle, "application/pdf")},
65
+ timeout=args.timeout,
66
+ )
67
+ except requests.RequestException as exc:
68
+ print(f"Error: failed to reach SignBuddy API at {verify_url}")
69
+ print(f"Detail: {exc}")
70
+ return 3
71
+
72
+ try:
73
+ payload = response.json()
74
+ except json.JSONDecodeError:
75
+ print(f"Error: SignBuddy API returned non-JSON response (HTTP {response.status_code})")
76
+ return 3
77
+
78
+ if response.status_code >= 400:
79
+ print(f"Error: SignBuddy API returned HTTP {response.status_code}")
80
+ detail = payload.get("detail")
81
+ if detail:
82
+ print(f"Detail: {detail}")
83
+ else:
84
+ print(json.dumps(payload, indent=2))
85
+ return 3
86
+
87
+ _print_result(payload, pdf_path)
88
+ return 0 if payload.get("verified") else 1
89
+
90
+
91
+ def _print_result(payload: dict, pdf_path: Path) -> None:
92
+ verified = bool(payload.get("verified"))
93
+ found = bool(payload.get("found"))
94
+
95
+ print(f"File: {pdf_path}")
96
+ print(f"Contract Number: {payload.get('contract_number') or 'N/A'}")
97
+ print(f"Verified: {'YES' if verified else 'NO'}")
98
+ print(f"Found in SignBuddy: {'YES' if found else 'NO'}")
99
+
100
+ if payload.get("contract_title"):
101
+ print(f"Contract Title: {payload['contract_title']}")
102
+
103
+ if payload.get("message"):
104
+ print(f"Message: {payload['message']}")
105
+
106
+ if payload.get("hash_mismatch"):
107
+ print("Hash Mismatch: YES (uploaded file does not match the recorded document)")
108
+
109
+ if payload.get("pdf_hash"):
110
+ print(f"Stored Hash: {payload['pdf_hash']}")
111
+ if payload.get("searched_hash"):
112
+ print(f"Uploaded Hash: {payload['searched_hash']}")
113
+ if payload.get("expected_hash"):
114
+ print(f"Expected Hash: {payload['expected_hash']}")
115
+
116
+ if payload.get("rekor_status"):
117
+ print(f"Rekor: {payload['rekor_status']}")
118
+ if payload.get("log_index") is not None:
119
+ print(f"Rekor Log Index: {payload['log_index']}")
120
+ if payload.get("entry_uuid"):
121
+ print(f"Rekor Entry UUID: {payload['entry_uuid']}")
122
+ if payload.get("integrated_time_formatted"):
123
+ print(f"Rekor Integrated Time: {payload['integrated_time_formatted']}")
124
+ if payload.get("ots_status"):
125
+ print(f"OpenTimestamps: {payload['ots_status']}")
126
+ if payload.get("bitcoin_block_height") is not None:
127
+ print(f"Bitcoin Block Height: {payload['bitcoin_block_height']}")
128
+ if payload.get("bitcoin_confirmed_at"):
129
+ print(f"Bitcoin Confirmed At: {payload['bitcoin_confirmed_at']}")
130
+
131
+ signers = payload.get("signers") or []
132
+ if signers:
133
+ print("Signers:")
134
+ for signer in signers:
135
+ name = signer.get("name") or "Unknown"
136
+ email = signer.get("email") or "N/A"
137
+ signed_at = signer.get("signed_at") or "N/A"
138
+ print(f" - {name} <{email}> signed_at={signed_at}")
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: signbuddy
3
+ Version: 1.0.0
4
+ Summary: CLI for verifying SignBuddy-signed PDF documents
5
+ Author: SignBuddy
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: requests>=2.31.0
9
+
10
+ # SignBuddy CLI
11
+
12
+ Verify whether a PDF was signed and recorded through [SignBuddy](https://signbuddy.eu).
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install signbuddy
18
+ ```
19
+
20
+ Or from local source:
21
+
22
+ ```bash
23
+ git clone <your-repo-url>
24
+ cd signbuddy
25
+ pip install .
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ No setup required — the CLI talks to the SignBuddy API out of the box.
31
+
32
+ ```bash
33
+ signbuddy --verify AGR-1234567890 /path/to/document.pdf
34
+ ```
35
+
36
+ The contract number is printed on the signed document (format `AGR-` followed by 10 digits).
37
+
38
+ ### Exit codes
39
+
40
+ | Code | Meaning |
41
+ | ---- | ---------------------------------------- |
42
+ | `0` | Document verified |
43
+ | `1` | Document not verified |
44
+ | `2` | Invalid input (file missing / not a PDF) |
45
+ | `3` | Could not reach the API / API error |
46
+
47
+ ## Options
48
+
49
+ | Option | Description |
50
+ | -------------------- | ------------------------------------ |
51
+ | `--timeout <secs>` | HTTP timeout in seconds (default 30) |
52
+ | `--version` | Print the CLI version |
53
+ | `-h`, `--help` | Show help |
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/signbuddy/__init__.py
4
+ src/signbuddy/__main__.py
5
+ src/signbuddy/cli.py
6
+ src/signbuddy.egg-info/PKG-INFO
7
+ src/signbuddy.egg-info/SOURCES.txt
8
+ src/signbuddy.egg-info/dependency_links.txt
9
+ src/signbuddy.egg-info/entry_points.txt
10
+ src/signbuddy.egg-info/requires.txt
11
+ src/signbuddy.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ signbuddy = signbuddy.cli:main
@@ -0,0 +1 @@
1
+ requests>=2.31.0
@@ -0,0 +1 @@
1
+ signbuddy