roast-api 1.3.0 → 1.4.0

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.
@@ -28,3 +28,24 @@ jobs:
28
28
  files: |
29
29
  api/client.js
30
30
  postman/roast-as-a-service.postman_collection.json
31
+
32
+ publish-pypi:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - uses: actions/setup-python@v5
38
+ with:
39
+ python-version: '3.x'
40
+
41
+ - name: Install build tools
42
+ run: pip install build twine
43
+
44
+ - name: Build package
45
+ run: python -m build
46
+
47
+ - name: Publish to PyPI
48
+ run: twine upload dist/*
49
+ env:
50
+ TWINE_USERNAME: __token__
51
+ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
package/README.md CHANGED
@@ -7,6 +7,8 @@
7
7
  <img src="https://hits.sh/maijied.github.io/roast-as-a-service.svg?view=today-total&style=flat-square&label=visitors&color=ec4899&labelColor=020617" alt="Visitor Count" />
8
8
  <a href="https://www.npmjs.com/package/roast-api"><img src="https://img.shields.io/npm/v/roast-api?style=flat-square&color=f97316&labelColor=020617" alt="npm version" /></a>
9
9
  <a href="https://www.npmjs.com/package/roast-api"><img src="https://img.shields.io/npm/dm/roast-api?style=flat-square&color=ec4899&labelColor=020617" alt="npm downloads" /></a>
10
+ <a href="https://pypi.org/project/roast-api/"><img src="https://img.shields.io/pypi/v/roast-api?style=flat-square&color=3b82f6&labelColor=020617" alt="PyPI version" /></a>
11
+ <a href="https://packagist.org/packages/maizied/roast-api"><img src="https://img.shields.io/packagist/v/maizied/roast-api?style=flat-square&color=10b981&labelColor=020617" alt="Packagist version" /></a>
10
12
  <a href="https://github.com/Maijied/roast-as-a-service/actions/workflows/deploy.yml"><img src="https://github.com/Maijied/roast-as-a-service/actions/workflows/deploy.yml/badge.svg" alt="Deploy to GitHub Pages" /></a>
11
13
  </p>
12
14
 
@@ -66,7 +68,31 @@ Load the client SDK directly in your browser:
66
68
  </script>
67
69
  ```
68
70
 
69
- ### 3. Direct Fetch
71
+ ### 3. Install via Composer (PHP)
72
+ ```bash
73
+ composer require maizied/roast-api
74
+ ```
75
+
76
+ ```php
77
+ use Maizied\RoastApi\RaaS;
78
+
79
+ $roast = RaaS::getRandomRoast(['lang' => 'en']);
80
+ echo $roast['text'];
81
+ ```
82
+
83
+ ### 4. Install via pip (Python)
84
+ ```bash
85
+ pip install roast-api
86
+ ```
87
+
88
+ ```python
89
+ from roast_api import get_random_roast
90
+
91
+ roast = get_random_roast(lang='en')
92
+ print(roast['text'])
93
+ ```
94
+
95
+ ### 5. Direct Fetch
70
96
  Or just fetch the JSON files directly:
71
97
 
72
98
  ```javascript
package/composer.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "maizied/roast-api",
3
+ "description": "A PHP client for RaaS — Roast as a Service. CDN-served static JSON API for developer roasts.",
4
+ "type": "library",
5
+ "license": "MIT",
6
+ "authors": [
7
+ {
8
+ "name": "Maizied",
9
+ "email": "mdshuvo40@gmail.com"
10
+ }
11
+ ],
12
+ "homepage": "https://maijied.github.io/roast-as-a-service/",
13
+ "keywords": [
14
+ "roast",
15
+ "api",
16
+ "developer",
17
+ "humor",
18
+ "fun",
19
+ "cdn"
20
+ ],
21
+ "require": {
22
+ "php": ">=7.4"
23
+ },
24
+ "autoload": {
25
+ "psr-4": {
26
+ "Maizied\\RoastApi\\": "src/"
27
+ }
28
+ },
29
+ "minimum-stability": "stable"
30
+ }
package/index.html CHANGED
@@ -53,6 +53,12 @@
53
53
  <a href="https://www.npmjs.com/package/roast-api" target="_blank"><img
54
54
  src="https://img.shields.io/npm/dm/roast-api?style=flat-square&color=ec4899&labelColor=020617"
55
55
  alt="npm downloads" /></a>
56
+ <a href="https://pypi.org/project/roast-api/" target="_blank"><img
57
+ src="https://img.shields.io/pypi/v/roast-api?style=flat-square&color=3b82f6&labelColor=020617"
58
+ alt="PyPI version" /></a>
59
+ <a href="https://packagist.org/packages/maizied/roast-api" target="_blank"><img
60
+ src="https://img.shields.io/packagist/v/maizied/roast-api?style=flat-square&color=10b981&labelColor=020617"
61
+ alt="Packagist version" /></a>
56
62
  <a href="https://github.com/Maijied/roast-as-a-service" target="_blank"><img
57
63
  src="https://img.shields.io/github/stars/Maijied/roast-as-a-service?style=flat-square&color=f97316&labelColor=020617"
58
64
  alt="GitHub stars" /></a>
@@ -163,8 +169,27 @@ RaaS.getRandomRoast({ lang: 'en' })
163
169
  RaaS.getRandomRoast({ lang: 'bn', intensity: 2 })
164
170
  .then(r =&gt; console.log(r.text));
165
171
  &lt;/script&gt;</code></pre>
172
+ <div class="code-card">
173
+ <h3>Composer (PHP)</h3>
174
+ <pre><code>composer require maizied/roast-api
175
+
176
+ // Usage
177
+ use Maizied\RoastApi\RaaS;
178
+
179
+ $roast = RaaS::getRandomRoast(['lang' =&gt; 'en']);
180
+ echo $roast['text'];</code></pre>
181
+ </div>
182
+ <div class="code-card">
183
+ <h3>pip (Python)</h3>
184
+ <pre><code>pip install roast-api
185
+
186
+ # Usage
187
+ from roast_api import get_random_roast
188
+
189
+ roast = get_random_roast(lang='en')
190
+ print(roast['text'])</code></pre>
191
+ </div>
166
192
  </div>
167
- </div>
168
193
  </section>
169
194
 
170
195
  <section class="section">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roast-api",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "A static JSON API for delivering developer roasts.",
5
5
  "main": "api/client.js",
6
6
  "scripts": {
package/pyproject.toml ADDED
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "roast-api"
7
+ version = "1.4.0"
8
+ description = "A Python client for RaaS — Roast as a Service. CDN-served static JSON API for developer roasts."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.7"
12
+ authors = [
13
+ {name = "Maizied", email = "mdshuvo40@gmail.com"}
14
+ ]
15
+ keywords = ["roast", "api", "developer", "humor", "fun", "cdn"]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Software Development :: Libraries :: Python Modules",
22
+ ]
23
+ dependencies = [
24
+ "requests>=2.20.0",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://maijied.github.io/roast-as-a-service/"
29
+ Repository = "https://github.com/Maijied/roast-as-a-service"
30
+
31
+ [tool.setuptools.packages.find]
32
+ include = ["roast_api*"]
@@ -0,0 +1,29 @@
1
+ """
2
+ RaaS — Roast as a Service Python Client
3
+
4
+ A lightweight Python client for fetching developer roasts from the
5
+ RaaS static JSON API hosted on GitHub Pages.
6
+
7
+ Usage:
8
+ from roast_api import get_random_roast, get_all_roasts
9
+
10
+ roast = get_random_roast(lang='en')
11
+ print(roast['text'])
12
+ """
13
+
14
+ from roast_api.client import (
15
+ get_random_roast,
16
+ get_all_roasts,
17
+ get_manifest,
18
+ get_shard,
19
+ clear_cache,
20
+ )
21
+
22
+ __version__ = "1.4.0"
23
+ __all__ = [
24
+ "get_random_roast",
25
+ "get_all_roasts",
26
+ "get_manifest",
27
+ "get_shard",
28
+ "clear_cache",
29
+ ]
@@ -0,0 +1,89 @@
1
+ """
2
+ RaaS Python Client — core logic.
3
+
4
+ Fetches roasts from the static JSON API on GitHub Pages,
5
+ with in-memory caching and optional filtering.
6
+ """
7
+
8
+ import random
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ import requests
12
+
13
+ BASE_URL = "https://maijied.github.io/roast-as-a-service/api"
14
+
15
+ # In-memory caches
16
+ _manifest_cache: Optional[Dict[str, Any]] = None
17
+ _shard_cache: Dict[str, Dict[str, Any]] = {}
18
+
19
+
20
+ def get_manifest() -> Dict[str, Any]:
21
+ """Fetch and cache the API manifest."""
22
+ global _manifest_cache
23
+ if _manifest_cache is not None:
24
+ return _manifest_cache
25
+
26
+ url = f"{BASE_URL}/manifest.json"
27
+ resp = requests.get(url, timeout=10)
28
+ resp.raise_for_status()
29
+ _manifest_cache = resp.json()
30
+ return _manifest_cache
31
+
32
+
33
+ def get_shard(lang: str = "en", shard: int = 1) -> Dict[str, Any]:
34
+ """Fetch and cache a specific language shard."""
35
+ key = f"{lang}-{shard}"
36
+ if key in _shard_cache:
37
+ return _shard_cache[key]
38
+
39
+ url = f"{BASE_URL}/{lang}/roasts-{lang}-{shard}.json"
40
+ resp = requests.get(url, timeout=10)
41
+ resp.raise_for_status()
42
+ _shard_cache[key] = resp.json()
43
+ return _shard_cache[key]
44
+
45
+
46
+ def get_random_roast(
47
+ lang: str = "en",
48
+ intensity: Optional[int] = None,
49
+ max_length: Optional[int] = None,
50
+ ) -> Dict[str, Any]:
51
+ """
52
+ Get a random roast.
53
+
54
+ Args:
55
+ lang: Language code ('en' or 'bn'). Default: 'en'.
56
+ intensity: Filter by intensity (1–3). Default: None (any).
57
+ max_length: Maximum text length. Default: None (any).
58
+
59
+ Returns:
60
+ A dict with keys: id, text, intensity, length.
61
+
62
+ Raises:
63
+ ValueError: If no roasts match the given filters.
64
+ """
65
+ data = get_shard(lang)
66
+ roasts: List[Dict[str, Any]] = data.get("roasts", [])
67
+
68
+ if intensity is not None:
69
+ roasts = [r for r in roasts if r.get("intensity") == intensity]
70
+ if max_length is not None:
71
+ roasts = [r for r in roasts if r.get("length", 0) <= max_length]
72
+
73
+ if not roasts:
74
+ raise ValueError("No roasts match the given filters.")
75
+
76
+ return random.choice(roasts)
77
+
78
+
79
+ def get_all_roasts(lang: str = "en") -> List[Dict[str, Any]]:
80
+ """Get all roasts for a language."""
81
+ data = get_shard(lang)
82
+ return data.get("roasts", [])
83
+
84
+
85
+ def clear_cache() -> None:
86
+ """Clear the in-memory caches."""
87
+ global _manifest_cache, _shard_cache
88
+ _manifest_cache = None
89
+ _shard_cache = {}
package/src/RaaS.php ADDED
@@ -0,0 +1,133 @@
1
+ <?php
2
+
3
+ namespace Maizied\RoastApi;
4
+
5
+ /**
6
+ * RaaS — Roast as a Service PHP Client
7
+ *
8
+ * A lightweight PHP client for fetching developer roasts from the
9
+ * RaaS static JSON API hosted on GitHub Pages.
10
+ *
11
+ * @package maizied/roast-api
12
+ * @author Maizied <mdshuvo40@gmail.com>
13
+ * @license MIT
14
+ * @link https://maijied.github.io/roast-as-a-service/
15
+ */
16
+ class RaaS
17
+ {
18
+ private const BASE_URL = 'https://maijied.github.io/roast-as-a-service/api';
19
+
20
+ /** @var array|null In-memory manifest cache */
21
+ private static ?array $manifestCache = null;
22
+
23
+ /** @var array<string, array> In-memory shard cache */
24
+ private static array $shardCache = [];
25
+
26
+ /**
27
+ * Fetch the API manifest.
28
+ *
29
+ * @return array Decoded manifest data
30
+ * @throws \RuntimeException If the fetch fails
31
+ */
32
+ public static function getManifest(): array
33
+ {
34
+ if (self::$manifestCache !== null) {
35
+ return self::$manifestCache;
36
+ }
37
+
38
+ $url = self::BASE_URL . '/manifest.json';
39
+ $json = @file_get_contents($url);
40
+
41
+ if ($json === false) {
42
+ throw new \RuntimeException("Failed to fetch manifest from {$url}");
43
+ }
44
+
45
+ self::$manifestCache = json_decode($json, true);
46
+ return self::$manifestCache;
47
+ }
48
+
49
+ /**
50
+ * Fetch a specific shard.
51
+ *
52
+ * @param string $lang Language code (e.g., 'en', 'bn')
53
+ * @param int $shard Shard number (default: 1)
54
+ * @return array Decoded shard data
55
+ * @throws \RuntimeException If the fetch fails
56
+ */
57
+ public static function getShard(string $lang = 'en', int $shard = 1): array
58
+ {
59
+ $key = "{$lang}-{$shard}";
60
+
61
+ if (isset(self::$shardCache[$key])) {
62
+ return self::$shardCache[$key];
63
+ }
64
+
65
+ $url = self::BASE_URL . "/{$lang}/roasts-{$lang}-{$shard}.json";
66
+ $json = @file_get_contents($url);
67
+
68
+ if ($json === false) {
69
+ throw new \RuntimeException("Failed to fetch shard from {$url}");
70
+ }
71
+
72
+ self::$shardCache[$key] = json_decode($json, true);
73
+ return self::$shardCache[$key];
74
+ }
75
+
76
+ /**
77
+ * Get a random roast.
78
+ *
79
+ * @param array $options {
80
+ * @type string $lang Language code ('en' or 'bn'). Default: 'en'.
81
+ * @type int $intensity Filter by intensity (1–3). Default: null (any).
82
+ * @type int $maxLength Maximum text length. Default: null (any).
83
+ * }
84
+ * @return array Roast object with keys: id, text, intensity, length
85
+ * @throws \RuntimeException If fetch fails or no roasts match filters
86
+ */
87
+ public static function getRandomRoast(array $options = []): array
88
+ {
89
+ $lang = $options['lang'] ?? 'en';
90
+ $intensity = $options['intensity'] ?? null;
91
+ $maxLength = $options['maxLength'] ?? null;
92
+
93
+ $shard = self::getShard($lang);
94
+ $roasts = $shard['roasts'] ?? [];
95
+
96
+ // Apply filters
97
+ if ($intensity !== null) {
98
+ $roasts = array_filter($roasts, fn($r) => ($r['intensity'] ?? 0) === $intensity);
99
+ }
100
+ if ($maxLength !== null) {
101
+ $roasts = array_filter($roasts, fn($r) => ($r['length'] ?? 0) <= $maxLength);
102
+ }
103
+
104
+ $roasts = array_values($roasts);
105
+
106
+ if (empty($roasts)) {
107
+ throw new \RuntimeException('No roasts match the given filters.');
108
+ }
109
+
110
+ return $roasts[array_rand($roasts)];
111
+ }
112
+
113
+ /**
114
+ * Get all roasts for a language.
115
+ *
116
+ * @param string $lang Language code. Default: 'en'.
117
+ * @return array List of roast objects
118
+ */
119
+ public static function getAllRoasts(string $lang = 'en'): array
120
+ {
121
+ $shard = self::getShard($lang);
122
+ return $shard['roasts'] ?? [];
123
+ }
124
+
125
+ /**
126
+ * Clear the in-memory cache.
127
+ */
128
+ public static function clearCache(): void
129
+ {
130
+ self::$manifestCache = null;
131
+ self::$shardCache = [];
132
+ }
133
+ }