PyPtt 2.3.9__tar.gz → 2.3.10__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 (105) hide show
  1. {pyptt-2.3.9 → pyptt-2.3.10}/PKG-INFO +1 -1
  2. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/__init__.py +1 -1
  3. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_time.py +6 -2
  4. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/screens.py +8 -6
  5. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt.egg-info/PKG-INFO +1 -1
  6. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt.egg-info/SOURCES.txt +1 -0
  7. pyptt-2.3.10/tests/test_main_menu_status_bar.py +24 -0
  8. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_vt100_parser.py +24 -0
  9. {pyptt-2.3.9 → pyptt-2.3.10}/LICENSE +0 -0
  10. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/PTT.py +0 -0
  11. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_bucket.py +0 -0
  12. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_call_status.py +0 -0
  13. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_change_pw.py +0 -0
  14. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_comment.py +0 -0
  15. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_del_post.py +0 -0
  16. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_board_info.py +0 -0
  17. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_board_list.py +0 -0
  18. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_bottom_post_list.py +0 -0
  19. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_favourite_board.py +0 -0
  20. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_newest_index.py +0 -0
  21. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_post.py +0 -0
  22. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_post_index.py +0 -0
  23. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_post_list.py +0 -0
  24. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_user.py +0 -0
  25. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_get_waterball.py +0 -0
  26. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_give_money.py +0 -0
  27. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_has_new_mail.py +0 -0
  28. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_loginout.py +0 -0
  29. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_mail.py +0 -0
  30. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_mark_post.py +0 -0
  31. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_post.py +0 -0
  32. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_reply_post.py +0 -0
  33. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_search_user.py +0 -0
  34. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_set_board_title.py +0 -0
  35. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_set_signature_file.py +0 -0
  36. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_api_util.py +0 -0
  37. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_uao/LICENSE +0 -0
  38. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_uao/__init__.py +0 -0
  39. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_uao/b2u.py +0 -0
  40. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/_uao/u2b.py +0 -0
  41. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/api_server.py +0 -0
  42. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/check_value.py +0 -0
  43. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/command.py +0 -0
  44. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/config.py +0 -0
  45. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/connect_core.py +0 -0
  46. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/data_type.py +0 -0
  47. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/exceptions.py +0 -0
  48. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/i18n.py +0 -0
  49. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/lang_en_US.py +0 -0
  50. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/lang_zh_TW.py +0 -0
  51. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/lib_util.py +0 -0
  52. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/log.py +0 -0
  53. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/service.py +0 -0
  54. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt/ssl_config.py +0 -0
  55. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt.egg-info/dependency_links.txt +0 -0
  56. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt.egg-info/requires.txt +0 -0
  57. {pyptt-2.3.9 → pyptt-2.3.10}/PyPtt.egg-info/top_level.txt +0 -0
  58. {pyptt-2.3.9 → pyptt-2.3.10}/README.md +0 -0
  59. {pyptt-2.3.9 → pyptt-2.3.10}/pyproject.toml +0 -0
  60. {pyptt-2.3.9 → pyptt-2.3.10}/setup.cfg +0 -0
  61. {pyptt-2.3.9 → pyptt-2.3.10}/setup.py +0 -0
  62. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_backup_effective.py +0 -0
  63. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_backup_param.py +0 -0
  64. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_bucket.py +0 -0
  65. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_change_pw.py +0 -0
  66. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_comment.py +0 -0
  67. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_connect_core_timeout.py +0 -0
  68. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_del_post.py +0 -0
  69. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_del_post_unit.py +0 -0
  70. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_exceptions.py +0 -0
  71. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_fast_post.py +0 -0
  72. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_board_info.py +0 -0
  73. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_board_list.py +0 -0
  74. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_bottom_post_list.py +0 -0
  75. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_content_formula.py +0 -0
  76. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_favourite_boards.py +0 -0
  77. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_mail.py +0 -0
  78. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_newest_index.py +0 -0
  79. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_post.py +0 -0
  80. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_post_list.py +0 -0
  81. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_post_list_parser.py +0 -0
  82. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_post_parser.py +0 -0
  83. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_time.py +0 -0
  84. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_user.py +0 -0
  85. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_get_waterball.py +0 -0
  86. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_give_money.py +0 -0
  87. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_half_dead_connection.py +0 -0
  88. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_i18n.py +0 -0
  89. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_incremental_parity.py +0 -0
  90. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_init.py +0 -0
  91. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_logger.py +0 -0
  92. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_login_logout.py +0 -0
  93. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_mail_send_and_del.py +0 -0
  94. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_mark_post.py +0 -0
  95. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_parse_query_post.py +0 -0
  96. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_post.py +0 -0
  97. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_post_anonymous.py +0 -0
  98. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_pyte_parity.py +0 -0
  99. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_reply_post.py +0 -0
  100. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_search_user.py +0 -0
  101. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_service.py +0 -0
  102. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_set_board_title.py +0 -0
  103. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_set_signature_file.py +0 -0
  104. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_status_line.py +0 -0
  105. {pyptt-2.3.9 → pyptt-2.3.10}/tests/test_uao_codec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPtt
3
- Version: 2.3.9
3
+ Version: 2.3.10
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)
@@ -1,4 +1,4 @@
1
- __version__ = '2.3.9'
1
+ __version__ = '2.3.10'
2
2
  __author__ = 'CodingMan'
3
3
  __email__ = 'pttcodingman@gmail.com'
4
4
 
@@ -37,9 +37,13 @@ def get_time(api) -> str:
37
37
 
38
38
  # 0:00
39
39
 
40
+ return _find_time(line_list)
41
+
42
+
43
+ def _find_time(line_list):
40
44
  for line in line_list:
41
- # 短格式的狀態列寫 '週三' 不是 '星期六', 所以不能比對 '星期'。
42
- if '線上' in line and '我是' in line:
45
+ # 狀態列有多種寫法 (週三/星期六, 新版沒有「我是」), 只認「線上」+ 時間。
46
+ if '線上' in line:
43
47
  result = pattern.search(line)
44
48
  if result is not None:
45
49
  return result.group(0)
@@ -66,11 +66,11 @@ class Target:
66
66
  # 正式站狀態列有兩種寫法, 短的那種會把逗號後的空格與中括號吃掉:
67
67
  # [5/23 星期六 16:40] [ 射手時 ] 線上27866人, 我是CodingMan [呼叫器]打開
68
68
  # 8/19週三22:35 [ 七夕 ] 線上30721人,我是DeepLearning 呼叫器關閉 (h)說明
69
- # 只比對兩種格式共同的最短關鍵詞。
69
+ # 新版 (2026-10) 已拿掉「我是」「呼叫器」:
70
+ # 主選單 世界郵政日 10/9 週五 15:04 | janice001 | 線上24305人 (h)說明
71
+ # 只比對三種格式共同的最短關鍵詞。
70
72
  MainMenu = [
71
73
  '離開,再見',
72
- '我是',
73
- '呼叫器',
74
74
  ]
75
75
 
76
76
  MainMenu_Exiting = [
@@ -267,9 +267,9 @@ def show(config, screen_queue, function_name=None):
267
267
 
268
268
  xy_pattern_h = re.compile(r'^=ESC=\[[\d]+;[\d]+H')
269
269
  xy_pattern_s = re.compile(r'^=ESC=\[[\d]+;[\d]+s')
270
- # SGR 顏色 + DEC 私有模式開關 (ESC[?2026h/l 同步輸出) + DSR 查詢 (ESC[6n, PTT 不等回應):
271
- # 後兩者若留著, 引擎會當未知 escape 而丟掉其後整段畫面
272
- _color_sgr = re.compile(r'\x1B\[(?:[\d+;]*m|\?[\d;]*[hl]|\d*n)')
270
+ # 依 ECMA-48 剝掉引擎不處理的 CSI (顏色、ESC[?2026h、ESC[6n…, PttCurrent M.1789835163.A.633),
271
+ # 以及被下一個 ESC 打斷的孤立 ESC (主選單動畫尾端的 '\x1b\x1b[?2026h'); 留著會讓引擎凍住畫面
272
+ _color_sgr = re.compile(r'\x1B(?=\x1B)|\x1B\[(?!(?:\d+;\d+)?[Hs]|K|2J)[0-?]*[ -/]*[@-~]')
273
273
 
274
274
 
275
275
  def _preprocess(data: str) -> str:
@@ -495,6 +495,8 @@ class IncrementalScreen(_VT100Engine):
495
495
  cut -= 1
496
496
  esc = s.rfind('\x1b')
497
497
  if esc != -1 and not _escape_complete(s[esc:]):
498
+ while esc > 0 and s[esc - 1] == '\x1b':
499
+ esc -= 1 # 孤立 ESC 要跟後面的 ESC 一起送, _preprocess 才認得
498
500
  cut = min(cut, esc)
499
501
  return cut
500
502
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyPtt
3
- Version: 2.3.9
3
+ Version: 2.3.10
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)
@@ -87,6 +87,7 @@ tests/test_init.py
87
87
  tests/test_logger.py
88
88
  tests/test_login_logout.py
89
89
  tests/test_mail_send_and_del.py
90
+ tests/test_main_menu_status_bar.py
90
91
  tests/test_mark_post.py
91
92
  tests/test_parse_query_post.py
92
93
  tests/test_post.py
@@ -0,0 +1,24 @@
1
+ import pytest
2
+
3
+ from PyPtt import screens
4
+ from PyPtt._api_get_time import _find_time
5
+
6
+ NEW = '主選單 世界郵政日 10/9 週五 15:04 | janice001 | 線上24305人 (h)說明'
7
+ OLD = '[5/23 星期六 16:40] [ 射手時 ] 線上27866人, 我是CodingMan [呼叫器]打開'
8
+ SHORT = '8/19週三22:35 [ 七夕 ] 線上30721人,我是DeepLearning 呼叫器關閉 (h)說明'
9
+
10
+
11
+ @pytest.mark.parametrize('bar, expected', [(NEW, '15:04'), (OLD, '16:40'), (SHORT, '22:35')])
12
+ def test_status_bar(bar, expected):
13
+ screen = f' (G)離開,再見\n\n{bar}'
14
+ assert all(t in screen for t in screens.Target.MainMenu)
15
+ assert _find_time(screen.split('\n')[-3:]) == expected
16
+
17
+
18
+ def test_find_time_none():
19
+ assert _find_time(['沒有狀態列']) is None
20
+
21
+
22
+ def test_cursor_to_goodbye_matches_main_menu():
23
+ # login 會在 CursorToGoodbye 尾端追加游標項目, 只比對 MainMenu 的前綴
24
+ assert screens.Target.CursorToGoodbye[:len(screens.Target.MainMenu)] == screens.Target.MainMenu
@@ -457,3 +457,27 @@ def test_dsr_query_is_ignored():
457
457
  # PTT 歡迎畫面送 ESC[6n (DSR 游標位置查詢) 且不等回應; 沒被吃掉時畫面凍結在登入提示之前 (LoginError)
458
458
  lines = parse(b'\x1b[2J\x1b[3;1Hhello\x1b[6n\x1b[4;1Hworld')
459
459
  assert 'hello' in lines[2] and 'world' in lines[3]
460
+
461
+
462
+ def test_stray_esc_and_unknown_csi_are_ignored():
463
+ # PTT 主選單動畫尾端送 '\x1b\x1b[?2026h' (孤立 ESC); 未知 CSI 依 ECMA-48 一律略過 (PttCurrent M.1789835163.A.633)
464
+ lines = parse(b'\x1b[30m \x1b\x1b[?2026h\x1b[H\x1b[2J\x1b[3;1Hhello\x1b[0K\x1b[>4;1m\x1b[4;1Hworld')
465
+ assert 'hello' in lines[2] and 'world' in lines[3]
466
+
467
+
468
+ def test_stray_esc_split_across_chunks():
469
+ from PyPtt.screens import IncrementalScreen
470
+ data = b'\x1b[2J\x1b[3;1Hhello\x1b\x1b[?2026h\x1b[4;1Hworld'
471
+ for cut in range(1, len(data)):
472
+ s = IncrementalScreen('utf-8')
473
+ s.feed(data[:cut])
474
+ s.feed(data[cut:])
475
+ lines = s.screen.split('\n')
476
+ assert 'hello' in lines[2] and 'world' in lines[3], cut
477
+ # ESC 後接非 ESC (串流會把尾端空白留到下一輪): 串流與批次須一致
478
+ data = b'a\x1b b'
479
+ for cut in range(1, len(data)):
480
+ s = IncrementalScreen('utf-8')
481
+ s.feed(data[:cut])
482
+ s.feed(data[cut:])
483
+ assert s.screen == VT100Parser(data, 'utf-8').screen, cut
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