PyPtt 2.3.6__tar.gz → 2.3.8__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.3.6 → pyptt-2.3.8}/PKG-INFO +1 -1
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/__init__.py +1 -1
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_call_status.py +24 -23
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_time.py +2 -1
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/screens.py +8 -3
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt.egg-info/PKG-INFO +1 -1
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt.egg-info/SOURCES.txt +1 -0
- pyptt-2.3.8/tests/test_status_line.py +41 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_vt100_parser.py +6 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/LICENSE +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/PTT.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_bucket.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_change_pw.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_comment.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_del_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_board_info.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_board_list.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_bottom_post_list.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_favourite_board.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_newest_index.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_post_index.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_post_list.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_user.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_get_waterball.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_give_money.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_has_new_mail.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_loginout.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_mail.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_mark_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_reply_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_search_user.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_set_board_title.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_set_signature_file.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_api_util.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_uao/LICENSE +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_uao/__init__.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_uao/b2u.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/_uao/u2b.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/api_server.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/check_value.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/command.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/config.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/connect_core.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/data_type.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/exceptions.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/i18n.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/lang_en_US.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/lang_zh_TW.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/lib_util.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/log.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/service.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt/ssl_config.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt.egg-info/dependency_links.txt +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt.egg-info/requires.txt +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/PyPtt.egg-info/top_level.txt +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/README.md +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/pyproject.toml +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/setup.cfg +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/setup.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_backup_effective.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_backup_param.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_bucket.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_change_pw.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_comment.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_connect_core_timeout.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_del_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_del_post_unit.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_exceptions.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_fast_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_board_info.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_board_list.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_bottom_post_list.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_content_formula.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_favourite_boards.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_mail.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_newest_index.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_post_list.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_post_list_parser.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_post_parser.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_time.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_user.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_get_waterball.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_give_money.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_half_dead_connection.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_i18n.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_incremental_parity.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_init.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_logger.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_login_logout.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_mail_send_and_del.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_mark_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_parse_query_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_post_anonymous.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_pyte_parity.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_reply_post.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_search_user.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_service.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_set_board_title.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_set_signature_file.py +0 -0
- {pyptt-2.3.6 → pyptt-2.3.8}/tests/test_uao_codec.py +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
1
3
|
from . import command
|
|
2
4
|
from . import connect_core
|
|
3
5
|
from . import data_type
|
|
@@ -6,6 +8,18 @@ from . import log
|
|
|
6
8
|
from . import screens
|
|
7
9
|
|
|
8
10
|
|
|
11
|
+
# 狀態列有 '[呼叫器]打開' 與 '呼叫器關閉' 兩種寫法, 中括號不保證存在。
|
|
12
|
+
_call_status_pattern = re.compile(r'呼叫器[\]\s]*(打開|拔掉|防水|好友|關閉)')
|
|
13
|
+
|
|
14
|
+
_call_status_map = {
|
|
15
|
+
'打開': data_type.CallStatus.ON,
|
|
16
|
+
'拔掉': data_type.CallStatus.UNPLUG,
|
|
17
|
+
'防水': data_type.CallStatus.WATERPROOF,
|
|
18
|
+
'好友': data_type.CallStatus.FRIEND,
|
|
19
|
+
'關閉': data_type.CallStatus.OFF,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
9
23
|
def get_call_status(api) -> data_type.CallStatus:
|
|
10
24
|
|
|
11
25
|
cmd_list = []
|
|
@@ -17,34 +31,21 @@ def get_call_status(api) -> data_type.CallStatus:
|
|
|
17
31
|
cmd = ''.join(cmd_list)
|
|
18
32
|
|
|
19
33
|
target_list = [
|
|
20
|
-
connect_core.TargetUnit(
|
|
21
|
-
connect_core.TargetUnit('[呼叫器]拔掉', log_level=log.DEBUG, break_detect=True),
|
|
22
|
-
connect_core.TargetUnit('[呼叫器]防水', log_level=log.DEBUG, break_detect=True),
|
|
23
|
-
connect_core.TargetUnit('[呼叫器]好友', log_level=log.DEBUG, break_detect=True),
|
|
24
|
-
connect_core.TargetUnit('[呼叫器]關閉', log_level=log.DEBUG, break_detect=True),
|
|
34
|
+
connect_core.TargetUnit(screens.Target.MainMenu, log_level=log.DEBUG, break_detect=True),
|
|
25
35
|
connect_core.TargetUnit('★', log_level=log.DEBUG, response=cmd),
|
|
26
36
|
]
|
|
27
37
|
|
|
28
|
-
for
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
raise exceptions.UnknownError('UnknownError')
|
|
34
|
-
|
|
35
|
-
if index == 0:
|
|
36
|
-
return data_type.CallStatus.ON
|
|
37
|
-
if index == 1:
|
|
38
|
-
return data_type.CallStatus.UNPLUG
|
|
39
|
-
if index == 2:
|
|
40
|
-
return data_type.CallStatus.WATERPROOF
|
|
41
|
-
if index == 3:
|
|
42
|
-
return data_type.CallStatus.FRIEND
|
|
43
|
-
if index == 4:
|
|
44
|
-
return data_type.CallStatus.OFF
|
|
38
|
+
for _ in range(2):
|
|
39
|
+
if api.connect_core.send(cmd, target_list) == 0:
|
|
40
|
+
break
|
|
41
|
+
else:
|
|
42
|
+
raise exceptions.UnknownError('UnknownError')
|
|
45
43
|
|
|
46
44
|
ori_screen = api.connect_core.get_screen_queue()[-1]
|
|
47
|
-
|
|
45
|
+
result = _call_status_pattern.search(ori_screen)
|
|
46
|
+
if result is None:
|
|
47
|
+
raise exceptions.UnknownError(ori_screen)
|
|
48
|
+
return _call_status_map[result.group(1)]
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
def set_call_status(api, call_status) -> None:
|
|
@@ -38,7 +38,8 @@ def get_time(api) -> str:
|
|
|
38
38
|
# 0:00
|
|
39
39
|
|
|
40
40
|
for line in line_list:
|
|
41
|
-
|
|
41
|
+
# 短格式的狀態列寫 '週三' 不是 '星期六', 所以不能比對 '星期'。
|
|
42
|
+
if '線上' in line and '我是' in line:
|
|
42
43
|
result = pattern.search(line)
|
|
43
44
|
if result is not None:
|
|
44
45
|
return result.group(0)
|
|
@@ -63,10 +63,14 @@ def _str_pos_at_cells(line: str, cells: int) -> int:
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
class Target:
|
|
66
|
+
# 正式站狀態列有兩種寫法, 短的那種會把逗號後的空格與中括號吃掉:
|
|
67
|
+
# [5/23 星期六 16:40] [ 射手時 ] 線上27866人, 我是CodingMan [呼叫器]打開
|
|
68
|
+
# 8/19週三22:35 [ 七夕 ] 線上30721人,我是DeepLearning 呼叫器關閉 (h)說明
|
|
69
|
+
# 只比對兩種格式共同的最短關鍵詞。
|
|
66
70
|
MainMenu = [
|
|
67
71
|
'離開,再見',
|
|
68
|
-
'
|
|
69
|
-
'
|
|
72
|
+
'我是',
|
|
73
|
+
'呼叫器',
|
|
70
74
|
]
|
|
71
75
|
|
|
72
76
|
MainMenu_Exiting = [
|
|
@@ -263,7 +267,8 @@ def show(config, screen_queue, function_name=None):
|
|
|
263
267
|
|
|
264
268
|
xy_pattern_h = re.compile(r'^=ESC=\[[\d]+;[\d]+H')
|
|
265
269
|
xy_pattern_s = re.compile(r'^=ESC=\[[\d]+;[\d]+s')
|
|
266
|
-
|
|
270
|
+
# SGR 顏色 + DEC 私有模式開關 (ESC[?2026h/l 同步輸出): 後者若留著, 引擎會當未知 escape 而丟掉其後整段畫面
|
|
271
|
+
_color_sgr = re.compile(r'\x1B\[(?:[\d+;]*m|\?[\d;]*[hl])')
|
|
267
272
|
|
|
268
273
|
|
|
269
274
|
def _preprocess(data: str) -> str:
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""PTT 主選單狀態列有長短兩種寫法, 兩種都必須認得。
|
|
2
|
+
|
|
3
|
+
長: [5/23 星期六 16:40] [ 射手時 ] 線上27866人, 我是CodingMan [呼叫器]打開
|
|
4
|
+
短: 8/19週三22:35 [ 七夕 ] 線上30721人,我是DeepLearning 呼叫器關閉 (h)說明
|
|
5
|
+
|
|
6
|
+
短的那種把逗號後的空格與呼叫器的中括號都吃掉了, 日期也從 '星期六' 縮成 '週三'。
|
|
7
|
+
"""
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
from PyPtt import data_type
|
|
11
|
+
from PyPtt._api_call_status import _call_status_map, _call_status_pattern
|
|
12
|
+
from PyPtt._api_get_time import pattern as time_pattern
|
|
13
|
+
from PyPtt.screens import Target
|
|
14
|
+
|
|
15
|
+
LONG = '[5/23 星期六 16:40] [ 射手時 ] 線上27866人, 我是CodingMan [呼叫器]打開 '
|
|
16
|
+
SHORT = '8/19週三22:35 [ 七夕 ] 線上30721人,我是DeepLearning 呼叫器關閉 (h)說明'
|
|
17
|
+
|
|
18
|
+
MENU_BODY = ' (G)oodbye 離開,再見…'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_main_menu_markers_match_both_status_lines():
|
|
22
|
+
for status in (LONG, SHORT):
|
|
23
|
+
screen = MENU_BODY + '\n' + status
|
|
24
|
+
assert all(t in screen for t in Target.MainMenu), status
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_get_time_line_filter_and_pattern():
|
|
28
|
+
for status, expected in ((LONG, '16:40'), (SHORT, '22:35')):
|
|
29
|
+
assert '線上' in status and '我是' in status
|
|
30
|
+
assert time_pattern.search(status).group(0) == expected
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_call_status_pattern():
|
|
34
|
+
assert _call_status_map[_call_status_pattern.search(LONG).group(1)] == data_type.CallStatus.ON
|
|
35
|
+
assert _call_status_map[_call_status_pattern.search(SHORT).group(1)] == data_type.CallStatus.OFF
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_call_status_pattern_covers_every_state():
|
|
39
|
+
for word, expected in _call_status_map.items():
|
|
40
|
+
for form in (f'[呼叫器]{word}', f'呼叫器{word}'):
|
|
41
|
+
assert _call_status_map[_call_status_pattern.search(form).group(1)] == expected
|
|
@@ -445,3 +445,9 @@ class TestRealFixtures:
|
|
|
445
445
|
assert '編號' in lines[2] and '看 板' in lines[2] and '類別' in lines[2]
|
|
446
446
|
# Footer action hints.
|
|
447
447
|
assert '增加看板' in lines[23]
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def test_dec_private_mode_escape_does_not_drop_screen():
|
|
451
|
+
# PTT 送 ESC[?2026h/l (同步輸出); 沒被吃掉時引擎會在此中止, 整個畫面變空白 (登入 LoginError)
|
|
452
|
+
lines = parse(b'\x1b[2J\x1b[?2026h\x1b[3;1Hhello\x1b[?2026l\x1b[4;1Hworld')
|
|
453
|
+
assert 'hello' in lines[2] and 'world' in lines[3]
|
|
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
|
|
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
|