Skip to content

Commit

Permalink
2.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
wkpn committed Feb 27, 2020
1 parent b418b37 commit 393ddc9
Show file tree
Hide file tree
Showing 11 changed files with 380 additions and 19 deletions.
57 changes: 57 additions & 0 deletions CHANGELOG-ru.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
Изменения
=========

`2.0.3`
-------

* ``get_thread_posts`` и ``get_thread_media`` теперь принимают тред по адресу

.. code-block:: python
>>> thread_media = await client.get_thread_media('https://2ch.hk/test/res/30972.html')
* добавлен список досок и соответствующие проверки
* добавлены новые исключения
* больше тестов
* почищен код

`2.0.2`
-------

* добавлены докстринги
* у проекта теперь есть логотип (может измениться)
* ``api_client`` перенесен в отдельный модуль

`2.0.1`
-------

* уменьшение количества потребляемой памяти за счет использования кортежей вместо списков
* улучшено покрытие тестами
* другие api эндпоинты
* больше аннотаций типов
* чистка кода

`2.0`
-----

* Апи клиент теперь может быть использован как менеджер контекста
* f-строки везде
* ``aiohttp`` заменен на ``httpx``
* аннотации типов
* ``download_thread_media`` теперь стримит файлы вместо полной загрузки

`1.4.3.1`
---------

* небольшой рефакторинг импортов (теперь можно писать ``from aio2ch import Api``)
* по дефолту все методы не возвращают ``status``, надо юзать ``return_status=True`` для его получения


`1.4.3`
-------

* добавлен параметр ``keywords`` к методу ``get_board_threads``
* добавлен метод для скачивания медиа из треда ``download_thread_media``

.. _changelog: https://github.com/wkpn/aio2ch/CHANGELOG-ru.rst
.. _readme: https://github.com/wkpn/aio2ch/README-ru.rst
18 changes: 18 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
Changelog
=========

`2.0.3`
-------

* ``get_thread_posts`` and ``get_thread_media`` now accepts thread passed as url

.. code-block:: python
>>> thread_media = await client.get_thread_media('https://2ch.hk/test/res/30972.html')
* added boards lists and according checks
* added new exceptions
* more tests
* more code cleanup
* added changelog_ and readme_ translations in Russian

`2.0.2`
-------

Expand Down Expand Up @@ -38,3 +53,6 @@ Changelog

* Added ``keywords`` parameter to ``get_board_threads`` method
* Added ``download_thread_media`` method

.. _changelog: https://github.com/wkpn/aio2ch/CHANGELOG-ru.rst
.. _readme: https://github.com/wkpn/aio2ch/README-ru.rst
132 changes: 132 additions & 0 deletions README-ru.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
|Logo|

|License|
|Downloads|
|PyPi|
|Python|

Полностью асинхронный read-only API wrapper для 2ch.hk (dvach, Двач)

Требования
----------

- httpx_
- aiofiles_

Установка через pip
-------------------
.. code-block:: bash
$ pip3 install aio2ch
Сборка из исходников
--------------------
.. code-block:: bash
$ git clone https://github.com/wkpn/aio2ch
$ cd ./aio2ch
$ python3 setup.py install
Использование
-------------

Простой пример (тогда надо вызвать ``client.close()`` в конце использования)

.. code-block:: python
>>> from aio2ch import Api
>>> client = Api()
>>> ...
>>> await client.close()
Или можно использовать как менеджер контекста

.. code-block:: python
>>> async with Api() as client:
... boards = await client.get_boards()
Получить все доски

.. code-block:: python
>>> boards = await client.get_boards()
>>> boards
[<Board name: Фагготрия, id: fag>, ... ]
Также дополнительно можно получить ``status`` для каждого метода. Полезно, если нужны ретраи

.. code-block:: python
>>> status, boards = await client.get_boards(return_status=True)
>>> status
200
>>> boards
[<Board name: Фагготрия, id: fag>, ... ]
Получить все треды с доски

.. code-block:: python
>>> threads = await client.get_board_threads(board='b')
>>> threads
[<Thread 180981319>, ... ]
Получить топ тредов с доски с заданной сортировкой (*views*, *score* or *posts_count*)

.. code-block:: python
>>> top_threads = await client.get_top_board_threads(board='b', method='views', num=3)
>>> top_threads
[<Thread 180894312>, <Thread 180946622>, <Thread 180963318>]
Получить все посты с треда (``thread`` инстанс ``Thread``)

.. code-block:: python
>>> thread_posts = await client.get_thread_posts(thread=thread)
>>> thread_posts
[<Post 180894312>, ... ]
Получить все посты с треда по его адресу

.. code-block:: python
>>> thread_posts = await client.get_thread_posts(thread='https://2ch.hk/test/res/30972.html')
>>> thread_posts
[<Post 30972>, ... ]
Получить все медиа с треда (пикчи, webm-ки и прочее)

.. code-block:: python
>>> thread_media = await client.get_thread_media(thread=thread)
>>> thread_media
[<File name: 15336559148500.jpg, path: /b/src/180979032/15336559148500.jpg, size: 19>, ... ]
Скачать все медиа с треда на диск в папку

.. code-block:: python
>>> await client.download_thread_media(files=thread_media, save_to='./downloads')
.. |License| image:: https://img.shields.io/pypi/l/aio2ch.svg
:target: https://pypi.python.org/pypi/aio2ch
.. |Downloads| image:: https://pepy.tech/badge/aio2ch
:target: https://pepy.tech/project/aio2ch
.. |PyPi| image:: https://img.shields.io/pypi/v/aio2ch.svg
:target: https://pypi.python.org/pypi/aio2ch
.. |Python| image:: https://img.shields.io/pypi/pyversions/aio2ch.svg
:target: https://pypi.python.org/pypi/aio2ch
.. |Logo| image:: https://raw.githubusercontent.com/wkpn/aio2ch/master/docs/img/banner.jpg
.. _httpx: https://github.com/encode/httpx
.. _aiofiles: https://github.com/Tinche/aiofiles
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ Get all thread's posts (``thread`` is an instance of ``Thread``)
>>> thread_posts
[<Post 180894312>, ... ]
Get all thread's posts by url

.. code-block:: python
>>> thread_posts = await client.get_thread_posts(thread='https://2ch.hk/test/res/30972.html')
>>> thread_posts
[<Post 30972>, ... ]
Get all media in all thread's posts (images, webm and so on)

.. code-block:: python
Expand Down
2 changes: 1 addition & 1 deletion aio2ch/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__author__ = 'wkpn'
__version__ = '2.0.2'
__version__ = '2.0.3'
__license__ = 'MIT'
__url__ = 'https://github.com/wkpn/aio2ch'

Expand Down
39 changes: 36 additions & 3 deletions aio2ch/api.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
__all__ = 'Api'

from .exceptions import NoBoardProvidedException, WrongSortMethodException
from .objects import Board, File, Post, Thread
from .exceptions import (
InvalidBoardIdException,
InvalidThreadUrlException,
NoBoardProvidedException,
WrongSortMethodException
)
from .objects import (
Board,
File,
Post,
Thread
)
from .helpers import BOARDS_LIST, get_board_and_thread_from_url
from .settings import SORTING_METHODS
from .api_client import ApiClient

from typing import Any, Dict, Iterable, Optional, Tuple, Type, Union
from typing import (
Any,
Dict,
Iterable,
Optional,
Tuple,
Type,
Union
)
from types import TracebackType

import aiofiles
Expand Down Expand Up @@ -57,6 +76,9 @@ async def get_board_threads(self,
if isinstance(board, Board):
board = board.id

if board not in BOARDS_LIST:
raise InvalidBoardIdException(f'Board {board} doesn\'t exist')

status, threads = await self._get(url=f'{self._api_client.api_url}/{board}/threads.json')
threads = threads['threads']
threads = tuple(Thread(thread, board) for thread in threads)
Expand Down Expand Up @@ -90,6 +112,9 @@ async def get_top_board_threads(self,
if isinstance(board, Board):
board = board.id

if board not in BOARDS_LIST:
raise InvalidBoardIdException(f'Board {board} doesn\'t exist')

result = await self.get_board_threads(board, return_status=return_status)

if return_status:
Expand Down Expand Up @@ -126,11 +151,19 @@ async def get_thread_posts(self,
if isinstance(thread, Thread):
board = thread.board
thread = thread.num
elif isinstance(thread, str):
board, thread = get_board_and_thread_from_url(thread)

if not all((board, thread)):
raise InvalidThreadUrlException(f'Invalid thread url {thread}')
elif isinstance(board, Board):
board = board.id
elif not board:
raise NoBoardProvidedException('Board id is not provided')

if board not in BOARDS_LIST:
raise InvalidBoardIdException(f'Board {board} doesn\'t exist')

status, posts = await self._get(url=f'{self._api_client.api_url}/{board}/res/{thread}.json')
posts = posts['threads'][0]['posts']
posts = tuple(Post(post) for post in posts)
Expand Down
8 changes: 8 additions & 0 deletions aio2ch/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ class WrongSortMethodException(Exception):

class NoBoardProvidedException(Exception):
pass


class InvalidThreadUrlException(Exception):
pass


class InvalidBoardIdException(Exception):
pass
32 changes: 32 additions & 0 deletions aio2ch/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Tuple, Union

import re


BOARDS_LIST: Tuple = (
'b', 'vg', 'po', 'fag', 'news', '2d', 'v', 'hw', 'sex', 'a', 'au', 'biz', 'wm', 'soc', 'wrk', 'rf', 'pr',
'brg', 'me', 'mobi', 'kpop', 'c', 'w', 'hi', 'mov', 'fiz', 'ftb', 'sp', 'cg', 'ma', 'hry', 'dr', 'tes',
's', 'alco', 'ra', 'obr', 'em', 'di', 'gsg', 'ch', 'mlp', 'mus', 'fa', 'ga', 'fs', 'zog', 'psy', 'whn',
'bi', 'by', 'fg', 'fet', 'tv', 'un', 'fd', 'mu', 'ukr', 'pa', 'qtr4', 'fl', 'mg', 're', 'gd', 'bo', 'sn',
'spc', 't', 'd', 'ew', 'dom', 'wh', 'td', 'mc', 'mmo', 'fur', 'e', 'asmr', 'vape', 'sci', 'tr', 'p', 'gg',
'cul', 'out', 'sw', 'pok', 'es', 'diy', 'trv', 'hc', 'media', 'jsf', 'wow', 'cute', 'kz', 'socionics', 'o',
'ruvn', 'izd', 'wr', 'r', 'h', 'moba', 'cc', 'gabe', 'se', 'wwe', 'int', 'hh', 'mo', 'ne', 'pvc', 'ph', 'sf',
'de', 'wp', 'bg', 'aa', 'ja', 'rm', 'to', 'vn', 'ho', 'web', 'br', 'gb', 'abu', 'old', 'guro', 'ussr', 'law',
'm', 'ya', 'r34', '8', 'mlpr', 'ro', 'who', 'srv', 'electrach', 'ing', 'got', 'crypt', 'lap', 'smo', 'hg',
'sad', 'fi', 'nvr', 'ind', 'ld', 'fem', 'vr', 'arg', 'char', 'hv', 'math', 'catalog', 'api', 'test'
)


def get_board_and_thread_from_url(thread_url: str) -> Union[Tuple[str, str], Tuple[None, None]]:
# https://2ch.hk/test/res/30972.html

pattern = 'https://[2a-z]+.[a-z]+/[a-z]+/res/[0-9]+.html'
match = re.compile(pattern).match(thread_url)

if match:
split = re.split('[/.]', thread_url)
board, thread = split[4], split[6]

return board, thread

return None, None
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from setuptools import setup
from aio2ch import __author__, __version__, __license__, __url__

from setuptools import setup


with open('README.rst', 'r', encoding='utf-8') as f:
long_description = f.read()
Expand Down
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ def thread():
return Thread(test_thread_data, test_board)


@pytest.fixture
def thread_url():
return 'https://2ch.hk/test/res/30972.html'


@pytest.fixture
def invalid_thread_url():
return 'https://dvach.hk/blah/blah/blah.css'


@pytest.fixture
def number_of_threads(number=5):
return number
Loading

0 comments on commit 393ddc9

Please sign in to comment.