peepstick 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.
- peepstick-0.1.0/PKG-INFO +103 -0
- peepstick-0.1.0/README.md +90 -0
- peepstick-0.1.0/peepstick/__init__.py +4 -0
- peepstick-0.1.0/peepstick/client.py +60 -0
- peepstick-0.1.0/peepstick/django/__init__.py +1 -0
- peepstick-0.1.0/peepstick/django/apps.py +8 -0
- peepstick-0.1.0/peepstick/django/templates/peepstick/error.html +8 -0
- peepstick-0.1.0/peepstick/django/templates/peepstick/ticket_form.html +36 -0
- peepstick-0.1.0/peepstick/django/urls.py +10 -0
- peepstick-0.1.0/peepstick/django/views.py +60 -0
- peepstick-0.1.0/peepstick/fastapi.py +58 -0
- peepstick-0.1.0/peepstick.egg-info/PKG-INFO +103 -0
- peepstick-0.1.0/peepstick.egg-info/SOURCES.txt +16 -0
- peepstick-0.1.0/peepstick.egg-info/dependency_links.txt +1 -0
- peepstick-0.1.0/peepstick.egg-info/requires.txt +8 -0
- peepstick-0.1.0/peepstick.egg-info/top_level.txt +1 -0
- peepstick-0.1.0/pyproject.toml +21 -0
- peepstick-0.1.0/setup.cfg +4 -0
peepstick-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: peepstick
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Drop-in ticket form + Slack routing for Django apps, powered by PeepsTick
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: requests>=2.25
|
|
8
|
+
Provides-Extra: django
|
|
9
|
+
Requires-Dist: django>=4.0; extra == "django"
|
|
10
|
+
Provides-Extra: fastapi
|
|
11
|
+
Requires-Dist: fastapi>=0.100; extra == "fastapi"
|
|
12
|
+
Requires-Dist: pydantic>=2.0; extra == "fastapi"
|
|
13
|
+
|
|
14
|
+
# peepstick
|
|
15
|
+
|
|
16
|
+
Drop this into a Python app to get ticket routing that goes straight to
|
|
17
|
+
your team's Slack channel — configured entirely from the PeepsTick dashboard.
|
|
18
|
+
|
|
19
|
+
This package never talks to Slack. It only ever calls your PeepsTick
|
|
20
|
+
project's API (`/api/form-schema`, `/api/tickets`). PeepsTick owns the
|
|
21
|
+
Slack webhook and does the actual posting server-side.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# FastAPI apps
|
|
27
|
+
pip install "peepstick[fastapi]"
|
|
28
|
+
|
|
29
|
+
# Django apps
|
|
30
|
+
pip install "peepstick[django]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## FastAPI setup
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from fastapi import FastAPI
|
|
37
|
+
from peepstick.fastapi import create_ticket_router
|
|
38
|
+
|
|
39
|
+
app = FastAPI()
|
|
40
|
+
app.include_router(
|
|
41
|
+
create_ticket_router(
|
|
42
|
+
api_key="tk_...", # from your project's page in the PeepsTick dashboard
|
|
43
|
+
base_url="https://peepstick.yourcompany.com",
|
|
44
|
+
),
|
|
45
|
+
prefix="/peepstick",
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This gives your app two routes your frontend can call — the API key
|
|
50
|
+
never reaches the browser:
|
|
51
|
+
|
|
52
|
+
- `GET /peepstick/form-schema` — the live fields configured for your project
|
|
53
|
+
- `POST /peepstick/tickets` — submits `{"fields": {...}}`, validated and
|
|
54
|
+
routed to Slack server-side
|
|
55
|
+
|
|
56
|
+
Add or rename fields in the PeepsTick dashboard and `/peepstick/form-schema`
|
|
57
|
+
updates automatically — no code changes or redeploys needed.
|
|
58
|
+
|
|
59
|
+
## Django setup
|
|
60
|
+
|
|
61
|
+
1. In `settings.py`, add the app and your project's credentials
|
|
62
|
+
(find the API key on your project's page in the PeepsTick dashboard):
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
INSTALLED_APPS = [
|
|
66
|
+
...,
|
|
67
|
+
"peepstick.django",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
PEEPSTICK_API_KEY = "tk_..."
|
|
71
|
+
PEEPSTICK_BASE_URL = "https://peepstick.yourcompany.com"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
2. In your project's `urls.py`:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from django.urls import include, path
|
|
78
|
+
|
|
79
|
+
urlpatterns = [
|
|
80
|
+
...,
|
|
81
|
+
path("tickets/", include("peepstick.django.urls")),
|
|
82
|
+
]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
3. Visit `/tickets/`. The form renders whatever fields are configured
|
|
86
|
+
for your project in the PeepsTick dashboard — add or rename fields
|
|
87
|
+
there and the form updates automatically, no code changes needed.
|
|
88
|
+
|
|
89
|
+
## Using the client directly
|
|
90
|
+
|
|
91
|
+
If you'd rather build your own view or call PeepsTick from a script:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from peepstick import PeepsTickClient
|
|
95
|
+
|
|
96
|
+
client = PeepsTickClient(api_key="tk_...", base_url="https://peepstick.yourcompany.com")
|
|
97
|
+
|
|
98
|
+
fields = client.get_form_schema()
|
|
99
|
+
ticket = client.submit_ticket({"title": "Login broken", "priority": "High"})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Both methods raise `peepstick.PeepsTickError` on failure (network
|
|
103
|
+
issues, invalid API key, missing required fields, etc).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# peepstick
|
|
2
|
+
|
|
3
|
+
Drop this into a Python app to get ticket routing that goes straight to
|
|
4
|
+
your team's Slack channel — configured entirely from the PeepsTick dashboard.
|
|
5
|
+
|
|
6
|
+
This package never talks to Slack. It only ever calls your PeepsTick
|
|
7
|
+
project's API (`/api/form-schema`, `/api/tickets`). PeepsTick owns the
|
|
8
|
+
Slack webhook and does the actual posting server-side.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# FastAPI apps
|
|
14
|
+
pip install "peepstick[fastapi]"
|
|
15
|
+
|
|
16
|
+
# Django apps
|
|
17
|
+
pip install "peepstick[django]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## FastAPI setup
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from fastapi import FastAPI
|
|
24
|
+
from peepstick.fastapi import create_ticket_router
|
|
25
|
+
|
|
26
|
+
app = FastAPI()
|
|
27
|
+
app.include_router(
|
|
28
|
+
create_ticket_router(
|
|
29
|
+
api_key="tk_...", # from your project's page in the PeepsTick dashboard
|
|
30
|
+
base_url="https://peepstick.yourcompany.com",
|
|
31
|
+
),
|
|
32
|
+
prefix="/peepstick",
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This gives your app two routes your frontend can call — the API key
|
|
37
|
+
never reaches the browser:
|
|
38
|
+
|
|
39
|
+
- `GET /peepstick/form-schema` — the live fields configured for your project
|
|
40
|
+
- `POST /peepstick/tickets` — submits `{"fields": {...}}`, validated and
|
|
41
|
+
routed to Slack server-side
|
|
42
|
+
|
|
43
|
+
Add or rename fields in the PeepsTick dashboard and `/peepstick/form-schema`
|
|
44
|
+
updates automatically — no code changes or redeploys needed.
|
|
45
|
+
|
|
46
|
+
## Django setup
|
|
47
|
+
|
|
48
|
+
1. In `settings.py`, add the app and your project's credentials
|
|
49
|
+
(find the API key on your project's page in the PeepsTick dashboard):
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
INSTALLED_APPS = [
|
|
53
|
+
...,
|
|
54
|
+
"peepstick.django",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
PEEPSTICK_API_KEY = "tk_..."
|
|
58
|
+
PEEPSTICK_BASE_URL = "https://peepstick.yourcompany.com"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
2. In your project's `urls.py`:
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from django.urls import include, path
|
|
65
|
+
|
|
66
|
+
urlpatterns = [
|
|
67
|
+
...,
|
|
68
|
+
path("tickets/", include("peepstick.django.urls")),
|
|
69
|
+
]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
3. Visit `/tickets/`. The form renders whatever fields are configured
|
|
73
|
+
for your project in the PeepsTick dashboard — add or rename fields
|
|
74
|
+
there and the form updates automatically, no code changes needed.
|
|
75
|
+
|
|
76
|
+
## Using the client directly
|
|
77
|
+
|
|
78
|
+
If you'd rather build your own view or call PeepsTick from a script:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from peepstick import PeepsTickClient
|
|
82
|
+
|
|
83
|
+
client = PeepsTickClient(api_key="tk_...", base_url="https://peepstick.yourcompany.com")
|
|
84
|
+
|
|
85
|
+
fields = client.get_form_schema()
|
|
86
|
+
ticket = client.submit_ticket({"title": "Login broken", "priority": "High"})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Both methods raise `peepstick.PeepsTickError` on failure (network
|
|
90
|
+
issues, invalid API key, missing required fields, etc).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Thin HTTP client for the PeepsTick ticket-routing API.
|
|
2
|
+
|
|
3
|
+
This is the only file in the package that talks to the network. It never
|
|
4
|
+
talks to Slack — it only ever calls your PeepsTick dashboard's API, which is
|
|
5
|
+
the piece that owns the Slack webhook and does the actual posting.
|
|
6
|
+
"""
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
DEFAULT_TIMEOUT = 5
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PeepsTickError(Exception):
|
|
13
|
+
"""Raised when the PeepsTick API returns an error or can't be reached."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PeepsTickClient:
|
|
17
|
+
"""Talks to a single PeepsTick project, identified by its API key."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, api_key: str, base_url: str, timeout: int = DEFAULT_TIMEOUT):
|
|
20
|
+
if not api_key:
|
|
21
|
+
raise ValueError("PeepsTick api_key is required")
|
|
22
|
+
if not base_url:
|
|
23
|
+
raise ValueError("PeepsTick base_url is required")
|
|
24
|
+
self.api_key = api_key
|
|
25
|
+
self.base_url = base_url.rstrip("/")
|
|
26
|
+
self.timeout = timeout
|
|
27
|
+
|
|
28
|
+
def get_form_schema(self):
|
|
29
|
+
"""Fetches the live form schema configured for this project."""
|
|
30
|
+
url = f"{self.base_url}/api/form-schema"
|
|
31
|
+
resp = self._request("get", url)
|
|
32
|
+
return self._parse(resp).get("formSchema", [])
|
|
33
|
+
|
|
34
|
+
def submit_ticket(self, fields: dict):
|
|
35
|
+
"""Submits ticket field values. PeepsTick validates, stores, and
|
|
36
|
+
relays to Slack server-side — this call never sees the webhook."""
|
|
37
|
+
url = f"{self.base_url}/api/tickets"
|
|
38
|
+
resp = self._request("post", url, json={"fields": fields})
|
|
39
|
+
return self._parse(resp).get("ticket")
|
|
40
|
+
|
|
41
|
+
def _request(self, method: str, url: str, **kwargs):
|
|
42
|
+
try:
|
|
43
|
+
return requests.request(
|
|
44
|
+
method, url, headers=self._headers(), timeout=self.timeout, **kwargs
|
|
45
|
+
)
|
|
46
|
+
except requests.RequestException as exc:
|
|
47
|
+
raise PeepsTickError(f"Could not reach PeepsTick at {url}: {exc}") from exc
|
|
48
|
+
|
|
49
|
+
def _headers(self):
|
|
50
|
+
return {"x-api-key": self.api_key, "Content-Type": "application/json"}
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def _parse(resp):
|
|
54
|
+
try:
|
|
55
|
+
data = resp.json()
|
|
56
|
+
except ValueError:
|
|
57
|
+
data = {}
|
|
58
|
+
if resp.status_code >= 400:
|
|
59
|
+
raise PeepsTickError(data.get("error", f"PeepsTick API error ({resp.status_code})"))
|
|
60
|
+
return data
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
default_app_config = "peepstick.django.apps.PeepsTickConfig"
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head><meta charset="utf-8"><title>Submit a ticket</title></head>
|
|
4
|
+
<body>
|
|
5
|
+
<h1>Submit a ticket</h1>
|
|
6
|
+
<form method="post" action="{% url 'peepstick:submit_ticket' %}">
|
|
7
|
+
{% csrf_token %}
|
|
8
|
+
{% for field in fields %}
|
|
9
|
+
<div class="peepstick-field">
|
|
10
|
+
<label for="{{ field.id }}">
|
|
11
|
+
{{ field.label }}{% if field.required %} *{% endif %}
|
|
12
|
+
</label>
|
|
13
|
+
|
|
14
|
+
{% if field.type == "textarea" %}
|
|
15
|
+
<textarea id="{{ field.id }}" name="{{ field.id }}"
|
|
16
|
+
{% if field.required %}required{% endif %}></textarea>
|
|
17
|
+
|
|
18
|
+
{% elif field.options %}
|
|
19
|
+
<select id="{{ field.id }}" name="{{ field.id }}"
|
|
20
|
+
{% if field.required %}required{% endif %}>
|
|
21
|
+
<option value="">Select…</option>
|
|
22
|
+
{% for option in field.options %}
|
|
23
|
+
<option value="{{ option }}">{{ option }}</option>
|
|
24
|
+
{% endfor %}
|
|
25
|
+
</select>
|
|
26
|
+
|
|
27
|
+
{% else %}
|
|
28
|
+
<input type="text" id="{{ field.id }}" name="{{ field.id }}"
|
|
29
|
+
{% if field.required %}required{% endif %}>
|
|
30
|
+
{% endif %}
|
|
31
|
+
</div>
|
|
32
|
+
{% endfor %}
|
|
33
|
+
<button type="submit">Submit</button>
|
|
34
|
+
</form>
|
|
35
|
+
</body>
|
|
36
|
+
</html>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.http import JsonResponse
|
|
5
|
+
from django.shortcuts import render
|
|
6
|
+
from django.views.decorators.csrf import csrf_exempt
|
|
7
|
+
from django.views.decorators.http import require_http_methods
|
|
8
|
+
|
|
9
|
+
from ..client import PeepsTickClient, PeepsTickError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _client():
|
|
13
|
+
"""Builds a PeepsTickClient from Django settings.
|
|
14
|
+
|
|
15
|
+
Add to settings.py:
|
|
16
|
+
PEEPSTICK_API_KEY = "tk_..." # from your PeepsTick project's dashboard
|
|
17
|
+
PEEPSTICK_BASE_URL = "https://peepstick.yourcompany.com"
|
|
18
|
+
"""
|
|
19
|
+
api_key = getattr(settings, "PEEPSTICK_API_KEY", None)
|
|
20
|
+
base_url = getattr(settings, "PEEPSTICK_BASE_URL", None)
|
|
21
|
+
if not api_key or not base_url:
|
|
22
|
+
raise PeepsTickError(
|
|
23
|
+
"PEEPSTICK_API_KEY and PEEPSTICK_BASE_URL must be set in Django settings"
|
|
24
|
+
)
|
|
25
|
+
return PeepsTickClient(api_key=api_key, base_url=base_url)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ticket_form(request):
|
|
29
|
+
"""Renders an HTML ticket form built from the project's live schema.
|
|
30
|
+
|
|
31
|
+
The schema (which fields exist, which are required) lives in the
|
|
32
|
+
PeepsTick dashboard, not in this package — this view just fetches
|
|
33
|
+
whatever the project owner configured and renders it.
|
|
34
|
+
"""
|
|
35
|
+
try:
|
|
36
|
+
fields = _client().get_form_schema()
|
|
37
|
+
except PeepsTickError as exc:
|
|
38
|
+
return render(request, "peepstick/error.html", {"error": str(exc)}, status=502)
|
|
39
|
+
return render(request, "peepstick/ticket_form.html", {"fields": fields})
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@csrf_exempt
|
|
43
|
+
@require_http_methods(["POST"])
|
|
44
|
+
def submit_ticket(request):
|
|
45
|
+
"""Accepts a ticket submission (form-encoded or JSON) and relays it
|
|
46
|
+
to PeepsTick's /api/tickets endpoint. PeepsTick validates it against the
|
|
47
|
+
project's schema, stores it, and posts to Slack server-side."""
|
|
48
|
+
if request.content_type == "application/json":
|
|
49
|
+
try:
|
|
50
|
+
fields = json.loads(request.body or "{}")
|
|
51
|
+
except json.JSONDecodeError:
|
|
52
|
+
return JsonResponse({"error": "Invalid JSON body"}, status=400)
|
|
53
|
+
else:
|
|
54
|
+
fields = request.POST.dict()
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
ticket = _client().submit_ticket(fields)
|
|
58
|
+
except PeepsTickError as exc:
|
|
59
|
+
return JsonResponse({"error": str(exc)}, status=502)
|
|
60
|
+
return JsonResponse({"ticket": ticket}, status=201)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""FastAPI integration for PeepsTick.
|
|
2
|
+
|
|
3
|
+
Mounts two routes on your own app so the browser only ever talks to your
|
|
4
|
+
server, never to PeepsTick directly — the API key stays server-side.
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from peepstick.fastapi import create_ticket_router
|
|
8
|
+
|
|
9
|
+
app = FastAPI()
|
|
10
|
+
app.include_router(
|
|
11
|
+
create_ticket_router(api_key="tk_...", base_url="https://peepstick.yourcompany.com"),
|
|
12
|
+
prefix="/peepstick",
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
This gives your app:
|
|
16
|
+
GET /peepstick/form-schema — proxies PeepsTick's live form schema
|
|
17
|
+
POST /peepstick/tickets — proxies a submission to PeepsTick
|
|
18
|
+
|
|
19
|
+
Requires FastAPI to be installed in your app already; it's not a
|
|
20
|
+
dependency of this package so you're never forced onto a version you
|
|
21
|
+
didn't choose.
|
|
22
|
+
"""
|
|
23
|
+
from typing import Any, Dict
|
|
24
|
+
|
|
25
|
+
from .client import PeepsTickClient, PeepsTickError
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_ticket_router(api_key: str, base_url: str, timeout: int = 5):
|
|
29
|
+
try:
|
|
30
|
+
from fastapi import APIRouter, HTTPException
|
|
31
|
+
from pydantic import BaseModel
|
|
32
|
+
except ImportError as exc:
|
|
33
|
+
raise ImportError(
|
|
34
|
+
"peepstick.fastapi requires fastapi and pydantic to be installed "
|
|
35
|
+
"in your app (pip install fastapi)"
|
|
36
|
+
) from exc
|
|
37
|
+
|
|
38
|
+
client = PeepsTickClient(api_key=api_key, base_url=base_url, timeout=timeout)
|
|
39
|
+
router = APIRouter()
|
|
40
|
+
|
|
41
|
+
class TicketSubmission(BaseModel):
|
|
42
|
+
fields: Dict[str, Any]
|
|
43
|
+
|
|
44
|
+
@router.get("/form-schema")
|
|
45
|
+
def get_form_schema():
|
|
46
|
+
try:
|
|
47
|
+
return {"formSchema": client.get_form_schema()}
|
|
48
|
+
except PeepsTickError as exc:
|
|
49
|
+
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
50
|
+
|
|
51
|
+
@router.post("/tickets", status_code=201)
|
|
52
|
+
def submit_ticket(submission: TicketSubmission):
|
|
53
|
+
try:
|
|
54
|
+
return {"ticket": client.submit_ticket(submission.fields)}
|
|
55
|
+
except PeepsTickError as exc:
|
|
56
|
+
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
|
57
|
+
|
|
58
|
+
return router
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: peepstick
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Drop-in ticket form + Slack routing for Django apps, powered by PeepsTick
|
|
5
|
+
Requires-Python: >=3.8
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: requests>=2.25
|
|
8
|
+
Provides-Extra: django
|
|
9
|
+
Requires-Dist: django>=4.0; extra == "django"
|
|
10
|
+
Provides-Extra: fastapi
|
|
11
|
+
Requires-Dist: fastapi>=0.100; extra == "fastapi"
|
|
12
|
+
Requires-Dist: pydantic>=2.0; extra == "fastapi"
|
|
13
|
+
|
|
14
|
+
# peepstick
|
|
15
|
+
|
|
16
|
+
Drop this into a Python app to get ticket routing that goes straight to
|
|
17
|
+
your team's Slack channel — configured entirely from the PeepsTick dashboard.
|
|
18
|
+
|
|
19
|
+
This package never talks to Slack. It only ever calls your PeepsTick
|
|
20
|
+
project's API (`/api/form-schema`, `/api/tickets`). PeepsTick owns the
|
|
21
|
+
Slack webhook and does the actual posting server-side.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# FastAPI apps
|
|
27
|
+
pip install "peepstick[fastapi]"
|
|
28
|
+
|
|
29
|
+
# Django apps
|
|
30
|
+
pip install "peepstick[django]"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## FastAPI setup
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from fastapi import FastAPI
|
|
37
|
+
from peepstick.fastapi import create_ticket_router
|
|
38
|
+
|
|
39
|
+
app = FastAPI()
|
|
40
|
+
app.include_router(
|
|
41
|
+
create_ticket_router(
|
|
42
|
+
api_key="tk_...", # from your project's page in the PeepsTick dashboard
|
|
43
|
+
base_url="https://peepstick.yourcompany.com",
|
|
44
|
+
),
|
|
45
|
+
prefix="/peepstick",
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This gives your app two routes your frontend can call — the API key
|
|
50
|
+
never reaches the browser:
|
|
51
|
+
|
|
52
|
+
- `GET /peepstick/form-schema` — the live fields configured for your project
|
|
53
|
+
- `POST /peepstick/tickets` — submits `{"fields": {...}}`, validated and
|
|
54
|
+
routed to Slack server-side
|
|
55
|
+
|
|
56
|
+
Add or rename fields in the PeepsTick dashboard and `/peepstick/form-schema`
|
|
57
|
+
updates automatically — no code changes or redeploys needed.
|
|
58
|
+
|
|
59
|
+
## Django setup
|
|
60
|
+
|
|
61
|
+
1. In `settings.py`, add the app and your project's credentials
|
|
62
|
+
(find the API key on your project's page in the PeepsTick dashboard):
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
INSTALLED_APPS = [
|
|
66
|
+
...,
|
|
67
|
+
"peepstick.django",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
PEEPSTICK_API_KEY = "tk_..."
|
|
71
|
+
PEEPSTICK_BASE_URL = "https://peepstick.yourcompany.com"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
2. In your project's `urls.py`:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from django.urls import include, path
|
|
78
|
+
|
|
79
|
+
urlpatterns = [
|
|
80
|
+
...,
|
|
81
|
+
path("tickets/", include("peepstick.django.urls")),
|
|
82
|
+
]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
3. Visit `/tickets/`. The form renders whatever fields are configured
|
|
86
|
+
for your project in the PeepsTick dashboard — add or rename fields
|
|
87
|
+
there and the form updates automatically, no code changes needed.
|
|
88
|
+
|
|
89
|
+
## Using the client directly
|
|
90
|
+
|
|
91
|
+
If you'd rather build your own view or call PeepsTick from a script:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from peepstick import PeepsTickClient
|
|
95
|
+
|
|
96
|
+
client = PeepsTickClient(api_key="tk_...", base_url="https://peepstick.yourcompany.com")
|
|
97
|
+
|
|
98
|
+
fields = client.get_form_schema()
|
|
99
|
+
ticket = client.submit_ticket({"title": "Login broken", "priority": "High"})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Both methods raise `peepstick.PeepsTickError` on failure (network
|
|
103
|
+
issues, invalid API key, missing required fields, etc).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
peepstick/__init__.py
|
|
4
|
+
peepstick/client.py
|
|
5
|
+
peepstick/fastapi.py
|
|
6
|
+
peepstick.egg-info/PKG-INFO
|
|
7
|
+
peepstick.egg-info/SOURCES.txt
|
|
8
|
+
peepstick.egg-info/dependency_links.txt
|
|
9
|
+
peepstick.egg-info/requires.txt
|
|
10
|
+
peepstick.egg-info/top_level.txt
|
|
11
|
+
peepstick/django/__init__.py
|
|
12
|
+
peepstick/django/apps.py
|
|
13
|
+
peepstick/django/urls.py
|
|
14
|
+
peepstick/django/views.py
|
|
15
|
+
peepstick/django/templates/peepstick/error.html
|
|
16
|
+
peepstick/django/templates/peepstick/ticket_form.html
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
peepstick
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "peepstick"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Drop-in ticket form + Slack routing for Django apps, powered by PeepsTick"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
dependencies = ["requests>=2.25"]
|
|
12
|
+
|
|
13
|
+
[project.optional-dependencies]
|
|
14
|
+
django = ["django>=4.0"]
|
|
15
|
+
fastapi = ["fastapi>=0.100", "pydantic>=2.0"]
|
|
16
|
+
|
|
17
|
+
[tool.setuptools.packages.find]
|
|
18
|
+
include = ["peepstick*"]
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.package-data]
|
|
21
|
+
"peepstick.django" = ["templates/peepstick/*.html"]
|