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.
Files changed (91) hide show
  1. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.github/workflows/publish.yml +7 -3
  2. chzzk_python-0.3.1/.github/workflows/release-please.yml +29 -0
  3. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/CHANGELOG.md +33 -0
  4. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/PKG-INFO +73 -1
  5. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/README.md +71 -0
  6. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/README_KO.md +71 -0
  7. chzzk_python-0.3.1/docs/unofficial-chat-websocket-protocol.md +272 -0
  8. chzzk_python-0.3.1/examples/.env.example +20 -0
  9. chzzk_python-0.3.1/examples/unofficial_chat.py +151 -0
  10. chzzk_python-0.3.1/examples/unofficial_chat_async.py +146 -0
  11. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/pyproject.toml +1 -0
  12. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/__init__.py +42 -0
  13. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/_version.py +2 -2
  14. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/exceptions/__init__.py +8 -0
  15. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/exceptions/errors.py +30 -0
  16. chzzk_python-0.3.1/src/chzzk/unofficial/__init__.py +87 -0
  17. chzzk_python-0.3.1/src/chzzk/unofficial/api/__init__.py +17 -0
  18. chzzk_python-0.3.1/src/chzzk/unofficial/api/base.py +32 -0
  19. chzzk_python-0.3.1/src/chzzk/unofficial/api/chat.py +47 -0
  20. chzzk_python-0.3.1/src/chzzk/unofficial/api/live.py +45 -0
  21. chzzk_python-0.3.1/src/chzzk/unofficial/api/user.py +39 -0
  22. chzzk_python-0.3.1/src/chzzk/unofficial/auth/__init__.py +9 -0
  23. chzzk_python-0.3.1/src/chzzk/unofficial/auth/cookie.py +155 -0
  24. chzzk_python-0.3.1/src/chzzk/unofficial/chat/__init__.py +10 -0
  25. chzzk_python-0.3.1/src/chzzk/unofficial/chat/client.py +653 -0
  26. chzzk_python-0.3.1/src/chzzk/unofficial/chat/connection.py +43 -0
  27. chzzk_python-0.3.1/src/chzzk/unofficial/chat/handler.py +252 -0
  28. chzzk_python-0.3.1/src/chzzk/unofficial/client.py +217 -0
  29. chzzk_python-0.3.1/src/chzzk/unofficial/http/__init__.py +20 -0
  30. chzzk_python-0.3.1/src/chzzk/unofficial/http/client.py +257 -0
  31. chzzk_python-0.3.1/src/chzzk/unofficial/http/endpoints.py +62 -0
  32. chzzk_python-0.3.1/src/chzzk/unofficial/models/__init__.py +28 -0
  33. chzzk_python-0.3.1/src/chzzk/unofficial/models/chat.py +128 -0
  34. chzzk_python-0.3.1/src/chzzk/unofficial/models/live.py +52 -0
  35. chzzk_python-0.3.1/src/chzzk/unofficial/models/user.py +18 -0
  36. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/uv.lock +2 -0
  37. chzzk_python-0.1.0/.github/workflows/release.yml +0 -32
  38. chzzk_python-0.1.0/cliff.toml +0 -49
  39. chzzk_python-0.1.0/examples/.env.example +0 -8
  40. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.github/workflows/ci.yml +0 -0
  41. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.gitignore +0 -0
  42. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/.python-version +0 -0
  43. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/LICENSE +0 -0
  44. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/oauth_server.py +0 -0
  45. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/realtime_chat.py +0 -0
  46. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/realtime_chat_async.py +0 -0
  47. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/examples/session_management.py +0 -0
  48. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/main.py +0 -0
  49. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/__init__.py +0 -0
  50. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/base.py +0 -0
  51. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/category.py +0 -0
  52. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/channel.py +0 -0
  53. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/chat.py +0 -0
  54. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/live.py +0 -0
  55. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/restriction.py +0 -0
  56. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/session.py +0 -0
  57. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/api/user.py +0 -0
  58. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/__init__.py +0 -0
  59. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/models.py +0 -0
  60. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/oauth.py +0 -0
  61. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/auth/token.py +0 -0
  62. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/client.py +0 -0
  63. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/http/__init__.py +0 -0
  64. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/http/client.py +0 -0
  65. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/http/endpoints.py +0 -0
  66. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/__init__.py +0 -0
  67. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/category.py +0 -0
  68. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/channel.py +0 -0
  69. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/chat.py +0 -0
  70. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/common.py +0 -0
  71. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/live.py +0 -0
  72. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/restriction.py +0 -0
  73. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/session.py +0 -0
  74. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/models/user.py +0 -0
  75. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/py.typed +0 -0
  76. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/realtime/__init__.py +0 -0
  77. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/src/chzzk/realtime/client.py +0 -0
  78. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/__init__.py +0 -0
  79. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/__init__.py +0 -0
  80. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_category.py +0 -0
  81. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_channel.py +0 -0
  82. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_chat.py +0 -0
  83. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_live.py +0 -0
  84. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_restriction.py +0 -0
  85. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_session.py +0 -0
  86. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/api/test_user.py +0 -0
  87. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/auth/__init__.py +0 -0
  88. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/auth/test_oauth.py +0 -0
  89. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/realtime/__init__.py +0 -0
  90. {chzzk_python-0.1.0 → chzzk_python-0.3.1}/tests/realtime/test_client.py +0 -0
  91. {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
- push:
5
- tags:
6
- - 'v*'
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.0
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=