├── .env.example ├── .github └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── blagues_api ├── __init__.py └── main.py ├── pyproject.toml └── tests └── test_blagues_api.py /.env.example: -------------------------------------------------------------------------------- 1 | TOKEN="" -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | ci: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-python@v5 17 | id: setup-python 18 | with: 19 | python-version: '3.12' 20 | 21 | - name: Install Poetry 22 | uses: snok/install-poetry@v1 23 | with: 24 | virtualenvs-create: true 25 | virtualenvs-in-project: true 26 | 27 | - name: Load cached venv 28 | id: cached-poetry-dependencies 29 | uses: actions/cache@v4 30 | with: 31 | path: .venv 32 | key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} 33 | 34 | - name: Install dependencies 35 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 36 | run: poetry install --no-interaction --no-root 37 | 38 | - name: Install library 39 | run: poetry install --no-interaction 40 | 41 | - name: Lint 42 | run: | 43 | source .venv/bin/activate 44 | poetry run flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 45 | poetry run flake8 . --count --exit-zero --max-complexity=10 --statistics 46 | poetry run black . --check 47 | poetry run isort . 48 | 49 | - name: Create .env file 50 | run: | 51 | echo "TOKEN=${{ secrets.BLAGUES_API_TOKEN }}" > .env 52 | 53 | - name: Run tests 54 | run: poetry run pytest --cov --cov-report=xml 55 | 56 | - name: Upload coverage 57 | uses: codecov/codecov-action@v4.0.1 58 | with: 59 | token: ${{ secrets.CODECOV_TOKEN }} 60 | file: ./coverage.xml 61 | fail_ci_if_error: true -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to PyPI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | 9 | permissions: 10 | id-token: write 11 | contents: read 12 | 13 | jobs: 14 | publish: 15 | runs-on: ubuntu-latest 16 | environment: release 17 | 18 | if: github.repository_owner == 'Blagues-API' 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 2 23 | 24 | - name: Check if version changed 25 | id: check_version 26 | run: | 27 | git diff HEAD^ HEAD -- pyproject.toml | grep '+version' && echo "version_changed=true" >> $GITHUB_OUTPUT || echo "version_changed=false" >> $GITHUB_OUTPUT 28 | 29 | - uses: actions/setup-python@v5 30 | if: steps.check_version.outputs.version_changed == 'true' 31 | id: setup-python 32 | with: 33 | python-version: '3.12' 34 | 35 | - name: Install Poetry 36 | if: steps.check_version.outputs.version_changed == 'true' 37 | uses: snok/install-poetry@v1 38 | with: 39 | virtualenvs-create: true 40 | virtualenvs-in-project: true 41 | 42 | - name: Load cached venv 43 | if: steps.check_version.outputs.version_changed == 'true' 44 | id: cached-poetry-dependencies 45 | uses: actions/cache@v4 46 | with: 47 | path: .venv 48 | key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} 49 | 50 | - name: Build 51 | if: steps.check_version.outputs.version_changed == 'true' 52 | run: poetry build 53 | 54 | - name: Publish package distributions to PyPI 55 | if: steps.check_version.outputs.version_changed == 'true' 56 | uses: pypa/gh-action-pypi-publish@release/v1 57 | with: 58 | packages-dir: dist/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | dist/ 3 | poetry.lock 4 | .env 5 | .pytest_cache/ 6 | .venv/ 7 | coverage.xml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2022 Blagues API contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlaguesAPI Python 2 | 3 | Ce paquet Python fournit une interface simple pour intéragir avec [Blagues API](https://www.blagues-api.fr/). 4 | **Important :** Ce paquet ne fournit que des méthodes **asynchrones**. 5 | 6 | ## Installation 7 | 8 | Vous pouvez simplement ajouter la dépendance à votre projet depuis PyPI : 9 | ``` 10 | pip install blagues-api 11 | ``` 12 | 13 | ## Utilisation 14 | 15 | Pour utiliser l'API, vous devez obtenir une clé gratuite sur le site officiel : https://www.blagues-api.fr/. Vous pourrez ensuite construire un objet `BlaguesAPI` : 16 | 17 | ```py 18 | from blagues_api import BlaguesAPI 19 | 20 | blagues = BlaguesAPI("VOTRE_TOKEN_ICI") 21 | ``` 22 | 23 | Toutes les méthodes excepté count renverront un objet `Blague`, qui permet d'accéder aux différentes propriétés renvoyées par l'API : `id`, `type`, `joke`, `answer`. En cas d'erreur, vous recevrez une erreur du type [`aiohttp.ClientResponseError`](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientResponseError). 24 | 25 | Les différents types de blagues peuvent être représentés au choix sous forme d'un string ou d'un objet `BlagueType` (exemple: `BlagueType.GENERAL`). La liste des types disponibles est notée dans sur le site officiel. 26 | 27 | ### Blague aléatoire 28 | 29 | ```py 30 | await blagues.random() 31 | # Blague(id=108, type=, joke="C'est l'histoire d'un poil. Avant, il était bien.", answer='Maintenant, il est pubien.') 32 | ``` 33 | 34 | Il est possible de spécifier des catégories à exclure : 35 | ```py 36 | await blagues.random(disallow=[BlagueType.LIMIT, BlagueType.BEAUF]) 37 | 38 | # Avec des strings 39 | await blagues.random(disallow=["limit", "beauf"]) 40 | ``` 41 | 42 | ### Blague aléatoire catégorisée 43 | 44 | ```py 45 | await blagues.random_categorized(BlagueType.DEV) 46 | # Blague(id=430, type=, joke='De quelle couleur sont tes yeux ?', answer='#1292f4 et toi ?') 47 | 48 | # Avec des strings 49 | await blagues.random_categorized("dev") 50 | ``` 51 | 52 | ### Blague par identifiant 53 | 54 | ```py 55 | await blagues.from_id(20) 56 | # Blague(id=20, type=, joke="Qu'est-ce qu'un chou au milieu de l'océan ?", answer='Un chou marin.') 57 | ``` 58 | 59 | ### Nombre de blagues 60 | 61 | ```py 62 | await blagues.count() 63 | # CountJoke(count=1730) 64 | ``` 65 | 66 | La méthode ci-dessus renverra un objet `CountJoke`, qui permet d'accéder a la propriété renvoyée par l'API : `count`. En cas d'erreur, vous recevrez une erreur du type [`aiohttp.ClientResponseError`](https://docs.aiohttp.org/en/stable/client_reference.html#aiohttp.ClientResponseError). 67 | -------------------------------------------------------------------------------- /blagues_api/__init__.py: -------------------------------------------------------------------------------- 1 | from .main import Blague, BlaguesAPI, BlagueType, CountJoke 2 | 3 | __all__ = ["BlaguesAPI", "BlagueType", "CountJoke", "Blague"] 4 | -------------------------------------------------------------------------------- /blagues_api/main.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import List 3 | 4 | import aiohttp 5 | import pydantic 6 | 7 | 8 | class BlagueType(str, Enum): 9 | GLOBAL = "global" 10 | DEV = "dev" 11 | DARK = "dark" 12 | LIMIT = "limit" 13 | BEAUF = "beauf" 14 | BLONDES = "blondes" 15 | 16 | def __str__(self): 17 | return str(self.value) 18 | 19 | 20 | class Blague(pydantic.BaseModel): 21 | id: int 22 | type: BlagueType 23 | joke: str 24 | answer: str 25 | 26 | 27 | class CountJoke(pydantic.BaseModel): 28 | count: int 29 | 30 | 31 | class BlaguesAPI: 32 | def __init__(self, token: str): 33 | self.token = token 34 | self.base_url = "https://www.blagues-api.fr/api" 35 | self.headers = {"Authorization": f"Bearer {self.token}"} 36 | 37 | async def _get(self, url: str, params: dict = None) -> dict: 38 | """Make a GET request to the API.""" 39 | async with aiohttp.ClientSession(raise_for_status=True) as session: 40 | async with session.get( 41 | self.base_url + url, headers=self.headers, params=params 42 | ) as resp: 43 | return await resp.json() 44 | 45 | async def random(self, *, disallow: List[str] = None) -> Blague: 46 | """ 47 | Get a random joke. 48 | Usage: random(disallow=['dev', 'dark',...]) 49 | """ 50 | endpoint = "/random" 51 | params = {"disallow": disallow} if disallow else {} 52 | data = await self._get(endpoint, params) 53 | 54 | return Blague.model_validate(data) 55 | 56 | async def random_categorized(self, category: str) -> Blague: 57 | """ 58 | Get a random joke from a specific category. 59 | Usage: random_categorized(category=BlagueType.DEV) 60 | """ 61 | endpoint = f"/type/{category}/random" 62 | data = await self._get(endpoint) 63 | 64 | return Blague.model_validate(data) 65 | 66 | async def from_id(self, id: int) -> Blague: 67 | """ 68 | Get a joke from its ID. 69 | Usage: from_id(1) 70 | """ 71 | endpoint = f"/id/{id}" 72 | data = await self._get(endpoint) 73 | 74 | return Blague.model_validate(data) 75 | 76 | async def count(self) -> CountJoke: 77 | """Get the number of jokes available.""" 78 | endpoint = "/count" 79 | data = await self._get(endpoint) 80 | 81 | return CountJoke.model_validate(data) 82 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "blagues_api" 3 | version = "1.0.4" 4 | description = "Official client for Blagues API" 5 | homepage = "https://www.blagues-api.fr/" 6 | authors = [ 7 | "baptiste0928 ", 8 | "DraftMan ", 9 | "Awakno " 10 | ] 11 | license = "MIT" 12 | readme = "README.md" 13 | repository = "https://github.com/Blagues-API/package-py" 14 | 15 | [tool.poetry.dependencies] 16 | python = "^3.8.1" 17 | aiohttp = "^3.9.5" 18 | pydantic = "^2.8.2" 19 | 20 | [tool.poetry.dev-dependencies] 21 | pytest = "^7.1.2" 22 | pytest-asyncio = "^0.18.3" 23 | python-dotenv = "^0.20.0" 24 | flake8 = "^7.1.0" 25 | flake8-pyproject = "^1.2.3" 26 | black = "^24.4.2" 27 | isort = "^5.13.2" 28 | pytest-cov = "^5.0.0" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | 34 | [tool.pytest.ini_options] 35 | asyncio_mode="auto" 36 | 37 | [tool.flake8] 38 | max-line-length = 120 39 | exclude = [".venv"] 40 | 41 | [tool.isort] 42 | profile = "black" 43 | -------------------------------------------------------------------------------- /tests/test_blagues_api.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from dotenv import load_dotenv 3 | import os 4 | 5 | from blagues_api import Blague, BlaguesAPI, BlagueType, CountJoke 6 | 7 | pytestmark = pytest.mark.asyncio 8 | 9 | load_dotenv() 10 | 11 | 12 | @pytest.fixture 13 | def token(): 14 | return os.getenv("TOKEN") 15 | 16 | 17 | @pytest.fixture 18 | def client(token): 19 | return BlaguesAPI(token) 20 | 21 | 22 | async def test_random_joke(client): 23 | response = await client.random() 24 | assert isinstance(response, Blague) 25 | 26 | 27 | async def test_random_with_disallowed(client): 28 | response = await client.random(disallow=["dark"]) 29 | assert isinstance(response, Blague) 30 | assert response.type != BlagueType.DARK 31 | 32 | 33 | async def test_random_with_allowed(client): 34 | response = await client.random_categorized(BlagueType.DARK) 35 | assert isinstance(response, Blague) 36 | assert response.type == BlagueType.DARK 37 | 38 | 39 | async def test_from_id(client): 40 | response = await client.from_id(1) 41 | assert isinstance(response, Blague) 42 | assert response.id == 1 43 | 44 | 45 | async def test_count_joke(client): 46 | response = await client.count() 47 | assert isinstance(response, CountJoke) 48 | --------------------------------------------------------------------------------