chzzk-python 0.1.0__tar.gz → 0.3.1__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.
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.github/workflows/publish.yml +7 -3
- chzzk_python-0.3.1/.github/workflows/release-please.yml +29 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/CHANGELOG.md +33 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/PKG-INFO +73 -1
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/README.md +71 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/README_KO.md +71 -0
- chzzk_python-0.3.1/docs/unofficial-chat-websocket-protocol.md +272 -0
- chzzk_python-0.3.1/examples/.env.example +20 -0
- chzzk_python-0.3.1/examples/unofficial_chat.py +151 -0
- chzzk_python-0.3.1/examples/unofficial_chat_async.py +146 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/pyproject.toml +1 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/__init__.py +42 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/_version.py +2 -2
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/exceptions/__init__.py +8 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/exceptions/errors.py +30 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/__init__.py +87 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/api/__init__.py +17 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/api/base.py +32 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/api/chat.py +47 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/api/live.py +45 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/api/user.py +39 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/auth/__init__.py +9 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/auth/cookie.py +155 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/chat/__init__.py +10 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/chat/client.py +653 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/chat/connection.py +43 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/chat/handler.py +252 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/client.py +217 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/http/__init__.py +20 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/http/client.py +257 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/http/endpoints.py +62 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/models/__init__.py +28 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/models/chat.py +128 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/models/live.py +52 -0
- chzzk_python-0.3.1/src/chzzk/unofficial/models/user.py +18 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/uv.lock +2 -0
- chzzk_python-0.1.0/.github/workflows/release.yml +0 -32
- chzzk_python-0.1.0/cliff.toml +0 -49
- chzzk_python-0.1.0/examples/.env.example +0 -8
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.github/workflows/ci.yml +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.gitignore +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.python-version +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/LICENSE +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/oauth_server.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/realtime_chat.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/realtime_chat_async.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/session_management.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/main.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/base.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/category.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/channel.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/chat.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/live.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/restriction.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/session.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/user.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/models.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/oauth.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/token.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/client.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/http/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/http/client.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/http/endpoints.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/category.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/channel.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/chat.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/common.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/live.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/restriction.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/session.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/user.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/py.typed +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/realtime/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/realtime/client.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_category.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_channel.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_chat.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_live.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_restriction.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_session.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_user.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/auth/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/auth/test_oauth.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/realtime/__init__.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/realtime/test_client.py +0 -0
- {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/test_client.py +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
name: Publish to PyPI
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_call:
|
|
7
|
+
workflow_dispatch:
|
|
7
8
|
|
|
8
9
|
jobs:
|
|
9
10
|
build:
|
|
@@ -49,6 +50,7 @@ jobs:
|
|
|
49
50
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
50
51
|
with:
|
|
51
52
|
repository-url: https://test.pypi.org/legacy/
|
|
53
|
+
skip-existing: true
|
|
52
54
|
|
|
53
55
|
publish-pypi:
|
|
54
56
|
needs: publish-testpypi
|
|
@@ -67,3 +69,5 @@ jobs:
|
|
|
67
69
|
|
|
68
70
|
- name: Publish to PyPI
|
|
69
71
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
72
|
+
with:
|
|
73
|
+
skip-existing: true
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Release Please
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
pull-requests: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
release-please:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
outputs:
|
|
16
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: googleapis/release-please-action@v4
|
|
19
|
+
id: release
|
|
20
|
+
with:
|
|
21
|
+
release-type: python
|
|
22
|
+
|
|
23
|
+
publish:
|
|
24
|
+
needs: release-please
|
|
25
|
+
if: ${{ needs.release-please.outputs.release_created }}
|
|
26
|
+
uses: ./.github/workflows/publish.yml
|
|
27
|
+
permissions:
|
|
28
|
+
contents: read
|
|
29
|
+
id-token: write
|
|
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.1](https://github.com/hypn4/chzzk-python/compare/v0.3.0...v0.3.1) (2026-01-23)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **ci:** add skip-existing to PyPI publish workflow ([84cce48](https://github.com/hypn4/chzzk-python/commit/84cce48))
|
|
14
|
+
|
|
15
|
+
## [0.3.0](https://github.com/hypn4/chzzk-python/compare/v0.2.0...v0.3.0) (2026-01-23)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* add unofficial API with Naver cookie authentication ([2481223](https://github.com/hypn4/chzzk-python/commit/2481223672b56c2de102d42fc4f17e7420e9df7a))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* **ci:** trigger publish workflow from release-please ([d63302a](https://github.com/hypn4/chzzk-python/commit/d63302a61e683c73246e5fbd62b7e55cd7bfdd02))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
|
|
30
|
+
* add unofficial API documentation to README ([0c7d575](https://github.com/hypn4/chzzk-python/commit/0c7d5756c6905ec9c4fa2715ffc3655516f9c1e3))
|
|
31
|
+
* add unofficial chat WebSocket protocol documentation ([bb2d4d3](https://github.com/hypn4/chzzk-python/commit/bb2d4d3d6fd058d365d8051169ba3318bdbc5c64))
|
|
32
|
+
* **examples:** add unofficial chat client examples ([8b68a41](https://github.com/hypn4/chzzk-python/commit/8b68a41964588ef40d4a5ce9cdd9bc4080cd5c09))
|
|
33
|
+
|
|
34
|
+
## [0.2.0](https://github.com/hypn4/chzzk-python/compare/v0.1.0...v0.2.0) (2026-01-23)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Features
|
|
38
|
+
|
|
39
|
+
* **ci:** add Release Please workflow for automated releases ([34be227](https://github.com/hypn4/chzzk-python/commit/34be227f6f75fe769f0a3760673520487b7dd004))
|
|
40
|
+
|
|
8
41
|
## [Unreleased]
|
|
9
42
|
|
|
10
43
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chzzk-python
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Unofficial Python SDK for Chzzk (NAVER Live Streaming Platform) API
|
|
5
5
|
Project-URL: Homepage, https://github.com/hypn4/chzzk-python
|
|
6
6
|
Project-URL: Repository, https://github.com/hypn4/chzzk-python
|
|
@@ -26,6 +26,7 @@ Requires-Dist: httpx>=0.28.0
|
|
|
26
26
|
Requires-Dist: pydantic>=2.10.0
|
|
27
27
|
Requires-Dist: python-socketio[asyncio-client]<5.0.0,>=4.6.0
|
|
28
28
|
Requires-Dist: websocket-client>=1.9.0
|
|
29
|
+
Requires-Dist: websockets>=12.0
|
|
29
30
|
Description-Content-Type: text/markdown
|
|
30
31
|
|
|
31
32
|
# chzzk-python
|
|
@@ -288,6 +289,75 @@ except ChzzkAPIError as e:
|
|
|
288
289
|
print(f"API Error: [{e.status_code}] {e.error_code}: {e.message}")
|
|
289
290
|
```
|
|
290
291
|
|
|
292
|
+
## Unofficial API
|
|
293
|
+
|
|
294
|
+
In addition to the official API, we provide an unofficial API using Naver cookie authentication.
|
|
295
|
+
This enables real-time chat receiving/sending functionality.
|
|
296
|
+
|
|
297
|
+
> ⚠️ The unofficial API may change at any time and is not officially supported.
|
|
298
|
+
|
|
299
|
+
### Unofficial Chat Client
|
|
300
|
+
|
|
301
|
+
**Synchronous version:**
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
from chzzk.unofficial import UnofficialChatClient, ChatMessage
|
|
305
|
+
|
|
306
|
+
chat = UnofficialChatClient(
|
|
307
|
+
nid_aut="your-nid-aut-cookie",
|
|
308
|
+
nid_ses="your-nid-ses-cookie",
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
@chat.on_chat
|
|
312
|
+
def on_chat(msg: ChatMessage):
|
|
313
|
+
print(f"{msg.nickname}: {msg.content}")
|
|
314
|
+
|
|
315
|
+
@chat.on_donation
|
|
316
|
+
def on_donation(msg):
|
|
317
|
+
print(f"{msg.nickname} donated {msg.pay_amount}won")
|
|
318
|
+
|
|
319
|
+
chat.connect("channel-id")
|
|
320
|
+
chat.send_message("Hello!")
|
|
321
|
+
chat.run_forever()
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Asynchronous version:**
|
|
325
|
+
|
|
326
|
+
```python
|
|
327
|
+
from chzzk.unofficial import AsyncUnofficialChatClient, ChatMessage
|
|
328
|
+
|
|
329
|
+
async with AsyncUnofficialChatClient(
|
|
330
|
+
nid_aut="your-nid-aut-cookie",
|
|
331
|
+
nid_ses="your-nid-ses-cookie",
|
|
332
|
+
) as chat:
|
|
333
|
+
@chat.on_chat
|
|
334
|
+
async def on_chat(msg: ChatMessage):
|
|
335
|
+
print(f"{msg.nickname}: {msg.content}")
|
|
336
|
+
|
|
337
|
+
await chat.connect("channel-id")
|
|
338
|
+
await chat.send_message("Hello!")
|
|
339
|
+
await chat.run_forever()
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### How to Get Naver Cookies
|
|
343
|
+
|
|
344
|
+
1. Log in to Naver
|
|
345
|
+
2. Browser Developer Tools (F12) → Application → Cookies
|
|
346
|
+
3. Copy `NID_AUT` and `NID_SES` cookie values
|
|
347
|
+
|
|
348
|
+
### Unofficial API Exception Handling
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from chzzk import ChatConnectionError, ChatNotLiveError
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
chat.connect("channel-id")
|
|
355
|
+
except ChatNotLiveError:
|
|
356
|
+
print("Channel is not currently live")
|
|
357
|
+
except ChatConnectionError as e:
|
|
358
|
+
print(f"Connection failed: {e}")
|
|
359
|
+
```
|
|
360
|
+
|
|
291
361
|
## Examples
|
|
292
362
|
|
|
293
363
|
See the [examples](examples/) directory for complete working examples:
|
|
@@ -296,6 +366,8 @@ See the [examples](examples/) directory for complete working examples:
|
|
|
296
366
|
- `realtime_chat.py` - Realtime chat/donation/subscription events (sync)
|
|
297
367
|
- `realtime_chat_async.py` - Realtime events (async)
|
|
298
368
|
- `session_management.py` - Session management example
|
|
369
|
+
- `unofficial_chat.py` - Unofficial chat client (sync)
|
|
370
|
+
- `unofficial_chat_async.py` - Unofficial chat client (async)
|
|
299
371
|
|
|
300
372
|
## API Reference
|
|
301
373
|
|
|
@@ -258,6 +258,75 @@ except ChzzkAPIError as e:
|
|
|
258
258
|
print(f"API Error: [{e.status_code}] {e.error_code}: {e.message}")
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
+
## Unofficial API
|
|
262
|
+
|
|
263
|
+
In addition to the official API, we provide an unofficial API using Naver cookie authentication.
|
|
264
|
+
This enables real-time chat receiving/sending functionality.
|
|
265
|
+
|
|
266
|
+
> ⚠️ The unofficial API may change at any time and is not officially supported.
|
|
267
|
+
|
|
268
|
+
### Unofficial Chat Client
|
|
269
|
+
|
|
270
|
+
**Synchronous version:**
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from chzzk.unofficial import UnofficialChatClient, ChatMessage
|
|
274
|
+
|
|
275
|
+
chat = UnofficialChatClient(
|
|
276
|
+
nid_aut="your-nid-aut-cookie",
|
|
277
|
+
nid_ses="your-nid-ses-cookie",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
@chat.on_chat
|
|
281
|
+
def on_chat(msg: ChatMessage):
|
|
282
|
+
print(f"{msg.nickname}: {msg.content}")
|
|
283
|
+
|
|
284
|
+
@chat.on_donation
|
|
285
|
+
def on_donation(msg):
|
|
286
|
+
print(f"{msg.nickname} donated {msg.pay_amount}won")
|
|
287
|
+
|
|
288
|
+
chat.connect("channel-id")
|
|
289
|
+
chat.send_message("Hello!")
|
|
290
|
+
chat.run_forever()
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Asynchronous version:**
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from chzzk.unofficial import AsyncUnofficialChatClient, ChatMessage
|
|
297
|
+
|
|
298
|
+
async with AsyncUnofficialChatClient(
|
|
299
|
+
nid_aut="your-nid-aut-cookie",
|
|
300
|
+
nid_ses="your-nid-ses-cookie",
|
|
301
|
+
) as chat:
|
|
302
|
+
@chat.on_chat
|
|
303
|
+
async def on_chat(msg: ChatMessage):
|
|
304
|
+
print(f"{msg.nickname}: {msg.content}")
|
|
305
|
+
|
|
306
|
+
await chat.connect("channel-id")
|
|
307
|
+
await chat.send_message("Hello!")
|
|
308
|
+
await chat.run_forever()
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### How to Get Naver Cookies
|
|
312
|
+
|
|
313
|
+
1. Log in to Naver
|
|
314
|
+
2. Browser Developer Tools (F12) → Application → Cookies
|
|
315
|
+
3. Copy `NID_AUT` and `NID_SES` cookie values
|
|
316
|
+
|
|
317
|
+
### Unofficial API Exception Handling
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
from chzzk import ChatConnectionError, ChatNotLiveError
|
|
321
|
+
|
|
322
|
+
try:
|
|
323
|
+
chat.connect("channel-id")
|
|
324
|
+
except ChatNotLiveError:
|
|
325
|
+
print("Channel is not currently live")
|
|
326
|
+
except ChatConnectionError as e:
|
|
327
|
+
print(f"Connection failed: {e}")
|
|
328
|
+
```
|
|
329
|
+
|
|
261
330
|
## Examples
|
|
262
331
|
|
|
263
332
|
See the [examples](examples/) directory for complete working examples:
|
|
@@ -266,6 +335,8 @@ See the [examples](examples/) directory for complete working examples:
|
|
|
266
335
|
- `realtime_chat.py` - Realtime chat/donation/subscription events (sync)
|
|
267
336
|
- `realtime_chat_async.py` - Realtime events (async)
|
|
268
337
|
- `session_management.py` - Session management example
|
|
338
|
+
- `unofficial_chat.py` - Unofficial chat client (sync)
|
|
339
|
+
- `unofficial_chat_async.py` - Unofficial chat client (async)
|
|
269
340
|
|
|
270
341
|
## API Reference
|
|
271
342
|
|
|
@@ -258,6 +258,75 @@ except ChzzkAPIError as e:
|
|
|
258
258
|
print(f"API 오류: [{e.status_code}] {e.error_code}: {e.message}")
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
+
## 비공식 API (Unofficial API)
|
|
262
|
+
|
|
263
|
+
공식 API 외에도 네이버 쿠키 인증을 통한 비공식 API를 제공합니다.
|
|
264
|
+
이를 통해 실시간 채팅 수신/전송이 가능합니다.
|
|
265
|
+
|
|
266
|
+
> ⚠️ 비공식 API는 언제든 변경될 수 있으며, 공식적으로 지원되지 않습니다.
|
|
267
|
+
|
|
268
|
+
### 비공식 채팅 클라이언트
|
|
269
|
+
|
|
270
|
+
**동기 버전:**
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
from chzzk.unofficial import UnofficialChatClient, ChatMessage
|
|
274
|
+
|
|
275
|
+
chat = UnofficialChatClient(
|
|
276
|
+
nid_aut="your-nid-aut-cookie",
|
|
277
|
+
nid_ses="your-nid-ses-cookie",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
@chat.on_chat
|
|
281
|
+
def on_chat(msg: ChatMessage):
|
|
282
|
+
print(f"{msg.nickname}: {msg.content}")
|
|
283
|
+
|
|
284
|
+
@chat.on_donation
|
|
285
|
+
def on_donation(msg):
|
|
286
|
+
print(f"{msg.nickname} donated {msg.pay_amount}won")
|
|
287
|
+
|
|
288
|
+
chat.connect("channel-id")
|
|
289
|
+
chat.send_message("안녕하세요!")
|
|
290
|
+
chat.run_forever()
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**비동기 버전:**
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
from chzzk.unofficial import AsyncUnofficialChatClient, ChatMessage
|
|
297
|
+
|
|
298
|
+
async with AsyncUnofficialChatClient(
|
|
299
|
+
nid_aut="your-nid-aut-cookie",
|
|
300
|
+
nid_ses="your-nid-ses-cookie",
|
|
301
|
+
) as chat:
|
|
302
|
+
@chat.on_chat
|
|
303
|
+
async def on_chat(msg: ChatMessage):
|
|
304
|
+
print(f"{msg.nickname}: {msg.content}")
|
|
305
|
+
|
|
306
|
+
await chat.connect("channel-id")
|
|
307
|
+
await chat.send_message("안녕하세요!")
|
|
308
|
+
await chat.run_forever()
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### 네이버 쿠키 획득 방법
|
|
312
|
+
|
|
313
|
+
1. 네이버에 로그인
|
|
314
|
+
2. 브라우저 개발자 도구 (F12) → Application → Cookies
|
|
315
|
+
3. `NID_AUT`와 `NID_SES` 쿠키 값 복사
|
|
316
|
+
|
|
317
|
+
### 비공식 API 예외 처리
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
from chzzk import ChatConnectionError, ChatNotLiveError
|
|
321
|
+
|
|
322
|
+
try:
|
|
323
|
+
chat.connect("channel-id")
|
|
324
|
+
except ChatNotLiveError:
|
|
325
|
+
print("채널이 현재 라이브 중이 아닙니다")
|
|
326
|
+
except ChatConnectionError as e:
|
|
327
|
+
print(f"연결 실패: {e}")
|
|
328
|
+
```
|
|
329
|
+
|
|
261
330
|
## 예제 코드
|
|
262
331
|
|
|
263
332
|
완전한 작동 예제는 [examples](examples/) 디렉토리를 참조하세요:
|
|
@@ -266,6 +335,8 @@ except ChzzkAPIError as e:
|
|
|
266
335
|
- `realtime_chat.py` - 실시간 채팅/후원/구독 이벤트 (동기)
|
|
267
336
|
- `realtime_chat_async.py` - 실시간 이벤트 (비동기)
|
|
268
337
|
- `session_management.py` - 세션 관리 예제
|
|
338
|
+
- `unofficial_chat.py` - 비공식 채팅 클라이언트 (동기)
|
|
339
|
+
- `unofficial_chat_async.py` - 비공식 채팅 클라이언트 (비동기)
|
|
269
340
|
|
|
270
341
|
## API 문서
|
|
271
342
|
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Chzzk 비공식 채팅 WebSocket 프로토콜 분석
|
|
2
|
+
|
|
3
|
+
> 최종 업데이트: 2026-01-23
|
|
4
|
+
> 분석 방법: Chrome DevTools WebSocket 메시지 캡처
|
|
5
|
+
|
|
6
|
+
## 개요
|
|
7
|
+
|
|
8
|
+
치지직(Chzzk) 라이브 채팅은 WebSocket을 통해 실시간으로 메시지를 주고받습니다. 이 문서는 브라우저에서 캡처한 실제 WebSocket 메시지를 기반으로 프로토콜 구조를 정리한 것입니다.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 용어 정리
|
|
13
|
+
|
|
14
|
+
| 용어 | 설명 | 예시 |
|
|
15
|
+
|------|------|------|
|
|
16
|
+
| `channelId` (스트리밍) | 스트리머의 채널 ID. 방송 URL에 포함됨 | `14f67c97d654f655afc0c9b3********` |
|
|
17
|
+
| `cid` (채팅) | 채팅 채널 ID. live-detail API에서 획득 | `N2FOy2` |
|
|
18
|
+
| `sid` (세션) | WebSocket 세션 ID. CONNECTED 응답에서 획득 | `S9g51s_L99y1f_qm4yfhMCdDV6Vxoi...` |
|
|
19
|
+
| `uid` | 사용자 ID 해시. getUserStatus API에서 획득 | `14f67c97d654f655afc0c9b3********` |
|
|
20
|
+
| `accTkn` | 채팅 액세스 토큰. access-token API에서 획득 | `EcXl9izPI9otGQ5yUr1i0wWLKH******` |
|
|
21
|
+
| `extraToken` | 추가 토큰. access-token API에서 획득 (채팅 전송 시 필요) | `uyZcBSQyl3OVq/hVGsuwTCh2Qm******` |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 프로토콜 버전
|
|
26
|
+
|
|
27
|
+
- **현재 버전**: `"3"`
|
|
28
|
+
- 모든 메시지의 `ver` 필드에 프로토콜 버전을 명시해야 함
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 명령어 코드 (cmd)
|
|
33
|
+
|
|
34
|
+
| 코드 | 이름 | 방향 | 설명 |
|
|
35
|
+
|------|------|------|------|
|
|
36
|
+
| `0` | PING | Server → Client | 서버에서 보내는 핑 |
|
|
37
|
+
| `10000` | PONG | Client → Server | 클라이언트의 퐁 응답 |
|
|
38
|
+
| `100` | CONNECT | Client → Server | 연결 요청 |
|
|
39
|
+
| `10100` | CONNECTED | Server → Client | 연결 완료 응답 |
|
|
40
|
+
| `3101` | SEND_CHAT | Client → Server | 채팅 메시지 전송 |
|
|
41
|
+
| `5101` | REQUEST_RECENT_CHAT | Client → Server | 최근 채팅 요청 |
|
|
42
|
+
| `15101` | RECENT_CHAT | Server → Client | 최근 채팅 응답 |
|
|
43
|
+
| `93101` | CHAT | Server → Client | 실시간 채팅 메시지 |
|
|
44
|
+
| `93102` | DONATION | Server → Client | 후원 메시지 |
|
|
45
|
+
| `93006` | EVENT | Server → Client | 이벤트 메시지 |
|
|
46
|
+
| `94005` | KICK | Server → Client | 강제퇴장 |
|
|
47
|
+
| `94006` | BLOCK | Server → Client | 차단 |
|
|
48
|
+
| `94008` | BLIND | Server → Client | 블라인드 처리 |
|
|
49
|
+
| `94010` | NOTICE | Server → Client | 공지 |
|
|
50
|
+
| `94015` | PENALTY | Server → Client | 제재 |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 메시지 구조
|
|
55
|
+
|
|
56
|
+
### 1. PONG (cmd: 10000)
|
|
57
|
+
|
|
58
|
+
서버의 PING에 대한 응답.
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"ver": "3",
|
|
63
|
+
"cmd": 10000
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. CONNECT (cmd: 100)
|
|
68
|
+
|
|
69
|
+
WebSocket 연결 후 인증 요청.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"ver": "3",
|
|
74
|
+
"cmd": 100,
|
|
75
|
+
"svcid": "game",
|
|
76
|
+
"cid": "N2FOy2",
|
|
77
|
+
"bdy": {
|
|
78
|
+
"uid": "14f67c97d654f655afc0c9b3********",
|
|
79
|
+
"devType": 2001,
|
|
80
|
+
"accTkn": "EcXl9izPI9otGQ5yUr1i0wWLKHqfPCFTsn/hV+Ec5b5jmyEiTDARbYOZ8OxYGkjq****************",
|
|
81
|
+
"auth": "SEND"
|
|
82
|
+
},
|
|
83
|
+
"tid": 1
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**필드 설명:**
|
|
88
|
+
- `svcid`: 서비스 ID (항상 `"game"`)
|
|
89
|
+
- `cid`: 채팅 채널 ID
|
|
90
|
+
- `bdy.uid`: 사용자 ID 해시 (메시지 전송 시 필수)
|
|
91
|
+
- `bdy.devType`: 디바이스 타입 (`2001` = PC)
|
|
92
|
+
- `bdy.accTkn`: 채팅 액세스 토큰
|
|
93
|
+
- `bdy.auth`: 인증 모드 (`"SEND"` = 전송 가능, `"READ"` = 읽기 전용)
|
|
94
|
+
- `tid`: 트랜잭션 ID
|
|
95
|
+
|
|
96
|
+
### 3. CONNECTED (cmd: 10100)
|
|
97
|
+
|
|
98
|
+
연결 완료 응답. **중요: `sid`를 저장해야 함.**
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"ver": "3",
|
|
103
|
+
"cmd": 10100,
|
|
104
|
+
"bdy": {
|
|
105
|
+
"sid": "S9g51s_L99y1f_qm4yfhMCdDV6VxoiPz!fqyXd_8tXFYKo!Qa_War3NOEcVK!84Y7Scugt3t********"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**중요**: `bdy.sid`를 저장하여 이후 채팅 전송 시 사용해야 합니다.
|
|
111
|
+
|
|
112
|
+
### 4. REQUEST_RECENT_CHAT (cmd: 5101)
|
|
113
|
+
|
|
114
|
+
최근 채팅 메시지 요청.
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"ver": "3",
|
|
119
|
+
"cmd": 5101,
|
|
120
|
+
"svcid": "game",
|
|
121
|
+
"cid": "N2FOy2",
|
|
122
|
+
"sid": 1,
|
|
123
|
+
"bdy": {
|
|
124
|
+
"recentMessageCount": 50
|
|
125
|
+
},
|
|
126
|
+
"tid": 3
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**참고**: 이 요청의 `sid`는 세션 ID가 아닌 숫자 `1`입니다.
|
|
131
|
+
|
|
132
|
+
### 5. SEND_CHAT (cmd: 3101) - 핵심
|
|
133
|
+
|
|
134
|
+
채팅 메시지 전송. **가장 중요한 메시지 구조.**
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"ver": "3",
|
|
139
|
+
"cmd": 3101,
|
|
140
|
+
"svcid": "game",
|
|
141
|
+
"cid": "N2FOy2",
|
|
142
|
+
"sid": "S9g51s_L99y1f_qm4yfhMCdDV6VxoiPz!fqyXd_8tXFYKo!Qa_War3NOEcVK!84Y7Scugt3t********",
|
|
143
|
+
"retry": false,
|
|
144
|
+
"bdy": {
|
|
145
|
+
"msg": "메시지 내용",
|
|
146
|
+
"msgTypeCode": 1,
|
|
147
|
+
"extras": "{\"chatType\":\"STREAMING\",\"osType\":\"PC\",\"extraToken\":\"uyZcBSQyl3OVq/hVGsuwTCh2Qm+RMtLIY0+FqBimmo1D2KWUKNlzCbrXxR81QZ8Vx5yIqW4oaz9u********\",\"streamingChannelId\":\"14f67c97d654f655afc0c9b3********\",\"emojis\":{}}",
|
|
148
|
+
"msgTime": 1769139665965
|
|
149
|
+
},
|
|
150
|
+
"tid": 6
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**필드 설명:**
|
|
155
|
+
- `sid`: CONNECTED 응답에서 받은 세션 ID 문자열 (숫자 아님!)
|
|
156
|
+
- `retry`: 재시도 여부
|
|
157
|
+
- `bdy.msg`: 메시지 내용
|
|
158
|
+
- `bdy.msgTypeCode`: 메시지 타입 (`1` = 일반 텍스트)
|
|
159
|
+
- `bdy.extras`: JSON 문자열로 인코딩된 추가 데이터
|
|
160
|
+
- `bdy.msgTime`: 밀리초 단위 타임스탬프
|
|
161
|
+
|
|
162
|
+
**extras 필드 상세 (JSON 파싱 후):**
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"chatType": "STREAMING",
|
|
167
|
+
"osType": "PC",
|
|
168
|
+
"extraToken": "uyZcBSQyl3OVq/hVGsuwTCh2Qm+RMtLIY0+FqBimmo1D2KWUKNlzCbrXxR81QZ8Vx5yIqW4oaz9u********",
|
|
169
|
+
"streamingChannelId": "14f67c97d654f655afc0c9b3********",
|
|
170
|
+
"emojis": {}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
| 필드 | 필수 | 설명 |
|
|
175
|
+
|------|------|------|
|
|
176
|
+
| `chatType` | O | 채팅 타입 (`"STREAMING"`) |
|
|
177
|
+
| `osType` | O | OS 타입 (`"PC"`, `"MOBILE"` 등) |
|
|
178
|
+
| `extraToken` | O | access-token API에서 받은 `extraToken` |
|
|
179
|
+
| `streamingChannelId` | O | 스트리밍 채널 ID (connect 시 사용한 원래 channel ID) |
|
|
180
|
+
| `emojis` | X | 이모지 데이터 (빈 객체 가능) |
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 채팅 전송 플로우
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
1. live-detail API 호출
|
|
188
|
+
└─> chatChannelId 획득 (예: "N2FOy2")
|
|
189
|
+
|
|
190
|
+
2. access-token API 호출 (channelId, chatType=STREAMING)
|
|
191
|
+
└─> accessToken, extraToken 획득
|
|
192
|
+
|
|
193
|
+
3. getUserStatus API 호출
|
|
194
|
+
└─> userIdHash 획득 (uid로 사용)
|
|
195
|
+
|
|
196
|
+
4. WebSocket 연결
|
|
197
|
+
└─> wss://kr-ss1.chat.naver.com/chat
|
|
198
|
+
|
|
199
|
+
5. CONNECT 메시지 전송 (cmd: 100)
|
|
200
|
+
└─> uid, accTkn, auth="SEND" 포함
|
|
201
|
+
|
|
202
|
+
6. CONNECTED 응답 수신 (cmd: 10100)
|
|
203
|
+
└─> bdy.sid 저장 (중요!)
|
|
204
|
+
|
|
205
|
+
7. 채팅 전송 시 SEND_CHAT 메시지 (cmd: 3101)
|
|
206
|
+
└─> sid (세션 ID 문자열)
|
|
207
|
+
└─> extras에 extraToken, streamingChannelId 포함
|
|
208
|
+
└─> msgTime에 현재 시간(ms) 포함
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## API 엔드포인트
|
|
214
|
+
|
|
215
|
+
### access-token API
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
GET https://api.chzzk.naver.com/service/v3/channel/{channelId}/chat/access-token
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Query Parameters:**
|
|
222
|
+
- `channelId`: 스트리밍 채널 ID
|
|
223
|
+
- `chatType`: `STREAMING`
|
|
224
|
+
|
|
225
|
+
**Response:**
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"code": 200,
|
|
229
|
+
"content": {
|
|
230
|
+
"accessToken": "EcXl9izPI9otGQ5yUr1i0wWLKH******",
|
|
231
|
+
"extraToken": "uyZcBSQyl3OVq/hVGsuwTCh2Qm******",
|
|
232
|
+
"realNameAuth": false,
|
|
233
|
+
"temporaryRestrict": null
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### getUserStatus API
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
GET https://api.chzzk.naver.com/service/v1/user/status
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Response:**
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"code": 200,
|
|
248
|
+
"content": {
|
|
249
|
+
"loggedIn": true,
|
|
250
|
+
"userIdHash": "14f67c97d654f655afc0c9b3********",
|
|
251
|
+
...
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 주의사항
|
|
259
|
+
|
|
260
|
+
1. **프로토콜 버전**: 모든 메시지에 `"ver": "3"` 사용
|
|
261
|
+
2. **세션 ID**: CONNECTED 응답의 `sid`는 문자열이며, SEND_CHAT에서 반드시 사용해야 함
|
|
262
|
+
3. **extras**: JSON 문자열로 인코딩하여 전송
|
|
263
|
+
4. **extraToken**: 채팅 전송 시 반드시 포함해야 함
|
|
264
|
+
5. **msgTime**: 밀리초 단위의 현재 타임스탬프 사용
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 변경 이력
|
|
269
|
+
|
|
270
|
+
| 날짜 | 변경 내용 |
|
|
271
|
+
|------|----------|
|
|
272
|
+
| 2026-01-23 | 최초 작성. Chrome DevTools WebSocket 캡처 기반 프로토콜 분석 |
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Chzzk OAuth Configuration
|
|
2
|
+
# Copy this file to .env and fill in your credentials
|
|
3
|
+
|
|
4
|
+
CHZZK_CLIENT_ID=your-client-id
|
|
5
|
+
CHZZK_CLIENT_SECRET=your-client-secret
|
|
6
|
+
CHZZK_REDIRECT_URI=http://localhost:8080/callback
|
|
7
|
+
CHZZK_TOKEN_FILE=~/.chzzk_token.json
|
|
8
|
+
CHZZK_PORT=8080
|
|
9
|
+
|
|
10
|
+
# Unofficial API (Naver Cookie Authentication)
|
|
11
|
+
# 브라우저에서 네이버 로그인 후 개발자 도구에서 쿠키 확인
|
|
12
|
+
# 1. 브라우저에서 chzzk.naver.com에 로그인
|
|
13
|
+
# 2. 개발자 도구 (F12) -> Application -> Cookies -> chzzk.naver.com
|
|
14
|
+
# 3. NID_AUT와 NID_SES 값을 복사
|
|
15
|
+
CHZZK_NID_AUT=your-nid-aut-cookie
|
|
16
|
+
CHZZK_NID_SES=your-nid-ses-cookie
|
|
17
|
+
|
|
18
|
+
# 시청할 채널 ID (선택사항, 명령행에서도 지정 가능)
|
|
19
|
+
# 채널 ID는 https://chzzk.naver.com/채널ID 형식의 URL에서 확인 가능
|
|
20
|
+
CHZZK_CHANNEL_ID=
|