dailydotsnek 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.
- dailydotsnek-1.0.0/.gitignore +1 -0
- dailydotsnek-1.0.0/CHANGELOG.md +6 -0
- dailydotsnek-1.0.0/CONTRIBUTING.md +1 -0
- dailydotsnek-1.0.0/PKG-INFO +77 -0
- dailydotsnek-1.0.0/README.md +70 -0
- dailydotsnek-1.0.0/docs/api.md +33 -0
- dailydotsnek-1.0.0/docs/changelog.md +10 -0
- dailydotsnek-1.0.0/docs/contributing.md +32 -0
- dailydotsnek-1.0.0/docs/index.md +13 -0
- dailydotsnek-1.0.0/docs/installation.md +20 -0
- dailydotsnek-1.0.0/docs/publishing.md +46 -0
- dailydotsnek-1.0.0/docs/usage.md +37 -0
- dailydotsnek-1.0.0/mapper.py +175 -0
- dailydotsnek-1.0.0/mkdocs.yml +24 -0
- dailydotsnek-1.0.0/pyproject.toml +14 -0
- dailydotsnek-1.0.0/src/snek/__init__.py +0 -0
- dailydotsnek-1.0.0/src/snek/rawFetch.py +109 -0
- dailydotsnek-1.0.0/src/snek/rawFetch_fixed.py +107 -0
- dailydotsnek-1.0.0/src/snek/snekSDK.py +239 -0
- dailydotsnek-1.0.0/src/snek/tokenSys.py +20 -0
- dailydotsnek-1.0.0/test.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
See docs/contributing.md for contribution guidelines and local development instructions.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: dailydotsnek
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python SDK for Daily.dev api
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# dailydotsnek
|
|
9
|
+
|
|
10
|
+
A lightweight, well-documented Python SDK for the daily.dev public API.
|
|
11
|
+
|
|
12
|
+
Features
|
|
13
|
+
- Clean, minimal API client interfaces
|
|
14
|
+
- Environment- or manual-token authentication helpers
|
|
15
|
+
- Full method coverage for feeds, posts, bookmarks, profile, and search
|
|
16
|
+
|
|
17
|
+
Installation
|
|
18
|
+
|
|
19
|
+
Install from PyPI when the package is published:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install dailydotsnek
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
(For local development):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Quickstart
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from snek.tokenSys import AutoApiToken, ManualApiToken
|
|
35
|
+
from snek.snekSDK import SnekSDK
|
|
36
|
+
|
|
37
|
+
# AutoApiToken reads the DAILY_DEV_KEY env var (via python-dotenv)
|
|
38
|
+
token = AutoApiToken()
|
|
39
|
+
# or
|
|
40
|
+
# token = ManualApiToken("your_api_key_here")
|
|
41
|
+
|
|
42
|
+
client = SnekSDK(token)
|
|
43
|
+
|
|
44
|
+
posts = client.getPopularFeed()
|
|
45
|
+
print(posts)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Documentation
|
|
49
|
+
|
|
50
|
+
A full, polished documentation site is included under `docs/` and configured with MkDocs.
|
|
51
|
+
|
|
52
|
+
Building the package (locally)
|
|
53
|
+
|
|
54
|
+
- Build a wheel with hatch (project uses hatchling):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python -m pip install -U hatch
|
|
58
|
+
hatch build
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- The built artifacts will be in `dist/`.
|
|
62
|
+
|
|
63
|
+
Publishing (prepare-only):
|
|
64
|
+
|
|
65
|
+
This repository has been prepared for PyPI publishing. To publish, configure credentials (see docs/ for instructions) and run a publish command such as:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# With twine
|
|
69
|
+
python -m pip install --upgrade build twine
|
|
70
|
+
python -m build
|
|
71
|
+
python -m twine upload dist/*
|
|
72
|
+
|
|
73
|
+
# or with hatch
|
|
74
|
+
hatch publish # requires hatch and configuration
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
See the docs/ for more information and examples.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# dailydotsnek
|
|
2
|
+
|
|
3
|
+
A lightweight, well-documented Python SDK for the daily.dev public API.
|
|
4
|
+
|
|
5
|
+
Features
|
|
6
|
+
- Clean, minimal API client interfaces
|
|
7
|
+
- Environment- or manual-token authentication helpers
|
|
8
|
+
- Full method coverage for feeds, posts, bookmarks, profile, and search
|
|
9
|
+
|
|
10
|
+
Installation
|
|
11
|
+
|
|
12
|
+
Install from PyPI when the package is published:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install dailydotsnek
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
(For local development):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install -e .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Quickstart
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from snek.tokenSys import AutoApiToken, ManualApiToken
|
|
28
|
+
from snek.snekSDK import SnekSDK
|
|
29
|
+
|
|
30
|
+
# AutoApiToken reads the DAILY_DEV_KEY env var (via python-dotenv)
|
|
31
|
+
token = AutoApiToken()
|
|
32
|
+
# or
|
|
33
|
+
# token = ManualApiToken("your_api_key_here")
|
|
34
|
+
|
|
35
|
+
client = SnekSDK(token)
|
|
36
|
+
|
|
37
|
+
posts = client.getPopularFeed()
|
|
38
|
+
print(posts)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Documentation
|
|
42
|
+
|
|
43
|
+
A full, polished documentation site is included under `docs/` and configured with MkDocs.
|
|
44
|
+
|
|
45
|
+
Building the package (locally)
|
|
46
|
+
|
|
47
|
+
- Build a wheel with hatch (project uses hatchling):
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
python -m pip install -U hatch
|
|
51
|
+
hatch build
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- The built artifacts will be in `dist/`.
|
|
55
|
+
|
|
56
|
+
Publishing (prepare-only):
|
|
57
|
+
|
|
58
|
+
This repository has been prepared for PyPI publishing. To publish, configure credentials (see docs/ for instructions) and run a publish command such as:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# With twine
|
|
62
|
+
python -m pip install --upgrade build twine
|
|
63
|
+
python -m build
|
|
64
|
+
python -m twine upload dist/*
|
|
65
|
+
|
|
66
|
+
# or with hatch
|
|
67
|
+
hatch publish # requires hatch and configuration
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
See the docs/ for more information and examples.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# API Reference (hand-curated)
|
|
2
|
+
|
|
3
|
+
This page provides a concise, example-driven reference for the primary `SnekSDK` methods. The SDK wraps the daily.dev public API and returns decoded JSON objects when responses include a JSON body.
|
|
4
|
+
|
|
5
|
+
## SnekSDK
|
|
6
|
+
|
|
7
|
+
Create an instance with a token (see Quickstart):
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
client = SnekSDK(token)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Key methods
|
|
14
|
+
|
|
15
|
+
- `getPersonalisedFeed()` — personalized feed for authenticated user
|
|
16
|
+
- `getPopularFeed()` — popular posts
|
|
17
|
+
- `getDiscussedFeed()` — posts with active discussions
|
|
18
|
+
- `getTagFeed(tag)` — posts for a tag
|
|
19
|
+
- `getPost(post_id)` — single post
|
|
20
|
+
|
|
21
|
+
Bookmarks
|
|
22
|
+
- `getBookmarks()`
|
|
23
|
+
- `addBookmarks(post_ids, list_id=None)`
|
|
24
|
+
- `getBookmarkLists()`
|
|
25
|
+
|
|
26
|
+
Profile
|
|
27
|
+
- `getProfile()`
|
|
28
|
+
- `updateProfile(**data)`
|
|
29
|
+
|
|
30
|
+
Search
|
|
31
|
+
- `searchPosts(query)`, `searchTags(query)`, `searchSources(query)`
|
|
32
|
+
|
|
33
|
+
Refer to the source in `src/snek/snekSDK.py` for the full, up-to-date list of methods and parameter shapes.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for considering contributing to dailydotsnek! This project welcomes improvements to documentation, bug fixes, and new features.
|
|
4
|
+
|
|
5
|
+
Guidelines
|
|
6
|
+
|
|
7
|
+
- Open an issue to discuss larger changes before implementing.
|
|
8
|
+
- Fork the repo and create a topic branch for your change.
|
|
9
|
+
- Keep changes small and focused; write tests for new behaviors.
|
|
10
|
+
|
|
11
|
+
Code style
|
|
12
|
+
|
|
13
|
+
- Target Python 3.10+.
|
|
14
|
+
- Use type hints where helpful.
|
|
15
|
+
- Keep public API backwards-compatible.
|
|
16
|
+
|
|
17
|
+
Testing
|
|
18
|
+
|
|
19
|
+
Add tests under a `tests/` directory and run with `pytest`.
|
|
20
|
+
|
|
21
|
+
Documentation
|
|
22
|
+
|
|
23
|
+
Docs are written in Markdown and built with MkDocs. To preview locally:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install mkdocs mkdocs-material
|
|
27
|
+
mkdocs serve
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
License
|
|
31
|
+
|
|
32
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# dailydotsnek
|
|
2
|
+
|
|
3
|
+
Welcome to the official documentation for dailydotsnek — a polished, developer-friendly Python SDK for the daily.dev public API.
|
|
4
|
+
|
|
5
|
+
This documentation is designed to be concise, readable, and example-driven. Use the navigation on the left to find installation instructions, a quickstart, API reference, contributing guidelines, and changelog.
|
|
6
|
+
|
|
7
|
+
Highlights
|
|
8
|
+
|
|
9
|
+
- Ergonomic token helpers (AutoApiToken / ManualApiToken)
|
|
10
|
+
- A single SnekSDK client with convenience methods for feeds, posts, bookmarks, profile, and search
|
|
11
|
+
- Designed for Python 3.10+
|
|
12
|
+
|
|
13
|
+
If you're new here, start with Installation and Quickstart for a 2-minute walkthrough.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
Install the published package from PyPI (when available):
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install dailydotsnek
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
For local development, install in editable mode:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
python -m pip install -U pip
|
|
13
|
+
python -m pip install -e .
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requirements
|
|
17
|
+
- Python 3.10+
|
|
18
|
+
- Optional: python-dotenv if using AutoApiToken
|
|
19
|
+
|
|
20
|
+
Virtual environments are recommended for development and testing.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Publishing to TestPyPI / PyPI
|
|
2
|
+
|
|
3
|
+
This project is prepared for PyPI publishing using standard tools. Two common approaches are described below.
|
|
4
|
+
|
|
5
|
+
Prerequisites
|
|
6
|
+
|
|
7
|
+
- Python 3.10+ installed
|
|
8
|
+
- An account on Test PyPI (https://test.pypi.org/) and/or PyPI (https://pypi.org/)
|
|
9
|
+
- `twine` and `build` installed locally
|
|
10
|
+
|
|
11
|
+
Create a `.pypirc` (optional)
|
|
12
|
+
|
|
13
|
+
Create `~/.pypirc` to store credentials so `twine` can pick them up automatically:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
[distutils]
|
|
17
|
+
index-servers =
|
|
18
|
+
pypi
|
|
19
|
+
testpypi
|
|
20
|
+
|
|
21
|
+
[pypi]
|
|
22
|
+
username = __token__
|
|
23
|
+
password = <pypi-api-token>
|
|
24
|
+
|
|
25
|
+
[testpypi]
|
|
26
|
+
repository: https://test.pypi.org/legacy/
|
|
27
|
+
username = __token__
|
|
28
|
+
password = <testpypi-api-token>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Build and upload (TestPyPI recommended first)
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python -m pip install --upgrade build twine
|
|
35
|
+
python -m build
|
|
36
|
+
# Upload to test PyPI
|
|
37
|
+
python -m twine upload --repository testpypi dist/*
|
|
38
|
+
|
|
39
|
+
# After verification, upload to real PyPI
|
|
40
|
+
python -m twine upload dist/*
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Notes
|
|
44
|
+
|
|
45
|
+
- Use API tokens rather than your account password. Create them in your PyPI account settings.
|
|
46
|
+
- For automated releases, use GitHub Actions or another CI; keep tokens in repository secrets and restrict publish jobs.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Quickstart & Usage
|
|
2
|
+
|
|
3
|
+
## Authentication
|
|
4
|
+
|
|
5
|
+
Two token helpers are provided:
|
|
6
|
+
|
|
7
|
+
- `AutoApiToken()` — loads `DAILY_DEV_KEY` from environment using python-dotenv.
|
|
8
|
+
- `ManualApiToken("<token>")` — provide token explicitly.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
from snek.tokenSys import AutoApiToken, ManualApiToken
|
|
12
|
+
from snek.snekSDK import SnekSDK
|
|
13
|
+
|
|
14
|
+
# Auto (dotenv)
|
|
15
|
+
token = AutoApiToken()
|
|
16
|
+
client = SnekSDK(token)
|
|
17
|
+
|
|
18
|
+
# Manual
|
|
19
|
+
# token = ManualApiToken("sk_live_...")
|
|
20
|
+
# client = SnekSDK(token)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Example: get popular feed
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
posts = client.getPopularFeed()
|
|
27
|
+
for p in posts.get("data", []):
|
|
28
|
+
print(p.get("title"))
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Error handling
|
|
32
|
+
|
|
33
|
+
The client surface raises requests HTTP errors on non-2xx responses. Catch `requests.HTTPError` to handle API errors.
|
|
34
|
+
|
|
35
|
+
## Rate limits
|
|
36
|
+
|
|
37
|
+
Use `getRateLimit()`, `getRateRemaining()` and `getRateResetTime()` to inspect API rate-limit headers.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import ast
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
IGNORED_DIRS = {
|
|
11
|
+
"__pycache__",
|
|
12
|
+
".git",
|
|
13
|
+
".venv",
|
|
14
|
+
"venv",
|
|
15
|
+
"node_modules",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def find_symbols(path: Path) -> list[str]:
|
|
20
|
+
"""Return public functions/classes defined directly in a Python file."""
|
|
21
|
+
try:
|
|
22
|
+
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
|
23
|
+
except SyntaxError as exc:
|
|
24
|
+
print(f"warning: could not parse {path}: {exc}")
|
|
25
|
+
return []
|
|
26
|
+
|
|
27
|
+
symbols: list[str] = []
|
|
28
|
+
|
|
29
|
+
for node in tree.body:
|
|
30
|
+
if isinstance(
|
|
31
|
+
node,
|
|
32
|
+
(
|
|
33
|
+
ast.FunctionDef,
|
|
34
|
+
ast.AsyncFunctionDef,
|
|
35
|
+
ast.ClassDef,
|
|
36
|
+
),
|
|
37
|
+
):
|
|
38
|
+
if not node.name.startswith("_"):
|
|
39
|
+
symbols.append(node.name)
|
|
40
|
+
|
|
41
|
+
return sorted(symbols)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def python_files(directory: Path) -> list[Path]:
|
|
45
|
+
"""Find Python files directly inside a directory."""
|
|
46
|
+
return sorted(
|
|
47
|
+
path
|
|
48
|
+
for path in directory.iterdir()
|
|
49
|
+
if path.is_file()
|
|
50
|
+
and path.suffix == ".py"
|
|
51
|
+
and path.name != "__init__.py"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def package_directories(src: Path) -> list[Path]:
|
|
56
|
+
"""Find every directory containing Python/package code."""
|
|
57
|
+
directories: set[Path] = set()
|
|
58
|
+
|
|
59
|
+
for path in src.rglob("*"):
|
|
60
|
+
if not path.is_dir():
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
if any(part in IGNORED_DIRS for part in path.parts):
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
if any(path.glob("*.py")):
|
|
67
|
+
directories.add(path)
|
|
68
|
+
|
|
69
|
+
# Include parents so nested packages get __init__.py files too.
|
|
70
|
+
for directory in list(directories):
|
|
71
|
+
current = directory
|
|
72
|
+
while current != src and current != current.parent:
|
|
73
|
+
directories.add(current)
|
|
74
|
+
current = current.parent
|
|
75
|
+
|
|
76
|
+
return sorted(directories)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def module_import(module: str, symbols: list[str]) -> str:
|
|
80
|
+
"""Create an import line for a module."""
|
|
81
|
+
if symbols:
|
|
82
|
+
names = ", ".join(symbols)
|
|
83
|
+
return f"from .{module} import {names}"
|
|
84
|
+
|
|
85
|
+
return f"from . import {module}"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def generate_init(directory: Path, src: Path) -> str:
|
|
89
|
+
"""Generate the contents of an __init__.py."""
|
|
90
|
+
files = python_files(directory)
|
|
91
|
+
|
|
92
|
+
lines = [
|
|
93
|
+
'"""Automatically generated package exports."""',
|
|
94
|
+
"",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
# Export functions/classes from Python files.
|
|
98
|
+
for file in files:
|
|
99
|
+
symbols = find_symbols(file)
|
|
100
|
+
module = file.stem
|
|
101
|
+
|
|
102
|
+
lines.append(module_import(module, symbols))
|
|
103
|
+
|
|
104
|
+
# Export child packages.
|
|
105
|
+
child_packages = []
|
|
106
|
+
|
|
107
|
+
for child in sorted(directory.iterdir()):
|
|
108
|
+
if not child.is_dir():
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
if child.name in IGNORED_DIRS:
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
if any(child.glob("*.py")) or any(child.iterdir()):
|
|
115
|
+
child_packages.append(child.name)
|
|
116
|
+
|
|
117
|
+
if files and child_packages:
|
|
118
|
+
lines.append("")
|
|
119
|
+
|
|
120
|
+
for package in child_packages:
|
|
121
|
+
lines.append(f"from . import {package}")
|
|
122
|
+
|
|
123
|
+
lines.append("")
|
|
124
|
+
|
|
125
|
+
return "\n".join(lines)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def map_src(src: Path, *, dry_run: bool = False) -> None:
|
|
129
|
+
if not src.exists():
|
|
130
|
+
raise FileNotFoundError(f"Source directory does not exist: {src}")
|
|
131
|
+
|
|
132
|
+
if not src.is_dir():
|
|
133
|
+
raise NotADirectoryError(f"Not a directory: {src}")
|
|
134
|
+
|
|
135
|
+
directories = package_directories(src)
|
|
136
|
+
|
|
137
|
+
for directory in directories:
|
|
138
|
+
init_file = directory / "__init__.py"
|
|
139
|
+
content = generate_init(directory, src)
|
|
140
|
+
|
|
141
|
+
if dry_run:
|
|
142
|
+
print(f"\n### {init_file}")
|
|
143
|
+
print(content)
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
init_file.write_text(content, encoding="utf-8")
|
|
147
|
+
print(f"generated {init_file}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def main() -> None:
|
|
151
|
+
parser = argparse.ArgumentParser(
|
|
152
|
+
description="Generate __init__.py exports for a Python src/ tree."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
parser.add_argument(
|
|
156
|
+
"src",
|
|
157
|
+
nargs="?",
|
|
158
|
+
type=Path,
|
|
159
|
+
default=Path("src"),
|
|
160
|
+
help="Source directory (default: src)",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
parser.add_argument(
|
|
164
|
+
"--dry-run",
|
|
165
|
+
action="store_true",
|
|
166
|
+
help="Print generated files without modifying anything.",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
args = parser.parse_args()
|
|
170
|
+
|
|
171
|
+
map_src(args.src, dry_run=args.dry_run)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
main()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
site_name: dailydotsnek
|
|
2
|
+
site_description: "Professional documentation for the dailydotsnek Python SDK"
|
|
3
|
+
site_author: "Your Name"
|
|
4
|
+
repo_url: https://github.com/yourusername/dailydotsnek
|
|
5
|
+
|
|
6
|
+
nav:
|
|
7
|
+
- Home: index.md
|
|
8
|
+
- Installation: installation.md
|
|
9
|
+
- Quickstart: usage.md
|
|
10
|
+
- API Reference: api.md
|
|
11
|
+
- Contributing: contributing.md
|
|
12
|
+
- Changelog: changelog.md
|
|
13
|
+
|
|
14
|
+
theme:
|
|
15
|
+
name: material
|
|
16
|
+
palette:
|
|
17
|
+
primary: 'indigo'
|
|
18
|
+
accent: 'indigo'
|
|
19
|
+
|
|
20
|
+
markdown_extensions:
|
|
21
|
+
- admonition
|
|
22
|
+
- codehilite
|
|
23
|
+
- toc:
|
|
24
|
+
permalink: true
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dailydotsnek"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Python SDK for Daily.dev api"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[tool.hatch.build.targets.wheel]
|
|
14
|
+
packages = ["src/snek"]
|
|
File without changes
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
from .tokenSys import ManualApiToken, AutoApiToken
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RawFetch:
|
|
8
|
+
def __init__(self, token: ManualApiToken | AutoApiToken):
|
|
9
|
+
self.token = token
|
|
10
|
+
self.base_url = "https://api.daily.dev/public/v1"
|
|
11
|
+
|
|
12
|
+
def fetch(
|
|
13
|
+
self,
|
|
14
|
+
endpoint: str,
|
|
15
|
+
method: str = "GET",
|
|
16
|
+
params: dict | None = None,
|
|
17
|
+
json: dict | list | None = None,
|
|
18
|
+
):
|
|
19
|
+
url = f"{self.base_url}/{endpoint.lstrip('/')}"
|
|
20
|
+
|
|
21
|
+
headers = {
|
|
22
|
+
"Authorization": f"Bearer {self.token.get_token()}",
|
|
23
|
+
"Accept": "application/json",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
response = requests.request(
|
|
27
|
+
method=method,
|
|
28
|
+
url=url,
|
|
29
|
+
headers=headers,
|
|
30
|
+
params=params,
|
|
31
|
+
json=json,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if response.ok:
|
|
35
|
+
# Some successful requests may return no body.
|
|
36
|
+
if not response.content:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
return response.json()
|
|
40
|
+
|
|
41
|
+
response.raise_for_status()
|
|
42
|
+
|
|
43
|
+
# ─────────────────────────────────────────────
|
|
44
|
+
# Convenience methods
|
|
45
|
+
# ─────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
def get(
|
|
48
|
+
self,
|
|
49
|
+
endpoint: str,
|
|
50
|
+
params: dict | None = None,
|
|
51
|
+
):
|
|
52
|
+
return self.fetch(
|
|
53
|
+
endpoint,
|
|
54
|
+
method="GET",
|
|
55
|
+
params=params,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def post(
|
|
59
|
+
self,
|
|
60
|
+
endpoint: str,
|
|
61
|
+
json: dict | list | None = None,
|
|
62
|
+
params: dict | None = None,
|
|
63
|
+
):
|
|
64
|
+
return self.fetch(
|
|
65
|
+
endpoint,
|
|
66
|
+
method="POST",
|
|
67
|
+
params=params,
|
|
68
|
+
json=json,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def put(
|
|
72
|
+
self,
|
|
73
|
+
endpoint: str,
|
|
74
|
+
json: dict | list | None = None,
|
|
75
|
+
params: dict | None = None,
|
|
76
|
+
):
|
|
77
|
+
return self.fetch(
|
|
78
|
+
endpoint,
|
|
79
|
+
method="PUT",
|
|
80
|
+
params=params,
|
|
81
|
+
json=json,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def patch(
|
|
85
|
+
self,
|
|
86
|
+
endpoint: str,
|
|
87
|
+
json: dict | list | None = None,
|
|
88
|
+
params: dict | None = None,
|
|
89
|
+
):
|
|
90
|
+
return self.fetch(
|
|
91
|
+
endpoint,
|
|
92
|
+
method="PATCH",
|
|
93
|
+
params=params,
|
|
94
|
+
json=json,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def delete(
|
|
98
|
+
self,
|
|
99
|
+
endpoint: str,
|
|
100
|
+
params: dict | None = None,
|
|
101
|
+
json: dict | list | None = None,
|
|
102
|
+
):
|
|
103
|
+
return self.fetch(
|
|
104
|
+
endpoint,
|
|
105
|
+
method="DELETE",
|
|
106
|
+
params=params,
|
|
107
|
+
json=json,
|
|
108
|
+
)
|
|
109
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from .tokenSys import ManualApiToken, AutoApiToken
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RawFetch:
|
|
7
|
+
def __init__(self, token: ManualApiToken | AutoApiToken):
|
|
8
|
+
self.token = token
|
|
9
|
+
self.base_url = "https://api.daily.dev/public/v1"
|
|
10
|
+
|
|
11
|
+
def fetch(
|
|
12
|
+
self,
|
|
13
|
+
endpoint: str,
|
|
14
|
+
method: str = "GET",
|
|
15
|
+
params: dict | None = None,
|
|
16
|
+
json: dict | list | None = None,
|
|
17
|
+
):
|
|
18
|
+
url = f"{self.base_url}/{endpoint.lstrip('/') }"
|
|
19
|
+
|
|
20
|
+
headers = {
|
|
21
|
+
"Authorization": f"Bearer {self.token.get_token()}",
|
|
22
|
+
"Accept": "application/json",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
response = requests.request(
|
|
26
|
+
method=method,
|
|
27
|
+
url=url,
|
|
28
|
+
headers=headers,
|
|
29
|
+
params=params,
|
|
30
|
+
json=json,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
if response.ok:
|
|
34
|
+
# Some successful requests may return no body.
|
|
35
|
+
if not response.content:
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
return response.json()
|
|
39
|
+
|
|
40
|
+
response.raise_for_status()
|
|
41
|
+
|
|
42
|
+
# ─────────────────────────────────────────────
|
|
43
|
+
# Convenience methods
|
|
44
|
+
# ─────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
def get(
|
|
47
|
+
self,
|
|
48
|
+
endpoint: str,
|
|
49
|
+
params: dict | None = None,
|
|
50
|
+
):
|
|
51
|
+
return self.fetch(
|
|
52
|
+
endpoint,
|
|
53
|
+
method="GET",
|
|
54
|
+
params=params,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def post(
|
|
58
|
+
self,
|
|
59
|
+
endpoint: str,
|
|
60
|
+
json: dict | list | None = None,
|
|
61
|
+
params: dict | None = None,
|
|
62
|
+
):
|
|
63
|
+
return self.fetch(
|
|
64
|
+
endpoint,
|
|
65
|
+
method="POST",
|
|
66
|
+
params=params,
|
|
67
|
+
json=json,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def put(
|
|
71
|
+
self,
|
|
72
|
+
endpoint: str,
|
|
73
|
+
json: dict | list | None = None,
|
|
74
|
+
params: dict | None = None,
|
|
75
|
+
):
|
|
76
|
+
return self.fetch(
|
|
77
|
+
endpoint,
|
|
78
|
+
method="PUT",
|
|
79
|
+
params=params,
|
|
80
|
+
json=json,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def patch(
|
|
84
|
+
self,
|
|
85
|
+
endpoint: str,
|
|
86
|
+
json: dict | list | None = None,
|
|
87
|
+
params: dict | None = None,
|
|
88
|
+
):
|
|
89
|
+
return self.fetch(
|
|
90
|
+
endpoint,
|
|
91
|
+
method="PATCH",
|
|
92
|
+
params=params,
|
|
93
|
+
json=json,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def delete(
|
|
97
|
+
self,
|
|
98
|
+
endpoint: str,
|
|
99
|
+
params: dict | None = None,
|
|
100
|
+
json: dict | list | None = None,
|
|
101
|
+
):
|
|
102
|
+
return self.fetch(
|
|
103
|
+
endpoint,
|
|
104
|
+
method="DELETE",
|
|
105
|
+
params=params,
|
|
106
|
+
json=json,
|
|
107
|
+
)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
from .tokenSys import ManualApiToken, AutoApiToken
|
|
5
|
+
from .rawFetch_fixed import RawFetch
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SnekSDK:
|
|
9
|
+
def __init__(self, token: ManualApiToken | AutoApiToken):
|
|
10
|
+
self.token = token
|
|
11
|
+
self.base_url = "https://api.daily.dev/public/v1"
|
|
12
|
+
self.fetch = RawFetch(token=self.token)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def getRateLimit(self):
|
|
17
|
+
return self.fetch.fetch("x-ratelimit-limit")
|
|
18
|
+
|
|
19
|
+
def getRateRemaining(self):
|
|
20
|
+
return self.fetch.fetch("x-ratelimit-remaining")
|
|
21
|
+
|
|
22
|
+
def getRateResetTime(self):
|
|
23
|
+
return self.fetch.fetch("x-ratelimit-reset")
|
|
24
|
+
|
|
25
|
+
def getRetryTime(self):
|
|
26
|
+
return self.fetch.fetch("retry-after")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def getPersonalisedFeed(self):
|
|
30
|
+
return self.fetch.fetch("feeds/foryou")
|
|
31
|
+
|
|
32
|
+
def getPopularFeed(self):
|
|
33
|
+
return self.fetch.fetch("feeds/popular")
|
|
34
|
+
|
|
35
|
+
def getDiscussedFeed(self):
|
|
36
|
+
return self.fetch.fetch("feeds/discussed")
|
|
37
|
+
|
|
38
|
+
def getTagFeed(self, tag: str):
|
|
39
|
+
return self.fetch.fetch(f"feeds/tag/{tag}")
|
|
40
|
+
|
|
41
|
+
def getSourceFeed(self, source: str):
|
|
42
|
+
return self.fetch.fetch(f"feeds/source/{source}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def getPost(self, post_id: str):
|
|
47
|
+
return self.fetch.fetch(f"posts/{post_id}")
|
|
48
|
+
|
|
49
|
+
def getPostComments(self, post_id: str):
|
|
50
|
+
return self.fetch.fetch(f"posts/{post_id}/comments")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def searchPosts(self, query: str):
|
|
55
|
+
return self.fetch.fetch("search/posts", params={"q": query})
|
|
56
|
+
|
|
57
|
+
def searchTags(self, query: str):
|
|
58
|
+
return self.fetch.fetch("search/tags", params={"q": query})
|
|
59
|
+
|
|
60
|
+
def searchSources(self, query: str):
|
|
61
|
+
return self.fetch.fetch("search/sources", params={"q": query})
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def getBookmarks(self):
|
|
66
|
+
return self.fetch.fetch("bookmarks/")
|
|
67
|
+
|
|
68
|
+
def addBookmarks(self, post_ids: list[str], list_id: str | None = None):
|
|
69
|
+
data: dict[str, object] = {
|
|
70
|
+
"postIds": post_ids,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if list_id is not None:
|
|
74
|
+
data["listId"] = list_id
|
|
75
|
+
|
|
76
|
+
return self.fetch.fetch(
|
|
77
|
+
"bookmarks/",
|
|
78
|
+
method="POST",
|
|
79
|
+
json=data,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def searchBookmarks(self, query: str):
|
|
83
|
+
return self.fetch.fetch(
|
|
84
|
+
"bookmarks/search",
|
|
85
|
+
params={"q": query},
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def getBookmarkLists(self):
|
|
89
|
+
return self.fetch.fetch("bookmarks/lists")
|
|
90
|
+
|
|
91
|
+
def createBookmarkList(self, name: str, icon: str | None = None):
|
|
92
|
+
data = {
|
|
93
|
+
"name": name,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if icon is not None:
|
|
97
|
+
data["icon"] = icon
|
|
98
|
+
|
|
99
|
+
return self.fetch.fetch(
|
|
100
|
+
"bookmarks/lists",
|
|
101
|
+
method="POST",
|
|
102
|
+
json=data,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def deleteBookmarkList(self, list_id: str):
|
|
106
|
+
return self.fetch.fetch(
|
|
107
|
+
f"bookmarks/lists/{list_id}",
|
|
108
|
+
method="DELETE",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def deleteBookmark(self, bookmark_id: str):
|
|
112
|
+
return self.fetch.fetch(
|
|
113
|
+
f"bookmarks/{bookmark_id}",
|
|
114
|
+
method="DELETE",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def moveBookmark(self, bookmark_id: str, list_id: str | None):
|
|
118
|
+
return self.fetch.fetch(
|
|
119
|
+
f"bookmarks/{bookmark_id}",
|
|
120
|
+
method="PATCH",
|
|
121
|
+
json={
|
|
122
|
+
"listId": list_id,
|
|
123
|
+
},
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def getProfile(self):
|
|
129
|
+
return self.fetch.fetch("profile")
|
|
130
|
+
|
|
131
|
+
def updateProfile(self, **data):
|
|
132
|
+
return self.fetch.fetch(
|
|
133
|
+
"profile",
|
|
134
|
+
method="PATCH",
|
|
135
|
+
json=data,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def searchStack(self, query: str):
|
|
141
|
+
return self.fetch.fetch(
|
|
142
|
+
"profile/stack/search",
|
|
143
|
+
params={"q": query},
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def getStack(self):
|
|
147
|
+
return self.fetch.fetch("profile/stack/")
|
|
148
|
+
|
|
149
|
+
def addStackItem(self, **data):
|
|
150
|
+
return self.fetch.fetch(
|
|
151
|
+
"profile/stack/",
|
|
152
|
+
method="POST",
|
|
153
|
+
json=data,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
def updateStackItem(self, item_id: str, **data):
|
|
157
|
+
return self.fetch.fetch(
|
|
158
|
+
f"profile/stack/{item_id}",
|
|
159
|
+
method="PATCH",
|
|
160
|
+
json=data,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
def deleteStackItem(self, item_id: str):
|
|
164
|
+
return self.fetch.fetch(
|
|
165
|
+
f"profile/stack/{item_id}",
|
|
166
|
+
method="DELETE",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def reorderStack(self, **data):
|
|
170
|
+
return self.fetch.fetch(
|
|
171
|
+
"profile/stack/reorder",
|
|
172
|
+
method="PUT",
|
|
173
|
+
json=data,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def getExperiences(self):
|
|
179
|
+
return self.fetch.fetch("profile/experiences/")
|
|
180
|
+
|
|
181
|
+
def createExperience(self, **data):
|
|
182
|
+
return self.fetch.fetch(
|
|
183
|
+
"profile/experiences/",
|
|
184
|
+
method="POST",
|
|
185
|
+
json=data,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def getExperience(self, experience_id: str):
|
|
189
|
+
return self.fetch.fetch(
|
|
190
|
+
f"profile/experiences/{experience_id}",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def updateExperience(self, experience_id: str, **data):
|
|
194
|
+
return self.fetch.fetch(
|
|
195
|
+
f"profile/experiences/{experience_id}",
|
|
196
|
+
method="PUT",
|
|
197
|
+
json=data,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def deleteExperience(self, experience_id: str):
|
|
201
|
+
return self.fetch.fetch(
|
|
202
|
+
f"profile/experiences/{experience_id}",
|
|
203
|
+
method="DELETE",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def getTags(self):
|
|
209
|
+
return self.fetch.fetch("tags/")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def recommendByKeyword(self, keyword: str):
|
|
214
|
+
return self.fetch.fetch(
|
|
215
|
+
"recommend/keyword",
|
|
216
|
+
params={"keyword": keyword},
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def recommendSemantic(self, **params):
|
|
220
|
+
"""
|
|
221
|
+
Deprecated by daily.dev.
|
|
222
|
+
Prefer recommendByKeyword().
|
|
223
|
+
"""
|
|
224
|
+
return self.fetch.fetch(
|
|
225
|
+
"recommend/semantic",
|
|
226
|
+
params=params,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def signup(self, **data):
|
|
231
|
+
return self.fetch.fetch(
|
|
232
|
+
"signup/",
|
|
233
|
+
method="POST",
|
|
234
|
+
json=data,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import dotenv
|
|
3
|
+
|
|
4
|
+
class ManualApiToken:
|
|
5
|
+
def __init__(self, token: str):
|
|
6
|
+
self.token = token
|
|
7
|
+
|
|
8
|
+
def get_token(self) -> str:
|
|
9
|
+
return self.token
|
|
10
|
+
|
|
11
|
+
class AutoApiToken:
|
|
12
|
+
"""
|
|
13
|
+
Works ONLY if you have a enviroment variable named DAILY_DEV_KEY.
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self):
|
|
16
|
+
dotenv.load_dotenv()
|
|
17
|
+
self.token = os.getenv("DAILY_DEV_KEY")
|
|
18
|
+
|
|
19
|
+
def get_token(self) -> str:
|
|
20
|
+
return str(self.token)
|
|
File without changes
|