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.
Files changed (92) hide show
  1. {pyptt-2.1.7 → pyptt-2.2.0}/PKG-INFO +1 -1
  2. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/PTT.py +16 -2
  3. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/__init__.py +1 -1
  4. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_post.py +57 -2
  5. pyptt-2.2.0/PyPtt/ssl_config.py +24 -0
  6. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/PKG-INFO +1 -1
  7. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/SOURCES.txt +1 -0
  8. pyptt-2.2.0/tests/test_post_anonymous.py +96 -0
  9. pyptt-2.1.7/PyPtt/ssl_config.py +0 -24
  10. {pyptt-2.1.7 → pyptt-2.2.0}/LICENSE +0 -0
  11. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_bucket.py +0 -0
  12. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_call_status.py +0 -0
  13. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_change_pw.py +0 -0
  14. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_comment.py +0 -0
  15. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_del_post.py +0 -0
  16. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_board_info.py +0 -0
  17. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_board_list.py +0 -0
  18. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_bottom_post_list.py +0 -0
  19. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_favourite_board.py +0 -0
  20. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_newest_index.py +0 -0
  21. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_post.py +0 -0
  22. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_post_index.py +0 -0
  23. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_post_list.py +0 -0
  24. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_time.py +0 -0
  25. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_user.py +0 -0
  26. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_get_waterball.py +0 -0
  27. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_give_money.py +0 -0
  28. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_has_new_mail.py +0 -0
  29. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_loginout.py +0 -0
  30. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_mail.py +0 -0
  31. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_mark_post.py +0 -0
  32. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_reply_post.py +0 -0
  33. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_search_user.py +0 -0
  34. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_set_board_title.py +0 -0
  35. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/_api_util.py +0 -0
  36. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/api_server.py +0 -0
  37. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/check_value.py +0 -0
  38. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/command.py +0 -0
  39. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/config.py +0 -0
  40. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/connect_core.py +0 -0
  41. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/data_type.py +0 -0
  42. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/exceptions.py +0 -0
  43. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/i18n.py +0 -0
  44. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/lang_en_US.py +0 -0
  45. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/lang_zh_TW.py +0 -0
  46. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/lib_util.py +0 -0
  47. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/log.py +0 -0
  48. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/screens.py +0 -0
  49. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt/service.py +0 -0
  50. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/dependency_links.txt +0 -0
  51. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/requires.txt +0 -0
  52. {pyptt-2.1.7 → pyptt-2.2.0}/PyPtt.egg-info/top_level.txt +0 -0
  53. {pyptt-2.1.7 → pyptt-2.2.0}/README.md +0 -0
  54. {pyptt-2.1.7 → pyptt-2.2.0}/pyproject.toml +0 -0
  55. {pyptt-2.1.7 → pyptt-2.2.0}/setup.cfg +0 -0
  56. {pyptt-2.1.7 → pyptt-2.2.0}/setup.py +0 -0
  57. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_bucket.py +0 -0
  58. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_change_pw.py +0 -0
  59. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_comment.py +0 -0
  60. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_del_post.py +0 -0
  61. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_del_post_unit.py +0 -0
  62. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_exceptions.py +0 -0
  63. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_fast_post.py +0 -0
  64. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_board_info.py +0 -0
  65. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_board_list.py +0 -0
  66. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_bottom_post_list.py +0 -0
  67. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_content_formula.py +0 -0
  68. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_favourite_boards.py +0 -0
  69. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_mail.py +0 -0
  70. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_newest_index.py +0 -0
  71. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post.py +0 -0
  72. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post_list.py +0 -0
  73. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post_list_parser.py +0 -0
  74. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_post_parser.py +0 -0
  75. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_time.py +0 -0
  76. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_user.py +0 -0
  77. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_get_waterball.py +0 -0
  78. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_give_money.py +0 -0
  79. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_i18n.py +0 -0
  80. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_init.py +0 -0
  81. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_logger.py +0 -0
  82. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_login_logout.py +0 -0
  83. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_mail_send_and_del.py +0 -0
  84. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_mark_post.py +0 -0
  85. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_parse_query_post.py +0 -0
  86. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_post.py +0 -0
  87. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_pyte_parity.py +0 -0
  88. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_reply_post.py +0 -0
  89. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_search_user.py +0 -0
  90. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_service.py +0 -0
  91. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_set_board_title.py +0 -0
  92. {pyptt-2.1.7 → pyptt-2.2.0}/tests/test_vt100_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPtt
3
- Version: 2.1.7
3
+ Version: 2.2.0
4
4
  Summary: PyPtt github: https://github.com/PyPtt/PyPtt
5
5
  Author-email: CodingMan <pttcodingman@gmail.com>
6
6
  License: GNU Lesser General Public License v3 (LGPLv3)
@@ -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) -> None:
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:
@@ -1,4 +1,4 @@
1
- __version__ = '2.1.7'
1
+ __version__ = '2.2.0'
2
2
  __author__ = 'CodingMan'
3
3
  __email__ = 'pttcodingman@gmail.com'
4
4
 
@@ -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
- def post(api, board: str, title: str, content: str, title_index: int, sign_file: [str | int]) -> None:
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
- connect_core.TargetUnit('確定要儲存檔案嗎', response='s' + command.enter),
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
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPtt
3
- Version: 2.1.7
3
+ Version: 2.2.0
4
4
  Summary: PyPtt github: https://github.com/PyPtt/PyPtt
5
5
  Author-email: CodingMan <pttcodingman@gmail.com>
6
6
  License: GNU Lesser General Public License v3 (LGPLv3)
@@ -80,6 +80,7 @@ tests/test_mail_send_and_del.py
80
80
  tests/test_mark_post.py
81
81
  tests/test_parse_query_post.py
82
82
  tests/test_post.py
83
+ tests/test_post_anonymous.py
83
84
  tests/test_pyte_parity.py
84
85
  tests/test_reply_post.py
85
86
  tests/test_search_user.py
@@ -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}'
@@ -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