shindan-cli 2.2.0__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/PKG-INFO +2 -2
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/pyproject.toml +7 -12
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/constants.py +0 -8
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/get_results.py +29 -35
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/interactive.py +2 -2
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/shindan.py +7 -2
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/.gitignore +0 -0
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/LICENSE +0 -0
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/README.md +0 -0
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/__init__.py +0 -0
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/main.py +0 -0
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/models.py +0 -0
- {shindan_cli-2.2.0 → shindan_cli-2.2.1}/shindan_cli/py.typed +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shindan-cli
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: ShindanMaker (https://shindanmaker.com) CLI
|
|
5
5
|
Project-URL: Repository, https://github.com/eggplants/shindan-cli
|
|
6
6
|
Author-email: eggplants <w10776e8w@yahoo.co.jp>
|
|
7
|
-
License: MIT
|
|
7
|
+
License-Expression: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Keywords: cli,shindanmaker
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
[build-system]
|
|
2
2
|
build-backend = "hatchling.build"
|
|
3
|
-
|
|
4
3
|
requires = [ "hatchling", "uv-dynamic-versioning" ]
|
|
5
4
|
|
|
6
5
|
[project]
|
|
@@ -11,7 +10,7 @@ keywords = [
|
|
|
11
10
|
"cli",
|
|
12
11
|
"shindanmaker",
|
|
13
12
|
]
|
|
14
|
-
license =
|
|
13
|
+
license = "MIT"
|
|
15
14
|
authors = [ { name = "eggplants", email = "w10776e8w@yahoo.co.jp" } ]
|
|
16
15
|
requires-python = ">=3.10,<4"
|
|
17
16
|
classifiers = [
|
|
@@ -32,7 +31,6 @@ dependencies = [
|
|
|
32
31
|
"lxml>=5.3,<7",
|
|
33
32
|
"requests>=2.32.3,<3",
|
|
34
33
|
]
|
|
35
|
-
|
|
36
34
|
urls.Repository = "https://github.com/eggplants/shindan-cli"
|
|
37
35
|
scripts.shindan = "shindan_cli.main:main"
|
|
38
36
|
|
|
@@ -48,22 +46,19 @@ docs = [
|
|
|
48
46
|
"pdoc>=16",
|
|
49
47
|
]
|
|
50
48
|
|
|
51
|
-
[tool.hatch
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
include = [ "shindan_cli" ]
|
|
56
|
-
|
|
57
|
-
[tool.hatch.build.targets.wheel]
|
|
58
|
-
include = [ "shindan_cli" ]
|
|
49
|
+
[tool.hatch]
|
|
50
|
+
build.targets.sdist.include = [ "shindan_cli" ]
|
|
51
|
+
build.targets.wheel.include = [ "shindan_cli" ]
|
|
52
|
+
version.source = "uv-dynamic-versioning"
|
|
59
53
|
|
|
60
54
|
[tool.uv]
|
|
61
|
-
|
|
55
|
+
python-downloads = "never"
|
|
62
56
|
default-groups = [
|
|
63
57
|
"dev",
|
|
64
58
|
"docs",
|
|
65
59
|
]
|
|
66
60
|
exclude-newer = "P7D"
|
|
61
|
+
preview = true
|
|
67
62
|
|
|
68
63
|
[tool.ruff]
|
|
69
64
|
line-length = 120
|
|
@@ -7,34 +7,26 @@ class AIParams(TypedDict):
|
|
|
7
7
|
_token: str
|
|
8
8
|
randname: str
|
|
9
9
|
type: Literal["ai"]
|
|
10
|
-
shindan_token: str
|
|
11
10
|
encrypted_exec_key: str
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class BranchParams(TypedDict):
|
|
15
14
|
_token: str
|
|
16
15
|
randname: str
|
|
17
|
-
hiddenName: str
|
|
18
16
|
type: Literal["branch"]
|
|
19
|
-
shindan_token: str
|
|
20
17
|
rbr: str
|
|
21
18
|
|
|
22
19
|
|
|
23
20
|
class CheckParams(TypedDict):
|
|
24
21
|
_token: str
|
|
25
22
|
randname: str
|
|
26
|
-
hiddenName: str
|
|
27
23
|
type: Literal["check"]
|
|
28
|
-
shindan_token: str
|
|
29
|
-
# input-check-choice[choice_id]: str
|
|
30
24
|
|
|
31
25
|
|
|
32
26
|
class NameParams(TypedDict):
|
|
33
27
|
_token: str
|
|
34
28
|
randname: str
|
|
35
|
-
hiddenName: str
|
|
36
29
|
type: Literal["name"]
|
|
37
|
-
shindan_token: str
|
|
38
30
|
|
|
39
31
|
|
|
40
32
|
class TargetKeysByType(TypedDict):
|
|
@@ -3,18 +3,22 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import re
|
|
6
|
-
|
|
6
|
+
import time
|
|
7
|
+
from typing import TYPE_CHECKING, cast
|
|
7
8
|
|
|
8
9
|
from bs4 import BeautifulSoup, Tag
|
|
9
10
|
|
|
10
|
-
from .constants import
|
|
11
|
+
from .constants import HEADERS, AIParams, BranchParams, CheckParams, NameParams
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
13
14
|
from requests import Session
|
|
14
15
|
|
|
15
16
|
from .models import ShindanResult, UserInputs
|
|
16
17
|
|
|
17
|
-
Params =
|
|
18
|
+
Params = AIParams | BranchParams | CheckParams | NameParams
|
|
19
|
+
|
|
20
|
+
_MAX_RETRIES = 3
|
|
21
|
+
_RETRY_WAIT = 4.0
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
def __get_result(
|
|
@@ -24,15 +28,23 @@ def __get_result(
|
|
|
24
28
|
is_renewal: bool = False,
|
|
25
29
|
shindan_url: str,
|
|
26
30
|
) -> ShindanResult:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
result_tag: Tag | None = None
|
|
32
|
+
|
|
33
|
+
for attempt in range(_MAX_RETRIES):
|
|
34
|
+
if attempt > 0:
|
|
35
|
+
time.sleep(_RETRY_WAIT)
|
|
36
|
+
result_page = session.post(
|
|
37
|
+
shindan_url + ("/r" if is_renewal else ""),
|
|
38
|
+
data=params,
|
|
39
|
+
headers=HEADERS,
|
|
40
|
+
)
|
|
41
|
+
soup = BeautifulSoup(result_page.text, features="lxml")
|
|
42
|
+
found = soup.find(id="share-copytext-shindanresult-textarea")
|
|
43
|
+
if isinstance(found, Tag) and found.text:
|
|
44
|
+
result_tag = found
|
|
45
|
+
break
|
|
34
46
|
|
|
35
|
-
if
|
|
47
|
+
if result_tag is None:
|
|
36
48
|
msg = f"Could not find a tag contains the result, returns: {result_tag}"
|
|
37
49
|
raise TypeError(msg)
|
|
38
50
|
|
|
@@ -54,6 +66,7 @@ def get_result_by_ai(
|
|
|
54
66
|
*,
|
|
55
67
|
user_inputs: UserInputs,
|
|
56
68
|
hashtag: str | None,
|
|
69
|
+
csrf_token: str,
|
|
57
70
|
shindan_url: str,
|
|
58
71
|
) -> ShindanResult:
|
|
59
72
|
"""Get result by AI type shindan.
|
|
@@ -63,34 +76,14 @@ def get_result_by_ai(
|
|
|
63
76
|
params (Params): input parameters fetched from shindan page
|
|
64
77
|
user_inputs (UserInputs): user inputs
|
|
65
78
|
hashtag (str | None): hashtag
|
|
79
|
+
csrf_token (str): CSRF token extracted from the shindan page
|
|
66
80
|
shindan_url (str): shindan url
|
|
67
81
|
|
|
68
82
|
Returns:
|
|
69
83
|
ShindanResult: the returned result from <https://shindanmaker.com>
|
|
70
84
|
|
|
71
85
|
"""
|
|
72
|
-
|
|
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)
|
|
86
|
+
ai_headers = {**HEADERS, "x-csrf-token": csrf_token}
|
|
94
87
|
|
|
95
88
|
result_sse = session.post(
|
|
96
89
|
f"{shindan_url}/ai_result",
|
|
@@ -100,7 +93,7 @@ def get_result_by_ai(
|
|
|
100
93
|
"ai_result_request_times": 0,
|
|
101
94
|
"encrypted_exec_key": params["encrypted_exec_key"],
|
|
102
95
|
},
|
|
103
|
-
headers=
|
|
96
|
+
headers=ai_headers,
|
|
104
97
|
)
|
|
105
98
|
gpt_results = "".join(
|
|
106
99
|
re.findall(r'"content":"([^"]+)', result_sse.text),
|
|
@@ -151,8 +144,9 @@ def get_result_by_check(
|
|
|
151
144
|
ShindanResult: the returned result from <https://shindanmaker.com>
|
|
152
145
|
|
|
153
146
|
"""
|
|
147
|
+
check_params = cast("dict[str, str]", params)
|
|
154
148
|
for choice_id, answer_id in user_choices.items():
|
|
155
|
-
|
|
149
|
+
check_params[f"input-check-choice[{choice_id}]"] = answer_id
|
|
156
150
|
|
|
157
151
|
return __get_result(session, params, is_renewal=True, shindan_url=shindan_url)
|
|
158
152
|
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
|
-
from shindan_cli.models import
|
|
7
|
+
from shindan_cli.models import QuestionBranch, QuestionBranchChoice, QuestionChoice, QuestionChoiceChoice, UserInput
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from bs4 import BeautifulSoup
|
|
@@ -27,7 +27,7 @@ def get_user_inputs(
|
|
|
27
27
|
|
|
28
28
|
"""
|
|
29
29
|
user_inputs: dict[str, UserInput] = {}
|
|
30
|
-
form_labels = source.select("form#shindanForm
|
|
30
|
+
form_labels = source.select("form#shindanForm label")
|
|
31
31
|
for idx, question in enumerate([*form_labels, *range(10 - len(form_labels))]):
|
|
32
32
|
if isinstance(question, int):
|
|
33
33
|
user_inputs[f"user_input_{idx + 1}"] = UserInput({"q": "", "a": None})
|
|
@@ -55,10 +55,10 @@ def shindan(
|
|
|
55
55
|
shindan_url = f"{BASE_URL}/{page_id}"
|
|
56
56
|
|
|
57
57
|
session = cloudscraper.create_scraper()
|
|
58
|
-
assert isinstance(session, Session)
|
|
58
|
+
assert isinstance(session, Session) # noqa: S101
|
|
59
59
|
|
|
60
60
|
shindan_page = session.get(shindan_url, headers=HEADERS)
|
|
61
|
-
if shindan_page.status_code != 200:
|
|
61
|
+
if shindan_page.status_code != 200: # noqa: PLR2004
|
|
62
62
|
raise ShindanError(shindan_page.status_code)
|
|
63
63
|
|
|
64
64
|
source = BeautifulSoup(shindan_page.text, features="lxml")
|
|
@@ -89,11 +89,16 @@ def shindan(
|
|
|
89
89
|
str,
|
|
90
90
|
):
|
|
91
91
|
hashtag = None
|
|
92
|
+
csrf_meta = source.select_one('meta[name="csrf-token"]')
|
|
93
|
+
if not csrf_meta or not isinstance(csrf_token := csrf_meta.get("content"), str):
|
|
94
|
+
msg = "Could not find CSRF token on the shindan page."
|
|
95
|
+
raise ShindanError(msg)
|
|
92
96
|
return get_result_by_ai(
|
|
93
97
|
session,
|
|
94
98
|
params,
|
|
95
99
|
user_inputs=get_user_inputs(source, shindan_name),
|
|
96
100
|
hashtag=hashtag,
|
|
101
|
+
csrf_token=csrf_token,
|
|
97
102
|
shindan_url=shindan_url,
|
|
98
103
|
)
|
|
99
104
|
if params["type"] == "branch":
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|