shindan-cli 1.3.0__tar.gz → 2.2.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.
- shindan_cli-2.2.0/.gitignore +144 -0
- {shindan_cli-1.3.0 → shindan_cli-2.2.0}/PKG-INFO +46 -49
- {shindan_cli-1.3.0 → shindan_cli-2.2.0}/README.md +30 -32
- shindan_cli-2.2.0/pyproject.toml +86 -0
- shindan_cli-2.2.0/shindan_cli/__init__.py +14 -0
- shindan_cli-2.2.0/shindan_cli/constants.py +66 -0
- shindan_cli-2.2.0/shindan_cli/get_results.py +185 -0
- shindan_cli-2.2.0/shindan_cli/interactive.py +160 -0
- {shindan_cli-1.3.0 → shindan_cli-2.2.0}/shindan_cli/main.py +35 -9
- shindan_cli-2.2.0/shindan_cli/models.py +55 -0
- shindan_cli-2.2.0/shindan_cli/shindan.py +122 -0
- shindan_cli-1.3.0/pyproject.toml +0 -87
- shindan_cli-1.3.0/shindan_cli/__init__.py +0 -7
- shindan_cli-1.3.0/shindan_cli/shindan.py +0 -96
- {shindan_cli-1.3.0 → shindan_cli-2.2.0}/LICENSE +0 -0
- {shindan_cli-1.3.0 → shindan_cli-2.2.0}/shindan_cli/py.typed +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
cov.xml
|
|
2
|
+
|
|
3
|
+
# https://github.com/github/gitignore/blob/master/Python.gitignore
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py,cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
#Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
101
|
+
__pypackages__/
|
|
102
|
+
|
|
103
|
+
# Celery stuff
|
|
104
|
+
celerybeat-schedule
|
|
105
|
+
celerybeat.pid
|
|
106
|
+
|
|
107
|
+
# SageMath parsed files
|
|
108
|
+
*.sage.py
|
|
109
|
+
|
|
110
|
+
# Environments
|
|
111
|
+
.env
|
|
112
|
+
.venv
|
|
113
|
+
env/
|
|
114
|
+
venv/
|
|
115
|
+
ENV/
|
|
116
|
+
env.bak/
|
|
117
|
+
venv.bak/
|
|
118
|
+
|
|
119
|
+
# Spyder project settings
|
|
120
|
+
.spyderproject
|
|
121
|
+
.spyproject
|
|
122
|
+
|
|
123
|
+
# Rope project settings
|
|
124
|
+
.ropeproject
|
|
125
|
+
|
|
126
|
+
# mkdocs documentation
|
|
127
|
+
/site
|
|
128
|
+
|
|
129
|
+
# mypy
|
|
130
|
+
.mypy_cache/
|
|
131
|
+
.dmypy.json
|
|
132
|
+
dmypy.json
|
|
133
|
+
|
|
134
|
+
# Pyre type checker
|
|
135
|
+
.pyre/
|
|
136
|
+
|
|
137
|
+
# pytype static type analyzer
|
|
138
|
+
.pytype/
|
|
139
|
+
|
|
140
|
+
# Cython debug symbols
|
|
141
|
+
cython_debug/
|
|
142
|
+
|
|
143
|
+
# vscode
|
|
144
|
+
.vscode/
|
|
@@ -1,58 +1,49 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: shindan-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: ShindanMaker (https://shindanmaker.com) CLI
|
|
5
|
-
|
|
5
|
+
Project-URL: Repository, https://github.com/eggplants/shindan-cli
|
|
6
|
+
Author-email: eggplants <w10776e8w@yahoo.co.jp>
|
|
6
7
|
License: MIT
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Author-email: w10776e8w@yahoo.co.jp
|
|
10
|
-
Requires-Python: >=3.7,<4
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Keywords: cli,shindanmaker
|
|
11
10
|
Classifier: Development Status :: 3 - Alpha
|
|
12
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: <4,>=3.10
|
|
20
|
+
Requires-Dist: beautifulsoup4<5,>=4.12.3
|
|
21
|
+
Requires-Dist: cloudscraper<2,>=1.2.71
|
|
22
|
+
Requires-Dist: lxml<7,>=5.3
|
|
23
|
+
Requires-Dist: requests<3,>=2.32.3
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
26
|
# shindan-cli
|
|
27
27
|
|
|
28
|
-
[](
|
|
31
|
+
<https://github.com/eggplants/shindan-cli/actions/workflows/ci.yml>
|
|
32
|
+
) [](
|
|
31
35
|
<https://github.com/eggplants/shindan-cli/actions/workflows/release.yml>
|
|
32
36
|
) [](
|
|
39
|
+
<https://pypi.org/project/shindan-cli>
|
|
40
|
+
) [
|
|
37
|
-
](
|
|
42
|
+
)](
|
|
38
43
|
<https://pypi.org/project/shindan-cli>
|
|
39
44
|
)
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
<https://github.com/eggplants/shindan-cli/actions/workflows/test.yml/badge.svg>
|
|
43
|
-
)](
|
|
44
|
-
<https://github.com/eggplants/shindan-cli/actions/workflows/test.yml>
|
|
45
|
-
) [](
|
|
48
|
-
<https://codeclimate.com/github/eggplants/shindan-cli/maintainability>
|
|
49
|
-
) [](
|
|
52
|
-
<https://codeclimate.com/github/eggplants/shindan-cli/test_coverage>
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
ShindanMaker (<https://shindanmaker.com>) CLI
|
|
46
|
+
ShindanMaker (診断メーカー, <https://shindanmaker.com>) CLI + Library
|
|
56
47
|
|
|
57
48
|
## Install
|
|
58
49
|
|
|
@@ -62,6 +53,13 @@ pip install shindan-cli
|
|
|
62
53
|
|
|
63
54
|
## Usage
|
|
64
55
|
|
|
56
|
+
Supported types of diagnosis:
|
|
57
|
+
|
|
58
|
+
- [Name-based diagnosis (名前診断)](https://shindanmaker.com/list/name)
|
|
59
|
+
- [Branching diagnosis (分岐診断)](https://shindanmaker.com/list/branch)
|
|
60
|
+
- [AI diagnosis (AI診断)](https://shindanmaker.com/list/ai)
|
|
61
|
+
- [Check Diagnosis (チェック診断)](https://shindanmaker.com/list/check)
|
|
62
|
+
|
|
65
63
|
### CLI
|
|
66
64
|
|
|
67
65
|
```shellsession
|
|
@@ -128,44 +126,43 @@ MIT
|
|
|
128
126
|
|
|
129
127
|
- C#
|
|
130
128
|
- [misodengaku/ShindanMaker](https://github.com/misodengaku/ShindanMaker)
|
|
131
|
-
- Library
|
|
129
|
+
- Library (Unmaintained)
|
|
132
130
|
- Go
|
|
133
131
|
- [kakakaya/goshindan](https://github.com/kakakaya/goshindan)
|
|
134
|
-
- Library + CLI
|
|
132
|
+
- Library + CLI (Archived)
|
|
135
133
|
- <https://pkg.go.dev/github.com/kakakaya/goshindan>
|
|
136
134
|
- Java
|
|
137
135
|
- [shibafu528/shindan4j](https://github.com/shibafu528/shindan4j)
|
|
138
|
-
- Library
|
|
136
|
+
- Library (Unmaintained)
|
|
139
137
|
- <https://jitpack.io/#shibafu528/shindan4j>
|
|
140
138
|
- JavaScript
|
|
141
139
|
- [asawo/shindan-scraper](https://github.com/asawo/shindan-scraper)
|
|
142
|
-
- Library
|
|
140
|
+
- Library (Unmaintained)
|
|
143
141
|
- [stawberri/shindan](https://github.com/stawberri/shindan)
|
|
144
142
|
- Library (Archived)
|
|
145
143
|
- <https://www.npmjs.com/package/shindan>
|
|
146
144
|
- Perl
|
|
147
145
|
- [Likk/WebService-ShindanMaker](https://github.com/Likk/WebService-ShindanMaker)
|
|
148
|
-
- Library
|
|
146
|
+
- Library (Unmaintained)
|
|
149
147
|
- PHP
|
|
150
148
|
- [moroya/php-shindanmaker](https://github.com/moroya/php-shindanmaker)
|
|
151
|
-
- Library
|
|
149
|
+
- Library (Unmaintained)
|
|
152
150
|
- <https://packagist.org/packages/moroya/php-shindanmaker>
|
|
153
151
|
- Python
|
|
154
152
|
- [Le96/auto_shindanmaker](https://github.com/Le96/auto_shindanmaker)
|
|
155
|
-
- Bot Server
|
|
153
|
+
- Bot Server (Unmaintained)
|
|
156
154
|
- [tanitanin/shindan-python](https://github.com/tanitanin/shindan-python)
|
|
157
|
-
- CLI (
|
|
155
|
+
- CLI (Unmaintained)
|
|
158
156
|
- Ruby
|
|
159
157
|
- [osak/shindanmaker](https://github.com/osak/shindanmaker)
|
|
160
|
-
- [Mikutter](https://github.com/mikutter/mikutter) Plugin
|
|
158
|
+
- [Mikutter](https://github.com/mikutter/mikutter) Plugin (Unmaintained)
|
|
161
159
|
- [gouf/shindan](https://github.com/gouf/shindan)
|
|
162
|
-
- Library
|
|
160
|
+
- Library (Unmaintained)
|
|
163
161
|
- [ikaruga777/shindan-cli](https://github.com/ikaruga777/shindan-cli)
|
|
164
|
-
- CLI
|
|
162
|
+
- CLI (Unmaintained)
|
|
165
163
|
- [yasuhito/shindan](https://github.com/yasuhito/shindan)
|
|
166
|
-
- CLI
|
|
164
|
+
- CLI (Unmaintained)
|
|
167
165
|
- <https://rubygems.org/gems/shindan>
|
|
168
166
|
- TypeScript
|
|
169
167
|
- [dqn/shindanmaker-js](https://github.com/dqn/shindanmaker-js)
|
|
170
|
-
- Library
|
|
171
|
-
|
|
168
|
+
- Library (Archived)
|
|
@@ -1,33 +1,24 @@
|
|
|
1
1
|
# shindan-cli
|
|
2
2
|
|
|
3
|
-
[](
|
|
6
|
+
<https://github.com/eggplants/shindan-cli/actions/workflows/ci.yml>
|
|
7
|
+
) [](
|
|
6
10
|
<https://github.com/eggplants/shindan-cli/actions/workflows/release.yml>
|
|
7
11
|
) [](
|
|
14
|
+
<https://pypi.org/project/shindan-cli>
|
|
15
|
+
) [
|
|
12
|
-
](
|
|
17
|
+
)](
|
|
13
18
|
<https://pypi.org/project/shindan-cli>
|
|
14
19
|
)
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
<https://github.com/eggplants/shindan-cli/actions/workflows/test.yml/badge.svg>
|
|
18
|
-
)](
|
|
19
|
-
<https://github.com/eggplants/shindan-cli/actions/workflows/test.yml>
|
|
20
|
-
) [](
|
|
23
|
-
<https://codeclimate.com/github/eggplants/shindan-cli/maintainability>
|
|
24
|
-
) [](
|
|
27
|
-
<https://codeclimate.com/github/eggplants/shindan-cli/test_coverage>
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
ShindanMaker (<https://shindanmaker.com>) CLI
|
|
21
|
+
ShindanMaker (診断メーカー, <https://shindanmaker.com>) CLI + Library
|
|
31
22
|
|
|
32
23
|
## Install
|
|
33
24
|
|
|
@@ -37,6 +28,13 @@ pip install shindan-cli
|
|
|
37
28
|
|
|
38
29
|
## Usage
|
|
39
30
|
|
|
31
|
+
Supported types of diagnosis:
|
|
32
|
+
|
|
33
|
+
- [Name-based diagnosis (名前診断)](https://shindanmaker.com/list/name)
|
|
34
|
+
- [Branching diagnosis (分岐診断)](https://shindanmaker.com/list/branch)
|
|
35
|
+
- [AI diagnosis (AI診断)](https://shindanmaker.com/list/ai)
|
|
36
|
+
- [Check Diagnosis (チェック診断)](https://shindanmaker.com/list/check)
|
|
37
|
+
|
|
40
38
|
### CLI
|
|
41
39
|
|
|
42
40
|
```shellsession
|
|
@@ -103,43 +101,43 @@ MIT
|
|
|
103
101
|
|
|
104
102
|
- C#
|
|
105
103
|
- [misodengaku/ShindanMaker](https://github.com/misodengaku/ShindanMaker)
|
|
106
|
-
- Library
|
|
104
|
+
- Library (Unmaintained)
|
|
107
105
|
- Go
|
|
108
106
|
- [kakakaya/goshindan](https://github.com/kakakaya/goshindan)
|
|
109
|
-
- Library + CLI
|
|
107
|
+
- Library + CLI (Archived)
|
|
110
108
|
- <https://pkg.go.dev/github.com/kakakaya/goshindan>
|
|
111
109
|
- Java
|
|
112
110
|
- [shibafu528/shindan4j](https://github.com/shibafu528/shindan4j)
|
|
113
|
-
- Library
|
|
111
|
+
- Library (Unmaintained)
|
|
114
112
|
- <https://jitpack.io/#shibafu528/shindan4j>
|
|
115
113
|
- JavaScript
|
|
116
114
|
- [asawo/shindan-scraper](https://github.com/asawo/shindan-scraper)
|
|
117
|
-
- Library
|
|
115
|
+
- Library (Unmaintained)
|
|
118
116
|
- [stawberri/shindan](https://github.com/stawberri/shindan)
|
|
119
117
|
- Library (Archived)
|
|
120
118
|
- <https://www.npmjs.com/package/shindan>
|
|
121
119
|
- Perl
|
|
122
120
|
- [Likk/WebService-ShindanMaker](https://github.com/Likk/WebService-ShindanMaker)
|
|
123
|
-
- Library
|
|
121
|
+
- Library (Unmaintained)
|
|
124
122
|
- PHP
|
|
125
123
|
- [moroya/php-shindanmaker](https://github.com/moroya/php-shindanmaker)
|
|
126
|
-
- Library
|
|
124
|
+
- Library (Unmaintained)
|
|
127
125
|
- <https://packagist.org/packages/moroya/php-shindanmaker>
|
|
128
126
|
- Python
|
|
129
127
|
- [Le96/auto_shindanmaker](https://github.com/Le96/auto_shindanmaker)
|
|
130
|
-
- Bot Server
|
|
128
|
+
- Bot Server (Unmaintained)
|
|
131
129
|
- [tanitanin/shindan-python](https://github.com/tanitanin/shindan-python)
|
|
132
|
-
- CLI (
|
|
130
|
+
- CLI (Unmaintained)
|
|
133
131
|
- Ruby
|
|
134
132
|
- [osak/shindanmaker](https://github.com/osak/shindanmaker)
|
|
135
|
-
- [Mikutter](https://github.com/mikutter/mikutter) Plugin
|
|
133
|
+
- [Mikutter](https://github.com/mikutter/mikutter) Plugin (Unmaintained)
|
|
136
134
|
- [gouf/shindan](https://github.com/gouf/shindan)
|
|
137
|
-
- Library
|
|
135
|
+
- Library (Unmaintained)
|
|
138
136
|
- [ikaruga777/shindan-cli](https://github.com/ikaruga777/shindan-cli)
|
|
139
|
-
- CLI
|
|
137
|
+
- CLI (Unmaintained)
|
|
140
138
|
- [yasuhito/shindan](https://github.com/yasuhito/shindan)
|
|
141
|
-
- CLI
|
|
139
|
+
- CLI (Unmaintained)
|
|
142
140
|
- <https://rubygems.org/gems/shindan>
|
|
143
141
|
- TypeScript
|
|
144
142
|
- [dqn/shindanmaker-js](https://github.com/dqn/shindanmaker-js)
|
|
145
|
-
- Library
|
|
143
|
+
- Library (Archived)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "hatchling.build"
|
|
3
|
+
|
|
4
|
+
requires = [ "hatchling", "uv-dynamic-versioning" ]
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "shindan-cli"
|
|
8
|
+
description = "ShindanMaker (https://shindanmaker.com) CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
keywords = [
|
|
11
|
+
"cli",
|
|
12
|
+
"shindanmaker",
|
|
13
|
+
]
|
|
14
|
+
license = { text = "MIT" }
|
|
15
|
+
authors = [ { name = "eggplants", email = "w10776e8w@yahoo.co.jp" } ]
|
|
16
|
+
requires-python = ">=3.10,<4"
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
dynamic = [ "version" ]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"beautifulsoup4>=4.12.3,<5",
|
|
31
|
+
"cloudscraper>=1.2.71,<2",
|
|
32
|
+
"lxml>=5.3,<7",
|
|
33
|
+
"requests>=2.32.3,<3",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
urls.Repository = "https://github.com/eggplants/shindan-cli"
|
|
37
|
+
scripts.shindan = "shindan_cli.main:main"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"pymarkdownlnt>=0.9.33",
|
|
42
|
+
"pyproject-fmt>=2.11.1",
|
|
43
|
+
"pytest>=9.0.2",
|
|
44
|
+
"pytest-asyncio>=1.3",
|
|
45
|
+
"pytest-cov>=7",
|
|
46
|
+
]
|
|
47
|
+
docs = [
|
|
48
|
+
"pdoc>=16",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.hatch.version]
|
|
52
|
+
source = "uv-dynamic-versioning"
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.sdist]
|
|
55
|
+
include = [ "shindan_cli" ]
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.wheel]
|
|
58
|
+
include = [ "shindan_cli" ]
|
|
59
|
+
|
|
60
|
+
[tool.uv]
|
|
61
|
+
preview = true
|
|
62
|
+
default-groups = [
|
|
63
|
+
"dev",
|
|
64
|
+
"docs",
|
|
65
|
+
]
|
|
66
|
+
exclude-newer = "P7D"
|
|
67
|
+
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 120
|
|
70
|
+
format.indent-style = "space"
|
|
71
|
+
format.quote-style = "double"
|
|
72
|
+
lint.select = [ "ALL" ]
|
|
73
|
+
lint.ignore = [
|
|
74
|
+
"COM812",
|
|
75
|
+
"D203", # 1 blank line required before class docstring
|
|
76
|
+
"D213", # Multi-line docstring summary should start at the second line
|
|
77
|
+
]
|
|
78
|
+
lint.per-file-ignores."tests/*.py" = [
|
|
79
|
+
"D",
|
|
80
|
+
"S101", # Use of assert detected
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
[tool.uv-dynamic-versioning]
|
|
84
|
+
vcs = "git"
|
|
85
|
+
style = "semver"
|
|
86
|
+
fallback-version = "0.0.0"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
""".. include:: ../README.md""" # noqa: D400, D415
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.metadata
|
|
6
|
+
|
|
7
|
+
from .shindan import ShindanError, ShindanResult, shindan
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
__version__ = importlib.metadata.version(__name__)
|
|
11
|
+
except importlib.metadata.PackageNotFoundError:
|
|
12
|
+
__version__ = "0.0.0"
|
|
13
|
+
|
|
14
|
+
__all__ = ("ShindanError", "ShindanResult", "shindan")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Constants for the shindan CLI."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, TypedDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AIParams(TypedDict):
|
|
7
|
+
_token: str
|
|
8
|
+
randname: str
|
|
9
|
+
type: Literal["ai"]
|
|
10
|
+
shindan_token: str
|
|
11
|
+
encrypted_exec_key: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BranchParams(TypedDict):
|
|
15
|
+
_token: str
|
|
16
|
+
randname: str
|
|
17
|
+
hiddenName: str
|
|
18
|
+
type: Literal["branch"]
|
|
19
|
+
shindan_token: str
|
|
20
|
+
rbr: str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CheckParams(TypedDict):
|
|
24
|
+
_token: str
|
|
25
|
+
randname: str
|
|
26
|
+
hiddenName: str
|
|
27
|
+
type: Literal["check"]
|
|
28
|
+
shindan_token: str
|
|
29
|
+
# input-check-choice[choice_id]: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class NameParams(TypedDict):
|
|
33
|
+
_token: str
|
|
34
|
+
randname: str
|
|
35
|
+
hiddenName: str
|
|
36
|
+
type: Literal["name"]
|
|
37
|
+
shindan_token: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TargetKeysByType(TypedDict):
|
|
41
|
+
ai: AIParams
|
|
42
|
+
branch: BranchParams
|
|
43
|
+
check: CheckParams
|
|
44
|
+
name: NameParams
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
TARGET_KEYS_BY_TYPE = {
|
|
48
|
+
"ai": AIParams.__annotations__.keys(),
|
|
49
|
+
"branch": BranchParams.__annotations__.keys(),
|
|
50
|
+
"check": CheckParams.__annotations__.keys(),
|
|
51
|
+
"name": NameParams.__annotations__.keys(),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
BASE_URL = "https://shindanmaker.com"
|
|
55
|
+
|
|
56
|
+
HEADERS = {
|
|
57
|
+
"User-Agent": (
|
|
58
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
|
59
|
+
),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
__all__ = (
|
|
63
|
+
"BASE_URL",
|
|
64
|
+
"HEADERS",
|
|
65
|
+
"TARGET_KEYS_BY_TYPE",
|
|
66
|
+
)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Module contains functions to get the result from <https://shindanmaker.com>."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from typing import TYPE_CHECKING, Union
|
|
7
|
+
|
|
8
|
+
from bs4 import BeautifulSoup, Tag
|
|
9
|
+
|
|
10
|
+
from .constants import BASE_URL, HEADERS, AIParams, BranchParams, CheckParams, NameParams
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from requests import Session
|
|
14
|
+
|
|
15
|
+
from .models import ShindanResult, UserInputs
|
|
16
|
+
|
|
17
|
+
Params = Union[AIParams, BranchParams, CheckParams, NameParams]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def __get_result(
|
|
21
|
+
session: Session,
|
|
22
|
+
params: Params,
|
|
23
|
+
*,
|
|
24
|
+
is_renewal: bool = False,
|
|
25
|
+
shindan_url: str,
|
|
26
|
+
) -> ShindanResult:
|
|
27
|
+
result_page = session.post(
|
|
28
|
+
shindan_url + ("/r" if is_renewal else ""),
|
|
29
|
+
data=params,
|
|
30
|
+
headers=HEADERS,
|
|
31
|
+
)
|
|
32
|
+
soup = BeautifulSoup(result_page.text, features="lxml")
|
|
33
|
+
result_tag = soup.find(id="share-copytext-shindanresult-textarea")
|
|
34
|
+
|
|
35
|
+
if not isinstance(result_tag, Tag) or not result_tag.text:
|
|
36
|
+
msg = f"Could not find a tag contains the result, returns: {result_tag}"
|
|
37
|
+
raise TypeError(msg)
|
|
38
|
+
|
|
39
|
+
*results, hashtag, shindan_url, _ = result_tag.text.split("\n")
|
|
40
|
+
|
|
41
|
+
if not results[-1]:
|
|
42
|
+
results.pop(-1)
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"results": results,
|
|
46
|
+
"hashtags": hashtag.split("\xa0"),
|
|
47
|
+
"shindan_url": shindan_url,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def get_result_by_ai(
|
|
52
|
+
session: Session,
|
|
53
|
+
params: AIParams,
|
|
54
|
+
*,
|
|
55
|
+
user_inputs: UserInputs,
|
|
56
|
+
hashtag: str | None,
|
|
57
|
+
shindan_url: str,
|
|
58
|
+
) -> ShindanResult:
|
|
59
|
+
"""Get result by AI type shindan.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
session (Session): session object
|
|
63
|
+
params (Params): input parameters fetched from shindan page
|
|
64
|
+
user_inputs (UserInputs): user inputs
|
|
65
|
+
hashtag (str | None): hashtag
|
|
66
|
+
shindan_url (str): shindan url
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
ShindanResult: the returned result from <https://shindanmaker.com>
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
res = session.post(
|
|
73
|
+
shindan_url,
|
|
74
|
+
data=params,
|
|
75
|
+
headers=HEADERS,
|
|
76
|
+
)
|
|
77
|
+
meta = BeautifulSoup(
|
|
78
|
+
res.text,
|
|
79
|
+
features="lxml",
|
|
80
|
+
).select_one('meta[name="csrf-token"]')
|
|
81
|
+
if not res.ok or not meta or not isinstance(csrf_token := meta.get("content"), str):
|
|
82
|
+
msg = f"Failed to get the csrf token. ({res.status_code})"
|
|
83
|
+
raise ValueError(msg)
|
|
84
|
+
HEADERS.update({"x-csrf-token": csrf_token})
|
|
85
|
+
if not res.ok:
|
|
86
|
+
res = session.post(
|
|
87
|
+
f"{BASE_URL}/ai_life_update",
|
|
88
|
+
data={"ai_life": 3},
|
|
89
|
+
headers=HEADERS,
|
|
90
|
+
)
|
|
91
|
+
if not res.ok:
|
|
92
|
+
msg = f"Failed to update AI life. ({res.status_code})"
|
|
93
|
+
raise ValueError(msg)
|
|
94
|
+
|
|
95
|
+
result_sse = session.post(
|
|
96
|
+
f"{shindan_url}/ai_result",
|
|
97
|
+
json={
|
|
98
|
+
"form_values": user_inputs,
|
|
99
|
+
"shindan_token": params["_token"],
|
|
100
|
+
"ai_result_request_times": 0,
|
|
101
|
+
"encrypted_exec_key": params["encrypted_exec_key"],
|
|
102
|
+
},
|
|
103
|
+
headers=HEADERS,
|
|
104
|
+
)
|
|
105
|
+
gpt_results = "".join(
|
|
106
|
+
re.findall(r'"content":"([^"]+)', result_sse.text),
|
|
107
|
+
).split("\n")
|
|
108
|
+
return {
|
|
109
|
+
"results": gpt_results,
|
|
110
|
+
"hashtags": [hashtag, "#shindanmaker"] if hashtag else ["#shindanmaker"],
|
|
111
|
+
"shindan_url": shindan_url,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_result_by_branch(
|
|
116
|
+
session: Session,
|
|
117
|
+
params: BranchParams,
|
|
118
|
+
*,
|
|
119
|
+
shindan_url: str,
|
|
120
|
+
) -> ShindanResult:
|
|
121
|
+
"""Get result by branch type shindan.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
session (Session): session object
|
|
125
|
+
params (Params): input parameters fetched from shindan page
|
|
126
|
+
shindan_url (str): shindan url
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
ShindanResult: the returned result from <https://shindanmaker.com>
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
return __get_result(session, params, is_renewal=True, shindan_url=shindan_url)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def get_result_by_check(
|
|
136
|
+
session: Session,
|
|
137
|
+
params: CheckParams,
|
|
138
|
+
*,
|
|
139
|
+
user_choices: dict[int, str],
|
|
140
|
+
shindan_url: str,
|
|
141
|
+
) -> ShindanResult:
|
|
142
|
+
"""Get result by check type shindan.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
session (Session): session object
|
|
146
|
+
params (Params): input parameters fetched from shindan page
|
|
147
|
+
user_choices (dict[int, str]): choices inputted by user on terminal
|
|
148
|
+
shindan_url (str): shindan url
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
ShindanResult: the returned result from <https://shindanmaker.com>
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
for choice_id, answer_id in user_choices.items():
|
|
155
|
+
params[f"input-check-choice[{choice_id}]"] = answer_id # type: ignore[literal-required]
|
|
156
|
+
|
|
157
|
+
return __get_result(session, params, is_renewal=True, shindan_url=shindan_url)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def get_result_by_name(
|
|
161
|
+
session: Session,
|
|
162
|
+
params: Params,
|
|
163
|
+
*,
|
|
164
|
+
shindan_url: str,
|
|
165
|
+
) -> ShindanResult:
|
|
166
|
+
"""Get result by name type shindan.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
session (Session): session object
|
|
170
|
+
params (Params): input parameters fetched from shindan page
|
|
171
|
+
shindan_url (str): shindan url
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
ShindanResult: the returned result from <https://shindanmaker.com>
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
return __get_result(session, params, shindan_url=shindan_url)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
__all__ = (
|
|
181
|
+
"get_result_by_ai",
|
|
182
|
+
"get_result_by_branch",
|
|
183
|
+
"get_result_by_check",
|
|
184
|
+
"get_result_by_name",
|
|
185
|
+
)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Interactive functions for the shindan CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from shindan_cli.models import QuestionBranchChoice, QuestionChoiceChoice, UserInput, QuestionBranch, QuestionChoice
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from bs4 import BeautifulSoup
|
|
11
|
+
|
|
12
|
+
from .models import UserInputs
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_user_inputs(
|
|
16
|
+
source: BeautifulSoup,
|
|
17
|
+
shindan_name: str,
|
|
18
|
+
) -> UserInputs:
|
|
19
|
+
"""Get user inputs from the shindan source.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
source (BeautifulSoup): The source of the shindan page.
|
|
23
|
+
shindan_name (str): The name of the shindan.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
UserInputs: The user inputs.
|
|
27
|
+
|
|
28
|
+
"""
|
|
29
|
+
user_inputs: dict[str, UserInput] = {}
|
|
30
|
+
form_labels = source.select("form#shindanForm > div.px-3 > div > span")
|
|
31
|
+
for idx, question in enumerate([*form_labels, *range(10 - len(form_labels))]):
|
|
32
|
+
if isinstance(question, int):
|
|
33
|
+
user_inputs[f"user_input_{idx + 1}"] = UserInput({"q": "", "a": None})
|
|
34
|
+
continue
|
|
35
|
+
question_text = question.text
|
|
36
|
+
if question_text == "あなたの名前":
|
|
37
|
+
user_inputs[f"user_input_{idx + 1}"] = UserInput({"q": question_text, "a": shindan_name})
|
|
38
|
+
continue
|
|
39
|
+
answer_text = None
|
|
40
|
+
while not answer_text:
|
|
41
|
+
answer_text = input(f"[{question_text}]: ").strip()
|
|
42
|
+
user_inputs[f"user_input_{idx + 1}"] = UserInput({"q": question_text, "a": answer_text})
|
|
43
|
+
return user_inputs
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_rbr(source: BeautifulSoup) -> str:
|
|
47
|
+
"""Get the result of the shindan from the source.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
source (BeautifulSoup): The source of the shindan page.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
str: The result of the shindan.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
question_branches: dict[str, QuestionBranch] = {}
|
|
57
|
+
for branch in source.select("div[id^='shindan_branch_'][data-kind='0']"):
|
|
58
|
+
branch_id = branch.get("id")
|
|
59
|
+
question = branch.select_one("p.shindan_branch_question")
|
|
60
|
+
if not isinstance(branch_id, str) or not question:
|
|
61
|
+
continue
|
|
62
|
+
question_branches[branch_id.replace("shindan_branch_", "")] = QuestionBranch(
|
|
63
|
+
{
|
|
64
|
+
"question": question.text.strip(),
|
|
65
|
+
"choices": [
|
|
66
|
+
QuestionBranchChoice(
|
|
67
|
+
{
|
|
68
|
+
"label": choice.text,
|
|
69
|
+
"next_branch_id": next_branch_id,
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
for choice in branch.select("button")
|
|
73
|
+
if isinstance(next_branch_id := choice.get("data-next_branch"), str)
|
|
74
|
+
],
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
goal_branches: dict[str, str] = {}
|
|
79
|
+
for branch in source.select("div[id^='shindan_branch_'][data-kind='1']"):
|
|
80
|
+
branch_id = branch.get("id")
|
|
81
|
+
rbr = branch.get("data-rbr")
|
|
82
|
+
if not isinstance(branch_id, str) or not isinstance(rbr, str):
|
|
83
|
+
continue
|
|
84
|
+
goal_branches[branch_id.replace("shindan_branch_", "")] = rbr
|
|
85
|
+
|
|
86
|
+
current_branch_id = "1"
|
|
87
|
+
while current_branch_id not in goal_branches:
|
|
88
|
+
current_branch = question_branches[current_branch_id]
|
|
89
|
+
choices = current_branch["choices"]
|
|
90
|
+
|
|
91
|
+
print("[Q.", current_branch["question"], "]") # noqa: T201
|
|
92
|
+
for idx, choice in enumerate(choices):
|
|
93
|
+
print(f"> {idx}:", choice["label"]) # noqa: T201
|
|
94
|
+
|
|
95
|
+
inputted_text = input(">>> ").strip()
|
|
96
|
+
if not inputted_text:
|
|
97
|
+
continue
|
|
98
|
+
selected_id = int(inputted_text)
|
|
99
|
+
if 0 <= selected_id < len(choices):
|
|
100
|
+
current_branch_id = choices[selected_id]["next_branch_id"]
|
|
101
|
+
|
|
102
|
+
return goal_branches[current_branch_id]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_choices(source: BeautifulSoup) -> dict[int, str]:
|
|
106
|
+
"""Get the choices from the shindan source.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
source (BeautifulSoup): The source of the shindan page.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
dict[int, str]: The choices.
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
question_choices: dict[int, QuestionChoice] = {}
|
|
116
|
+
for question in source.select("div.check-username-target"):
|
|
117
|
+
order_id = question.get("data-order")
|
|
118
|
+
question_text = question.select_one("div > p > span")
|
|
119
|
+
if not isinstance(order_id, str) or not question_text:
|
|
120
|
+
continue
|
|
121
|
+
question_choices[int(order_id)] = QuestionChoice(
|
|
122
|
+
{
|
|
123
|
+
"question": question_text.text.strip(),
|
|
124
|
+
"choices": [
|
|
125
|
+
QuestionChoiceChoice({"label": choice.text.strip(), "choice_id": choice_key})
|
|
126
|
+
for choice in question.select("button.list-group-item")
|
|
127
|
+
if isinstance(choice_key := choice.get("data-choice_key"), str)
|
|
128
|
+
],
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
current_question_id = 1
|
|
132
|
+
total_questions = len(question_choices)
|
|
133
|
+
answers: dict[int, str] = {}
|
|
134
|
+
while len(answers) < total_questions:
|
|
135
|
+
current_question = question_choices[current_question_id]
|
|
136
|
+
choices = current_question["choices"]
|
|
137
|
+
print( # noqa: T201
|
|
138
|
+
f"= {current_question_id}/{total_questions} =",
|
|
139
|
+
current_question["question"].strip(),
|
|
140
|
+
"===",
|
|
141
|
+
sep="\n",
|
|
142
|
+
)
|
|
143
|
+
for idx, choice in enumerate(choices):
|
|
144
|
+
print(f"> {idx}:", choice["label"]) # noqa: T201
|
|
145
|
+
|
|
146
|
+
inputted_text = input(">>> ").strip()
|
|
147
|
+
if not inputted_text:
|
|
148
|
+
continue
|
|
149
|
+
selected_id = int(inputted_text)
|
|
150
|
+
if 0 <= selected_id < len(choices):
|
|
151
|
+
answers[current_question_id] = choices[selected_id]["choice_id"]
|
|
152
|
+
current_question_id += 1
|
|
153
|
+
return answers
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
__all__ = (
|
|
157
|
+
"get_choices",
|
|
158
|
+
"get_rbr",
|
|
159
|
+
"get_user_inputs",
|
|
160
|
+
)
|
|
@@ -8,24 +8,45 @@ from pprint import pprint
|
|
|
8
8
|
from . import __version__, shindan
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def
|
|
11
|
+
def __check_natural(v: str) -> int:
|
|
12
12
|
if int(v) < 0:
|
|
13
|
-
|
|
13
|
+
msg = f"{v!r} is an invalid natural int"
|
|
14
|
+
raise argparse.ArgumentTypeError(msg)
|
|
14
15
|
return int(v)
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def
|
|
18
|
+
def __parse_args(*, test: list[str] | None = None) -> argparse.Namespace:
|
|
18
19
|
parser = argparse.ArgumentParser(
|
|
19
20
|
prog="shindan",
|
|
20
21
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
21
22
|
description="ShindanMaker (https://shindanmaker.com) CLI",
|
|
22
23
|
)
|
|
23
|
-
parser.add_argument(
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"page_id",
|
|
26
|
+
metavar="ID",
|
|
27
|
+
type=__check_natural,
|
|
28
|
+
help="shindan page id",
|
|
29
|
+
)
|
|
24
30
|
parser.add_argument("shindan_name", metavar="NAME", type=str, help="shindan name")
|
|
25
31
|
parser.add_argument("-w", "--wait", action="store_true", help="insert random wait")
|
|
26
|
-
parser.add_argument(
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"-H",
|
|
34
|
+
"--hashtag",
|
|
35
|
+
action="store_true",
|
|
36
|
+
help="add hashtag `#shindanmaker`",
|
|
37
|
+
)
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"-l",
|
|
40
|
+
"--link",
|
|
41
|
+
action="store_true",
|
|
42
|
+
help="add link to last of output",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"-V",
|
|
46
|
+
"--version",
|
|
47
|
+
action="version",
|
|
48
|
+
version=f"%(prog)s {__version__}",
|
|
49
|
+
)
|
|
29
50
|
|
|
30
51
|
# secret option!
|
|
31
52
|
parser.add_argument("--debug", action="store_true", help=argparse.SUPPRESS)
|
|
@@ -36,8 +57,13 @@ def _parse_args(*, test: list[str] | None = None) -> argparse.Namespace:
|
|
|
36
57
|
|
|
37
58
|
|
|
38
59
|
def main(*, test: list[str] | None = None) -> None:
|
|
39
|
-
"""Run CLI.
|
|
40
|
-
|
|
60
|
+
"""Run the shindan CLI.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
test (list[str] | None, optional): test arguments. Defaults to None.
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
args = __parse_args(test=test)
|
|
41
67
|
result = shindan(args.page_id, args.shindan_name, wait=args.wait)
|
|
42
68
|
|
|
43
69
|
if args.debug:
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Models for shindan-cli."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TypedDict
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ShindanResult(TypedDict):
|
|
9
|
+
"""TypedDict Class for result of shindan."""
|
|
10
|
+
|
|
11
|
+
results: list[str]
|
|
12
|
+
hashtags: list[str]
|
|
13
|
+
shindan_url: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class QuestionBranchChoice(TypedDict):
|
|
17
|
+
label: str
|
|
18
|
+
next_branch_id: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QuestionBranch(TypedDict):
|
|
22
|
+
"""TypedDict Class for question branch."""
|
|
23
|
+
|
|
24
|
+
question: str
|
|
25
|
+
choices: list[QuestionBranchChoice]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class QuestionChoiceChoice(TypedDict):
|
|
29
|
+
label: str
|
|
30
|
+
choice_id: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class QuestionChoice(TypedDict):
|
|
34
|
+
"""TypedDict Class for question choice."""
|
|
35
|
+
|
|
36
|
+
question: str
|
|
37
|
+
choices: list[QuestionChoiceChoice]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class UserInput(TypedDict):
|
|
41
|
+
"""TypedDict Class for user input."""
|
|
42
|
+
|
|
43
|
+
q: str
|
|
44
|
+
a: str | None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
UserInputs = dict[str, UserInput]
|
|
48
|
+
|
|
49
|
+
__all__ = (
|
|
50
|
+
"QuestionBranch",
|
|
51
|
+
"QuestionChoice",
|
|
52
|
+
"ShindanResult",
|
|
53
|
+
"UserInput",
|
|
54
|
+
"UserInputs",
|
|
55
|
+
)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Implements the function to get the shindan result from <https://shindanmaker.com>."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import random
|
|
6
|
+
import time
|
|
7
|
+
from typing import cast
|
|
8
|
+
|
|
9
|
+
import cloudscraper # type: ignore[unused-ignore,import-not-found,import-untyped]
|
|
10
|
+
from bs4 import BeautifulSoup
|
|
11
|
+
from requests.sessions import Session
|
|
12
|
+
|
|
13
|
+
from .constants import BASE_URL, HEADERS, TARGET_KEYS_BY_TYPE
|
|
14
|
+
from .get_results import (
|
|
15
|
+
Params,
|
|
16
|
+
get_result_by_ai,
|
|
17
|
+
get_result_by_branch,
|
|
18
|
+
get_result_by_check,
|
|
19
|
+
get_result_by_name,
|
|
20
|
+
)
|
|
21
|
+
from .interactive import get_choices, get_rbr, get_user_inputs
|
|
22
|
+
from .models import ShindanResult
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ShindanError(Exception):
|
|
26
|
+
"""Error class for shindan-cli."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def shindan(
|
|
30
|
+
page_id: int,
|
|
31
|
+
shindan_name: str,
|
|
32
|
+
*,
|
|
33
|
+
wait: bool | None = False,
|
|
34
|
+
) -> ShindanResult:
|
|
35
|
+
"""Get the shindan result from <https://shindanmaker.com>.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
page_id : int
|
|
39
|
+
shindan page id (e.g. `1036646`)
|
|
40
|
+
shindan_name : str
|
|
41
|
+
shindan name (e.g. your name)
|
|
42
|
+
wait : bool | None, optional
|
|
43
|
+
enable random waits while fetching shindan data, by default False
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
ShindanResult: the returned result from <https://shindanmaker.com>
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ShindanError
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
if not isinstance(page_id, int) or page_id < 0:
|
|
53
|
+
msg = f"invalid page id: {page_id}"
|
|
54
|
+
raise ShindanError(msg)
|
|
55
|
+
shindan_url = f"{BASE_URL}/{page_id}"
|
|
56
|
+
|
|
57
|
+
session = cloudscraper.create_scraper()
|
|
58
|
+
assert isinstance(session, Session)
|
|
59
|
+
|
|
60
|
+
shindan_page = session.get(shindan_url, headers=HEADERS)
|
|
61
|
+
if shindan_page.status_code != 200:
|
|
62
|
+
raise ShindanError(shindan_page.status_code)
|
|
63
|
+
|
|
64
|
+
source = BeautifulSoup(shindan_page.text, features="lxml")
|
|
65
|
+
|
|
66
|
+
shindan_type_input = source.select_one('input[name="type"]')
|
|
67
|
+
if not shindan_type_input or not shindan_type_input.get("value"):
|
|
68
|
+
msg = "Cannot detect shindan type!"
|
|
69
|
+
raise ValueError(msg)
|
|
70
|
+
shindan_type = str(shindan_type_input.get("value"))
|
|
71
|
+
params = cast(
|
|
72
|
+
"Params",
|
|
73
|
+
{
|
|
74
|
+
key: key_input.get("value")
|
|
75
|
+
for key in TARGET_KEYS_BY_TYPE[shindan_type]
|
|
76
|
+
if (key_input := source.select_one(f'input[name="{key}"]'))
|
|
77
|
+
},
|
|
78
|
+
)
|
|
79
|
+
# overwrite randname (old: shindanName)
|
|
80
|
+
params["randname"] = shindan_name
|
|
81
|
+
|
|
82
|
+
if wait:
|
|
83
|
+
time.sleep(random.uniform(2, 5)) # noqa: S311
|
|
84
|
+
|
|
85
|
+
if params["type"] == "ai":
|
|
86
|
+
hashtag_title = source.select_one("h1#shindanTitle")
|
|
87
|
+
if not hashtag_title or not isinstance(
|
|
88
|
+
hashtag := hashtag_title.get("data-shindan_hashtag"),
|
|
89
|
+
str,
|
|
90
|
+
):
|
|
91
|
+
hashtag = None
|
|
92
|
+
return get_result_by_ai(
|
|
93
|
+
session,
|
|
94
|
+
params,
|
|
95
|
+
user_inputs=get_user_inputs(source, shindan_name),
|
|
96
|
+
hashtag=hashtag,
|
|
97
|
+
shindan_url=shindan_url,
|
|
98
|
+
)
|
|
99
|
+
if params["type"] == "branch":
|
|
100
|
+
params["rbr"] = get_rbr(source)
|
|
101
|
+
return get_result_by_branch(
|
|
102
|
+
session,
|
|
103
|
+
params,
|
|
104
|
+
shindan_url=shindan_url,
|
|
105
|
+
)
|
|
106
|
+
if params["type"] == "check":
|
|
107
|
+
return get_result_by_check(
|
|
108
|
+
session,
|
|
109
|
+
params,
|
|
110
|
+
user_choices=get_choices(source),
|
|
111
|
+
shindan_url=shindan_url,
|
|
112
|
+
)
|
|
113
|
+
if params["type"] == "name":
|
|
114
|
+
return get_result_by_name(
|
|
115
|
+
session,
|
|
116
|
+
params,
|
|
117
|
+
shindan_url=shindan_url,
|
|
118
|
+
)
|
|
119
|
+
raise NotImplementedError(shindan_type)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
__all__ = ("ShindanResult", "shindan")
|
shindan_cli-1.3.0/pyproject.toml
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# poetry self add poetry-bumpversion
|
|
2
|
-
|
|
3
|
-
[build-system]
|
|
4
|
-
build-backend = "poetry.core.masonry.api"
|
|
5
|
-
requires = [
|
|
6
|
-
"poetry-core",
|
|
7
|
-
]
|
|
8
|
-
|
|
9
|
-
[tool]
|
|
10
|
-
[tool.black]
|
|
11
|
-
line-length = 120
|
|
12
|
-
target-version = ['py39']
|
|
13
|
-
|
|
14
|
-
[tool.isort]
|
|
15
|
-
profile = "black"
|
|
16
|
-
|
|
17
|
-
[tool.mypy]
|
|
18
|
-
pretty = true
|
|
19
|
-
python_version = "3.9"
|
|
20
|
-
show_error_codes = true
|
|
21
|
-
strict = true
|
|
22
|
-
|
|
23
|
-
[tool.ruff]
|
|
24
|
-
ignore = [
|
|
25
|
-
"D203", # 1 blank line required before class docstring
|
|
26
|
-
"D213", # Multi-line docstring summary should start at the second line
|
|
27
|
-
]
|
|
28
|
-
line-length = 120
|
|
29
|
-
select = ["ALL"]
|
|
30
|
-
|
|
31
|
-
[tool.ruff.mccabe]
|
|
32
|
-
max-complexity = 18
|
|
33
|
-
|
|
34
|
-
[tool.ruff.per-file-ignores]
|
|
35
|
-
"tests/*.py" = [
|
|
36
|
-
"D",
|
|
37
|
-
"S101", # Use of assert detected
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
[tool.poetry]
|
|
41
|
-
authors = ["eggplants <w10776e8w@yahoo.co.jp>"]
|
|
42
|
-
classifiers = [
|
|
43
|
-
"Development Status :: 3 - Alpha",
|
|
44
|
-
"License :: OSI Approved :: MIT License",
|
|
45
|
-
"Programming Language :: Python :: 3",
|
|
46
|
-
"Programming Language :: Python :: 3.7",
|
|
47
|
-
"Programming Language :: Python :: 3.8",
|
|
48
|
-
"Programming Language :: Python :: 3.9",
|
|
49
|
-
"Programming Language :: Python :: 3.10",
|
|
50
|
-
"Programming Language :: Python :: 3 :: Only",
|
|
51
|
-
]
|
|
52
|
-
description = "ShindanMaker (https://shindanmaker.com) CLI"
|
|
53
|
-
keywords = ["shindanmaker", "cli"]
|
|
54
|
-
license = "MIT"
|
|
55
|
-
name = "shindan_cli"
|
|
56
|
-
packages = [{include = "shindan_cli"}]
|
|
57
|
-
readme = "README.md"
|
|
58
|
-
repository = "https://github.com/eggplants/shindan-cli"
|
|
59
|
-
version = "1.3.0"
|
|
60
|
-
|
|
61
|
-
[tool.poetry.dependencies]
|
|
62
|
-
beautifulsoup4 = "^4.11.2"
|
|
63
|
-
lxml = "^4.9.2"
|
|
64
|
-
python = ">=3.7,<4"
|
|
65
|
-
requests = "^2.28.2"
|
|
66
|
-
|
|
67
|
-
[tool.poetry.group.dev.dependencies]
|
|
68
|
-
lxml-stubs = "^0.4.0"
|
|
69
|
-
mypy = ">=0.991,<1.4"
|
|
70
|
-
pre-commit = "^2.20.0"
|
|
71
|
-
pytest = "^7.2.2"
|
|
72
|
-
pytest-cov = "^4.0.0"
|
|
73
|
-
taskipy = "^1.10.3"
|
|
74
|
-
types-beautifulsoup4 = "^4.11.6.7"
|
|
75
|
-
types-requests = "^2.28.11.14"
|
|
76
|
-
|
|
77
|
-
[tool.poetry.scripts]
|
|
78
|
-
shindan = "shindan_cli.main:main"
|
|
79
|
-
|
|
80
|
-
[tool.poetry_bumpversion.file."shindan_cli/__init__.py"]
|
|
81
|
-
|
|
82
|
-
[tool.taskipy.tasks]
|
|
83
|
-
lint = "pre-commit run -a"
|
|
84
|
-
profile = "python -m cProfile"
|
|
85
|
-
setup = "poetry install && pre-commit install -t pre-commit -t pre-push"
|
|
86
|
-
test = "pytest --cov=shindan_cli --cov-report=term"
|
|
87
|
-
"test:ci" = "task test --cov-report=xml:cov.xml"
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"""Implements the function to get the shindan result from <https://shindanmaker.com>."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import random
|
|
6
|
-
import time
|
|
7
|
-
from typing import TypedDict
|
|
8
|
-
|
|
9
|
-
import requests
|
|
10
|
-
from bs4 import BeautifulSoup, Tag
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class ShindanResult(TypedDict):
|
|
14
|
-
"""TypedDict Class for result of shindan."""
|
|
15
|
-
|
|
16
|
-
results: list[str]
|
|
17
|
-
hashtags: list[str]
|
|
18
|
-
shindan_url: str
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class ShindanError(Exception):
|
|
22
|
-
"""Error class for shindan-cli."""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def shindan(page_id: int, shindan_name: str, *, wait: bool | None = False) -> ShindanResult:
|
|
26
|
-
"""Get the shindan result from <https://shindanmaker.com>.
|
|
27
|
-
|
|
28
|
-
Parameters
|
|
29
|
-
----------
|
|
30
|
-
page_id : int
|
|
31
|
-
shindan page id (e.g. `1036646`)
|
|
32
|
-
shindan_name : str
|
|
33
|
-
shindan name (e.g. your name)
|
|
34
|
-
wait : bool | None, optional
|
|
35
|
-
enable random waits while fetching shindan data, by default False
|
|
36
|
-
|
|
37
|
-
Returns
|
|
38
|
-
-------
|
|
39
|
-
ShindanResult
|
|
40
|
-
the returned result from <https://shindanmaker.com>
|
|
41
|
-
|
|
42
|
-
Raises
|
|
43
|
-
------
|
|
44
|
-
ShindanError
|
|
45
|
-
"""
|
|
46
|
-
headers = {
|
|
47
|
-
"User-Agent": (
|
|
48
|
-
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
49
|
-
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
50
|
-
"Chrome/96.0.4664.45 Safari/537.36"
|
|
51
|
-
),
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if not isinstance(page_id, int) or page_id < 0:
|
|
55
|
-
msg = f"invalid page id: {page_id}"
|
|
56
|
-
raise ShindanError(msg)
|
|
57
|
-
|
|
58
|
-
url = f"https://shindanmaker.com/{page_id}"
|
|
59
|
-
|
|
60
|
-
session = requests.session()
|
|
61
|
-
s = session.get(url, headers=headers)
|
|
62
|
-
if s.status_code != requests.codes.ok:
|
|
63
|
-
raise ShindanError(s.status_code)
|
|
64
|
-
|
|
65
|
-
source = BeautifulSoup(s.text, features="lxml")
|
|
66
|
-
|
|
67
|
-
# q, _token, shindanName, hiddenName, type, shindan_token, q
|
|
68
|
-
params = {i["name"]: i["value"] for i in source.find_all("input")[1:5]}
|
|
69
|
-
|
|
70
|
-
# overwrite shindanName
|
|
71
|
-
params["shindanName"] = shindan_name
|
|
72
|
-
|
|
73
|
-
login = session.post(url, data=params, headers=headers)
|
|
74
|
-
if wait:
|
|
75
|
-
time.sleep(random.uniform(2, 5)) # noqa: S311
|
|
76
|
-
|
|
77
|
-
soup = BeautifulSoup(login.text, features="lxml")
|
|
78
|
-
result_tag = soup.find(id="share-copytext-shindanresult-textarea")
|
|
79
|
-
|
|
80
|
-
if not isinstance(result_tag, Tag) or not result_tag.text:
|
|
81
|
-
msg = f"Could not find a tag contains the result, returns: {result_tag}"
|
|
82
|
-
raise ShindanError(msg)
|
|
83
|
-
|
|
84
|
-
*results, hashtag, shindan_url, _ = result_tag.text.split("\n")
|
|
85
|
-
|
|
86
|
-
if not results[-1]:
|
|
87
|
-
results.pop(-1)
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
"results": results,
|
|
91
|
-
"hashtags": hashtag.split("\xa0"),
|
|
92
|
-
"shindan_url": shindan_url,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
__all__ = ("ShindanResult", "shindan")
|
|
File without changes
|
|
File without changes
|