PyPtt 2.1.7__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.
- {pyptt-2.1.7 → pyptt-2.2.0}/PKG-INFO +1 -1
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/PTT.py +16 -2
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/__init__.py +1 -1
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_post.py +57 -2
- pyptt-2.2.0/PyPtt/ssl_config.py +24 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/PKG-INFO +1 -1
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/SOURCES.txt +1 -0
- pyptt-2.2.0/tests/test_post_anonymous.py +96 -0
- pyptt-2.1.7/PyPtt/ssl_config.py +0 -24
- {pyptt-2.1.7 → pyptt-2.2.0}/LICENSE +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_bucket.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_call_status.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_change_pw.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_comment.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_del_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_board_info.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_board_list.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_bottom_post_list.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_favourite_board.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_newest_index.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_post_index.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_post_list.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_time.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_user.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_waterball.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_give_money.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_has_new_mail.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_loginout.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_mail.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_mark_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_reply_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_search_user.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_set_board_title.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_util.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/api_server.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/check_value.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/command.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/config.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/connect_core.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/data_type.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/exceptions.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/i18n.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/lang_en_US.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/lang_zh_TW.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/lib_util.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/log.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/screens.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/service.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/dependency_links.txt +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/requires.txt +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/top_level.txt +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/README.md +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/pyproject.toml +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/setup.cfg +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/setup.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_bucket.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_change_pw.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_comment.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_del_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_del_post_unit.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_exceptions.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_fast_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_board_info.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_board_list.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_bottom_post_list.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_content_formula.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_favourite_boards.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_mail.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_newest_index.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post_list.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post_list_parser.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post_parser.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_time.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_user.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_waterball.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_give_money.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_i18n.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_init.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_logger.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_login_logout.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_mail_send_and_del.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_mark_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_parse_query_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_pyte_parity.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_reply_post.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_search_user.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_service.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_set_board_title.py +0 -0
- {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_vt100_parser.py +0 -0
|
@@ -436,7 +436,8 @@ class API:
|
|
|
436
436
|
return _api_get_newest_index.get_newest_index(
|
|
437
437
|
self, index_type, board, search_type, search_condition, search_list)
|
|
438
438
|
|
|
439
|
-
def post(self, board: str, title_index: int, title: str, content: str, sign_file: [str | int] = 0
|
|
439
|
+
def post(self, board: str, title_index: int, title: str, content: str, sign_file: [str | int] = 0,
|
|
440
|
+
anonymous: bool = False, display_id: Optional[str] = None) -> None:
|
|
440
441
|
"""
|
|
441
442
|
發文。
|
|
442
443
|
|
|
@@ -446,6 +447,12 @@ class API:
|
|
|
446
447
|
title (str): 文章標題。
|
|
447
448
|
content (str): 文章內容。
|
|
448
449
|
sign_file (str | int): 編號或隨機簽名檔 (x),預設為 0 (不選)。
|
|
450
|
+
anonymous (bool): 是否匿名發文,預設為 False。僅在該看板本身具有匿名 (BRD_ANONYMOUS)
|
|
451
|
+
屬性時才會生效,一般看板無法透過此參數強制匿名;PyPtt 只能在該提示出現時回覆它。
|
|
452
|
+
display_id (str): 匿名發文時欲顯示的自訂名稱;預設為 None,代表使用 pttbbs 預設值
|
|
453
|
+
(預設匿名板為 "Anonymous.",否則為原 ID)。pttbbs 會在暱名 (含預設的 "Anonymous")
|
|
454
|
+
後方加上一個 "."(例如 display_id='Fox' 會顯示為 "Fox."),此為 pttbbs 固定行為,
|
|
455
|
+
無法調整。僅能在 anonymous=True 時使用。
|
|
449
456
|
|
|
450
457
|
Returns:
|
|
451
458
|
None
|
|
@@ -455,6 +462,7 @@ class API:
|
|
|
455
462
|
RequireLogin: 需要登入。
|
|
456
463
|
NoSuchBoard: 看板不存在。
|
|
457
464
|
NoPermission: 沒有發佈權限。
|
|
465
|
+
ParameterError: display_id 於 anonymous=False 時提供。
|
|
458
466
|
|
|
459
467
|
範例::
|
|
460
468
|
|
|
@@ -464,13 +472,19 @@ class API:
|
|
|
464
472
|
try:
|
|
465
473
|
# .. login ..
|
|
466
474
|
ptt_bot.post(board='Test', title_index=1, title='PyPtt 程式貼文測試', content='測試內容', sign_file=0)
|
|
475
|
+
# 匿名發文 (需看板本身具有匿名屬性)
|
|
476
|
+
ptt_bot.post(board='AnonBoard', title_index=1, title='匿名貼文測試', content='測試內容',
|
|
477
|
+
sign_file=0, anonymous=True)
|
|
478
|
+
# 匿名發文並自訂顯示名稱
|
|
479
|
+
ptt_bot.post(board='AnonBoard', title_index=1, title='匿名貼文測試', content='測試內容',
|
|
480
|
+
sign_file=0, anonymous=True, display_id='Fox')
|
|
467
481
|
# .. do something ..
|
|
468
482
|
finally:
|
|
469
483
|
ptt_bot.logout()
|
|
470
484
|
|
|
471
485
|
"""
|
|
472
486
|
|
|
473
|
-
_api_post.post(self, board, title, content, title_index, sign_file)
|
|
487
|
+
_api_post.post(self, board, title, content, title_index, sign_file, anonymous, display_id)
|
|
474
488
|
|
|
475
489
|
def comment(self, board: str, comment_type: data_type.CommentType, content: str, aid: Optional[str] = None,
|
|
476
490
|
index: int = 0) -> None:
|
|
@@ -92,7 +92,49 @@ sign_file_list = [str(x) for x in range(0, 10)]
|
|
|
92
92
|
sign_file_list.append('x')
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
# --- post()'s `anonymous` / `display_id` params (issue #84) ---
|
|
96
|
+
#
|
|
97
|
+
# pttbbs only shows the "請輸入你想用的ID" / "確定[y/N]?" prompt pair when the
|
|
98
|
+
# *board itself* carries BRD_ANONYMOUS server-side (mbbsd/edit.c write_header,
|
|
99
|
+
# `if (currbrdattr & BRD_ANONYMOUS)`). On a normal board the two TargetUnits
|
|
100
|
+
# below simply never match anything on screen -- there is no way for PyPtt to
|
|
101
|
+
# force anonymity on a board that isn't flagged anonymous. See
|
|
102
|
+
# scripts/mkboard.py / scripts/bootstrap_local_pttbbs.py's AnonTest board for
|
|
103
|
+
# how to provision one locally.
|
|
104
|
+
#
|
|
105
|
+
# Response mapping, matching pttbbs' own rules for that prompt:
|
|
106
|
+
# anonymous=False -> 'r' (real userid; safe default, and a
|
|
107
|
+
# no-op on non-anonymous boards)
|
|
108
|
+
# anonymous=True, no display_id -> '' (Enter; "Anonymous." -- pttbbs
|
|
109
|
+
# appends the same trailing '.' as the
|
|
110
|
+
# display_id case below -- on a
|
|
111
|
+
# default-anonymous board, real userid
|
|
112
|
+
# otherwise -- pttbbs' own behavior)
|
|
113
|
+
# anonymous=True, display_id -> that string; pttbbs appends a trailing
|
|
114
|
+
# '.' to it (e.g. display_id='Fox' shows
|
|
115
|
+
# up as author 'Fox.') -- fixed pttbbs
|
|
116
|
+
# behavior, not adjustable by PyPtt.
|
|
117
|
+
def _anonymous_response(anonymous: bool, display_id: [str | None]) -> str:
|
|
118
|
+
if display_id is not None:
|
|
119
|
+
check_value.check_type(display_id, str, 'display_id')
|
|
120
|
+
if not anonymous:
|
|
121
|
+
raise exceptions.ParameterError('display_id requires anonymous=True')
|
|
122
|
+
# pttbbs (mbbsd/edit.c) treats a leading '-' as "this is a deleted
|
|
123
|
+
# name" and re-shows the same prompt instead of accepting it, which
|
|
124
|
+
# would make PyPtt's TargetUnit answer it the same way forever.
|
|
125
|
+
if display_id.startswith('-'):
|
|
126
|
+
raise exceptions.ParameterError("display_id must not start with '-'")
|
|
127
|
+
for c in display_id:
|
|
128
|
+
if ord(c) < 0x20 or ord(c) == 0x7f:
|
|
129
|
+
raise exceptions.ParameterError(
|
|
130
|
+
f'display_id must not contain control characters (found {c!r})')
|
|
131
|
+
return display_id
|
|
132
|
+
|
|
133
|
+
return '' if anonymous else 'r'
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def post(api, board: str, title: str, content: str, title_index: int, sign_file: [str | int],
|
|
137
|
+
anonymous: bool = False, display_id: [str | None] = None) -> None:
|
|
96
138
|
_api_util.one_thread(api)
|
|
97
139
|
|
|
98
140
|
if not api._is_login:
|
|
@@ -105,10 +147,13 @@ def post(api, board: str, title: str, content: str, title_index: int, sign_file:
|
|
|
105
147
|
check_value.check_type(title_index, int, 'title_index')
|
|
106
148
|
check_value.check_type(title, str, 'title')
|
|
107
149
|
check_value.check_type(content, str, 'content')
|
|
150
|
+
check_value.check_type(anonymous, bool, 'anonymous')
|
|
108
151
|
|
|
109
152
|
if str(sign_file).lower() not in sign_file_list:
|
|
110
153
|
raise exceptions.ParameterError(f'wrong parameter sign_file: {sign_file}')
|
|
111
154
|
|
|
155
|
+
anonymous_response = _anonymous_response(anonymous, display_id)
|
|
156
|
+
|
|
112
157
|
_api_util.check_board(api, board)
|
|
113
158
|
_api_util.goto_board(api, board)
|
|
114
159
|
|
|
@@ -147,7 +192,17 @@ def post(api, board: str, title: str, content: str, title_index: int, sign_file:
|
|
|
147
192
|
|
|
148
193
|
target_list = [
|
|
149
194
|
connect_core.TargetUnit('任意鍵繼續', break_detect=True),
|
|
150
|
-
|
|
195
|
+
# max_match=1: pttbbs' "檔案處理" screen keeps every answered prompt's
|
|
196
|
+
# echoed text on screen as the flow proceeds through the next ones
|
|
197
|
+
# (confirmed live -- "確定要儲存檔案嗎? s" stays visible through the
|
|
198
|
+
# anonymous-post prompts below). Without max_match=1 this target
|
|
199
|
+
# re-matches on every later screen in the same flow and re-sends
|
|
200
|
+
# 's' + Enter into whatever field currently has focus (e.g. the
|
|
201
|
+
# anonymous-ID or confirm prompt), corrupting the answer.
|
|
202
|
+
connect_core.TargetUnit('確定要儲存檔案嗎', response='s' + command.enter, max_match=1),
|
|
203
|
+
# Only shown on a BRD_ANONYMOUS board -- no-op on a normal board.
|
|
204
|
+
connect_core.TargetUnit('請輸入你想用的ID', response=anonymous_response + command.enter, max_match=1),
|
|
205
|
+
connect_core.TargetUnit('確定[y/N]', response='y' + command.enter, max_match=1),
|
|
151
206
|
connect_core.TargetUnit('x=隨機', response=str(sign_file) + command.enter),
|
|
152
207
|
]
|
|
153
208
|
api.connect_core.send(
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
key = """-----BEGIN EC PARAMETERS-----
|
|
2
|
+
BggqhkjOPQMBBw==
|
|
3
|
+
-----END EC PARAMETERS-----
|
|
4
|
+
-----BEGIN EC PRIVATE KEY-----
|
|
5
|
+
MHcCAQEEIK/vT3CLlx1CdJ6OlvrB70J+1+QzpKnb0DGYe9u1cWaboAoGCCqGSM49
|
|
6
|
+
AwEHoUQDQgAEhzeNEtPu4xVRU7aGhl59oKVJZsZue2B/eEQkmme2gsMzzS6vGJKj
|
|
7
|
+
6mzkEmDNZv/DLFxccYesWd0comFxe/xOyA==
|
|
8
|
+
-----END EC PRIVATE KEY-----
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
cert = """-----BEGIN CERTIFICATE-----
|
|
12
|
+
MIIB5zCCAY8CFGe33bG5Ilg7iZcbrVQyTUOOD9uiMAoGCCqGSM49BAMCMHcxCzAJ
|
|
13
|
+
BgNVBAYTAlRXMQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UE
|
|
14
|
+
CgwMT3JnYW5pemF0aW9uMRwwGgYDVQQLDBNPcmdhbml6YXRpb25hbCBVbml0MRQw
|
|
15
|
+
EgYDVQQDDAtDb21tb24gTmFtZTAeFw0yNjA3MTkxMjI2NDVaFw0zNjA3MTYxMjI2
|
|
16
|
+
NDVaMHcxCzAJBgNVBAYTAlRXMQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0
|
|
17
|
+
eTEVMBMGA1UECgwMT3JnYW5pemF0aW9uMRwwGgYDVQQLDBNPcmdhbml6YXRpb25h
|
|
18
|
+
bCBVbml0MRQwEgYDVQQDDAtDb21tb24gTmFtZTBZMBMGByqGSM49AgEGCCqGSM49
|
|
19
|
+
AwEHA0IABIc3jRLT7uMVUVO2hoZefaClSWbGbntgf3hEJJpntoLDM80urxiSo+ps
|
|
20
|
+
5BJgzWb/wyxcXHGHrFndHKJhcXv8TsgwCgYIKoZIzj0EAwIDRgAwQwIfcZoiJkfz
|
|
21
|
+
rUDQbdsoh6/7UsBnxSB/01+u6KIfZgepvwIgPT5JXHcIMPgo/dt5ph75qYq78GAN
|
|
22
|
+
zVh3EBftVZSO8po=
|
|
23
|
+
-----END CERTIFICATE-----
|
|
24
|
+
"""
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Tests PyPtt issue #84 (匿名發文): post()'s anonymous/display_id params
|
|
2
|
+
against a board that actually carries pttbbs' BRD_ANONYMOUS/BRD_DEFAULTANONYMOUS
|
|
3
|
+
flags. Only pttbbs boards flagged this way ever show the anonymous prompt --
|
|
4
|
+
PyPtt cannot force anonymity on a normal board (see PyPtt/_api_post.py). Only
|
|
5
|
+
scripts/bootstrap_local_pttbbs.py's local container provisions such a board
|
|
6
|
+
(AnonTest), so this file is LOCALHOST-only.
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
import time
|
|
10
|
+
import uuid
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
import PyPtt
|
|
15
|
+
from PyPtt import PostField, NewIndex
|
|
16
|
+
|
|
17
|
+
if os.environ.get('PTT_HOST') != 'LOCALHOST':
|
|
18
|
+
pytest.skip(
|
|
19
|
+
'test_post_anonymous.py needs the AnonTest board, which only '
|
|
20
|
+
'scripts/bootstrap_local_pttbbs.py provisions. Set PTT_HOST=LOCALHOST to run.',
|
|
21
|
+
allow_module_level=True)
|
|
22
|
+
|
|
23
|
+
ANON_BOARD = 'AnonTest'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _find_own_post(ptt_bot, title_marker):
|
|
27
|
+
"""Scan back from the newest post on ANON_BOARD for the one whose title
|
|
28
|
+
contains title_marker, and return its parsed post dict."""
|
|
29
|
+
newest_index = ptt_bot.get_newest_index(index_type=NewIndex.BOARD, board=ANON_BOARD)
|
|
30
|
+
for i in range(10):
|
|
31
|
+
index = newest_index - i
|
|
32
|
+
if index <= 0:
|
|
33
|
+
break
|
|
34
|
+
post = ptt_bot.get_post(board=ANON_BOARD, index=index)
|
|
35
|
+
if title_marker in post[PostField.title]:
|
|
36
|
+
return post
|
|
37
|
+
raise AssertionError(f'could not find a post titled with marker {title_marker!r}')
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_post_anonymous(ptt_bots):
|
|
41
|
+
"""anonymous=True, display_id=None -> pttbbs masks the author as
|
|
42
|
+
'Anonymous.' (AnonTest is bootstrapped BRD_DEFAULTANONYMOUS, so an empty
|
|
43
|
+
response to the anon prompt means 'Anonymous', not the real id; pttbbs
|
|
44
|
+
appends a trailing '.' the same way it does for a custom display_id)."""
|
|
45
|
+
ptt_bot = ptt_bots[0]
|
|
46
|
+
marker = uuid.uuid4().hex[:12]
|
|
47
|
+
title = f'PyPtt anon test {marker}'
|
|
48
|
+
|
|
49
|
+
ptt_bot.post(board=ANON_BOARD, title_index=1, title=title,
|
|
50
|
+
content='PyPtt anonymous post test content.\n', sign_file=0,
|
|
51
|
+
anonymous=True)
|
|
52
|
+
|
|
53
|
+
time.sleep(1)
|
|
54
|
+
post = _find_own_post(ptt_bot, marker)
|
|
55
|
+
|
|
56
|
+
author = post[PostField.author]
|
|
57
|
+
assert author.split(' ')[0] == 'Anonymous.', f'expected masked author "Anonymous.", got {author!r}'
|
|
58
|
+
assert ptt_bot.ptt_id not in author, f'real ptt_id leaked into author field: {author!r}'
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_post_anonymous_with_display_id(ptt_bots):
|
|
62
|
+
"""anonymous=True, display_id='PyPttFox' -> pttbbs masks the author as
|
|
63
|
+
the custom name plus a trailing '.' (pttbbs behavior, not adjustable)."""
|
|
64
|
+
ptt_bot = ptt_bots[0]
|
|
65
|
+
marker = uuid.uuid4().hex[:12]
|
|
66
|
+
title = f'PyPtt anon custom id test {marker}'
|
|
67
|
+
display_id = 'PyPttFox'
|
|
68
|
+
|
|
69
|
+
ptt_bot.post(board=ANON_BOARD, title_index=1, title=title,
|
|
70
|
+
content='PyPtt anonymous post (custom display id) test content.\n', sign_file=0,
|
|
71
|
+
anonymous=True, display_id=display_id)
|
|
72
|
+
|
|
73
|
+
time.sleep(1)
|
|
74
|
+
post = _find_own_post(ptt_bot, marker)
|
|
75
|
+
|
|
76
|
+
author = post[PostField.author]
|
|
77
|
+
assert author.split(' ')[0] == f'{display_id}.', f'expected masked author "{display_id}.", got {author!r}'
|
|
78
|
+
assert ptt_bot.ptt_id not in author, f'real ptt_id leaked into author field: {author!r}'
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_post_not_anonymous_shows_real_id(ptt_bots):
|
|
82
|
+
"""anonymous=False (default) -> even on an anonymous-capable board, the
|
|
83
|
+
real ptt_id is used (safe default, backward compatible with pre-#84
|
|
84
|
+
callers of post())."""
|
|
85
|
+
ptt_bot = ptt_bots[0]
|
|
86
|
+
marker = uuid.uuid4().hex[:12]
|
|
87
|
+
title = f'PyPtt non-anon test {marker}'
|
|
88
|
+
|
|
89
|
+
ptt_bot.post(board=ANON_BOARD, title_index=1, title=title,
|
|
90
|
+
content='PyPtt non-anonymous post test content.\n', sign_file=0)
|
|
91
|
+
|
|
92
|
+
time.sleep(1)
|
|
93
|
+
post = _find_own_post(ptt_bot, marker)
|
|
94
|
+
|
|
95
|
+
author = post[PostField.author]
|
|
96
|
+
assert author.split(' ')[0] == ptt_bot.ptt_id, f'expected real author {ptt_bot.ptt_id!r}, got {author!r}'
|
pyptt-2.1.7/PyPtt/ssl_config.py
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
key = """-----BEGIN EC PARAMETERS-----
|
|
2
|
-
BggqhkjOPQMBBw==
|
|
3
|
-
-----END EC PARAMETERS-----
|
|
4
|
-
-----BEGIN EC PRIVATE KEY-----
|
|
5
|
-
MHcCAQEEIBl6n84gGhyoI6rh8jsDM6+MvbtX++1SctY8mkfAU/PqoAoGCCqGSM49
|
|
6
|
-
AwEHoUQDQgAEKrKMWokWd4Y6dFHFFr4pg9h340NKkekdCrYbxv1l2SUBUDkXUx28
|
|
7
|
-
fGqtXxYCuOsEQTeQrm0OmCq6U+aRXMvwaQ==
|
|
8
|
-
-----END EC PRIVATE KEY-----
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
cert = """-----BEGIN CERTIFICATE-----
|
|
12
|
-
MIIB6DCCAY8CFEXMlgDToWo6s00YgTOitW0BgNCdMAoGCCqGSM49BAMCMHcxCzAJ
|
|
13
|
-
BgNVBAYTAlRXMQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UE
|
|
14
|
-
CgwMT3JnYW5pemF0aW9uMRwwGgYDVQQLDBNPcmdhbml6YXRpb25hbCBVbml0MRQw
|
|
15
|
-
EgYDVQQDDAtDb21tb24gTmFtZTAeFw0yNjA3MTgwNDE0NTZaFw0zNjA3MTUwNDE0
|
|
16
|
-
NTZaMHcxCzAJBgNVBAYTAlRXMQ4wDAYDVQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0
|
|
17
|
-
eTEVMBMGA1UECgwMT3JnYW5pemF0aW9uMRwwGgYDVQQLDBNPcmdhbml6YXRpb25h
|
|
18
|
-
bCBVbml0MRQwEgYDVQQDDAtDb21tb24gTmFtZTBZMBMGByqGSM49AgEGCCqGSM49
|
|
19
|
-
AwEHA0IABCqyjFqJFneGOnRRxRa+KYPYd+NDSpHpHQq2G8b9ZdklAVA5F1MdvHxq
|
|
20
|
-
rV8WArjrBEE3kK5tDpgqulPmkVzL8GkwCgYIKoZIzj0EAwIDRwAwRAIgcfQdHGfL
|
|
21
|
-
m1j+zvm23ToVfbfGdlvcR4hXBQkWMQslAkICIEccC2jqieHYgaLRUgzoUVI/twm3
|
|
22
|
-
5uAjX0ZWPxTPuNvA
|
|
23
|
-
-----END CERTIFICATE-----
|
|
24
|
-
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|