socialscrape-cli 0.1.0__py3-none-any.whl

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,166 @@
1
+ # GENERATED COPY of sdks/mcp/socialscrape_mcp/catalog.py: edit that file, then run sdks/mcp/generate.py.
2
+ """The generated catalog (catalog.json), as plain objects.
3
+
4
+ `catalog.json` is written by `sdks/mcp/generate.py` from the endpoint registry
5
+ and holds only endpoints that are built (never stubs). Everything the server
6
+ knows about SocialScrape comes from here, so the server needs no import of the
7
+ api's own packages and can run in a small image of its own.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ from dataclasses import dataclass, field
14
+ from functools import lru_cache
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ CATALOG_PATH = Path(__file__).with_name("catalog.json")
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class Param:
23
+ name: str
24
+ type: str
25
+ required: bool
26
+ description: str
27
+ location: str = "query"
28
+ enum: tuple[str, ...] | None = None
29
+ default: Any = None
30
+ example: Any = None
31
+ minimum: float | None = None
32
+ maximum: float | None = None
33
+ hosts: tuple[str, ...] | None = None
34
+ aliases: tuple[str, ...] = ()
35
+ common: bool = False
36
+
37
+ @classmethod
38
+ def from_dict(cls, d: dict) -> "Param":
39
+ return cls(
40
+ name=d["name"], type=d["type"], required=d["required"], description=d["description"],
41
+ location=d.get("location", "query"), enum=tuple(d["enum"]) if d.get("enum") else None,
42
+ default=d.get("default"), example=d.get("example"), minimum=d.get("minimum"),
43
+ maximum=d.get("maximum"), hosts=tuple(d["hosts"]) if d.get("hosts") else None,
44
+ aliases=tuple(d.get("aliases", ())), common=bool(d.get("common")),
45
+ )
46
+
47
+ def json_schema(self) -> dict[str, Any]:
48
+ t = {"url": "string"}.get(self.type, self.type)
49
+ schema: dict[str, Any] = {"type": t}
50
+ if self.type == "url":
51
+ schema["format"] = "uri"
52
+ if self.enum:
53
+ schema["enum"] = list(self.enum)
54
+ if self.minimum is not None:
55
+ schema["minimum"] = self.minimum
56
+ if self.maximum is not None:
57
+ schema["maximum"] = self.maximum
58
+ return schema
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class Endpoint:
63
+ id: str
64
+ method: str
65
+ path: str
66
+ summary: str
67
+ description: str
68
+ kind: str
69
+ status: str
70
+ mode: str
71
+ response: str
72
+ paginated: bool
73
+ credits: str
74
+ one_of: tuple[tuple[str, ...], ...]
75
+ regional: bool
76
+ timeout_seconds: float
77
+ params: tuple[Param, ...]
78
+ examples: tuple[dict, ...]
79
+ group: str
80
+ resource: str
81
+
82
+ @classmethod
83
+ def from_dict(cls, d: dict) -> "Endpoint":
84
+ return cls(
85
+ id=d["id"], method=d["method"], path=d["path"], summary=d["summary"], description=d.get("description", ""),
86
+ kind=d["kind"], status=d["status"], mode=d["mode"], response=d["response"], paginated=d["paginated"],
87
+ credits=d["credits"], one_of=tuple(tuple(g) for g in d["one_of"]), regional=d["regional"],
88
+ timeout_seconds=d["timeout_seconds"], params=tuple(Param.from_dict(p) for p in d["params"]),
89
+ examples=tuple(d["examples"]), group=d["group"], resource=d["resource"],
90
+ )
91
+
92
+ def param(self, name: str) -> Param | None:
93
+ """A parameter by its name or any alias the api accepts."""
94
+ for p in self.params:
95
+ if p.name == name or name in p.aliases:
96
+ return p
97
+ return None
98
+
99
+ @property
100
+ def own_params(self) -> tuple[Param, ...]:
101
+ """Parameters specific to this endpoint (not trim / raw / max_age_days)."""
102
+ return tuple(p for p in self.params if not p.common)
103
+
104
+ def required_names(self) -> list[str]:
105
+ return [p.name for p in self.params if p.required]
106
+
107
+ def signature(self) -> str:
108
+ """'handle|url' style: what the caller must supply."""
109
+ parts = [p.name for p in self.params if p.required and p.location != "body"]
110
+ parts += ["|".join(g) for g in self.one_of]
111
+ return ", ".join(parts)
112
+
113
+
114
+ @dataclass(frozen=True)
115
+ class Group:
116
+ key: str
117
+ title: str
118
+ description: str
119
+ platform_enum: bool
120
+
121
+
122
+ @dataclass
123
+ class Catalog:
124
+ api: dict
125
+ credit_rules: dict[str, str]
126
+ errors: dict[str, dict]
127
+ models: dict[str, list[str]]
128
+ groups: list[Group]
129
+ roles: dict[str, dict]
130
+ endpoints: list[Endpoint]
131
+ by_id: dict[str, Endpoint] = field(default_factory=dict)
132
+
133
+ def __post_init__(self) -> None:
134
+ self.by_id = {e.id: e for e in self.endpoints}
135
+
136
+ @classmethod
137
+ def from_dict(cls, d: dict) -> "Catalog":
138
+ return cls(
139
+ api=d["api"], credit_rules=d["credit_rules"], errors=d["errors"], models=d["models"],
140
+ groups=[Group(g["key"], g["title"], g["description"], g["platform_enum"]) for g in d["groups"]],
141
+ roles=d["roles"], endpoints=[Endpoint.from_dict(e) for e in d["endpoints"]],
142
+ )
143
+
144
+ def group_endpoints(self, key: str) -> list[Endpoint]:
145
+ return [e for e in self.endpoints if e.group == key]
146
+
147
+ def group(self, key: str) -> Group | None:
148
+ return next((g for g in self.groups if g.key == key), None)
149
+
150
+ def find(self, group: str, resource: str) -> Endpoint | None:
151
+ for e in self.endpoints:
152
+ if e.group == group and e.resource == resource:
153
+ return e
154
+ return None
155
+
156
+ def credit_text(self, ep: Endpoint) -> str:
157
+ """The price in words, from the registry's credit rule."""
158
+ desc = self.credit_rules.get(ep.credits, ep.credits)
159
+ return {"free": "free", "standard": "1 credit", "transcript": "1 credit (captions); AI transcription 1 credit "
160
+ "up to 2 minutes, then 5 per started minute of the whole video", "per_5_items": "1 credit per 5 items",
161
+ "batch": "the sum of its requests"}.get(ep.credits, desc)
162
+
163
+
164
+ @lru_cache(maxsize=1)
165
+ def load_catalog(path: str | None = None) -> Catalog:
166
+ return Catalog.from_dict(json.loads(Path(path).read_text() if path else CATALOG_PATH.read_text()))