chzzk-python 0.12.0__tar.gz → 0.13.0__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.12.0 → chzzk_python-0.13.0}/.env.example +18 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/.github/workflows/docker.yml +6 -3
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/PKG-INFO +27 -1
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/README.md +26 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/README_KO.md +26 -0
- chzzk_python-0.13.0/docker-compose.yml +140 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/_version.py +2 -2
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/commands/chat.py +4 -4
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/formatter.py +4 -3
- chzzk_python-0.13.0/src/chzzk/cli/timezone.py +49 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/writers.py +10 -9
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/.dockerignore +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/.github/workflows/build.yml +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/.github/workflows/ci.yml +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/.github/workflows/publish.yml +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/.gitignore +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/.python-version +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/Dockerfile +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/LICENSE +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/chzzk.spec +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/docs/unofficial-chat-websocket-protocol.md +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/examples/.env.example +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/examples/oauth_server.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/examples/realtime_chat.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/examples/realtime_chat_async.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/examples/session_management.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/examples/unofficial_chat.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/examples/unofficial_chat_async.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/main.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/pyproject.toml +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/scripts/build.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/base.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/category.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/channel.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/chat.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/live.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/restriction.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/session.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/api/user.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/auth/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/auth/models.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/auth/oauth.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/auth/token.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/commands/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/commands/auth.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/commands/live.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/config.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/logging.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/cli/main.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/constants.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/exceptions/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/exceptions/errors.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/http/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/http/_base.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/http/client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/http/endpoints.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/logging.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/category.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/channel.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/chat.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/common.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/live.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/restriction.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/session.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/models/user.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/py.typed +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/realtime/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/realtime/client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/api/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/api/base.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/api/chat.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/api/live.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/api/user.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/auth/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/auth/cookie.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/chat/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/chat/client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/chat/connection.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/chat/handler.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/chat/monitor.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/http/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/http/_base.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/http/client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/http/endpoints.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/models/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/models/chat.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/models/live.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/models/reconnect.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/src/chzzk/unofficial/models/user.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/test_category.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/test_channel.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/test_chat.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/test_live.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/test_restriction.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/test_session.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/api/test_user.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/auth/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/auth/test_oauth.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/cli/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/cli/test_formatter.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/cli/test_writers.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/realtime/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/realtime/test_client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/test_client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/unofficial/__init__.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/unofficial/test_client.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/tests/unofficial/test_monitor.py +0 -0
- {chzzk_python-0.12.0 → chzzk_python-0.13.0}/uv.lock +0 -0
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Chzzk CLI Configuration
|
|
2
2
|
# Copy this file to .env and fill in your values
|
|
3
3
|
|
|
4
|
+
# Timezone Configuration
|
|
5
|
+
# Docker 컨테이너 시스템 시간대 (default: Asia/Seoul)
|
|
6
|
+
TZ=Asia/Seoul
|
|
7
|
+
|
|
8
|
+
# 채팅 타임스탬프 및 로그 파일명에 사용되는 시간대 (default: Asia/Seoul)
|
|
9
|
+
# TZ와 별도로 설정 가능하며, 로그 출력에만 영향
|
|
10
|
+
CHZZK_TIMEZONE=Asia/Seoul
|
|
11
|
+
|
|
4
12
|
# Authentication (Naver Cookie)
|
|
5
13
|
# 브라우저에서 네이버 로그인 후 개발자 도구에서 쿠키 확인
|
|
6
14
|
# 1. 브라우저에서 chzzk.naver.com에 로그인
|
|
@@ -9,6 +17,16 @@
|
|
|
9
17
|
CHZZK_NID_AUT=
|
|
10
18
|
CHZZK_NID_SES=
|
|
11
19
|
|
|
20
|
+
# Channel ID
|
|
21
|
+
# 채팅을 모니터링할 채널 ID
|
|
22
|
+
CHZZK_CHANNEL_ID=
|
|
23
|
+
|
|
24
|
+
# Channel IDs (Docker Compose only)
|
|
25
|
+
# Docker Compose 다중 채널 모니터링 전용 (chat-watch-1, chat-watch-2, chat-watch-3)
|
|
26
|
+
CHZZK_CHANNEL_ID_1=
|
|
27
|
+
CHZZK_CHANNEL_ID_2=
|
|
28
|
+
CHZZK_CHANNEL_ID_3=
|
|
29
|
+
|
|
12
30
|
# Logging Configuration
|
|
13
31
|
# Log level: DEBUG, INFO, WARNING, ERROR
|
|
14
32
|
CHZZK_LOG_LEVEL=WARNING
|
|
@@ -145,13 +145,16 @@ jobs:
|
|
|
145
145
|
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
|
146
146
|
$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)
|
|
147
147
|
|
|
148
|
-
- name:
|
|
148
|
+
- name: Get image digest
|
|
149
|
+
id: digest
|
|
149
150
|
run: |
|
|
150
|
-
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
|
|
151
|
+
DIGEST=$(docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} --format '{{json .Manifest.Digest}}' | tr -d '"')
|
|
152
|
+
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
|
|
153
|
+
echo "Image digest: $DIGEST"
|
|
151
154
|
|
|
152
155
|
- name: Generate artifact attestation
|
|
153
156
|
uses: actions/attest-build-provenance@v2
|
|
154
157
|
with:
|
|
155
158
|
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
156
|
-
subject-digest: ${{ steps.
|
|
159
|
+
subject-digest: ${{ steps.digest.outputs.digest }}
|
|
157
160
|
push-to-registry: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chzzk-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
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
|
|
@@ -90,6 +90,30 @@ docker run --rm -it \
|
|
|
90
90
|
|
|
91
91
|
Available tags: `latest`, `X.Y.Z`, `X.Y`, `X`
|
|
92
92
|
|
|
93
|
+
### Docker Compose
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Copy and configure environment variables
|
|
97
|
+
cp .env.example .env
|
|
98
|
+
# Edit .env with your credentials and channel ID
|
|
99
|
+
|
|
100
|
+
# Run CLI commands
|
|
101
|
+
docker compose run --rm chzzk --help
|
|
102
|
+
docker compose run --rm chzzk live info CHANNEL_ID
|
|
103
|
+
|
|
104
|
+
# Watch chat (background service with auto-restart)
|
|
105
|
+
docker compose --profile chat up -d chat-watch
|
|
106
|
+
|
|
107
|
+
# Interactive chat mode
|
|
108
|
+
docker compose --profile interactive run --rm chat-interactive
|
|
109
|
+
|
|
110
|
+
# View logs
|
|
111
|
+
docker compose --profile chat logs -f chat-watch
|
|
112
|
+
|
|
113
|
+
# Stop services
|
|
114
|
+
docker compose --profile chat down
|
|
115
|
+
```
|
|
116
|
+
|
|
93
117
|
## Quick Start
|
|
94
118
|
|
|
95
119
|
```python
|
|
@@ -552,6 +576,8 @@ chzzk chat send CHANNEL_ID -i --offline
|
|
|
552
576
|
| `CHZZK_CHAT_OUTPUT_FORMAT` | Default chat output format (jsonl, txt) |
|
|
553
577
|
| `CHZZK_POLL_INTERVAL` | Live status polling interval in seconds (default: 10) |
|
|
554
578
|
| `CHZZK_AUTO_RECONNECT` | Enable auto-reconnection (default: true, set "false" to disable) |
|
|
579
|
+
| `CHZZK_TIMEZONE` | Timezone for chat timestamps and log filenames (default: Asia/Seoul) |
|
|
580
|
+
| `TZ` | System timezone for Docker containers (default: Asia/Seoul) |
|
|
555
581
|
|
|
556
582
|
## Examples
|
|
557
583
|
|
|
@@ -53,6 +53,30 @@ docker run --rm -it \
|
|
|
53
53
|
|
|
54
54
|
Available tags: `latest`, `X.Y.Z`, `X.Y`, `X`
|
|
55
55
|
|
|
56
|
+
### Docker Compose
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Copy and configure environment variables
|
|
60
|
+
cp .env.example .env
|
|
61
|
+
# Edit .env with your credentials and channel ID
|
|
62
|
+
|
|
63
|
+
# Run CLI commands
|
|
64
|
+
docker compose run --rm chzzk --help
|
|
65
|
+
docker compose run --rm chzzk live info CHANNEL_ID
|
|
66
|
+
|
|
67
|
+
# Watch chat (background service with auto-restart)
|
|
68
|
+
docker compose --profile chat up -d chat-watch
|
|
69
|
+
|
|
70
|
+
# Interactive chat mode
|
|
71
|
+
docker compose --profile interactive run --rm chat-interactive
|
|
72
|
+
|
|
73
|
+
# View logs
|
|
74
|
+
docker compose --profile chat logs -f chat-watch
|
|
75
|
+
|
|
76
|
+
# Stop services
|
|
77
|
+
docker compose --profile chat down
|
|
78
|
+
```
|
|
79
|
+
|
|
56
80
|
## Quick Start
|
|
57
81
|
|
|
58
82
|
```python
|
|
@@ -515,6 +539,8 @@ chzzk chat send CHANNEL_ID -i --offline
|
|
|
515
539
|
| `CHZZK_CHAT_OUTPUT_FORMAT` | Default chat output format (jsonl, txt) |
|
|
516
540
|
| `CHZZK_POLL_INTERVAL` | Live status polling interval in seconds (default: 10) |
|
|
517
541
|
| `CHZZK_AUTO_RECONNECT` | Enable auto-reconnection (default: true, set "false" to disable) |
|
|
542
|
+
| `CHZZK_TIMEZONE` | Timezone for chat timestamps and log filenames (default: Asia/Seoul) |
|
|
543
|
+
| `TZ` | System timezone for Docker containers (default: Asia/Seoul) |
|
|
518
544
|
|
|
519
545
|
## Examples
|
|
520
546
|
|
|
@@ -53,6 +53,30 @@ docker run --rm -it \
|
|
|
53
53
|
|
|
54
54
|
사용 가능한 태그: `latest`, `X.Y.Z`, `X.Y`, `X`
|
|
55
55
|
|
|
56
|
+
### Docker Compose
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# 환경 변수 파일 복사 및 설정
|
|
60
|
+
cp .env.example .env
|
|
61
|
+
# .env 파일에 인증 정보와 채널 ID 입력
|
|
62
|
+
|
|
63
|
+
# CLI 명령어 실행
|
|
64
|
+
docker compose run --rm chzzk --help
|
|
65
|
+
docker compose run --rm chzzk live info CHANNEL_ID
|
|
66
|
+
|
|
67
|
+
# 채팅 모니터링 (백그라운드 서비스, 자동 재시작)
|
|
68
|
+
docker compose --profile chat up -d chat-watch
|
|
69
|
+
|
|
70
|
+
# 대화형 채팅 모드
|
|
71
|
+
docker compose --profile interactive run --rm chat-interactive
|
|
72
|
+
|
|
73
|
+
# 로그 확인
|
|
74
|
+
docker compose --profile chat logs -f chat-watch
|
|
75
|
+
|
|
76
|
+
# 서비스 종료
|
|
77
|
+
docker compose --profile chat down
|
|
78
|
+
```
|
|
79
|
+
|
|
56
80
|
## 빠른 시작
|
|
57
81
|
|
|
58
82
|
```python
|
|
@@ -515,6 +539,8 @@ chzzk chat send CHANNEL_ID -i --offline
|
|
|
515
539
|
| `CHZZK_CHAT_OUTPUT_FORMAT` | 기본 채팅 출력 형식 (jsonl, txt) |
|
|
516
540
|
| `CHZZK_POLL_INTERVAL` | 라이브 상태 폴링 간격 (초, 기본값: 10) |
|
|
517
541
|
| `CHZZK_AUTO_RECONNECT` | 자동 재연결 활성화 (기본값: true, 비활성화: "false") |
|
|
542
|
+
| `CHZZK_TIMEZONE` | 채팅 타임스탬프 및 로그 파일명 시간대 (기본값: Asia/Seoul) |
|
|
543
|
+
| `TZ` | Docker 컨테이너 시스템 시간대 (기본값: Asia/Seoul) |
|
|
518
544
|
|
|
519
545
|
## 예제 코드
|
|
520
546
|
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# chzzk-python Docker Compose
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# Setup:
|
|
6
|
+
# cp .env.example .env
|
|
7
|
+
# # Edit .env with your CHZZK_CHANNEL_ID_1, CHZZK_CHANNEL_ID_2, ...
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# # QR 로그인으로 쿠키 저장 (최초 1회)
|
|
11
|
+
# mkdir -p .chzzk .logs && chmod 777 .chzzk .logs
|
|
12
|
+
# docker compose run --rm -v ./.chzzk:/home/chzzk/.chzzk chzzk auth qr
|
|
13
|
+
# # 또는 docker run 사용:
|
|
14
|
+
# docker run --rm -it -v ./.chzzk:/home/chzzk/.chzzk ghcr.io/hypn4/chzzk-python:latest auth qr
|
|
15
|
+
# # 또는 호스트 사용자 권한으로 실행:
|
|
16
|
+
# docker run --rm -it --user $(id -u):$(id -g) -v ./.chzzk:/home/chzzk/.chzzk ghcr.io/hypn4/chzzk-python:latest auth qr
|
|
17
|
+
#
|
|
18
|
+
# # CLI 명령어 실행
|
|
19
|
+
# docker compose run --rm chzzk --help
|
|
20
|
+
# docker compose run --rm chzzk live info CHANNEL_ID
|
|
21
|
+
#
|
|
22
|
+
# # 채팅 모니터링 (백그라운드)
|
|
23
|
+
# # .env에 CHZZK_CHANNEL_ID_1, CHZZK_CHANNEL_ID_2, ... 설정 후:
|
|
24
|
+
# docker compose --profile chat up -d
|
|
25
|
+
# docker compose --profile chat logs -f
|
|
26
|
+
# docker compose --profile chat down
|
|
27
|
+
# # 특정 채널만 실행:
|
|
28
|
+
# docker compose --profile chat up -d chat-watch-1
|
|
29
|
+
#
|
|
30
|
+
# # 대화형 채팅
|
|
31
|
+
# docker compose --profile interactive run --rm chat-interactive
|
|
32
|
+
#
|
|
33
|
+
# =============================================================================
|
|
34
|
+
|
|
35
|
+
# YAML Anchor: 채팅 모니터링 서비스 공통 설정
|
|
36
|
+
x-chat-watch-base: &chat-watch-base
|
|
37
|
+
image: ghcr.io/hypn4/chzzk-python:latest
|
|
38
|
+
restart: unless-stopped
|
|
39
|
+
profiles:
|
|
40
|
+
- chat
|
|
41
|
+
environment:
|
|
42
|
+
- TZ=${TZ:-Asia/Seoul}
|
|
43
|
+
- CHZZK_TIMEZONE=${CHZZK_TIMEZONE:-Asia/Seoul}
|
|
44
|
+
- CHZZK_NID_AUT=${CHZZK_NID_AUT:-}
|
|
45
|
+
- CHZZK_NID_SES=${CHZZK_NID_SES:-}
|
|
46
|
+
- CHZZK_LOG_LEVEL=${CHZZK_LOG_LEVEL:-INFO}
|
|
47
|
+
- CHZZK_POLL_INTERVAL=${CHZZK_POLL_INTERVAL:-10}
|
|
48
|
+
volumes:
|
|
49
|
+
- ./.logs:/home/chzzk/logs
|
|
50
|
+
- ./.chzzk:/home/chzzk/.chzzk
|
|
51
|
+
|
|
52
|
+
services:
|
|
53
|
+
chzzk:
|
|
54
|
+
image: ghcr.io/hypn4/chzzk-python:latest
|
|
55
|
+
container_name: chzzk
|
|
56
|
+
restart: "no"
|
|
57
|
+
stdin_open: true
|
|
58
|
+
tty: true
|
|
59
|
+
environment:
|
|
60
|
+
- TZ=${TZ:-Asia/Seoul}
|
|
61
|
+
- CHZZK_TIMEZONE=${CHZZK_TIMEZONE:-Asia/Seoul}
|
|
62
|
+
- CHZZK_NID_AUT=${CHZZK_NID_AUT:-}
|
|
63
|
+
- CHZZK_NID_SES=${CHZZK_NID_SES:-}
|
|
64
|
+
- CHZZK_LOG_LEVEL=${CHZZK_LOG_LEVEL:-INFO}
|
|
65
|
+
- CHZZK_POLL_INTERVAL=${CHZZK_POLL_INTERVAL:-10}
|
|
66
|
+
- CHZZK_AUTO_RECONNECT=${CHZZK_AUTO_RECONNECT:-true}
|
|
67
|
+
volumes:
|
|
68
|
+
- ./.logs:/home/chzzk/logs
|
|
69
|
+
# Uncomment to persist cookies (run: docker compose run --rm chzzk auth qr)
|
|
70
|
+
# - ./.chzzk:/home/chzzk/.chzzk
|
|
71
|
+
# Override command as needed:
|
|
72
|
+
# command: ["chat", "watch", "CHANNEL_ID"]
|
|
73
|
+
# command: ["chat", "watch", "CHANNEL_ID", "--output-dir", "/home/chzzk/logs"]
|
|
74
|
+
# command: ["live", "info", "CHANNEL_ID"]
|
|
75
|
+
|
|
76
|
+
# Multi-channel chat monitoring (uses x-chat-watch-base anchor)
|
|
77
|
+
# 각 서비스는 CHZZK_CHANNEL_ID_N 환경변수를 사용하여 채널 지정
|
|
78
|
+
chat-watch-1:
|
|
79
|
+
<<: *chat-watch-base
|
|
80
|
+
container_name: chzzk-chat-watch-1
|
|
81
|
+
command:
|
|
82
|
+
- chat
|
|
83
|
+
- watch
|
|
84
|
+
- ${CHZZK_CHANNEL_ID_1:-}
|
|
85
|
+
- --output-dir
|
|
86
|
+
- /home/chzzk/logs
|
|
87
|
+
- --offline
|
|
88
|
+
- --auto-reconnect
|
|
89
|
+
|
|
90
|
+
chat-watch-2:
|
|
91
|
+
<<: *chat-watch-base
|
|
92
|
+
container_name: chzzk-chat-watch-2
|
|
93
|
+
command:
|
|
94
|
+
- chat
|
|
95
|
+
- watch
|
|
96
|
+
- ${CHZZK_CHANNEL_ID_2:-}
|
|
97
|
+
- --output-dir
|
|
98
|
+
- /home/chzzk/logs
|
|
99
|
+
- --offline
|
|
100
|
+
- --auto-reconnect
|
|
101
|
+
|
|
102
|
+
chat-watch-3:
|
|
103
|
+
<<: *chat-watch-base
|
|
104
|
+
container_name: chzzk-chat-watch-3
|
|
105
|
+
command:
|
|
106
|
+
- chat
|
|
107
|
+
- watch
|
|
108
|
+
- ${CHZZK_CHANNEL_ID_3:-}
|
|
109
|
+
- --output-dir
|
|
110
|
+
- /home/chzzk/logs
|
|
111
|
+
- --offline
|
|
112
|
+
- --auto-reconnect
|
|
113
|
+
|
|
114
|
+
# Example: Interactive chat mode
|
|
115
|
+
chat-interactive:
|
|
116
|
+
image: ghcr.io/hypn4/chzzk-python:latest
|
|
117
|
+
container_name: chzzk-chat-interactive
|
|
118
|
+
restart: "no"
|
|
119
|
+
profiles:
|
|
120
|
+
- interactive
|
|
121
|
+
stdin_open: true
|
|
122
|
+
tty: true
|
|
123
|
+
environment:
|
|
124
|
+
- TZ=${TZ:-Asia/Seoul}
|
|
125
|
+
- CHZZK_TIMEZONE=${CHZZK_TIMEZONE:-Asia/Seoul}
|
|
126
|
+
- CHZZK_NID_AUT=${CHZZK_NID_AUT:-}
|
|
127
|
+
- CHZZK_NID_SES=${CHZZK_NID_SES:-}
|
|
128
|
+
- CHZZK_LOG_LEVEL=${CHZZK_LOG_LEVEL:-INFO}
|
|
129
|
+
volumes:
|
|
130
|
+
- ./.logs:/home/chzzk/logs
|
|
131
|
+
# - ./.chzzk:/home/chzzk/.chzzk
|
|
132
|
+
command:
|
|
133
|
+
- chat
|
|
134
|
+
- send
|
|
135
|
+
- ${CHZZK_CHANNEL_ID:-}
|
|
136
|
+
- --interactive
|
|
137
|
+
- --output-dir
|
|
138
|
+
- /home/chzzk/logs
|
|
139
|
+
- --offline
|
|
140
|
+
- --auto-reconnect
|
|
@@ -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.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.13.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 13, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -7,7 +7,6 @@ import contextlib
|
|
|
7
7
|
import json
|
|
8
8
|
import logging
|
|
9
9
|
import signal
|
|
10
|
-
from datetime import datetime
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
from typing import Annotated
|
|
13
12
|
|
|
@@ -16,6 +15,7 @@ from prompt_toolkit import PromptSession
|
|
|
16
15
|
from prompt_toolkit.patch_stdout import patch_stdout
|
|
17
16
|
from rich.console import Console
|
|
18
17
|
|
|
18
|
+
from chzzk.cli import timezone as tz
|
|
19
19
|
from chzzk.cli.formatter import ChatFormatter, FormatConfig
|
|
20
20
|
from chzzk.cli.writers import ChatWriter, OutputFormat, create_writer, generate_chat_log_filename
|
|
21
21
|
from chzzk.constants import StatusText
|
|
@@ -57,7 +57,7 @@ def format_chat_message_json(msg: ChatMessage) -> str:
|
|
|
57
57
|
return json.dumps(
|
|
58
58
|
{
|
|
59
59
|
"type": "chat",
|
|
60
|
-
"timestamp":
|
|
60
|
+
"timestamp": tz.now().isoformat(),
|
|
61
61
|
"user_id_hash": msg.user_id_hash,
|
|
62
62
|
"nickname": msg.nickname,
|
|
63
63
|
"content": msg.content,
|
|
@@ -71,7 +71,7 @@ def format_donation_message_json(msg: DonationMessage) -> str:
|
|
|
71
71
|
return json.dumps(
|
|
72
72
|
{
|
|
73
73
|
"type": "donation",
|
|
74
|
-
"timestamp":
|
|
74
|
+
"timestamp": tz.now().isoformat(),
|
|
75
75
|
"user_id_hash": msg.user_id_hash,
|
|
76
76
|
"nickname": msg.nickname,
|
|
77
77
|
"content": msg.content,
|
|
@@ -85,7 +85,7 @@ def format_sent_message_json(content: str) -> str:
|
|
|
85
85
|
return json.dumps(
|
|
86
86
|
{
|
|
87
87
|
"type": "sent",
|
|
88
|
-
"timestamp":
|
|
88
|
+
"timestamp": tz.now().isoformat(),
|
|
89
89
|
"content": content,
|
|
90
90
|
}
|
|
91
91
|
)
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from datetime import datetime
|
|
7
6
|
from typing import TYPE_CHECKING, Any
|
|
8
7
|
|
|
9
8
|
from rich.markup import escape
|
|
10
9
|
|
|
10
|
+
from chzzk.cli import timezone as tz
|
|
11
|
+
|
|
11
12
|
if TYPE_CHECKING:
|
|
12
13
|
from chzzk.unofficial import ChatMessage, ChatProfile, DonationMessage
|
|
13
14
|
|
|
@@ -119,7 +120,7 @@ class ChatFormatter:
|
|
|
119
120
|
|
|
120
121
|
def _get_timestamp(self) -> str:
|
|
121
122
|
"""Get formatted timestamp string."""
|
|
122
|
-
return
|
|
123
|
+
return tz.now().strftime(self.config.time_format)
|
|
123
124
|
|
|
124
125
|
def _escape_markup(self, text: str) -> str:
|
|
125
126
|
"""Escape Rich markup characters in text.
|
|
@@ -214,7 +215,7 @@ class ChatFormatter:
|
|
|
214
215
|
"""
|
|
215
216
|
if not message_time:
|
|
216
217
|
return ""
|
|
217
|
-
dt =
|
|
218
|
+
dt = tz.from_timestamp(message_time / 1000)
|
|
218
219
|
return dt.strftime(self.config.time_format)
|
|
219
220
|
|
|
220
221
|
def _get_pay_type_str(self, pay_type: str | None) -> str:
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Timezone utilities for CLI output.
|
|
2
|
+
|
|
3
|
+
This module provides centralized timezone handling for consistent timestamp
|
|
4
|
+
display across different environments (local, Docker, etc.).
|
|
5
|
+
|
|
6
|
+
The timezone can be configured via the CHZZK_TIMEZONE environment variable.
|
|
7
|
+
Default timezone is Asia/Seoul (KST).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from zoneinfo import ZoneInfo
|
|
15
|
+
|
|
16
|
+
DEFAULT_TIMEZONE = "Asia/Seoul"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_timezone() -> ZoneInfo:
|
|
20
|
+
"""Get the configured timezone.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
ZoneInfo object for the configured timezone.
|
|
24
|
+
Uses CHZZK_TIMEZONE environment variable if set,
|
|
25
|
+
otherwise defaults to Asia/Seoul.
|
|
26
|
+
"""
|
|
27
|
+
tz_name = os.environ.get("CHZZK_TIMEZONE", DEFAULT_TIMEZONE)
|
|
28
|
+
return ZoneInfo(tz_name)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def now() -> datetime:
|
|
32
|
+
"""Get current datetime in the configured timezone.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Current datetime with timezone info.
|
|
36
|
+
"""
|
|
37
|
+
return datetime.now(get_timezone())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def from_timestamp(timestamp: float) -> datetime:
|
|
41
|
+
"""Convert Unix timestamp to datetime in the configured timezone.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
timestamp: Unix timestamp (seconds since epoch).
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Datetime object in the configured timezone.
|
|
48
|
+
"""
|
|
49
|
+
return datetime.fromtimestamp(timestamp, get_timezone())
|
|
@@ -4,11 +4,12 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
-
from datetime import datetime
|
|
8
7
|
from enum import StrEnum
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
from typing import TYPE_CHECKING
|
|
11
10
|
|
|
11
|
+
from chzzk.cli import timezone as tz
|
|
12
|
+
|
|
12
13
|
if TYPE_CHECKING:
|
|
13
14
|
from chzzk.unofficial import ChatMessage, DonationMessage
|
|
14
15
|
|
|
@@ -98,7 +99,7 @@ class JsonlWriter(ChatWriter):
|
|
|
98
99
|
|
|
99
100
|
data = {
|
|
100
101
|
"type": "chat",
|
|
101
|
-
"timestamp":
|
|
102
|
+
"timestamp": tz.now().isoformat(),
|
|
102
103
|
"user_id_hash": msg.user_id_hash,
|
|
103
104
|
"nickname": msg.nickname,
|
|
104
105
|
"content": msg.content,
|
|
@@ -111,7 +112,7 @@ class JsonlWriter(ChatWriter):
|
|
|
111
112
|
"""Write a donation message in JSONL format."""
|
|
112
113
|
data = {
|
|
113
114
|
"type": "donation",
|
|
114
|
-
"timestamp":
|
|
115
|
+
"timestamp": tz.now().isoformat(),
|
|
115
116
|
"user_id_hash": msg.user_id_hash,
|
|
116
117
|
"nickname": msg.nickname,
|
|
117
118
|
"content": msg.content,
|
|
@@ -124,7 +125,7 @@ class JsonlWriter(ChatWriter):
|
|
|
124
125
|
"""Write a sent message in JSONL format."""
|
|
125
126
|
data = {
|
|
126
127
|
"type": "sent",
|
|
127
|
-
"timestamp":
|
|
128
|
+
"timestamp": tz.now().isoformat(),
|
|
128
129
|
"content": content,
|
|
129
130
|
}
|
|
130
131
|
self._file.write(json.dumps(data, ensure_ascii=False) + "\n")
|
|
@@ -150,7 +151,7 @@ class TextWriter(ChatWriter):
|
|
|
150
151
|
|
|
151
152
|
def write_chat(self, msg: ChatMessage) -> None:
|
|
152
153
|
"""Write a chat message in text format."""
|
|
153
|
-
timestamp =
|
|
154
|
+
timestamp = tz.now().strftime("%H:%M:%S")
|
|
154
155
|
badge_name = None
|
|
155
156
|
if msg.profile:
|
|
156
157
|
# 1. Check profile.badge dict
|
|
@@ -175,14 +176,14 @@ class TextWriter(ChatWriter):
|
|
|
175
176
|
|
|
176
177
|
def write_donation(self, msg: DonationMessage) -> None:
|
|
177
178
|
"""Write a donation message in text format."""
|
|
178
|
-
timestamp =
|
|
179
|
+
timestamp = tz.now().strftime("%H:%M:%S")
|
|
179
180
|
content = msg.content or ""
|
|
180
181
|
self._file.write(f"[{timestamp}] {msg.pay_amount}원 {msg.nickname}: {content}\n")
|
|
181
182
|
self._file.flush()
|
|
182
183
|
|
|
183
184
|
def write_sent(self, content: str) -> None:
|
|
184
185
|
"""Write a sent message in text format."""
|
|
185
|
-
timestamp =
|
|
186
|
+
timestamp = tz.now().strftime("%H:%M:%S")
|
|
186
187
|
self._file.write(f"[{timestamp}] > {content}\n")
|
|
187
188
|
self._file.flush()
|
|
188
189
|
|
|
@@ -219,9 +220,9 @@ def generate_chat_log_filename(
|
|
|
219
220
|
# Parse date from open_date string (format: "YYYY-MM-DD HH:MM:SS")
|
|
220
221
|
date_str = open_date.split()[0].replace("-", "")
|
|
221
222
|
except (IndexError, ValueError):
|
|
222
|
-
date_str =
|
|
223
|
+
date_str = tz.now().strftime("%Y%m%d")
|
|
223
224
|
else:
|
|
224
|
-
date_str =
|
|
225
|
+
date_str = tz.now().strftime("%Y%m%d")
|
|
225
226
|
|
|
226
227
|
# Use live_id if available, otherwise use "offline"
|
|
227
228
|
live_id_str = str(live_id) if live_id else "offline"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|