Compare commits

...

31 Commits

Author SHA1 Message Date
taizan-hokouto
6c7dc03d06 Merge branch 'release/v0.5.5' 2021-07-24 23:00:30 +09:00
taizan-hokouto
f511049eaa Increment version 2021-07-24 22:59:46 +09:00
taizan-hokouto
76118ba196 Update classifiers 2021-07-24 22:59:26 +09:00
taizan-hokouto
83b10ab2f3 Merge branch 'feature/client' into develop 2021-07-24 22:50:28 +09:00
taizan-hokouto
604c52e608 Update requirements 2021-07-24 22:46:56 +09:00
taizan-hokouto
8949599232 Update pipfile 2021-07-24 22:46:56 +09:00
taizan-hokouto
c9c235061c Add tests 2021-07-24 22:46:56 +09:00
taizan-hokouto
328889689f Make it possible to use customized client 2021-07-24 22:46:56 +09:00
taizan-hokouto
e865c25a4e Merge branch 'develop' 2021-07-01 23:40:47 +09:00
taizan-hokouto
6d581c22f9 Increment version 2021-07-01 23:39:36 +09:00
taizan-hokouto
87aadeeb58 Enhance channelID extraction 2021-07-01 22:44:41 +09:00
taizan-hokouto
d331131cfe Merge branch 'master' into develop 2021-07-01 20:51:16 +09:00
taizan-hokuto
0dce1b009a Merge pull request #47 from taizan-hokuto/dependabot/pip/urllib3-1.26.5
Bump urllib3 from 1.26.4 to 1.26.5
2021-06-08 19:21:46 +09:00
dependabot[bot]
bd77cd2058 Bump urllib3 from 1.26.4 to 1.26.5
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.4 to 1.26.5.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.4...1.26.5)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-02 03:37:37 +00:00
taizan-hokuto
8e47e8d5db Merge pull request #44 from taizan-hokuto/dependabot/pip/urllib3-1.26.4
Bump urllib3 from 1.26.3 to 1.26.4
2021-04-25 12:54:19 +09:00
dependabot[bot]
946dd155d8 Bump urllib3 from 1.26.3 to 1.26.4
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.3 to 1.26.4.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.3...1.26.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-06 18:25:46 +00:00
taizan-hokouto
3565153597 Merge branch 'master' into develop 2021-02-20 21:20:10 +09:00
taizan-hokouto
f6c6ec5603 Modify .gitignore 2021-02-20 21:19:14 +09:00
taizan-hokuto
1cd0bab60b Ignore .gitignore 2021-02-20 21:05:23 +09:00
taizan-hokuto
1c246eea6d Merge pull request #40 from mark-ignacio/gitignore
add .gitignore for __pycache__
2021-02-20 21:04:07 +09:00
Mark Ignacio
3a365243d9 add .gitignore for __pycache__ 2021-02-13 10:08:52 -08:00
taizan-hokuto
7f8882c9e9 Merge pull request #38 from taizan-hokuto/dependabot/pip/cryptography-3.3.2
Bump cryptography from 3.3.1 to 3.3.2
2021-02-12 21:37:23 +09:00
taizan-hokouto
bc401ac80f Merge tag 'fix_param' into develop 2021-02-11 02:02:14 +09:00
taizan-hokouto
0a9837adca Merge branch 'hotfix/fix_param' 2021-02-11 02:02:14 +09:00
taizan-hokouto
f4bf30c0e9 Increment version 2021-02-11 01:57:45 +09:00
taizan-hokouto
acfba74821 Delete tests 2021-02-11 01:56:43 +09:00
taizan-hokouto
f46845c777 Fix parameters for live 2021-02-11 01:56:32 +09:00
dependabot[bot]
74f1536553 Bump cryptography from 3.3.1 to 3.3.2
Bumps [cryptography](https://github.com/pyca/cryptography) from 3.3.1 to 3.3.2.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/3.3.1...3.3.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-10 02:41:39 +00:00
taizan-hokuto
af4c2fe4b9 Merge pull request #36 from taizan-hokuto/dependabot/pip/bleach-3.3.0
Bump bleach from 3.2.1 to 3.3.0
2021-02-04 00:46:52 +09:00
dependabot[bot]
b7c656536d Bump bleach from 3.2.1 to 3.3.0
Bumps [bleach](https://github.com/mozilla/bleach) from 3.2.1 to 3.3.0.
- [Release notes](https://github.com/mozilla/bleach/releases)
- [Changelog](https://github.com/mozilla/bleach/blob/master/CHANGES)
- [Commits](https://github.com/mozilla/bleach/compare/v3.2.1...v3.3.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-02-02 23:12:20 +00:00
taizan-hokouto
faf875c0f5 Merge tag 'v0.5.2' into develop
v0.5.2
2021-01-17 22:41:20 +09:00
23 changed files with 450 additions and 453 deletions

39
.gitignore vendored Normal file
View File

@@ -0,0 +1,39 @@
# Byte-compiled / optimized / DLL files
__pycache__/
.pytest_cache/
#logs
log.txt
*.log
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
# VSCode
.vscode
# develop
*.egg-info/
# Pypi dist
dist/
README.rst
temporary/
# Pypi wheel
build/
exclude/

View File

@@ -4,7 +4,7 @@ verify_ssl = true
name = "pypi"
[packages]
httpx = {extras = ["http2"], version = "0.16.1"}
httpx = {extras = ["http2"]}
[dev-packages]
pytest-mock = "*"

235
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "e1eb34f14c75998519a90838b283ccd23bd168afa8e4837f956c5c4df66376f9"
"sha256": "74b83f2e50bc16f8d90c06ddc775d24ee427f8481a2501f62170bf5b76a2f1bd"
},
"pipfile-spec": 6,
"requires": {},
@@ -14,19 +14,28 @@
]
},
"default": {
"anyio": {
"hashes": [
"sha256:929a6852074397afe1d989002aa96d457e3e1e5441357c60d03e7eea0e65e1b0",
"sha256:ae57a67583e5ff8b4af47666ff5651c3732d45fd26c929253748e796af860374"
],
"markers": "python_full_version >= '3.6.2'",
"version": "==3.3.0"
},
"certifi": {
"hashes": [
"sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd",
"sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
],
"version": "==2020.11.8"
"version": "==2021.5.30"
},
"h11": {
"hashes": [
"sha256:3c6c61d69c6f13d41f1b80ab0322f1872702a3ba26e12aa864c928f6a43fbaab",
"sha256:ab6c335e1b6ef34b205d5ca3e228c9299cc7218b049819ec84a388c2525e5d87"
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
"version": "==0.11.0"
"markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"h2": {
"hashes": [
@@ -44,22 +53,22 @@
},
"httpcore": {
"hashes": [
"sha256:420700af11db658c782f7e8fda34f9dcd95e3ee93944dd97d78cb70247e0cd06",
"sha256:dd1d762d4f7c2702149d06be2597c35fb154c5eff9789a8c5823fbcf4d2978d6"
"sha256:b0d16f0012ec88d8cc848f5a55f8a03158405f4bca02ee49bc4ca2c1fda49f3e",
"sha256:db4c0dcb8323494d01b8c6d812d80091a31e520033e7b0120883d6f52da649ff"
],
"markers": "python_version >= '3.6'",
"version": "==0.12.2"
"version": "==0.13.6"
},
"httpx": {
"extras": [
"http2"
],
"hashes": [
"sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537",
"sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b"
"sha256:979afafecb7d22a1d10340bafb403cf2cb75aff214426ff206521fc79d26408c",
"sha256:9f99c15d33642d38bce8405df088c1c4cfd940284b4290cacbfb02e64f4877c6"
],
"index": "pypi",
"version": "==0.16.1"
"version": "==0.18.2"
},
"hyperframe": {
"hashes": [
@@ -70,20 +79,20 @@
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
],
"version": "==2.10"
"version": "==3.2"
},
"rfc3986": {
"extras": [
"idna2008"
],
"hashes": [
"sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d",
"sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
],
"version": "==1.4.0"
"version": "==1.5.0"
},
"sniffio": {
"hashes": [
@@ -95,6 +104,14 @@
}
},
"develop": {
"anyio": {
"hashes": [
"sha256:929a6852074397afe1d989002aa96d457e3e1e5441357c60d03e7eea0e65e1b0",
"sha256:ae57a67583e5ff8b4af47666ff5651c3732d45fd26c929253748e796af860374"
],
"markers": "python_full_version >= '3.6.2'",
"version": "==3.3.0"
},
"atomicwrites": {
"hashes": [
"sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197",
@@ -105,82 +122,92 @@
},
"attrs": {
"hashes": [
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.3.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.2.0"
},
"bleach": {
"hashes": [
"sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080",
"sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd"
"sha256:306483a5a9795474160ad57fce3ddd1b50551e981eed8e15a582d34cef28aafa",
"sha256:ae976d7174bba988c0b632def82fdc94235756edfb14e6558a9c5be555c9fb78"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==3.2.1"
"version": "==3.3.1"
},
"certifi": {
"hashes": [
"sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd",
"sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
],
"version": "==2020.11.8"
"version": "==2021.5.30"
},
"chardet": {
"charset-normalizer": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
"sha256:88fce3fa5b1a84fdcb3f603d889f723d1dd89b26059d0123ca435570e848d5e1",
"sha256:c46c3ace2d744cfbdebceaa3c19ae691f53ae621b39fd7570f59d14fb7f2fd12"
],
"version": "==3.0.4"
"markers": "python_version >= '3'",
"version": "==2.0.3"
},
"colorama": {
"hashes": [
"sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
"sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
],
"markers": "sys_platform == 'win32'",
"markers": "platform_system == 'Windows' and sys_platform == 'win32'",
"version": "==0.4.4"
},
"docutils": {
"hashes": [
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
"sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
"sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125",
"sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.16"
"version": "==0.17.1"
},
"h11": {
"hashes": [
"sha256:3c6c61d69c6f13d41f1b80ab0322f1872702a3ba26e12aa864c928f6a43fbaab",
"sha256:ab6c335e1b6ef34b205d5ca3e228c9299cc7218b049819ec84a388c2525e5d87"
"sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6",
"sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"
],
"version": "==0.11.0"
"markers": "python_version >= '3.6'",
"version": "==0.12.0"
},
"httpcore": {
"hashes": [
"sha256:420700af11db658c782f7e8fda34f9dcd95e3ee93944dd97d78cb70247e0cd06",
"sha256:dd1d762d4f7c2702149d06be2597c35fb154c5eff9789a8c5823fbcf4d2978d6"
"sha256:b0d16f0012ec88d8cc848f5a55f8a03158405f4bca02ee49bc4ca2c1fda49f3e",
"sha256:db4c0dcb8323494d01b8c6d812d80091a31e520033e7b0120883d6f52da649ff"
],
"markers": "python_version >= '3.6'",
"version": "==0.12.2"
"version": "==0.13.6"
},
"httpx": {
"extras": [
"http2"
],
"hashes": [
"sha256:126424c279c842738805974687e0518a94c7ae8d140cd65b9c4f77ac46ffa537",
"sha256:9cffb8ba31fac6536f2c8cde30df859013f59e4bcc5b8d43901cb3654a8e0a5b"
"sha256:979afafecb7d22a1d10340bafb403cf2cb75aff214426ff206521fc79d26408c",
"sha256:9f99c15d33642d38bce8405df088c1c4cfd940284b4290cacbfb02e64f4877c6"
],
"index": "pypi",
"version": "==0.16.1"
"version": "==0.18.2"
},
"idna": {
"hashes": [
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
],
"version": "==2.10"
"version": "==3.2"
},
"importlib-metadata": {
"hashes": [
"sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac",
"sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e"
],
"markers": "python_version >= '3.6'",
"version": "==4.6.1"
},
"iniconfig": {
"hashes": [
@@ -191,26 +218,26 @@
},
"keyring": {
"hashes": [
"sha256:12de23258a95f3b13e5b167f7a641a878e91eab8ef16fafc077720a95e6115bb",
"sha256:207bd66f2a9881c835dad653da04e196c678bf104f8252141d2d3c4f31051579"
"sha256:045703609dd3fccfcdb27da201684278823b72af515aedec1a8515719a038cb8",
"sha256:8f607d7d1cc502c43a932a275a56fe47db50271904513a379d39df1af277ac48"
],
"markers": "python_version >= '3.6'",
"version": "==21.5.0"
"version": "==23.0.1"
},
"packaging": {
"hashes": [
"sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236",
"sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"
"sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
"sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.7"
"markers": "python_version >= '3.6'",
"version": "==21.0"
},
"pkginfo": {
"hashes": [
"sha256:a6a4ac943b496745cec21f14f021bbd869d5e9b4f6ec06918cffea5a2f4b9193",
"sha256:ce14d7296c673dc4c61c759a0b6c14bae34e34eb819c0017bb6ca5b7292c56e9"
"sha256:37ecd857b47e5f55949c41ed061eb51a0bee97a87c969219d144c0e023982779",
"sha256:e7432f81d08adec7297633191bbf0bd47faf13cd8724c3a13250e51d542635bd"
],
"version": "==1.6.1"
"version": "==1.7.1"
},
"pluggy": {
"hashes": [
@@ -222,19 +249,19 @@
},
"py": {
"hashes": [
"sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
"sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.9.0"
"version": "==1.10.0"
},
"pygments": {
"hashes": [
"sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0",
"sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773"
"sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f",
"sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"
],
"markers": "python_version >= '3.5'",
"version": "==2.7.2"
"version": "==2.9.0"
},
"pyparsing": {
"hashes": [
@@ -246,27 +273,27 @@
},
"pytest": {
"hashes": [
"sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe",
"sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e"
"sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b",
"sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"
],
"markers": "python_version >= '3.5'",
"version": "==6.1.2"
"markers": "python_version >= '3.6'",
"version": "==6.2.4"
},
"pytest-httpx": {
"hashes": [
"sha256:0a7c56e559b23efbf857054cd74de60a7c540694a162423f89c70da6ad358d8e",
"sha256:d32e8f6fb7e028f0313f5f5a2d463c8673eb43fd11a9bfe8527299717a7764c4"
"sha256:1e135b8779060091fa1c87d8aff7904921e8bea95fce5e971a0262764d064b12",
"sha256:e262932f2d3ce380da8273c7bacbcfdc2c94e167fa94da29571caaf1f4d3ba27"
],
"index": "pypi",
"version": "==0.10.1"
"version": "==0.12.0"
},
"pytest-mock": {
"hashes": [
"sha256:024e405ad382646318c4281948aadf6fe1135632bea9cc67366ea0c4098ef5f2",
"sha256:a4d6d37329e4a893e77d9ffa89e838dd2b45d5dc099984cf03c703ac8411bb82"
"sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3",
"sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"
],
"index": "pypi",
"version": "==3.3.1"
"version": "==3.6.1"
},
"pywin32-ctypes": {
"hashes": [
@@ -278,18 +305,18 @@
},
"readme-renderer": {
"hashes": [
"sha256:267854ac3b1530633c2394ead828afcd060fc273217c42ac36b6be9c42cd9a9d",
"sha256:6b7e5aa59210a40de72eb79931491eaf46fefca2952b9181268bd7c7c65c260a"
"sha256:63b4075c6698fcfa78e584930f07f39e05d46f3ec97f65006e430b595ca6348c",
"sha256:92fd5ac2bf8677f310f3303aa4bce5b9d5f9f2094ab98c29f13791d7b805a3db"
],
"version": "==28.0"
"version": "==29.0"
},
"requests": {
"hashes": [
"sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8",
"sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.25.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==2.26.0"
},
"requests-toolbelt": {
"hashes": [
@@ -303,18 +330,18 @@
"idna2008"
],
"hashes": [
"sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d",
"sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"
"sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835",
"sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"
],
"version": "==1.4.0"
"version": "==1.5.0"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
"version": "==1.16.0"
},
"sniffio": {
"hashes": [
@@ -334,27 +361,27 @@
},
"tqdm": {
"hashes": [
"sha256:5c0d04e06ccc0da1bd3fa5ae4550effcce42fcad947b4a6cafa77bdc9b09ff22",
"sha256:9e7b8ab0ecbdbf0595adadd5f0ebbb9e69010e0bd48bbb0c15e550bf2a5292df"
"sha256:5aa445ea0ad8b16d82b15ab342de6b195a722d75fc1ef9934a46bba6feafbc64",
"sha256:8bb94db0d4468fea27d004a0f1d1c02da3cdedc00fe491c0de986b76a04d6b0a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.54.0"
"version": "==4.61.2"
},
"twine": {
"hashes": [
"sha256:34352fd52ec3b9d29837e6072d5a2a7c6fe4290e97bba46bb8d478b5c598f7ab",
"sha256:ba9ff477b8d6de0c89dd450e70b2185da190514e91c42cc62f96850025c10472"
"sha256:087328e9bb405e7ce18527a2dca4042a84c7918658f951110b38bc135acab218",
"sha256:4caec0f1ed78dc4c9b83ad537e453d03ce485725f2aea57f1bb3fdde78dae936"
],
"index": "pypi",
"version": "==3.2.0"
"version": "==3.4.2"
},
"urllib3": {
"hashes": [
"sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08",
"sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
"version": "==1.26.2"
"version": "==1.26.6"
},
"webencodings": {
"hashes": [
@@ -365,11 +392,19 @@
},
"wheel": {
"hashes": [
"sha256:906864fb722c0ab5f2f9c35b2c65e3af3c009402c108a709c0aca27bc2c9187b",
"sha256:aaef9b8c36db72f8bf7f1e54f85f875c4d466819940863ca0b3f3f77f0a1646f"
"sha256:78b5b185f0e5763c26ca1e324373aadd49182ca90e825f7853f4b2509215dc0e",
"sha256:e11eefd162658ea59a60a0f6c7d493a7190ea4b9a85e335b33489d9f17e0245e"
],
"index": "pypi",
"version": "==0.36.1"
"version": "==0.36.2"
},
"zipp": {
"hashes": [
"sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3",
"sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"
],
"markers": "python_version >= '3.6'",
"version": "==3.5.0"
}
}
}

View File

@@ -1,8 +1,8 @@
"""
pytchat is a lightweight python library to browse youtube livechat without Selenium or BeautifulSoup.
"""
__copyright__ = 'Copyright (C) 2019, 2020 taizan-hokuto'
__version__ = '0.5.2'
__copyright__ = 'Copyright (C) 2019, 2020, 2021 taizan-hokuto'
__version__ = '0.5.5'
__license__ = 'MIT'
__author__ = 'taizan-hokuto'
__author_email__ = '55448286+taizan-hokuto@users.noreply.github.com'

View File

@@ -4,7 +4,9 @@ from base64 import a85decode as dc
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36 Edg/86.0.622.63,gzip(gfe)',
}
m_headers = {
'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36 Edg/91.0.864.59',
}
_sml = dc(b"BQS?8F#ks-GB\\6`H#IhIF^eo7@rH3;H#IhIF^eor06T''Ch\\'(?XmbXF>%9<FC/iuG%G#jBOQ!ICLqcS5tQB2;gCZ)?UdXC;f$GR3)MM2<(0>O7mh!,G@+K5?SO9T@okV").decode()
_smr = dc(b"BQS?8F#ks-GB\\6`H#IhIF^eo7@rH3;H#IhIF^eor06T''Ch\\'(?XmbXF>%9<FC/iuG%G#jBOQ!iEb03+@<k(QAU-F)8U=fDGsP557S5F7CiNH7;)D3N77^*B6YU@\\?WfBr0emZX=#^").decode()

View File

@@ -29,9 +29,13 @@ class PytchatCore:
processor : ChatProcessor
client : httpx.Client
The client for connecting youtube.
You can specify any customized httpx client (e.g. coolies, user agent).
interruptable : bool
Allows keyboard interrupts.
Set this parameter to False if your own threading program causes
Set this parameter to False if your own multi-threading program causes
the problem.
force_replay : bool
@@ -57,6 +61,7 @@ class PytchatCore:
def __init__(self, video_id,
seektime=-1,
processor=DefaultProcessor(),
client = httpx.Client(http2=True),
interruptable=True,
force_replay=False,
topchat_only=False,
@@ -64,6 +69,7 @@ class PytchatCore:
logger=config.logger(__name__),
replay_continuation=None
):
self._client = client
self._video_id = util.extract_video_id(video_id)
self.seektime = seektime
if isinstance(processor, tuple):
@@ -95,7 +101,10 @@ class PytchatCore:
"""Fetch first continuation parameter,
create and start _listen loop.
"""
self.continuation = liveparam.getparam(self._video_id, past_sec=3)
self.continuation = liveparam.getparam(
self._video_id,
channel_id=util.get_channelid(self._client, self._video_id),
past_sec=3)
def _get_chat_component(self):
''' Fetch chat data and store them into buffer,
@@ -107,9 +116,8 @@ class PytchatCore:
parameter for next chat data
'''
try:
with httpx.Client(http2=True) as client:
if self.continuation and self._is_alive:
contents = self._get_contents(self.continuation, client, headers)
contents = self._get_contents(self.continuation, self._client, headers)
metadata, chatdata = self._parser.parse(contents)
timeout = metadata['timeoutMs'] / 1000
chat_component = {
@@ -136,9 +144,8 @@ class PytchatCore:
-------
'continuationContents' which includes metadata & chat data.
'''
livechat_json = (
self._get_livechat_json(continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
)
livechat_json = self._get_livechat_json(
continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
contents, dat = self._parser.get_contents(livechat_json)
if self._dat == '' and dat:
self._dat = dat
@@ -149,7 +156,8 @@ class PytchatCore:
self._fetch_url = config._smr
continuation = arcparam.getparam(
self._video_id, self.seektime, self._topchat_only, util.get_channelid(client, self._video_id))
livechat_json = self._get_livechat_json(continuation, client, replay=True, offset_ms=self.seektime * 1000)
livechat_json = self._get_livechat_json(
continuation, client, replay=True, offset_ms=self.seektime * 1000)
reload_continuation = self._parser.reload_continuation(
self._parser.get_contents(livechat_json)[0])
if reload_continuation:
@@ -170,10 +178,9 @@ class PytchatCore:
offset_ms = 0
param = util.get_param(continuation, dat=self._dat, replay=replay, offsetms=offset_ms)
for _ in range(MAX_RETRY + 1):
with httpx.Client(http2=True) as client:
try:
response = client.post(self._fetch_url, json=param)
livechat_json = json.loads(response.text)
livechat_json = response.json()
break
except (json.JSONDecodeError, httpx.ConnectTimeout, httpx.ReadTimeout, httpx.ConnectError) as e:
err = e
@@ -199,6 +206,8 @@ class PytchatCore:
return self._is_alive
def terminate(self):
if not self.is_alive():
return
self._is_alive = False
self.processor.finalize()

View File

@@ -78,6 +78,7 @@ class LiveChatAsync:
seektime=-1,
processor=DefaultProcessor(),
buffer=None,
client = httpx.AsyncClient(http2=True),
interruptable=True,
callback=None,
done_callback=None,
@@ -88,6 +89,7 @@ class LiveChatAsync:
logger=config.logger(__name__),
replay_continuation=None
):
self._client:httpx.AsyncClient = client
self._video_id = util.extract_video_id(video_id)
self.seektime = seektime
if isinstance(processor, tuple):
@@ -152,7 +154,12 @@ class LiveChatAsync:
create and start _listen loop.
"""
if not self.continuation:
self.continuation = liveparam.getparam(self._video_id, 3)
channel_id = await util.get_channelid_async(self._client, self._video_id)
self.continuation = liveparam.getparam(
self._video_id,
channel_id,
past_sec=3)
await self._listen(self.continuation)
async def _listen(self, continuation):
@@ -165,10 +172,10 @@ class LiveChatAsync:
parameter for next chat data
'''
try:
async with httpx.AsyncClient(http2=True) as client:
async with self._client as client:
while(continuation and self._is_alive):
continuation = await self._check_pause(continuation)
contents = await self._get_contents(continuation, client, headers)
contents = await self._get_contents(continuation, client, headers) #Q#
metadata, chatdata = self._parser.parse(contents)
continuation = metadata.get('continuation')
if continuation:
@@ -210,8 +217,12 @@ class LiveChatAsync:
'''
self._pauser.put_nowait(None)
if not self._is_replay:
continuation = liveparam.getparam(
self._video_id, 3, self._topchat_only)
async with self._client as client:
channel_id = await util.get_channelid_async(client, self.video_id)
continuation = liveparam.getparam(self._video_id,
channel_id,
past_sec=3)
return continuation
async def _get_contents(self, continuation, client, headers):
@@ -331,6 +342,8 @@ class LiveChatAsync:
self._logger.debug(f'[{self._video_id}] cancelled:{sender}')
def terminate(self):
if not self.is_alive():
return
if self._pauser.empty():
self._pauser.put_nowait(None)
self._is_alive = False

View File

@@ -78,6 +78,7 @@ class LiveChat:
def __init__(self, video_id,
seektime=-1,
processor=DefaultProcessor(),
client = httpx.Client(http2=True),
buffer=None,
interruptable=True,
callback=None,
@@ -88,6 +89,7 @@ class LiveChat:
logger=config.logger(__name__),
replay_continuation=None
):
self._client = client
self._video_id = util.extract_video_id(video_id)
self.seektime = seektime
if isinstance(processor, tuple):
@@ -148,7 +150,10 @@ class LiveChat:
create and start _listen loop.
"""
if not self.continuation:
self.continuation = liveparam.getparam(self._video_id, 3)
self.continuation = liveparam.getparam(
self._video_id,
channel_id=util.get_channelid(self._client, self._video_id),
past_sec=3)
self._listen(self.continuation)
def _listen(self, continuation):
@@ -161,7 +166,7 @@ class LiveChat:
parameter for next chat data
'''
try:
with httpx.Client(http2=True) as client:
with self._client as client:
while(continuation and self._is_alive):
continuation = self._check_pause(continuation)
contents = self._get_contents(continuation, client, headers)
@@ -207,7 +212,9 @@ class LiveChat:
self._pauser.put_nowait(None)
if not self._is_replay:
continuation = liveparam.getparam(
self._video_id, 3, self._topchat_only)
self._video_id, channel_id=util.get_channelid(httpx.Client(http2=True), self._video_id),
past_sec=3, topchat_only=self._topchat_only)
return continuation
def _get_contents(self, continuation, client, headers):
@@ -219,7 +226,8 @@ class LiveChat:
-------
'continuationContents' which includes metadata & chat data.
'''
livechat_json = self._get_livechat_json(continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
livechat_json = self._get_livechat_json(
continuation, client, replay=self._is_replay, offset_ms=self._last_offset_ms)
contents, dat = self._parser.get_contents(livechat_json)
if self._dat == '' and dat:
self._dat = dat
@@ -230,8 +238,8 @@ class LiveChat:
self._fetch_url = config._smr
continuation = arcparam.getparam(
self._video_id, self.seektime, self._topchat_only, util.get_channelid(client, self._video_id))
livechat_json = (self._get_livechat_json(
continuation, client, replay=True, offset_ms=self.seektime * 1000))
livechat_json = self._get_livechat_json(
continuation, client, replay=True, offset_ms=self.seektime * 1000)
reload_continuation = self._parser.reload_continuation(
self._parser.get_contents(livechat_json)[0])
if reload_continuation:
@@ -326,6 +334,8 @@ class LiveChat:
self._logger.debug(f'[{self._video_id}] cancelled:{sender}')
def terminate(self):
if not self.is_alive():
return
if self._pauser.empty():
self._pauser.put_nowait(None)
self._is_alive = False

View File

@@ -5,11 +5,16 @@ from base64 import urlsafe_b64encode as b64enc
from urllib.parse import quote
def _header(video_id) -> str:
return b64enc(enc.rs(1, enc.rs(1, enc.rs(1, video_id))) + enc.nm(4, 1))
def _header(video_id, channel_id) -> str:
S1_3 = enc.rs(1, video_id)
S1_5 = enc.rs(1, channel_id) + enc.rs(2, video_id)
S1 = enc.rs(3, S1_3) + enc.rs(5, S1_5)
S3 = enc.rs(48687757, enc.rs(1, video_id))
header_replay = enc.rs(1, S1) + enc.rs(3, S3) + enc.nm(4, 1)
return b64enc(header_replay)
def _build(video_id, ts1, ts2, ts3, ts4, ts5, topchat_only) -> str:
def _build(video_id, channel_id, ts1, ts2, ts3, ts4, ts5, topchat_only) -> str:
chattype = 4 if topchat_only else 1
b1 = enc.nm(1, 0)
@@ -23,7 +28,7 @@ def _build(video_id, ts1, ts2, ts3, ts4, ts5, topchat_only) -> str:
b11 = enc.nm(11, 3)
b15 = enc.nm(15, 0)
header = enc.rs(3, _header(video_id))
header = enc.rs(3, _header(video_id, channel_id))
timestamp1 = enc.nm(5, ts1)
s6 = enc.nm(6, 0)
s7 = enc.nm(7, 0)
@@ -53,7 +58,7 @@ def _times(past_sec):
return list(map(lambda x: int(x * 1000000), [_ts1, _ts2, _ts3, _ts4, _ts5]))
def getparam(video_id, past_sec=0, topchat_only=False) -> str:
def getparam(video_id, channel_id, past_sec=0, topchat_only=False) -> str:
'''
Parameter
---------
@@ -62,4 +67,4 @@ def getparam(video_id, past_sec=0, topchat_only=False) -> str:
topchat_only : bool
if True, fetch only 'top chat'
'''
return _build(video_id, *_times(past_sec), topchat_only)
return _build(video_id, channel_id, *_times(past_sec), topchat_only)

View File

@@ -7,6 +7,7 @@ from .renderer.paidmessage import LiveChatPaidMessageRenderer
from .renderer.paidsticker import LiveChatPaidStickerRenderer
from .renderer.legacypaid import LiveChatLegacyPaidMessageRenderer
from .renderer.membership import LiveChatMembershipItemRenderer
from .renderer.donation import LiveChatDonationAnnouncementRenderer
from .. chat_processor import ChatProcessor
from ... import config
@@ -124,7 +125,8 @@ class DefaultProcessor(ChatProcessor):
"liveChatPaidMessageRenderer": LiveChatPaidMessageRenderer(),
"liveChatPaidStickerRenderer": LiveChatPaidStickerRenderer(),
"liveChatLegacyPaidMessageRenderer": LiveChatLegacyPaidMessageRenderer(),
"liveChatMembershipItemRenderer": LiveChatMembershipItemRenderer()
"liveChatMembershipItemRenderer": LiveChatMembershipItemRenderer(),
"liveChatDonationAnnouncementRenderer": LiveChatDonationAnnouncementRenderer(),
}
def process(self, chat_components: list):

View File

@@ -0,0 +1,6 @@
from .base import BaseRenderer
class LiveChatDonationAnnouncementRenderer(BaseRenderer):
def settype(self):
self.chat.type = "donation"

View File

@@ -82,16 +82,17 @@ class RingQueue:
class SpeedCalculator(ChatProcessor, RingQueue):
"""
チャットの勢いを計算する。
Calculate the momentum of the chat.
一定期間のチャットデータのうち、最初のチャットの投稿時刻と
最後のチャットの投稿時刻の差を、チャット数で割り返し
1分あたりの速度に換算する。
Divide the difference between the time of the first chat and
the time of the last chat in the chat data over a period of
time by the number of chats and convert it to speed per minute.
Parameter
----------
capacity : int
RingQueueに格納するチャット勢い算出用データの最大数
Maximum number of data for calculating chat momentum
to be stored in RingQueue.
"""
def __init__(self, capacity=10):
@@ -111,17 +112,17 @@ class SpeedCalculator(ChatProcessor, RingQueue):
def _calc_speed(self):
"""
RingQueue内のチャット勢い算出用データリストを元に、
チャット速度を計算して返す
Calculates the chat speed based on the data list for calculating
the chat momentum in RingQueue.
Return
---------------------------
チャット速度(1分間で換算したチャット数)
Chat speed (number of chats converted per minute)
"""
try:
# キュー内の総チャット数
# Total number of chats in the queue
total = sum(item['chat_count'] for item in self.items)
# キュー内の最初と最後のチャットの時間差
# Interval between the first and last chats in the queue
duration = (self.items[self.last_pos]['endtime'] - self.items[self.first_pos]['starttime'])
if duration != 0:
return int(total * 60 / duration)
@@ -131,19 +132,12 @@ class SpeedCalculator(ChatProcessor, RingQueue):
def _put_chatdata(self, actions):
"""
チャットデータからタイムスタンプを読み取り、勢い測定用のデータを組み立て、
RingQueueに投入する。
200円以上のスパチャはtickerとmessageの2つのデータが生成されるが、
tickerの方は時刻データの場所が異なることを利用し、勢いの集計から除外している。
Parameter
---------
actions : List[dict]
チャットデータ(addChatItemAction) のリスト
List of addChatItemActions
"""
def _put_emptydata():
'''
チャットデータがない場合に空のデータをキューに投入する。
'''
timestamp_now = int(time.time())
self.put({
'chat_count': 0,
@@ -152,9 +146,6 @@ class SpeedCalculator(ChatProcessor, RingQueue):
})
def _get_timestamp(action: dict):
"""
チャットデータから時刻データを取り出す。
"""
try:
item = action['addChatItemAction']['item']
timestamp = int(item[list(item.keys())[0]]['timestampUsec'])
@@ -166,32 +157,24 @@ class SpeedCalculator(ChatProcessor, RingQueue):
_put_emptydata()
return
# actions内の時刻データを持つチャットデータの数
counter = 0
# actions内の最初のチャットデータの時刻
starttime = None
# actions内の最後のチャットデータの時刻
endtime = None
for action in actions:
# チャットデータからtimestampUsecを読み取る
# Get timestampUsec from chatdata
gettime = _get_timestamp(action)
# 時刻のないデータだった場合は次の行のデータで読み取り試行
if gettime is None:
continue
# 最初に有効な時刻を持つデータのtimestampをstarttimeに設定
if starttime is None:
starttime = gettime
# 最後のtimestampを設定(途中で時刻のないデータの場合もあるので上書きしていく)
endtime = gettime
# チャットの数をインクリメント
counter += 1
# チャット速度用のデータをRingQueueに送る
if starttime is None or endtime is None:
_put_emptydata()
return

View File

@@ -13,6 +13,8 @@ PATTERN_YTURL = re.compile(r"((?<=(v|V)/)|(?<=be/)|(?<=(\?|\&)v=)|(?<=embed/))([
PATTERN_CHANNEL = re.compile(r"\\\"channelId\\\":\\\"(.{24})\\\"")
PATTERN_M_CHANNEL = re.compile(r"\"channelId\":\"(.{24})\"")
YT_VIDEO_ID_LENGTH = 11
CLIENT_VERSION = ''.join(("2.", (datetime.datetime.today() - datetime.timedelta(days=1)).strftime("%Y%m%d"), ".01.00"))
@@ -100,6 +102,19 @@ def extract_video_id(url_or_id: str) -> str:
def get_channelid(client, video_id):
resp = client.get("https://www.youtube.com/embed/{}".format(quote(video_id)), headers=config.headers)
match = re.search(PATTERN_CHANNEL, resp.text)
try:
if match is None:
raise IndexError
ret = match.group(1)
except IndexError:
ret = get_channelid_2nd(client, video_id)
return ret
def get_channelid_2nd(client, video_id):
resp = client.get("https://m.youtube.com/watch?v={}".format(quote(video_id)), headers=config.m_headers)
match = re.search(PATTERN_M_CHANNEL, resp.text)
if match is None:
raise InvalidVideoIdException(f"Cannot find channel id for video id:{video_id}. This video id seems to be invalid.")
try:
@@ -108,9 +123,21 @@ def get_channelid(client, video_id):
raise InvalidVideoIdException(f"Invalid video id: {video_id}")
return ret
async def get_channelid_async(client, video_id):
resp = await client.get("https://www.youtube.com/embed/{}".format(quote(video_id)), headers=config.headers)
match = re.search(PATTERN_CHANNEL, resp.text)
try:
if match is None:
raise IndexError
ret = match.group(1)
except IndexError:
ret = await get_channelid_async_2nd(client, video_id)
return ret
async def get_channelid_async_2nd(client, video_id):
resp = await client.get("https://m.youtube.com/watch?v={}".format(quote(video_id)), headers=config.m_headers)
match = re.search(PATTERN_M_CHANNEL, resp.text)
if match is None:
raise InvalidVideoIdException(f"Cannot find channel id for video id:{video_id}. This video id seems to be invalid.")
try:

View File

@@ -1 +1 @@
httpx[http2]==0.16.1
httpx[http2]

View File

@@ -52,6 +52,7 @@ setup(
'Programming Language :: Python',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'License :: OSI Approved :: MIT License',
],
description="a python library for fetching youtube live chat.",

View File

@@ -1,140 +0,0 @@
from pytchat.processors.superchat.calculator import SuperchatCalculator
get_item = SuperchatCalculator()._get_item
dict_test = {
'root':{
'node0' : 'value0',
'node1' : 'value1',
'node2' : {
'node2-0' : 'value2-0'
},
'node3' : [
{'node3-0' : 'value3-0'},
{'node3-1' :
{'node3-1-0' : 'value3-1-0'}
}
],
'node4' : [],
'node5' : [
[
{'node5-1-0' : 'value5-1-0'},
{'node5-1-1' : 'value5-1-1'},
],
{'node5-0' : 'value5-0'},
]
}
}
items_test0 = [
'root',
'node1'
]
items_test_not_found0 = [
'root',
'other_data'
]
items_test_nest = [
'root',
'node2',
'node2-0'
]
items_test_list0 = [
'root',
'node3',
1,
'node3-1'
]
items_test_list1 = [
'root',
'node3',
1,
'node3-1',
'node3-1-0'
]
items_test_list2 = [
'root',
'node4',
None
]
items_test_list3 = [
'root',
'node4'
]
items_test_list_nest = [
'root',
'node5',
0,
1,
'node5-1-1'
]
items_test_list_nest_not_found1 = [
'root',
'node5',
0,
1,
'node5-1-1',
'nodez'
]
items_test_not_found1 = [
'root',
'node3',
2,
'node3-1',
'node3-1-0'
]
items_test_not_found2 = [
'root',
'node3',
2,
'node3-1',
'node3-1-0',
'nodex'
]
def test_get_items_0():
assert get_item(dict_test, items_test0) == 'value1'
def test_get_items_1():
assert get_item(dict_test, items_test_not_found0) is None
def test_get_items_2():
assert get_item(dict_test, items_test_nest) == 'value2-0'
def test_get_items_3():
assert get_item(
dict_test, items_test_list0) == {'node3-1-0' : 'value3-1-0'}
def test_get_items_4():
assert get_item(dict_test, items_test_list1) == 'value3-1-0'
def test_get_items_5():
assert get_item(dict_test, items_test_not_found1) == None
def test_get_items_6():
assert get_item(dict_test, items_test_not_found2) == None
def test_get_items_7():
assert get_item(dict_test, items_test_list2) == None
def test_get_items_8():
assert get_item(dict_test, items_test_list_nest) == 'value5-1-1'
def test_get_items_9():
assert get_item(dict_test, items_test_list_nest_not_found1) == None
def test_get_items_10():
assert get_item(dict_test, items_test_list3) == []

View File

@@ -67,6 +67,5 @@ def test_process_2():
'chatdata': load_chatdata(r"tests/testdata/calculator/replay_end.json")
}
assert False
SuperchatCalculator().process([chat_component])
except ChatParseException:
assert True

View File

@@ -0,0 +1,24 @@
import pytchat
from pytchat.processors.compatible.processor import CompatibleProcessor
root_keys = ('kind', 'etag', 'nextPageToken', 'pollingIntervalMillis', 'pageInfo', 'items')
item_keys = ('kind', 'etag', 'id', 'snippet', 'authorDetails')
snippet_keys = ('type', 'liveChatId', 'authorChannelId', 'publishedAt', 'hasDisplayContent', 'displayMessage', 'textMessageDetails')
author_details_keys = ('channelId', 'channelUrl', 'displayName', 'profileImageUrl', 'isVerified', 'isChatOwner', 'isChatSponsor', 'isChatModerator')
def test_compatible_processor():
stream = pytchat.create("Hj-wnLIYKjw", seektime = 6000, processor=CompatibleProcessor())
while stream.is_alive():
chat = stream.get()
for key in chat.keys():
assert key in root_keys
for key in chat["items"][0].keys():
assert key in item_keys
for key in chat["items"][0]["snippet"].keys():
assert key in snippet_keys
for key in chat["items"][0]["authorDetails"].keys():
assert key in author_details_keys
break

View File

@@ -0,0 +1,94 @@
import asyncio
import pytchat
from concurrent.futures import CancelledError
from pytchat.core_multithread.livechat import LiveChat
from pytchat.core_async.livechat import LiveChatAsync
cases = [
{
"video_id":"1X7oL0hDnMg", "seektime":1620,
"result1":{'textMessage': 84},
"result2":{'': 83, 'MODERATOR': 1}
},
{
"video_id":"Hj-wnLIYKjw", "seektime":420,
"result1":{'superChat': 1, 'newSponsor': 6, 'textMessage': 63, 'donation': 1},
"result2":{'': 59, 'MEMBER': 12}
},
{
"video_id":"S8dmq5YIUoc", "seektime":3,
"result1":{'textMessage': 86},
"result2":{'': 62, 'MEMBER': 21, 'OWNER': 2, 'VERIFIED': 1}
},{
"video_id":"yLrstz80MKs", "seektime":30,
"result1":{'superSticker': 8, 'superChat': 2, 'textMessage': 67},
"result2":{'': 73, 'MEMBER': 4}
}
]
def test_archived_stream():
for case in cases:
stream = pytchat.create(video_id=case["video_id"], seektime=case["seektime"])
while stream.is_alive():
chat = stream.get()
agg1 = {}
agg2 = {}
for c in chat.items:
if c.type in agg1:
agg1[c.type] += 1
else:
agg1[c.type] = 1
if c.author.type in agg2:
agg2[c.author.type] += 1
else:
agg2[c.author.type] = 1
break
assert agg1 == case["result1"]
assert agg2 == case["result2"]
def test_archived_stream_multithread():
for case in cases:
stream = LiveChat(video_id=case["video_id"], seektime=case["seektime"])
while stream.is_alive():
chat = stream.get()
agg1 = {}
agg2 = {}
for c in chat.items:
if c.type in agg1:
agg1[c.type] += 1
else:
agg1[c.type] = 1
if c.author.type in agg2:
agg2[c.author.type] += 1
else:
agg2[c.author.type] = 1
break
assert agg1 == case["result1"]
assert agg2 == case["result2"]
def test_async_live_stream():
async def test_loop():
for case in cases:
stream = LiveChatAsync(video_id=case["video_id"], seektime=case["seektime"])
while stream.is_alive():
chat = await stream.get()
agg1 = {}
agg2 = {}
for c in chat.items:
if c.type in agg1:
agg1[c.type] += 1
else:
agg1[c.type] = 1
if c.author.type in agg2:
agg2[c.author.type] += 1
else:
agg2[c.author.type] = 1
break
assert agg1 == case["result1"]
assert agg2 == case["result2"]
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(test_loop())
except CancelledError:
assert True

View File

@@ -1,48 +0,0 @@
import asyncio
import json
from pytest_httpx import HTTPXMock
from concurrent.futures import CancelledError
from pytchat.core_multithread.livechat import LiveChat
from pytchat.core_async.livechat import LiveChatAsync
from pytchat.exceptions import ResponseContextError
def _open_file(path):
with open(path, mode='r', encoding='utf-8') as f:
return f.read()
def add_response_file(httpx_mock: HTTPXMock, jsonfile_path: str):
testdata = json.loads(_open_file(jsonfile_path))
httpx_mock.add_response(json=testdata)
def test_async(httpx_mock: HTTPXMock):
add_response_file(httpx_mock, 'tests/testdata/paramgen_firstread.json')
async def test_loop():
try:
chat = LiveChatAsync(video_id='__test_id__')
_ = await chat.get()
assert chat.is_alive()
chat.terminate()
assert not chat.is_alive()
except ResponseContextError:
assert False
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(test_loop())
except CancelledError:
assert True
def test_multithread(httpx_mock: HTTPXMock):
add_response_file(httpx_mock, 'tests/testdata/paramgen_firstread.json')
try:
chat = LiveChat(video_id='__test_id__')
_ = chat.get()
assert chat.is_alive()
chat.terminate()
assert not chat.is_alive()
except ResponseContextError:
assert False

View File

@@ -1,71 +0,0 @@
import asyncio
import json
from pytest_httpx import HTTPXMock
from concurrent.futures import CancelledError
from pytchat.core_multithread.livechat import LiveChat
from pytchat.core_async.livechat import LiveChatAsync
from pytchat.processors.dummy_processor import DummyProcessor
def _open_file(path):
with open(path, mode='r', encoding='utf-8') as f:
return f.read()
def add_response_file(httpx_mock: HTTPXMock, jsonfile_path: str):
testdata = json.loads(_open_file(jsonfile_path))
httpx_mock.add_response(json=testdata)
def test_async_live_stream(httpx_mock: HTTPXMock):
add_response_file(httpx_mock, 'tests/testdata/test_stream.json')
async def test_loop():
chat = LiveChatAsync(video_id='__test_id__', processor=DummyProcessor())
chats = await chat.get()
rawdata = chats[0]["chatdata"]
assert list(rawdata[0]["addChatItemAction"]["item"].keys())[
0] == "liveChatTextMessageRenderer"
assert list(rawdata[1]["addChatItemAction"]["item"].keys())[
0] == "liveChatTextMessageRenderer"
assert list(rawdata[2]["addChatItemAction"]["item"].keys())[
0] == "liveChatPlaceholderItemRenderer"
assert list(rawdata[3]["addLiveChatTickerItemAction"]["item"].keys())[
0] == "liveChatTickerPaidMessageItemRenderer"
assert list(rawdata[4]["addChatItemAction"]["item"].keys())[
0] == "liveChatPaidMessageRenderer"
assert list(rawdata[5]["addChatItemAction"]["item"].keys())[
0] == "liveChatPaidStickerRenderer"
assert list(rawdata[6]["addLiveChatTickerItemAction"]["item"].keys())[
0] == "liveChatTickerSponsorItemRenderer"
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(test_loop())
except CancelledError:
assert True
def test_multithread_live_stream(httpx_mock: HTTPXMock):
add_response_file(httpx_mock, 'tests/testdata/test_stream.json')
chat = LiveChat(video_id='__test_id__', processor=DummyProcessor())
chats = chat.get()
rawdata = chats[0]["chatdata"]
# assert fetching livachat data
assert list(rawdata[0]["addChatItemAction"]["item"].keys())[
0] == "liveChatTextMessageRenderer"
assert list(rawdata[1]["addChatItemAction"]["item"].keys())[
0] == "liveChatTextMessageRenderer"
assert list(rawdata[2]["addChatItemAction"]["item"].keys())[
0] == "liveChatPlaceholderItemRenderer"
assert list(rawdata[3]["addLiveChatTickerItemAction"]["item"].keys())[
0] == "liveChatTickerPaidMessageItemRenderer"
assert list(rawdata[4]["addChatItemAction"]["item"].keys())[
0] == "liveChatPaidMessageRenderer"
assert list(rawdata[5]["addChatItemAction"]["item"].keys())[
0] == "liveChatPaidStickerRenderer"
assert list(rawdata[6]["addLiveChatTickerItemAction"]["item"].keys())[
0] == "liveChatTickerSponsorItemRenderer"
chat.terminate()

View File

@@ -1,9 +0,0 @@
import pytest
from pytchat.paramgen import liveparam
def test_liveparam_0(mocker):
_ts1= 1546268400
param = liveparam._build("01234567890",
*([_ts1*1000000 for i in range(5)]), topchat_only=False)
test_param="0ofMyAN1GhxDZzhLRFFvTE1ERXlNelExTmpjNE9UQWdBUT09KIC41tWqyt8CMAA4AEABShsIABAAGAAgADoAQABKAFCAuNbVqsrfAlgDeABQgLjW1arK3wJYgLjW1arK3wJoAYIBAggBiAEAmgECCACgAYC41tWqyt8C"
assert test_param == param

View File

@@ -0,0 +1,16 @@
import json
import pytchat
from pytchat.parser.live import Parser
from pytchat.processors.speed.calculator import SpeedCalculator
parser = Parser(is_replay=False)
def test_speed_1():
stream = pytchat.create("Hj-wnLIYKjw", seektime = 6000,processor=SpeedCalculator())
while stream.is_alive():
speed = stream.get()
assert speed > 100
break
test_speed_1()