chzzk-python 0.9.0__tar.gz → 0.9.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.9.0 → chzzk_python-0.9.1}/.env.example +6 -1
- chzzk_python-0.9.1/.github/workflows/build.yml +85 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/.gitignore +4 -1
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/PKG-INFO +17 -3
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/README.md +14 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/README_KO.md +14 -0
- chzzk_python-0.9.1/chzzk.spec +128 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/pyproject.toml +3 -2
- chzzk_python-0.9.1/scripts/build.py +126 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/_version.py +2 -2
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/commands/auth.py +2 -40
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/commands/chat.py +228 -204
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/formatter.py +0 -50
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/writers.py +38 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/cli/test_formatter.py +0 -42
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/uv.lock +217 -169
- chzzk_python-0.9.0/CHANGELOG.md +0 -183
- chzzk_python-0.9.0/src/chzzk/cli/tui/__init__.py +0 -6
- chzzk_python-0.9.0/src/chzzk/cli/tui/apps/__init__.py +0 -7
- chzzk_python-0.9.0/src/chzzk/cli/tui/apps/chat_interactive.py +0 -308
- chzzk_python-0.9.0/src/chzzk/cli/tui/apps/chat_viewer.py +0 -241
- chzzk_python-0.9.0/src/chzzk/cli/tui/apps/login.py +0 -218
- chzzk_python-0.9.0/src/chzzk/cli/tui/base.py +0 -52
- chzzk_python-0.9.0/src/chzzk/cli/tui/styles/app.tcss +0 -150
- chzzk_python-0.9.0/src/chzzk/cli/tui/utils.py +0 -82
- chzzk_python-0.9.0/src/chzzk/cli/tui/widgets/__init__.py +0 -6
- chzzk_python-0.9.0/src/chzzk/cli/tui/widgets/chat.py +0 -289
- chzzk_python-0.9.0/src/chzzk/cli/tui/widgets/input.py +0 -106
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/.github/workflows/ci.yml +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/.github/workflows/publish.yml +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/.python-version +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/LICENSE +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/docs/unofficial-chat-websocket-protocol.md +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/examples/.env.example +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/examples/oauth_server.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/examples/realtime_chat.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/examples/realtime_chat_async.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/examples/session_management.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/examples/unofficial_chat.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/examples/unofficial_chat_async.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/main.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/base.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/category.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/channel.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/chat.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/live.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/restriction.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/session.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/api/user.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/auth/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/auth/models.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/auth/oauth.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/auth/token.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/commands/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/commands/live.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/config.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/logging.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/cli/main.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/client.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/constants.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/exceptions/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/exceptions/errors.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/http/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/http/_base.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/http/client.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/http/endpoints.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/logging.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/category.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/channel.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/chat.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/common.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/live.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/restriction.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/session.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/models/user.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/py.typed +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/realtime/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/realtime/client.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/api/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/api/base.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/api/chat.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/api/live.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/api/user.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/auth/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/auth/cookie.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/chat/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/chat/client.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/chat/connection.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/chat/handler.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/chat/monitor.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/client.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/http/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/http/_base.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/http/client.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/http/endpoints.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/models/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/models/chat.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/models/live.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/models/reconnect.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/src/chzzk/unofficial/models/user.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/test_category.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/test_channel.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/test_chat.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/test_live.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/test_restriction.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/test_session.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/api/test_user.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/auth/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/auth/test_oauth.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/cli/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/cli/test_writers.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/realtime/__init__.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/realtime/test_client.py +0 -0
- {chzzk_python-0.9.0 → chzzk_python-0.9.1}/tests/test_client.py +0 -0
|
@@ -39,8 +39,13 @@ CHZZK_SENT_FORMAT=
|
|
|
39
39
|
CHZZK_TIME_FORMAT=
|
|
40
40
|
|
|
41
41
|
# Chat Output Configuration
|
|
42
|
-
# Save chat messages to file
|
|
42
|
+
# Save chat messages to file (explicit path)
|
|
43
43
|
CHZZK_CHAT_OUTPUT=
|
|
44
44
|
|
|
45
|
+
# Directory for auto-generated chat log filenames
|
|
46
|
+
# Filename format: {channel_id}_{live_id}_{YYYYMMDD}.{format}
|
|
47
|
+
# Note: --output and --output-dir are mutually exclusive
|
|
48
|
+
CHZZK_CHAT_OUTPUT_DIR=
|
|
49
|
+
|
|
45
50
|
# Output format: jsonl, txt (default: jsonl)
|
|
46
51
|
CHZZK_CHAT_OUTPUT_FORMAT=
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: Build Executables
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
include:
|
|
14
|
+
- os: ubuntu-latest
|
|
15
|
+
artifact_name: chzzk-linux
|
|
16
|
+
- os: windows-latest
|
|
17
|
+
artifact_name: chzzk-windows.exe
|
|
18
|
+
- os: macos-latest
|
|
19
|
+
artifact_name: chzzk-macos
|
|
20
|
+
|
|
21
|
+
runs-on: ${{ matrix.os }}
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
|
+
with:
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v7
|
|
30
|
+
with:
|
|
31
|
+
version: "latest"
|
|
32
|
+
|
|
33
|
+
- name: Set up Python
|
|
34
|
+
uses: actions/setup-python@v6
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.12"
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: uv sync --dev --all-extras
|
|
40
|
+
|
|
41
|
+
- name: Build executable
|
|
42
|
+
run: uv run python -m PyInstaller chzzk.spec --clean
|
|
43
|
+
|
|
44
|
+
- name: Verify executable (Unix)
|
|
45
|
+
if: runner.os != 'Windows'
|
|
46
|
+
run: |
|
|
47
|
+
chmod +x dist/${{ matrix.artifact_name }}
|
|
48
|
+
dist/${{ matrix.artifact_name }} --help
|
|
49
|
+
|
|
50
|
+
- name: Verify executable (Windows)
|
|
51
|
+
if: runner.os == 'Windows'
|
|
52
|
+
run: dist/${{ matrix.artifact_name }} --help
|
|
53
|
+
|
|
54
|
+
- name: Upload artifact
|
|
55
|
+
uses: actions/upload-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: ${{ matrix.artifact_name }}
|
|
58
|
+
path: dist/${{ matrix.artifact_name }}
|
|
59
|
+
if-no-files-found: error
|
|
60
|
+
|
|
61
|
+
release:
|
|
62
|
+
needs: build
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
65
|
+
permissions:
|
|
66
|
+
contents: write
|
|
67
|
+
|
|
68
|
+
steps:
|
|
69
|
+
- name: Download all artifacts
|
|
70
|
+
uses: actions/download-artifact@v4
|
|
71
|
+
with:
|
|
72
|
+
path: artifacts
|
|
73
|
+
|
|
74
|
+
- name: Prepare release files
|
|
75
|
+
run: |
|
|
76
|
+
mkdir -p release
|
|
77
|
+
cp artifacts/chzzk-linux/chzzk-linux release/
|
|
78
|
+
cp artifacts/chzzk-windows.exe/chzzk-windows.exe release/
|
|
79
|
+
cp artifacts/chzzk-macos/chzzk-macos release/
|
|
80
|
+
chmod +x release/chzzk-linux release/chzzk-macos
|
|
81
|
+
|
|
82
|
+
- name: Add executables to release
|
|
83
|
+
uses: softprops/action-gh-release@v2
|
|
84
|
+
with:
|
|
85
|
+
files: release/*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chzzk-python
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.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
|
|
@@ -29,8 +29,8 @@ Requires-Dist: python-socketio[asyncio-client]<5.0.0,>=4.6.0
|
|
|
29
29
|
Requires-Dist: websocket-client>=1.9.0
|
|
30
30
|
Requires-Dist: websockets>=12.0
|
|
31
31
|
Provides-Extra: cli
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist:
|
|
32
|
+
Requires-Dist: prompt-toolkit>=3.0.52; extra == 'cli'
|
|
33
|
+
Requires-Dist: rich>=14.3.1; extra == 'cli'
|
|
34
34
|
Requires-Dist: typer>=0.15.0; extra == 'cli'
|
|
35
35
|
Description-Content-Type: text/markdown
|
|
36
36
|
|
|
@@ -416,6 +416,14 @@ chzzk chat watch CHANNEL_ID
|
|
|
416
416
|
# Watch chat even when offline
|
|
417
417
|
chzzk chat watch CHANNEL_ID --offline
|
|
418
418
|
|
|
419
|
+
# Save chat to file
|
|
420
|
+
chzzk chat watch CHANNEL_ID --output chat.jsonl
|
|
421
|
+
chzzk chat watch CHANNEL_ID --output chat.txt --output-format txt
|
|
422
|
+
|
|
423
|
+
# Auto-generate filename based on stream info (recommended)
|
|
424
|
+
# Creates: {channel_id}_{live_id}_{YYYYMMDD}.jsonl
|
|
425
|
+
chzzk chat watch CHANNEL_ID --output-dir ./logs
|
|
426
|
+
|
|
419
427
|
# Send a single message (requires authentication)
|
|
420
428
|
chzzk chat send CHANNEL_ID "Hello!"
|
|
421
429
|
|
|
@@ -427,6 +435,9 @@ chzzk chat send CHANNEL_ID --interactive
|
|
|
427
435
|
# or
|
|
428
436
|
chzzk chat send CHANNEL_ID -i
|
|
429
437
|
|
|
438
|
+
# Interactive mode with chat logging
|
|
439
|
+
chzzk chat send CHANNEL_ID -i --output-dir ./logs
|
|
440
|
+
|
|
430
441
|
# Interactive mode with offline channel
|
|
431
442
|
chzzk chat send CHANNEL_ID -i --offline
|
|
432
443
|
```
|
|
@@ -447,6 +458,9 @@ chzzk chat send CHANNEL_ID -i --offline
|
|
|
447
458
|
| `CHZZK_NID_AUT` | NID_AUT cookie value |
|
|
448
459
|
| `CHZZK_NID_SES` | NID_SES cookie value |
|
|
449
460
|
| `CHZZK_LOG_LEVEL` | Default log level |
|
|
461
|
+
| `CHZZK_CHAT_OUTPUT` | Default chat output file path |
|
|
462
|
+
| `CHZZK_CHAT_OUTPUT_DIR` | Default chat output directory (auto-generates filename) |
|
|
463
|
+
| `CHZZK_CHAT_OUTPUT_FORMAT` | Default chat output format (jsonl, txt) |
|
|
450
464
|
|
|
451
465
|
## Examples
|
|
452
466
|
|
|
@@ -380,6 +380,14 @@ chzzk chat watch CHANNEL_ID
|
|
|
380
380
|
# Watch chat even when offline
|
|
381
381
|
chzzk chat watch CHANNEL_ID --offline
|
|
382
382
|
|
|
383
|
+
# Save chat to file
|
|
384
|
+
chzzk chat watch CHANNEL_ID --output chat.jsonl
|
|
385
|
+
chzzk chat watch CHANNEL_ID --output chat.txt --output-format txt
|
|
386
|
+
|
|
387
|
+
# Auto-generate filename based on stream info (recommended)
|
|
388
|
+
# Creates: {channel_id}_{live_id}_{YYYYMMDD}.jsonl
|
|
389
|
+
chzzk chat watch CHANNEL_ID --output-dir ./logs
|
|
390
|
+
|
|
383
391
|
# Send a single message (requires authentication)
|
|
384
392
|
chzzk chat send CHANNEL_ID "Hello!"
|
|
385
393
|
|
|
@@ -391,6 +399,9 @@ chzzk chat send CHANNEL_ID --interactive
|
|
|
391
399
|
# or
|
|
392
400
|
chzzk chat send CHANNEL_ID -i
|
|
393
401
|
|
|
402
|
+
# Interactive mode with chat logging
|
|
403
|
+
chzzk chat send CHANNEL_ID -i --output-dir ./logs
|
|
404
|
+
|
|
394
405
|
# Interactive mode with offline channel
|
|
395
406
|
chzzk chat send CHANNEL_ID -i --offline
|
|
396
407
|
```
|
|
@@ -411,6 +422,9 @@ chzzk chat send CHANNEL_ID -i --offline
|
|
|
411
422
|
| `CHZZK_NID_AUT` | NID_AUT cookie value |
|
|
412
423
|
| `CHZZK_NID_SES` | NID_SES cookie value |
|
|
413
424
|
| `CHZZK_LOG_LEVEL` | Default log level |
|
|
425
|
+
| `CHZZK_CHAT_OUTPUT` | Default chat output file path |
|
|
426
|
+
| `CHZZK_CHAT_OUTPUT_DIR` | Default chat output directory (auto-generates filename) |
|
|
427
|
+
| `CHZZK_CHAT_OUTPUT_FORMAT` | Default chat output format (jsonl, txt) |
|
|
414
428
|
|
|
415
429
|
## Examples
|
|
416
430
|
|
|
@@ -380,6 +380,14 @@ chzzk chat watch CHANNEL_ID
|
|
|
380
380
|
# 오프라인 상태에서도 채팅 보기
|
|
381
381
|
chzzk chat watch CHANNEL_ID --offline
|
|
382
382
|
|
|
383
|
+
# 채팅을 파일로 저장
|
|
384
|
+
chzzk chat watch CHANNEL_ID --output chat.jsonl
|
|
385
|
+
chzzk chat watch CHANNEL_ID --output chat.txt --output-format txt
|
|
386
|
+
|
|
387
|
+
# 방송 정보 기반 자동 파일명 생성 (권장)
|
|
388
|
+
# 생성 형식: {channel_id}_{live_id}_{YYYYMMDD}.jsonl
|
|
389
|
+
chzzk chat watch CHANNEL_ID --output-dir ./logs
|
|
390
|
+
|
|
383
391
|
# 단일 메시지 전송 (인증 필요)
|
|
384
392
|
chzzk chat send CHANNEL_ID "안녕하세요!"
|
|
385
393
|
|
|
@@ -391,6 +399,9 @@ chzzk chat send CHANNEL_ID --interactive
|
|
|
391
399
|
# 또는
|
|
392
400
|
chzzk chat send CHANNEL_ID -i
|
|
393
401
|
|
|
402
|
+
# 대화형 모드에서 채팅 로그 저장
|
|
403
|
+
chzzk chat send CHANNEL_ID -i --output-dir ./logs
|
|
404
|
+
|
|
394
405
|
# 오프라인 채널에서 대화형 모드
|
|
395
406
|
chzzk chat send CHANNEL_ID -i --offline
|
|
396
407
|
```
|
|
@@ -411,6 +422,9 @@ chzzk chat send CHANNEL_ID -i --offline
|
|
|
411
422
|
| `CHZZK_NID_AUT` | NID_AUT 쿠키 값 |
|
|
412
423
|
| `CHZZK_NID_SES` | NID_SES 쿠키 값 |
|
|
413
424
|
| `CHZZK_LOG_LEVEL` | 기본 로그 레벨 |
|
|
425
|
+
| `CHZZK_CHAT_OUTPUT` | 기본 채팅 출력 파일 경로 |
|
|
426
|
+
| `CHZZK_CHAT_OUTPUT_DIR` | 기본 채팅 출력 디렉토리 (파일명 자동 생성) |
|
|
427
|
+
| `CHZZK_CHAT_OUTPUT_FORMAT` | 기본 채팅 출력 형식 (jsonl, txt) |
|
|
414
428
|
|
|
415
429
|
## 예제 코드
|
|
416
430
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# -*- mode: python ; coding: utf-8 -*-
|
|
2
|
+
"""PyInstaller spec file for chzzk CLI."""
|
|
3
|
+
|
|
4
|
+
import platform
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|
8
|
+
|
|
9
|
+
# Determine output name based on platform
|
|
10
|
+
system = platform.system().lower()
|
|
11
|
+
if system == "linux":
|
|
12
|
+
exe_name = "chzzk-linux"
|
|
13
|
+
elif system == "windows":
|
|
14
|
+
exe_name = "chzzk-windows"
|
|
15
|
+
elif system == "darwin":
|
|
16
|
+
exe_name = "chzzk-macos"
|
|
17
|
+
else:
|
|
18
|
+
exe_name = f"chzzk-{system}"
|
|
19
|
+
|
|
20
|
+
# Collect hidden imports for all required packages
|
|
21
|
+
hidden_imports = [
|
|
22
|
+
# CLI framework
|
|
23
|
+
*collect_submodules("typer"),
|
|
24
|
+
*collect_submodules("click"),
|
|
25
|
+
# Rich console
|
|
26
|
+
*collect_submodules("rich"),
|
|
27
|
+
# Textual TUI
|
|
28
|
+
*collect_submodules("textual"),
|
|
29
|
+
# HTTP client
|
|
30
|
+
*collect_submodules("httpx"),
|
|
31
|
+
*collect_submodules("httpcore"),
|
|
32
|
+
# WebSocket
|
|
33
|
+
*collect_submodules("websockets"),
|
|
34
|
+
*collect_submodules("websocket"),
|
|
35
|
+
# Socket.IO
|
|
36
|
+
*collect_submodules("socketio"),
|
|
37
|
+
*collect_submodules("engineio"),
|
|
38
|
+
# Data validation
|
|
39
|
+
*collect_submodules("pydantic"),
|
|
40
|
+
*collect_submodules("pydantic_core"),
|
|
41
|
+
# Async support
|
|
42
|
+
*collect_submodules("anyio"),
|
|
43
|
+
# SSL certificates
|
|
44
|
+
"certifi",
|
|
45
|
+
# Encoding
|
|
46
|
+
"idna",
|
|
47
|
+
# Environment variables
|
|
48
|
+
"dotenv",
|
|
49
|
+
# Markdown for textual
|
|
50
|
+
"markdown_it",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
# Collect data files
|
|
54
|
+
datas = [
|
|
55
|
+
# SSL certificates
|
|
56
|
+
*collect_data_files("certifi"),
|
|
57
|
+
# Textual CSS and themes
|
|
58
|
+
*collect_data_files("textual"),
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
# Add app.tcss from chzzk CLI
|
|
62
|
+
tcss_path = Path("src/chzzk/cli/tui/styles/app.tcss")
|
|
63
|
+
if tcss_path.exists():
|
|
64
|
+
datas.append((str(tcss_path), "chzzk/cli/tui/styles"))
|
|
65
|
+
|
|
66
|
+
# Excluded modules to reduce binary size
|
|
67
|
+
excludes = [
|
|
68
|
+
"tkinter",
|
|
69
|
+
"_tkinter",
|
|
70
|
+
"unittest",
|
|
71
|
+
"email",
|
|
72
|
+
"xml",
|
|
73
|
+
"pydoc",
|
|
74
|
+
"doctest",
|
|
75
|
+
"argparse",
|
|
76
|
+
"difflib",
|
|
77
|
+
"inspect",
|
|
78
|
+
"pdb",
|
|
79
|
+
"profile",
|
|
80
|
+
"pstats",
|
|
81
|
+
"calendar",
|
|
82
|
+
"gettext",
|
|
83
|
+
"pickle",
|
|
84
|
+
"PIL",
|
|
85
|
+
"numpy",
|
|
86
|
+
"pandas",
|
|
87
|
+
"matplotlib",
|
|
88
|
+
"scipy",
|
|
89
|
+
"test",
|
|
90
|
+
"tests",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
a = Analysis(
|
|
94
|
+
["src/chzzk/cli/main.py"],
|
|
95
|
+
pathex=[],
|
|
96
|
+
binaries=[],
|
|
97
|
+
datas=datas,
|
|
98
|
+
hiddenimports=hidden_imports,
|
|
99
|
+
hookspath=[],
|
|
100
|
+
hooksconfig={},
|
|
101
|
+
runtime_hooks=[],
|
|
102
|
+
excludes=excludes,
|
|
103
|
+
noarchive=False,
|
|
104
|
+
optimize=0,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
pyz = PYZ(a.pure)
|
|
108
|
+
|
|
109
|
+
exe = EXE(
|
|
110
|
+
pyz,
|
|
111
|
+
a.scripts,
|
|
112
|
+
a.binaries,
|
|
113
|
+
a.datas,
|
|
114
|
+
[],
|
|
115
|
+
name=exe_name,
|
|
116
|
+
debug=False,
|
|
117
|
+
bootloader_ignore_signals=False,
|
|
118
|
+
strip=False,
|
|
119
|
+
upx=True,
|
|
120
|
+
upx_exclude=[],
|
|
121
|
+
runtime_tmpdir=None,
|
|
122
|
+
console=True,
|
|
123
|
+
disable_windowed_traceback=False,
|
|
124
|
+
argv_emulation=False,
|
|
125
|
+
target_arch=None,
|
|
126
|
+
codesign_identity=None,
|
|
127
|
+
entitlements_file=None,
|
|
128
|
+
)
|
|
@@ -36,8 +36,8 @@ dependencies = [
|
|
|
36
36
|
[project.optional-dependencies]
|
|
37
37
|
cli = [
|
|
38
38
|
"typer>=0.15.0",
|
|
39
|
-
"rich>=
|
|
40
|
-
"
|
|
39
|
+
"rich>=14.3.1",
|
|
40
|
+
"prompt-toolkit>=3.0.52",
|
|
41
41
|
]
|
|
42
42
|
|
|
43
43
|
[project.scripts]
|
|
@@ -54,6 +54,7 @@ Changelog = "https://github.com/hypn4/chzzk-python/blob/main/CHANGELOG.md"
|
|
|
54
54
|
dev = [
|
|
55
55
|
"flask>=3.1.2",
|
|
56
56
|
"mypy>=1.19.1",
|
|
57
|
+
"pyinstaller>=6.18.0",
|
|
57
58
|
"pytest>=9.0.2",
|
|
58
59
|
"pytest-asyncio>=1.3.0",
|
|
59
60
|
"pytest-httpx>=0.35.0",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Local build script for chzzk CLI executable."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import platform
|
|
8
|
+
import shutil
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
logging.basicConfig(
|
|
14
|
+
level=logging.INFO,
|
|
15
|
+
format="%(message)s",
|
|
16
|
+
)
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_executable_name() -> str:
|
|
21
|
+
"""Get the expected executable name based on platform."""
|
|
22
|
+
system = platform.system().lower()
|
|
23
|
+
names = {
|
|
24
|
+
"linux": "chzzk-linux",
|
|
25
|
+
"windows": "chzzk-windows.exe",
|
|
26
|
+
"darwin": "chzzk-macos",
|
|
27
|
+
}
|
|
28
|
+
return names.get(system, f"chzzk-{system}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clean_directories() -> None:
|
|
32
|
+
"""Clean previous build and dist directories."""
|
|
33
|
+
root = Path(__file__).parent.parent
|
|
34
|
+
build_dir = root / "build"
|
|
35
|
+
dist_dir = root / "dist"
|
|
36
|
+
|
|
37
|
+
for directory in [build_dir, dist_dir]:
|
|
38
|
+
if directory.exists():
|
|
39
|
+
logger.info("Cleaning %s...", directory)
|
|
40
|
+
shutil.rmtree(directory)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def build_executable() -> Path | None:
|
|
44
|
+
"""Build the executable using PyInstaller."""
|
|
45
|
+
root = Path(__file__).parent.parent
|
|
46
|
+
spec_file = root / "chzzk.spec"
|
|
47
|
+
|
|
48
|
+
if not spec_file.exists():
|
|
49
|
+
logger.error("Error: %s not found", spec_file)
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
logger.info("Building executable with PyInstaller...")
|
|
53
|
+
result = subprocess.run(
|
|
54
|
+
[sys.executable, "-m", "PyInstaller", str(spec_file), "--clean"],
|
|
55
|
+
cwd=root,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if result.returncode != 0:
|
|
59
|
+
logger.error("Error: PyInstaller build failed")
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
# Find the output executable
|
|
63
|
+
dist_dir = root / "dist"
|
|
64
|
+
exe_name = get_executable_name()
|
|
65
|
+
exe_path = dist_dir / exe_name
|
|
66
|
+
|
|
67
|
+
if exe_path.exists():
|
|
68
|
+
return exe_path
|
|
69
|
+
|
|
70
|
+
# Try without extension on Windows (in case .exe wasn't added)
|
|
71
|
+
if platform.system().lower() == "windows":
|
|
72
|
+
exe_path_no_ext = dist_dir / exe_name.replace(".exe", "")
|
|
73
|
+
if exe_path_no_ext.exists():
|
|
74
|
+
return exe_path_no_ext
|
|
75
|
+
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def format_size(size_bytes: int) -> str:
|
|
80
|
+
"""Format file size in human-readable format."""
|
|
81
|
+
for unit in ["B", "KB", "MB", "GB"]:
|
|
82
|
+
if size_bytes < 1024:
|
|
83
|
+
return f"{size_bytes:.2f} {unit}"
|
|
84
|
+
size_bytes /= 1024
|
|
85
|
+
return f"{size_bytes:.2f} TB"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def main() -> int:
|
|
89
|
+
"""Main build function."""
|
|
90
|
+
logger.info("=== chzzk CLI Build Script ===\n")
|
|
91
|
+
|
|
92
|
+
# Clean previous builds
|
|
93
|
+
clean_directories()
|
|
94
|
+
|
|
95
|
+
# Build executable
|
|
96
|
+
exe_path = build_executable()
|
|
97
|
+
|
|
98
|
+
if exe_path is None:
|
|
99
|
+
logger.error("\nBuild failed!")
|
|
100
|
+
return 1
|
|
101
|
+
|
|
102
|
+
# Report results
|
|
103
|
+
file_size = exe_path.stat().st_size
|
|
104
|
+
logger.info("\n=== Build Complete ===")
|
|
105
|
+
logger.info("Output: %s", exe_path)
|
|
106
|
+
logger.info("Size: %s", format_size(file_size))
|
|
107
|
+
|
|
108
|
+
# Quick verification
|
|
109
|
+
logger.info("\nVerifying executable...")
|
|
110
|
+
result = subprocess.run(
|
|
111
|
+
[str(exe_path), "--version"],
|
|
112
|
+
capture_output=True,
|
|
113
|
+
text=True,
|
|
114
|
+
)
|
|
115
|
+
if result.returncode == 0:
|
|
116
|
+
logger.info("Version: %s", result.stdout.strip())
|
|
117
|
+
else:
|
|
118
|
+
logger.warning("Warning: Could not verify executable version")
|
|
119
|
+
if result.stderr:
|
|
120
|
+
logger.warning("stderr: %s", result.stderr.strip())
|
|
121
|
+
|
|
122
|
+
return 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
if __name__ == "__main__":
|
|
126
|
+
sys.exit(main())
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.9.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 9,
|
|
31
|
+
__version__ = version = '0.9.1'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 9, 1)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -9,9 +9,6 @@ import typer
|
|
|
9
9
|
from rich.console import Console
|
|
10
10
|
from rich.panel import Panel
|
|
11
11
|
|
|
12
|
-
from chzzk.cli.tui import can_run_tui, run_tui
|
|
13
|
-
from chzzk.cli.tui.apps import LoginApp
|
|
14
|
-
|
|
15
12
|
if TYPE_CHECKING:
|
|
16
13
|
from chzzk.cli.config import ConfigManager
|
|
17
14
|
|
|
@@ -55,13 +52,6 @@ def login(
|
|
|
55
52
|
help="NID_SES cookie value from Naver login",
|
|
56
53
|
),
|
|
57
54
|
] = None,
|
|
58
|
-
no_tui: Annotated[
|
|
59
|
-
bool,
|
|
60
|
-
typer.Option(
|
|
61
|
-
"--no-tui",
|
|
62
|
-
help="Disable TUI and use simple prompts",
|
|
63
|
-
),
|
|
64
|
-
] = False,
|
|
65
55
|
) -> None:
|
|
66
56
|
"""Save Naver authentication cookies.
|
|
67
57
|
|
|
@@ -69,14 +59,11 @@ def login(
|
|
|
69
59
|
1. Open browser DevTools (F12)
|
|
70
60
|
2. Go to Application > Cookies > naver.com
|
|
71
61
|
3. Find NID_AUT and NID_SES values
|
|
72
|
-
|
|
73
|
-
By default, opens an interactive TUI for entering cookies.
|
|
74
|
-
Use --no-tui to use simple prompts instead.
|
|
75
62
|
"""
|
|
76
63
|
config = get_config(ctx)
|
|
77
64
|
json_output = ctx.obj.get("json_output", False)
|
|
78
65
|
|
|
79
|
-
# If both values provided via CLI, skip
|
|
66
|
+
# If both values provided via CLI, skip prompts
|
|
80
67
|
if nid_aut and nid_ses:
|
|
81
68
|
config.save_cookies(nid_aut, nid_ses)
|
|
82
69
|
if json_output:
|
|
@@ -91,32 +78,7 @@ def login(
|
|
|
91
78
|
)
|
|
92
79
|
return
|
|
93
80
|
|
|
94
|
-
#
|
|
95
|
-
if not json_output and not no_tui and can_run_tui():
|
|
96
|
-
login_app = LoginApp(config=config, nid_aut=nid_aut, nid_ses=nid_ses)
|
|
97
|
-
run_tui(login_app)
|
|
98
|
-
|
|
99
|
-
if login_app.result.cancelled:
|
|
100
|
-
console.print("[yellow]Login cancelled[/yellow]")
|
|
101
|
-
raise typer.Exit(0)
|
|
102
|
-
|
|
103
|
-
if login_app.result.success:
|
|
104
|
-
if json_output:
|
|
105
|
-
console.print(json.dumps({"status": "success", "message": "Cookies saved"}))
|
|
106
|
-
else:
|
|
107
|
-
console.print(
|
|
108
|
-
Panel(
|
|
109
|
-
f"Cookies saved to [cyan]{config.config_dir}[/cyan]",
|
|
110
|
-
title="[green]Login successful[/green]",
|
|
111
|
-
border_style="green",
|
|
112
|
-
)
|
|
113
|
-
)
|
|
114
|
-
else:
|
|
115
|
-
console.print("[red]Login failed[/red]")
|
|
116
|
-
raise typer.Exit(1)
|
|
117
|
-
return
|
|
118
|
-
|
|
119
|
-
# Fallback to simple prompts
|
|
81
|
+
# Use simple prompts
|
|
120
82
|
try:
|
|
121
83
|
final_nid_aut, final_nid_ses = _prompt_login_fallback(config)
|
|
122
84
|
except (KeyboardInterrupt, EOFError):
|