PyPtt 2.1.1__tar.gz → 2.1.3__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.1 → pyptt-2.1.3}/PKG-INFO +1 -1
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/PTT.py +9 -2
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/__init__.py +1 -1
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_del_post.py +41 -12
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_board_list.py +4 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_favourite_board.py +1 -0
- pyptt-2.1.3/PyPtt/_api_get_post_list.py +111 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_waterball.py +45 -44
- pyptt-2.1.3/PyPtt/_api_has_new_mail.py +77 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_util.py +167 -136
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/config.py +5 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/connect_core.py +23 -2
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/screens.py +2 -2
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/ssl_config.py +9 -9
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt.egg-info/PKG-INFO +1 -1
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt.egg-info/SOURCES.txt +1 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_del_post.py +62 -0
- pyptt-2.1.3/tests/test_get_content_formula.py +431 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_post_list_parser.py +54 -0
- pyptt-2.1.1/PyPtt/_api_get_post_list.py +0 -104
- pyptt-2.1.1/PyPtt/_api_has_new_mail.py +0 -76
- {pyptt-2.1.1 → pyptt-2.1.3}/LICENSE +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_bucket.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_call_status.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_change_pw.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_comment.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_board_info.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_bottom_post_list.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_newest_index.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_post_index.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_time.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_get_user.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_give_money.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_loginout.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_mail.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_mark_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_reply_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_search_user.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/_api_set_board_title.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/api_server.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/check_value.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/command.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/data_type.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/exceptions.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/i18n.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/lang_en_US.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/lang_zh_TW.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/lib_util.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/log.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt/service.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt.egg-info/dependency_links.txt +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt.egg-info/requires.txt +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/PyPtt.egg-info/top_level.txt +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/README.md +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/pyproject.toml +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/setup.cfg +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/setup.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_bucket.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_change_pw.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_comment.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_exceptions.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_fast_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_board_info.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_board_list.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_bottom_post_list.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_favourite_boards.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_mail.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_newest_index.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_post_list.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_post_parser.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_time.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_user.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_get_waterball.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_give_money.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_i18n.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_init.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_logger.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_mail_send_and_del.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_mark_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_parse_query_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_pyte_parity.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_reply_post.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_search_user.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_service.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_set_board_title.py +0 -0
- {pyptt-2.1.1 → pyptt-2.1.3}/tests/test_vt100_parser.py +0 -0
|
@@ -150,6 +150,11 @@ class API:
|
|
|
150
150
|
check_value.check_type(verify_ssl, bool, 'verify_ssl')
|
|
151
151
|
self.config.verify_ssl = verify_ssl
|
|
152
152
|
|
|
153
|
+
screen_height = kwargs.get('screen_height', 24)
|
|
154
|
+
check_value.check_type(screen_height, int, 'screen_height')
|
|
155
|
+
check_value.check_range(screen_height, 24, 254, 'screen_height')
|
|
156
|
+
self.config.screen_height = screen_height
|
|
157
|
+
|
|
153
158
|
self.connect_core = connect_core.API(self)
|
|
154
159
|
self._exist_board_list = set()
|
|
155
160
|
self._exist_user_list = set()
|
|
@@ -1066,7 +1071,7 @@ class API:
|
|
|
1066
1071
|
|
|
1067
1072
|
return _api_get_bottom_post_list.get_bottom_post_list(self, board)
|
|
1068
1073
|
|
|
1069
|
-
def del_post(self, board: str, aid: Optional[str] = None, index: int = 0) -> None:
|
|
1074
|
+
def del_post(self, board: str, aid: Optional[str] = None, index: int = 0, reason: Optional[str] = None) -> None:
|
|
1070
1075
|
"""
|
|
1071
1076
|
刪除文章。
|
|
1072
1077
|
|
|
@@ -1074,6 +1079,7 @@ class API:
|
|
|
1074
1079
|
board (str): 看板名稱。
|
|
1075
1080
|
aid (str): 文章編號。
|
|
1076
1081
|
index (int): 文章編號。
|
|
1082
|
+
reason (str): 板主刪除他人文章時,加註於刪除後標題的理由(僅板主刪除他板友文章時有效)。
|
|
1077
1083
|
|
|
1078
1084
|
Returns:
|
|
1079
1085
|
None
|
|
@@ -1084,6 +1090,7 @@ class API:
|
|
|
1084
1090
|
NoSuchBoard: 看板不存在。
|
|
1085
1091
|
NoSuchPost: 文章不存在。
|
|
1086
1092
|
NoPermission: 沒有權限。
|
|
1093
|
+
ParameterError: 對自己的文章傳入 reason(加註理由僅適用於版主刪除他人文章)。
|
|
1087
1094
|
|
|
1088
1095
|
範例::
|
|
1089
1096
|
|
|
@@ -1098,7 +1105,7 @@ class API:
|
|
|
1098
1105
|
ptt_bot.logout()
|
|
1099
1106
|
"""
|
|
1100
1107
|
|
|
1101
|
-
_api_del_post.del_post(self, board, aid, index)
|
|
1108
|
+
_api_del_post.del_post(self, board, aid, index, reason)
|
|
1102
1109
|
|
|
1103
1110
|
def get_post_list(self, board: str, limit: int = 20, offset: int = 0) -> list[dict]:
|
|
1104
1111
|
"""
|
|
@@ -14,7 +14,7 @@ from . import log
|
|
|
14
14
|
from . import screens
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def del_post(api, board: str, post_aid: Optional[str] = None, post_index: int = 0) -> None:
|
|
17
|
+
def del_post(api, board: str, post_aid: Optional[str] = None, post_index: int = 0, reason: Optional[str] = None) -> None:
|
|
18
18
|
_api_util.one_thread(api)
|
|
19
19
|
|
|
20
20
|
if not api.is_registered_user:
|
|
@@ -27,6 +27,8 @@ def del_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
27
27
|
if post_aid is not None:
|
|
28
28
|
check_value.check_type(post_aid, str, 'PostAID')
|
|
29
29
|
check_value.check_type(post_index, int, 'PostIndex')
|
|
30
|
+
if reason is not None:
|
|
31
|
+
check_value.check_type(reason, str, 'reason')
|
|
30
32
|
|
|
31
33
|
if len(board) == 0:
|
|
32
34
|
raise exceptions.ParameterError(f'board error parameter: {board}')
|
|
@@ -64,6 +66,13 @@ def del_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
64
66
|
log.logger.info(i18n.delete_post, '...', i18n.success)
|
|
65
67
|
return
|
|
66
68
|
|
|
69
|
+
# PTT only offers the "R加註理由" annotation when a moderator deletes
|
|
70
|
+
# another user's post. On your own post the reason can never be applied,
|
|
71
|
+
# so reject it rather than silently dropping it.
|
|
72
|
+
if reason and api.ptt_id.lower() == post_info[data_type.PostField.author].lower():
|
|
73
|
+
raise exceptions.ParameterError(
|
|
74
|
+
'reason is only valid when a moderator deletes another user\'s post')
|
|
75
|
+
|
|
67
76
|
if check_author:
|
|
68
77
|
if api.ptt_id.lower() != post_info[data_type.PostField.author].lower():
|
|
69
78
|
log.logger.info(i18n.delete_post, '...', i18n.fail)
|
|
@@ -90,20 +99,40 @@ def del_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
90
99
|
def confirm_delete_handler(screen):
|
|
91
100
|
api.confirm = True
|
|
92
101
|
|
|
102
|
+
# A board moderator deleting another user's post gets an extra
|
|
103
|
+
# "R加註理由" option that lets us annotate the title shown after deletion.
|
|
104
|
+
use_reason = bool(reason) and not check_author
|
|
105
|
+
|
|
106
|
+
def confirm_delete_response(screen):
|
|
107
|
+
if use_reason and '加註理由' in screen:
|
|
108
|
+
return 'r' + command.enter
|
|
109
|
+
return 'y' + command.enter
|
|
110
|
+
|
|
93
111
|
target_list = [
|
|
94
112
|
connect_core.TargetUnit('請按任意鍵繼續', response=' '),
|
|
95
|
-
connect_core.TargetUnit('請確定刪除(Y/N
|
|
96
|
-
max_match=1),
|
|
97
|
-
connect_core.TargetUnit(screens.Target.InBoard, break_detect=True),
|
|
113
|
+
connect_core.TargetUnit('請確定刪除(Y/N', response=confirm_delete_response,
|
|
114
|
+
handler=confirm_delete_handler, max_match=1),
|
|
98
115
|
]
|
|
99
116
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
if use_reason:
|
|
118
|
+
target_list.append(
|
|
119
|
+
connect_core.TargetUnit(
|
|
120
|
+
'請輸入刪除後要顯示的標題',
|
|
121
|
+
response=command.ctrl_e + ' ' + reason + command.enter,
|
|
122
|
+
max_match=1))
|
|
123
|
+
target_list.append(
|
|
124
|
+
connect_core.TargetUnit(
|
|
125
|
+
'請再次確定是否要用上述理由刪除(Y/N',
|
|
126
|
+
response='y' + command.enter,
|
|
127
|
+
max_match=1))
|
|
128
|
+
|
|
129
|
+
target_list.append(
|
|
130
|
+
connect_core.TargetUnit(screens.Target.InBoard, break_detect=True))
|
|
131
|
+
|
|
132
|
+
api.connect_core.send(cmd, target_list)
|
|
133
|
+
|
|
134
|
+
if not api.confirm:
|
|
135
|
+
log.logger.info(i18n.delete_post, '...', i18n.fail)
|
|
136
|
+
raise exceptions.NoPermission(i18n.no_permission)
|
|
108
137
|
|
|
109
138
|
log.logger.info(i18n.delete_post, '...', i18n.success)
|
|
@@ -65,6 +65,10 @@ def get_board_list(api) -> list:
|
|
|
65
65
|
cmd = ''.join(cmd_list)
|
|
66
66
|
|
|
67
67
|
board_list = []
|
|
68
|
+
# NOTE: expanded_screen is intentionally NOT used here.
|
|
69
|
+
# get_board_list traverses all boards sequentially; a mid-session NAWS resize
|
|
70
|
+
# causes PTT to render the first page at the old size before the resize takes
|
|
71
|
+
# effect, producing inconsistent page boundaries and skipping boards.
|
|
68
72
|
while True:
|
|
69
73
|
|
|
70
74
|
api.connect_core.send(
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from typing import Dict, List
|
|
2
|
+
|
|
3
|
+
from . import _api_util
|
|
4
|
+
from . import check_value
|
|
5
|
+
from . import command
|
|
6
|
+
from . import connect_core
|
|
7
|
+
from . import data_type
|
|
8
|
+
from . import exceptions
|
|
9
|
+
from . import i18n
|
|
10
|
+
from . import log
|
|
11
|
+
from . import screens
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_post_list(api, board: str, limit: int = 20, offset: int = 0) -> List[Dict]:
|
|
15
|
+
_api_util.one_thread(api)
|
|
16
|
+
|
|
17
|
+
if not api._is_login:
|
|
18
|
+
raise exceptions.RequireLogin(i18n.require_login)
|
|
19
|
+
|
|
20
|
+
check_value.check_type(board, str, 'board')
|
|
21
|
+
if limit is not None:
|
|
22
|
+
check_value.check_type(limit, int, 'limit')
|
|
23
|
+
if offset is not None:
|
|
24
|
+
check_value.check_type(offset, int, 'offset')
|
|
25
|
+
|
|
26
|
+
max_index = api.get_newest_index(data_type.NewIndex.BOARD, board=board)
|
|
27
|
+
|
|
28
|
+
check_value.check_range(limit, min_value=1, max_value=max_index, name='limit')
|
|
29
|
+
check_value.check_range(offset, min_value=0, max_value=max_index - 1, name='offset')
|
|
30
|
+
|
|
31
|
+
start_index = max_index - limit - offset + 1
|
|
32
|
+
end_index = start_index + limit - 1
|
|
33
|
+
|
|
34
|
+
# the first page shows 10 posts, each next page shows 20 posts
|
|
35
|
+
if limit <= 10:
|
|
36
|
+
max_round = 1
|
|
37
|
+
else:
|
|
38
|
+
# 1 is for the first page, +1 is for rounding up
|
|
39
|
+
# so +2
|
|
40
|
+
max_round = (limit - 10) // 20 + 2
|
|
41
|
+
|
|
42
|
+
cmd_list = []
|
|
43
|
+
|
|
44
|
+
cmd_list.append(str(max(1, start_index - 100)))
|
|
45
|
+
cmd_list.append(command.enter)
|
|
46
|
+
cmd_list.append(str(start_index))
|
|
47
|
+
cmd_list.append(command.enter)
|
|
48
|
+
|
|
49
|
+
cmd = ''.join(cmd_list)
|
|
50
|
+
|
|
51
|
+
target_list = [
|
|
52
|
+
connect_core.TargetUnit(screens.Target.InBoard, log_level=log.DEBUG, break_detect=True)
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
post_list = []
|
|
56
|
+
|
|
57
|
+
with _api_util.expanded_screen(api):
|
|
58
|
+
for _ in range(max_round):
|
|
59
|
+
|
|
60
|
+
index = api.connect_core.send(cmd, target_list)
|
|
61
|
+
cmd = command.page_down
|
|
62
|
+
last_screen = api.connect_core.get_screen_queue()[-1]
|
|
63
|
+
|
|
64
|
+
is_parse_area = False
|
|
65
|
+
|
|
66
|
+
for line in last_screen.splitlines()[3:-1]:
|
|
67
|
+
|
|
68
|
+
if not line.startswith(api.cursor) and not is_parse_area:
|
|
69
|
+
continue
|
|
70
|
+
is_parse_area = True
|
|
71
|
+
|
|
72
|
+
if line.strip().startswith(api.cursor):
|
|
73
|
+
# replace the first cursor in the line
|
|
74
|
+
line = line.replace(api.cursor, ' ' * len(api.cursor.encode('big5uao')), 1)
|
|
75
|
+
|
|
76
|
+
if api.cursor == data_type.Cursor.NEW:
|
|
77
|
+
# Pad one leading space so the fixed-column offsets below
|
|
78
|
+
# apply equally to cursor and non-cursor rows. Before the
|
|
79
|
+
# VT100Parser cell-tracking fix, the parser inserted this
|
|
80
|
+
# padding implicitly via its off-by-one column quirk; the
|
|
81
|
+
# offsets here were tuned to that padded layout.
|
|
82
|
+
line = f' {line}'
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
cur_index = int(line[:8].strip())
|
|
86
|
+
except ValueError:
|
|
87
|
+
# Pinned/announcement posts use '★' instead of a numeric
|
|
88
|
+
# index — they have no sequential index and cannot be
|
|
89
|
+
# fetched by index, so skip them.
|
|
90
|
+
continue
|
|
91
|
+
status = line[8:10].strip()
|
|
92
|
+
comment = line[10:12].strip()
|
|
93
|
+
list_date = line[12:17].strip()
|
|
94
|
+
author = line[18:31].strip()
|
|
95
|
+
title = line[31:]
|
|
96
|
+
|
|
97
|
+
cur_post = {
|
|
98
|
+
data_type.PostField.board: board,
|
|
99
|
+
data_type.PostField.index: cur_index,
|
|
100
|
+
data_type.PostField.list_date: list_date,
|
|
101
|
+
data_type.PostField.author: author,
|
|
102
|
+
data_type.PostField.title: title,
|
|
103
|
+
data_type.PostField.push_number: comment
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
post_list.append(cur_post)
|
|
107
|
+
|
|
108
|
+
if cur_index >= end_index:
|
|
109
|
+
return post_list
|
|
110
|
+
|
|
111
|
+
raise exceptions.UnknownError(i18n.unknown_error)
|
|
@@ -101,51 +101,52 @@ def get_waterball(api, post_action: WaterballPostAction = WaterballPostAction.KE
|
|
|
101
101
|
seen_lines = set()
|
|
102
102
|
all_content_parts = []
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
index = api.connect_core.send(cmd, target_list)
|
|
112
|
-
|
|
113
|
-
if index == 0:
|
|
114
|
-
# no waterball records
|
|
115
|
-
api.connect_core.send('q' + command.go_main_menu, [
|
|
116
|
-
connect_core.TargetUnit(screens.Target.MainMenu, log_level=log.DEBUG, break_detect=True),
|
|
117
|
-
])
|
|
118
|
-
log.logger.info(i18n.get_waterball, '...', i18n.success)
|
|
119
|
-
return []
|
|
120
|
-
|
|
121
|
-
if index < 0:
|
|
122
|
-
break
|
|
123
|
-
|
|
124
|
-
screen = api.connect_core.get_screen_queue()[-1]
|
|
125
|
-
for line in screen.split('\n'):
|
|
126
|
-
line = line.strip()
|
|
127
|
-
if line and line not in seen_lines:
|
|
128
|
-
seen_lines.add(line)
|
|
129
|
-
all_content_parts.append(line)
|
|
130
|
-
|
|
131
|
-
if index == 1:
|
|
132
|
-
# WaterBallListEnd — collect done, exit viewer; handle post-action prompt
|
|
133
|
-
action_cmd = _post_action_cmd[post_action]
|
|
134
|
-
target_list_exit = [
|
|
135
|
-
connect_core.TargetUnit(screens.Target.WaterBallPostAction, log_level=log.DEBUG,
|
|
136
|
-
response=action_cmd + command.enter),
|
|
137
|
-
connect_core.TargetUnit(screens.Target.WaterballCleanup, log_level=log.DEBUG,
|
|
138
|
-
response='y' + command.enter),
|
|
139
|
-
connect_core.TargetUnit(screens.Target.MainMenu, log_level=log.DEBUG, break_detect=True),
|
|
104
|
+
with _api_util.expanded_screen(api):
|
|
105
|
+
while True:
|
|
106
|
+
target_list = [
|
|
107
|
+
connect_core.TargetUnit('◆ 暫無訊息記錄', log_level=log.DEBUG, break_detect=True),
|
|
108
|
+
connect_core.TargetUnit(screens.Target.WaterBallListEnd, log_level=log.DEBUG, break_detect=True),
|
|
109
|
+
connect_core.TargetUnit(screens.Target.InWaterBallList, log_level=log.DEBUG, break_detect=True),
|
|
140
110
|
]
|
|
141
|
-
|
|
142
|
-
api.connect_core.send(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
111
|
+
|
|
112
|
+
index = api.connect_core.send(cmd, target_list)
|
|
113
|
+
|
|
114
|
+
if index == 0:
|
|
115
|
+
# no waterball records
|
|
116
|
+
api.connect_core.send('q' + command.go_main_menu, [
|
|
117
|
+
connect_core.TargetUnit(screens.Target.MainMenu, log_level=log.DEBUG, break_detect=True),
|
|
118
|
+
])
|
|
119
|
+
log.logger.info(i18n.get_waterball, '...', i18n.success)
|
|
120
|
+
return []
|
|
121
|
+
|
|
122
|
+
if index < 0:
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
screen = api.connect_core.get_screen_queue()[-1]
|
|
126
|
+
for line in screen.split('\n'):
|
|
127
|
+
line = line.strip()
|
|
128
|
+
if line and line not in seen_lines:
|
|
129
|
+
seen_lines.add(line)
|
|
130
|
+
all_content_parts.append(line)
|
|
131
|
+
|
|
132
|
+
if index == 1:
|
|
133
|
+
# WaterBallListEnd — collect done, exit viewer; handle post-action prompt
|
|
134
|
+
action_cmd = _post_action_cmd[post_action]
|
|
135
|
+
target_list_exit = [
|
|
136
|
+
connect_core.TargetUnit(screens.Target.WaterBallPostAction, log_level=log.DEBUG,
|
|
137
|
+
response=action_cmd + command.enter),
|
|
138
|
+
connect_core.TargetUnit(screens.Target.WaterballCleanup, log_level=log.DEBUG,
|
|
139
|
+
response='y' + command.enter),
|
|
140
|
+
connect_core.TargetUnit(screens.Target.MainMenu, log_level=log.DEBUG, break_detect=True),
|
|
141
|
+
]
|
|
142
|
+
api.connect_core.send('q', target_list_exit)
|
|
143
|
+
api.connect_core.send(command.go_main_menu, [
|
|
144
|
+
connect_core.TargetUnit(screens.Target.MainMenu, log_level=log.DEBUG, break_detect=True),
|
|
145
|
+
])
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
# index == 2: InWaterBallList — next page
|
|
149
|
+
cmd = command.ctrl_f
|
|
149
150
|
|
|
150
151
|
result = []
|
|
151
152
|
for line in _merge_continuation_lines(all_content_parts):
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from . import _api_util
|
|
4
|
+
from . import command
|
|
5
|
+
from . import connect_core
|
|
6
|
+
from . import log
|
|
7
|
+
from . import screens
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def has_new_mail(api) -> int:
|
|
11
|
+
cmd_list = []
|
|
12
|
+
cmd_list.append(command.go_main_menu)
|
|
13
|
+
cmd_list.append(command.ctrl_z)
|
|
14
|
+
cmd_list.append('m')
|
|
15
|
+
# cmd_list.append('1')
|
|
16
|
+
# cmd_list.append(command.enter)
|
|
17
|
+
cmd = ''.join(cmd_list)
|
|
18
|
+
current_capacity = None
|
|
19
|
+
plus_count = 0
|
|
20
|
+
index_pattern = re.compile(r'(\d+)')
|
|
21
|
+
checked_index_list = []
|
|
22
|
+
break_detect = False
|
|
23
|
+
|
|
24
|
+
target_list = [
|
|
25
|
+
connect_core.TargetUnit(screens.Target.InMailBox, log_level=log.DEBUG, break_detect=True)
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
api.connect_core.send(
|
|
29
|
+
cmd,
|
|
30
|
+
target_list,
|
|
31
|
+
)
|
|
32
|
+
current_capacity, _ = _api_util.get_mailbox_capacity(api)
|
|
33
|
+
if current_capacity > 20:
|
|
34
|
+
cmd_list = []
|
|
35
|
+
cmd_list.append(command.go_main_menu)
|
|
36
|
+
cmd_list.append(command.ctrl_z)
|
|
37
|
+
cmd_list.append('m')
|
|
38
|
+
cmd_list.append('1')
|
|
39
|
+
cmd_list.append(command.enter)
|
|
40
|
+
cmd = ''.join(cmd_list)
|
|
41
|
+
|
|
42
|
+
with _api_util.expanded_screen(api):
|
|
43
|
+
while True:
|
|
44
|
+
if current_capacity > 20:
|
|
45
|
+
api.connect_core.send(
|
|
46
|
+
cmd,
|
|
47
|
+
target_list,
|
|
48
|
+
)
|
|
49
|
+
last_screen = api.connect_core.get_screen_queue()[-1]
|
|
50
|
+
|
|
51
|
+
last_screen_list = last_screen.split('\n')
|
|
52
|
+
last_screen_list = last_screen_list[3:-1]
|
|
53
|
+
last_screen_list = [x[:10] for x in last_screen_list]
|
|
54
|
+
|
|
55
|
+
current_plus_count = 0
|
|
56
|
+
for line in last_screen_list:
|
|
57
|
+
if str(current_capacity) in line:
|
|
58
|
+
break_detect = True
|
|
59
|
+
|
|
60
|
+
index_result = index_pattern.search(line)
|
|
61
|
+
if index_result is None:
|
|
62
|
+
continue
|
|
63
|
+
current_index = index_result.group(0)
|
|
64
|
+
if current_index in checked_index_list:
|
|
65
|
+
continue
|
|
66
|
+
checked_index_list.append(current_index)
|
|
67
|
+
if '+' not in line:
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
current_plus_count += 1
|
|
71
|
+
|
|
72
|
+
plus_count += current_plus_count
|
|
73
|
+
if break_detect:
|
|
74
|
+
break
|
|
75
|
+
cmd = command.ctrl_f
|
|
76
|
+
|
|
77
|
+
return plus_count
|