PyPtt 2.0.2__tar.gz → 2.0.4__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.0.2 → pyptt-2.0.4}/PKG-INFO +2 -2
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/PTT.py +45 -4
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/__init__.py +1 -1
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_bottom_post_list.py +2 -6
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_newest_index.py +11 -2
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_post.py +117 -273
- pyptt-2.0.4/PyPtt/_api_get_waterball.py +149 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_util.py +28 -8
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/data_type.py +16 -6
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/lang_en_US.py +1 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/lang_zh_TW.py +1 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/screens.py +4 -0
- pyptt-2.0.4/PyPtt/ssl_config.py +24 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/PKG-INFO +2 -2
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/SOURCES.txt +4 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/requires.txt +1 -1
- {pyptt-2.0.2 → pyptt-2.0.4}/pyproject.toml +1 -1
- pyptt-2.0.4/tests/test_get_post_parser.py +514 -0
- pyptt-2.0.4/tests/test_get_waterball.py +33 -0
- pyptt-2.0.4/tests/test_parse_query_post.py +242 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_service.py +23 -7
- pyptt-2.0.2/PyPtt/ssl_config.py +0 -24
- {pyptt-2.0.2 → pyptt-2.0.4}/LICENSE +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_bucket.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_call_status.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_change_pw.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_comment.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_del_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_board_info.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_board_list.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_favourite_board.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_post_index.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_post_list.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_time.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_user.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_give_money.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_has_new_mail.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_loginout.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_mail.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_mark_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_reply_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_search_user.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_set_board_title.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/api_server.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/check_value.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/command.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/config.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/connect_core.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/exceptions.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/i18n.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/lib_util.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/log.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/service.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/dependency_links.txt +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/top_level.txt +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/README.md +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/setup.cfg +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/setup.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_change_pw.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_comment.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_del_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_exceptions.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_board_info.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_board_list.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_bottom_post_list.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_favourite_boards.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_mail.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_newest_index.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_post_list.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_time.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_user.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_give_money.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_i18n.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_init.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_logger.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_reply_post.py +0 -0
- {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_search_user.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyPtt
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
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)
|
|
@@ -28,7 +28,7 @@ License-File: LICENSE
|
|
|
28
28
|
Requires-Dist: progressbar2
|
|
29
29
|
Requires-Dist: websockets==12.0
|
|
30
30
|
Requires-Dist: uao
|
|
31
|
-
Requires-Dist: requests==2.
|
|
31
|
+
Requires-Dist: requests==2.33.0
|
|
32
32
|
Requires-Dist: AutoStrEnum
|
|
33
33
|
Requires-Dist: PyYAML
|
|
34
34
|
Provides-Extra: api
|
|
@@ -20,6 +20,7 @@ from . import _api_get_time
|
|
|
20
20
|
from . import _api_get_user
|
|
21
21
|
from . import _api_give_money
|
|
22
22
|
from . import _api_loginout
|
|
23
|
+
from . import _api_get_waterball
|
|
23
24
|
from . import _api_mail
|
|
24
25
|
from . import _api_mark_post
|
|
25
26
|
from . import _api_post
|
|
@@ -151,6 +152,7 @@ class API:
|
|
|
151
152
|
self._goto_board_list = set()
|
|
152
153
|
self._board_info_list = dict()
|
|
153
154
|
self._newest_index_data = data_type.TimedDict(timeout=2)
|
|
155
|
+
self._mail_capacity = None
|
|
154
156
|
self.cursor = None
|
|
155
157
|
|
|
156
158
|
log.logger.debug('thread_id', self._thread_id)
|
|
@@ -294,10 +296,12 @@ class API:
|
|
|
294
296
|
|
|
295
297
|
Args:
|
|
296
298
|
board (str): 看板名稱。
|
|
297
|
-
aid (str):
|
|
298
|
-
index: 文章編號。
|
|
299
|
-
|
|
300
|
-
|
|
299
|
+
aid (str): 文章 AID,例如 ``'1TJH_XY0'``。
|
|
300
|
+
index (int): 文章編號。
|
|
301
|
+
search_type (:ref:`search-type`): 搜尋類型,與 ``search_condition`` 搭配使用。
|
|
302
|
+
search_condition (str): 搜尋條件,例如關鍵字或作者 ID。
|
|
303
|
+
search_list (List[Tuple]): 多條件搜尋清單,每個元素為 ``(SearchType, condition)``。
|
|
304
|
+
query (bool): 是否為查詢模式,不進入文章內容,僅取得列表資訊。
|
|
301
305
|
|
|
302
306
|
Returns:
|
|
303
307
|
Dict,文章內容。詳見 :ref:`post-field`
|
|
@@ -939,6 +943,43 @@ class API:
|
|
|
939
943
|
|
|
940
944
|
_api_mail.del_mail(self, index)
|
|
941
945
|
|
|
946
|
+
def get_waterball(self, post_action: data_type.WaterballPostAction = data_type.WaterballPostAction.KEEP) -> List[Dict]:
|
|
947
|
+
"""
|
|
948
|
+
取得水球紀錄。
|
|
949
|
+
|
|
950
|
+
Args:
|
|
951
|
+
post_action (WaterballPostAction): 取得水球後的處理方式。
|
|
952
|
+
- ``WaterballPostAction.KEEP``(預設):保留水球記錄。
|
|
953
|
+
- ``WaterballPostAction.CLEAR``:清除水球記錄。
|
|
954
|
+
- ``WaterballPostAction.MAILBOX``:存入信箱。
|
|
955
|
+
|
|
956
|
+
Returns:
|
|
957
|
+
List[Dict],水球紀錄清單,詳見 :ref:`waterball-field`。
|
|
958
|
+
|
|
959
|
+
Raises:
|
|
960
|
+
RequireLogin: 需要登入。
|
|
961
|
+
UnregisteredUser: 未註冊使用者。
|
|
962
|
+
|
|
963
|
+
範例::
|
|
964
|
+
|
|
965
|
+
import PyPtt
|
|
966
|
+
|
|
967
|
+
ptt_bot = PyPtt.API()
|
|
968
|
+
try:
|
|
969
|
+
# .. login ..
|
|
970
|
+
waterball_list = ptt_bot.get_waterball(post_action=PyPtt.WaterballPostAction.KEEP)
|
|
971
|
+
for waterball in waterball_list:
|
|
972
|
+
print(waterball[PyPtt.WaterballField.type])
|
|
973
|
+
print(waterball[PyPtt.WaterballField.target])
|
|
974
|
+
print(waterball[PyPtt.WaterballField.content])
|
|
975
|
+
print(waterball[PyPtt.WaterballField.date])
|
|
976
|
+
# .. do something ..
|
|
977
|
+
finally:
|
|
978
|
+
ptt_bot.logout()
|
|
979
|
+
"""
|
|
980
|
+
|
|
981
|
+
return _api_get_waterball.get_waterball(self, post_action)
|
|
982
|
+
|
|
942
983
|
def change_pw(self, new_password: str) -> None:
|
|
943
984
|
"""
|
|
944
985
|
更改密碼。
|
|
@@ -51,12 +51,8 @@ def get_bottom_post_list(api, board):
|
|
|
51
51
|
api.connect_core.send(cmd, target_list)
|
|
52
52
|
last_screen = api.connect_core.get_screen_queue()[-1]
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
api,
|
|
57
|
-
last_screen)
|
|
58
|
-
|
|
59
|
-
aid_list.append(post_aid)
|
|
54
|
+
q = _api_util.parse_query_post(api, last_screen)
|
|
55
|
+
aid_list.append(q.aid)
|
|
60
56
|
|
|
61
57
|
cmd_list = []
|
|
62
58
|
cmd_list.append(command.enter)
|
|
@@ -166,9 +166,18 @@ def get_newest_index(api, index_type: data_type.NewIndex, board: Optional[str] =
|
|
|
166
166
|
def get_index(api):
|
|
167
167
|
current_capacity, _ = _api_util.get_mailbox_capacity(api)
|
|
168
168
|
last_screen = api.connect_core.get_screen_queue()[-1]
|
|
169
|
-
|
|
169
|
+
cursor_lines = [x for x in last_screen.split('\n') if x.strip().startswith(api.cursor)]
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
if not cursor_lines:
|
|
172
|
+
return current_capacity if current_capacity > 0 else 0
|
|
173
|
+
|
|
174
|
+
cursor_line = cursor_lines[0]
|
|
175
|
+
match = re.compile(r'(\d+)').search(cursor_line)
|
|
176
|
+
|
|
177
|
+
if match is None:
|
|
178
|
+
return current_capacity if current_capacity > 0 else 0
|
|
179
|
+
|
|
180
|
+
list_index = int(match.group(0))
|
|
172
181
|
|
|
173
182
|
if search_type == 0 and search_list is None:
|
|
174
183
|
if list_index > current_capacity:
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
import re
|
|
5
5
|
import time
|
|
6
|
-
from typing import Dict, Optional
|
|
6
|
+
from typing import Dict, List, Optional, Tuple
|
|
7
7
|
|
|
8
8
|
from AutoStrEnum import AutoJsonEncoder
|
|
9
9
|
|
|
@@ -151,146 +151,117 @@ def _get_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
151
151
|
PostField.push_number: None,
|
|
152
152
|
PostField.is_lock: False,
|
|
153
153
|
PostField.full_content: None,
|
|
154
|
-
PostField.is_unconfirmed: False
|
|
154
|
+
PostField.is_unconfirmed: False,
|
|
155
|
+
}
|
|
155
156
|
|
|
156
|
-
post_author = None
|
|
157
|
-
post_title = None
|
|
158
157
|
if index < 0 or index == 1:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
return _parse_deleted_post(api, post, board, last_screen)
|
|
159
|
+
|
|
160
|
+
# index == 0: QueryPost screen matched
|
|
161
|
+
q = _api_util.parse_query_post(api, last_screen)
|
|
162
|
+
|
|
163
|
+
post[PostField.board] = board
|
|
164
|
+
post[PostField.aid] = q.aid
|
|
165
|
+
post[PostField.index] = q.index
|
|
166
|
+
post[PostField.author] = q.author
|
|
167
|
+
post[PostField.title] = q.title
|
|
168
|
+
post[PostField.url] = q.url
|
|
169
|
+
post[PostField.money] = q.money
|
|
170
|
+
post[PostField.list_date] = q.list_date
|
|
171
|
+
post[PostField.push_number] = q.push_number
|
|
172
|
+
|
|
173
|
+
if q.lock_post:
|
|
174
|
+
post[PostField.is_lock] = True
|
|
175
|
+
post[PostField.pass_format_check] = True
|
|
176
|
+
return post
|
|
165
177
|
|
|
166
|
-
|
|
167
|
-
|
|
178
|
+
if query:
|
|
179
|
+
post[PostField.pass_format_check] = True
|
|
180
|
+
return post
|
|
168
181
|
|
|
169
|
-
|
|
170
|
-
|
|
182
|
+
origin_post, has_control_code = _api_util.get_content(api)
|
|
183
|
+
post[PostField.has_control_code] = has_control_code
|
|
184
|
+
post[PostField.is_unconfirmed] = api.Unconfirmed
|
|
171
185
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
list_date = None
|
|
176
|
-
else:
|
|
177
|
-
list_date = pattern_result.group(0)
|
|
178
|
-
list_date = list_date[-5:]
|
|
186
|
+
if origin_post is None:
|
|
187
|
+
log.logger.info(i18n.post_deleted)
|
|
188
|
+
return post
|
|
179
189
|
|
|
180
|
-
|
|
181
|
-
pattern_result = pattern.search(cursor_line)
|
|
182
|
-
if pattern_result is not None:
|
|
183
|
-
post_del_status = data_type.PostStatus.DELETED_BY_AUTHOR
|
|
184
|
-
else:
|
|
185
|
-
pattern = re.compile(r'<[\w]+>')
|
|
186
|
-
pattern_result = pattern.search(cursor_line)
|
|
187
|
-
post_del_status = data_type.PostStatus.DELETED_BY_MODERATOR
|
|
190
|
+
post[PostField.full_content] = origin_post
|
|
188
191
|
|
|
189
|
-
|
|
190
|
-
# > 76060 8/28 - □ (本文已被刪除) [weida7332]
|
|
191
|
-
# print(f'O=>{CursorLine}<')
|
|
192
|
-
if pattern_result is not None:
|
|
193
|
-
post_author = pattern_result.group(0)[1:-1]
|
|
194
|
-
else:
|
|
195
|
-
post_author = None
|
|
196
|
-
post_del_status = data_type.PostStatus.DELETED_BY_UNKNOWN
|
|
197
|
-
|
|
198
|
-
log.logger.debug('ListDate', list_date)
|
|
199
|
-
log.logger.debug('PostAuthor', post_author)
|
|
200
|
-
log.logger.debug('post_del_status', post_del_status)
|
|
201
|
-
|
|
202
|
-
post.update({
|
|
203
|
-
PostField.board: board,
|
|
204
|
-
PostField.author: post_author,
|
|
205
|
-
PostField.list_date: list_date,
|
|
206
|
-
PostField.post_status: post_del_status,
|
|
207
|
-
PostField.pass_format_check: True
|
|
208
|
-
})
|
|
192
|
+
return _parse_post_content(api, post, board, origin_post)
|
|
209
193
|
|
|
210
|
-
return post
|
|
211
194
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
_api_util.parse_query_post(
|
|
216
|
-
api,
|
|
217
|
-
last_screen)
|
|
218
|
-
|
|
219
|
-
if lock_post:
|
|
220
|
-
post.update({
|
|
221
|
-
PostField.board: board,
|
|
222
|
-
PostField.aid: post_aid,
|
|
223
|
-
PostField.index: post_index,
|
|
224
|
-
PostField.author: post_author,
|
|
225
|
-
PostField.title: post_title,
|
|
226
|
-
PostField.url: post_web,
|
|
227
|
-
PostField.money: post_money,
|
|
228
|
-
PostField.list_date: list_date,
|
|
229
|
-
PostField.pass_format_check: True,
|
|
230
|
-
PostField.push_number: push_number,
|
|
231
|
-
PostField.is_lock: True})
|
|
232
|
-
return post
|
|
195
|
+
def _parse_deleted_post(api, post: Dict, board: str, last_screen: str) -> Dict:
|
|
196
|
+
log.logger.debug(i18n.post_deleted)
|
|
197
|
+
log.logger.debug('OriScreen', last_screen)
|
|
233
198
|
|
|
234
|
-
if
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
PostField.author: post_author,
|
|
240
|
-
PostField.title: post_title,
|
|
241
|
-
PostField.url: post_web,
|
|
242
|
-
PostField.money: post_money,
|
|
243
|
-
PostField.list_date: list_date,
|
|
244
|
-
PostField.pass_format_check: True,
|
|
245
|
-
PostField.push_number: push_number})
|
|
246
|
-
return post
|
|
199
|
+
cursor_lines = [line for line in last_screen.split('\n') if line.startswith(api.cursor)]
|
|
200
|
+
if len(cursor_lines) != 1:
|
|
201
|
+
raise exceptions.UnknownError(last_screen)
|
|
202
|
+
cursor_line = cursor_lines[0]
|
|
203
|
+
log.logger.debug('CursorLine', cursor_line)
|
|
247
204
|
|
|
248
|
-
|
|
205
|
+
pattern = re.compile(r'[\d]+\/[\d]+')
|
|
206
|
+
pattern_result = pattern.search(cursor_line)
|
|
207
|
+
if pattern_result is None:
|
|
208
|
+
list_date = None
|
|
209
|
+
else:
|
|
210
|
+
list_date = pattern_result.group(0)[-5:]
|
|
249
211
|
|
|
250
|
-
|
|
251
|
-
|
|
212
|
+
pattern = re.compile(r'\[[\w]+\]')
|
|
213
|
+
pattern_result = pattern.search(cursor_line)
|
|
214
|
+
if pattern_result is not None:
|
|
215
|
+
post_del_status = data_type.PostStatus.DELETED_BY_AUTHOR
|
|
216
|
+
else:
|
|
217
|
+
pattern = re.compile(r'<[\w]+>')
|
|
218
|
+
pattern_result = pattern.search(cursor_line)
|
|
219
|
+
post_del_status = data_type.PostStatus.DELETED_BY_MODERATOR
|
|
252
220
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
221
|
+
# > 79843 9/11 - □ (本文已被吃掉)<
|
|
222
|
+
# > 76060 8/28 - □ (本文已被刪除) [weida7332]
|
|
223
|
+
if pattern_result is not None:
|
|
224
|
+
post_author = pattern_result.group(0)[1:-1]
|
|
225
|
+
else:
|
|
226
|
+
post_author = None
|
|
227
|
+
post_del_status = data_type.PostStatus.DELETED_BY_UNKNOWN
|
|
228
|
+
|
|
229
|
+
log.logger.debug('ListDate', list_date)
|
|
230
|
+
log.logger.debug('PostAuthor', post_author)
|
|
231
|
+
log.logger.debug('post_del_status', post_del_status)
|
|
232
|
+
|
|
233
|
+
post[PostField.board] = board
|
|
234
|
+
post[PostField.author] = post_author
|
|
235
|
+
post[PostField.list_date] = list_date
|
|
236
|
+
post[PostField.post_status] = post_del_status
|
|
237
|
+
post[PostField.pass_format_check] = True
|
|
238
|
+
return post
|
|
268
239
|
|
|
269
|
-
post_author_pattern_new = re.compile(r'作者 (.+) 看板')
|
|
270
|
-
post_author_pattern_old = re.compile(r'作者 (.+)')
|
|
271
|
-
board_pattern = re.compile(r'看板 (.+)')
|
|
272
240
|
|
|
273
|
-
|
|
241
|
+
def _parse_post_content(api, post: Dict, board: str, origin_post: str) -> Dict:
|
|
274
242
|
post_content = None
|
|
275
243
|
ip = None
|
|
276
244
|
location = None
|
|
277
|
-
push_list = []
|
|
278
245
|
|
|
279
246
|
# 格式確認,亂改的我也沒辦法Q_Q
|
|
280
247
|
origin_post_lines = origin_post.split('\n')
|
|
281
|
-
|
|
282
248
|
author_line = origin_post_lines[0]
|
|
283
249
|
|
|
284
250
|
if board.lower() == 'allpost':
|
|
251
|
+
board_pattern = re.compile(r'看板 (.+)')
|
|
285
252
|
board_line = author_line[author_line.find(')') + 1:]
|
|
286
253
|
pattern_result = board_pattern.search(board_line)
|
|
287
254
|
if pattern_result is not None:
|
|
288
|
-
board_temp =
|
|
255
|
+
board_temp = pattern_result.group(0)
|
|
289
256
|
board_temp = board_temp[2:].strip()
|
|
290
257
|
if len(board_temp) > 0:
|
|
291
258
|
board = board_temp
|
|
259
|
+
post[PostField.board] = board
|
|
292
260
|
log.logger.debug(i18n.board, board)
|
|
293
261
|
|
|
262
|
+
post_author_pattern_new = re.compile(r'作者 (.+) 看板')
|
|
263
|
+
post_author_pattern_old = re.compile(r'作者 (.+)')
|
|
264
|
+
|
|
294
265
|
pattern_result = post_author_pattern_new.search(author_line)
|
|
295
266
|
if pattern_result is not None:
|
|
296
267
|
post_author = pattern_result.group(0)
|
|
@@ -299,65 +270,21 @@ def _get_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
299
270
|
pattern_result = post_author_pattern_old.search(author_line)
|
|
300
271
|
if pattern_result is None:
|
|
301
272
|
log.logger.info(i18n.substandard_post, i18n.author)
|
|
302
|
-
|
|
303
|
-
post.update({
|
|
304
|
-
PostField.board: board,
|
|
305
|
-
PostField.aid: post_aid,
|
|
306
|
-
PostField.index: post_index,
|
|
307
|
-
PostField.author: post_author,
|
|
308
|
-
PostField.date: post_date,
|
|
309
|
-
PostField.title: post_title,
|
|
310
|
-
PostField.url: post_web,
|
|
311
|
-
PostField.money: post_money,
|
|
312
|
-
PostField.content: post_content,
|
|
313
|
-
PostField.ip: ip,
|
|
314
|
-
PostField.comments: push_list,
|
|
315
|
-
PostField.list_date: list_date,
|
|
316
|
-
PostField.has_control_code: has_control_code,
|
|
317
|
-
PostField.pass_format_check: False,
|
|
318
|
-
PostField.location: location,
|
|
319
|
-
PostField.push_number: push_number,
|
|
320
|
-
PostField.full_content: origin_post,
|
|
321
|
-
PostField.is_unconfirmed: api.Unconfirmed, })
|
|
322
|
-
|
|
323
273
|
return post
|
|
324
274
|
post_author = pattern_result.group(0)
|
|
325
275
|
post_author = post_author[:post_author.rfind(')') + 1]
|
|
326
276
|
post_author = post_author[4:].strip()
|
|
327
|
-
|
|
277
|
+
post[PostField.author] = post_author
|
|
328
278
|
log.logger.debug(i18n.author, post_author)
|
|
329
279
|
|
|
330
280
|
post_title_pattern = re.compile(r'標題 (.+)')
|
|
331
|
-
|
|
332
281
|
title_line = origin_post_lines[1]
|
|
333
282
|
pattern_result = post_title_pattern.search(title_line)
|
|
334
283
|
if pattern_result is None:
|
|
335
284
|
log.logger.info(i18n.substandard_post, i18n.title)
|
|
336
|
-
|
|
337
|
-
post.update({
|
|
338
|
-
PostField.board: board,
|
|
339
|
-
PostField.aid: post_aid,
|
|
340
|
-
PostField.index: post_index,
|
|
341
|
-
PostField.author: post_author,
|
|
342
|
-
PostField.date: post_date,
|
|
343
|
-
PostField.title: post_title,
|
|
344
|
-
PostField.url: post_web,
|
|
345
|
-
PostField.money: post_money,
|
|
346
|
-
PostField.content: post_content,
|
|
347
|
-
PostField.ip: ip,
|
|
348
|
-
PostField.comments: push_list,
|
|
349
|
-
PostField.list_date: list_date,
|
|
350
|
-
PostField.has_control_code: has_control_code,
|
|
351
|
-
PostField.pass_format_check: False,
|
|
352
|
-
PostField.location: location,
|
|
353
|
-
PostField.push_number: push_number,
|
|
354
|
-
PostField.full_content: origin_post,
|
|
355
|
-
PostField.is_unconfirmed: api.Unconfirmed, })
|
|
356
|
-
|
|
357
285
|
return post
|
|
358
|
-
post_title = pattern_result.group(0)
|
|
359
|
-
|
|
360
|
-
|
|
286
|
+
post_title = pattern_result.group(0)[4:].strip()
|
|
287
|
+
post[PostField.title] = post_title
|
|
361
288
|
log.logger.debug(i18n.title, post_title)
|
|
362
289
|
|
|
363
290
|
post_date_pattern = re.compile(r'時間 .{24}')
|
|
@@ -365,88 +292,53 @@ def _get_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
365
292
|
pattern_result = post_date_pattern.search(date_line)
|
|
366
293
|
if pattern_result is None:
|
|
367
294
|
log.logger.info(i18n.substandard_post, i18n.date)
|
|
368
|
-
|
|
369
|
-
post.update({
|
|
370
|
-
PostField.board: board,
|
|
371
|
-
PostField.aid: post_aid,
|
|
372
|
-
PostField.index: post_index,
|
|
373
|
-
PostField.author: post_author,
|
|
374
|
-
PostField.date: post_date,
|
|
375
|
-
PostField.title: post_title,
|
|
376
|
-
PostField.url: post_web,
|
|
377
|
-
PostField.money: post_money,
|
|
378
|
-
PostField.content: post_content,
|
|
379
|
-
PostField.ip: ip,
|
|
380
|
-
PostField.comments: push_list,
|
|
381
|
-
PostField.list_date: list_date,
|
|
382
|
-
PostField.has_control_code: has_control_code,
|
|
383
|
-
PostField.pass_format_check: False,
|
|
384
|
-
PostField.location: location,
|
|
385
|
-
PostField.push_number: push_number,
|
|
386
|
-
PostField.full_content: origin_post,
|
|
387
|
-
PostField.is_unconfirmed: api.Unconfirmed, })
|
|
388
|
-
|
|
389
295
|
return post
|
|
390
|
-
post_date = pattern_result.group(0)
|
|
391
|
-
|
|
392
|
-
|
|
296
|
+
post_date = pattern_result.group(0)[4:].strip()
|
|
297
|
+
post[PostField.date] = post_date
|
|
393
298
|
log.logger.debug(i18n.date, post_date)
|
|
394
299
|
|
|
395
300
|
content_fail = True
|
|
396
|
-
if screens.Target.content_start
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
else:
|
|
400
|
-
post_content = origin_post
|
|
401
|
-
post_content = post_content[
|
|
402
|
-
post_content.find(screens.Target.content_start) + len(screens.Target.content_start) + 1:]
|
|
403
|
-
# print('Type 2')
|
|
404
|
-
# print(f'PostContent [{PostContent}]')
|
|
301
|
+
if screens.Target.content_start in origin_post:
|
|
302
|
+
post_content = origin_post[
|
|
303
|
+
origin_post.find(screens.Target.content_start) + len(screens.Target.content_start) + 1:]
|
|
405
304
|
for content_end in screens.Target.content_end_list:
|
|
406
305
|
# + 3 = 把 --\n 拿掉
|
|
407
|
-
# print(f'EC [{EC}]')
|
|
408
306
|
if content_end in post_content:
|
|
409
307
|
content_fail = False
|
|
410
|
-
|
|
411
308
|
post_content = post_content[:post_content.rfind(content_end) + 3]
|
|
412
|
-
origin_post_lines = origin_post[origin_post.find(content_end):]
|
|
413
|
-
# post_content = post_content.strip()
|
|
414
|
-
origin_post_lines = origin_post_lines.split('\n')
|
|
309
|
+
origin_post_lines = origin_post[origin_post.find(content_end):].split('\n')
|
|
415
310
|
break
|
|
416
311
|
|
|
417
312
|
if content_fail:
|
|
418
313
|
log.logger.info(i18n.substandard_post, i18n.content)
|
|
314
|
+
return post
|
|
315
|
+
post[PostField.content] = post_content
|
|
316
|
+
log.logger.debug(i18n.content, post_content)
|
|
419
317
|
|
|
420
|
-
|
|
421
|
-
PostField.board: board,
|
|
422
|
-
PostField.aid: post_aid,
|
|
423
|
-
PostField.index: post_index,
|
|
424
|
-
PostField.author: post_author,
|
|
425
|
-
PostField.date: post_date,
|
|
426
|
-
PostField.title: post_title,
|
|
427
|
-
PostField.url: post_web,
|
|
428
|
-
PostField.money: post_money,
|
|
429
|
-
PostField.content: post_content,
|
|
430
|
-
PostField.ip: ip,
|
|
431
|
-
PostField.comments: push_list,
|
|
432
|
-
PostField.list_date: list_date,
|
|
433
|
-
PostField.has_control_code: has_control_code,
|
|
434
|
-
PostField.pass_format_check: False,
|
|
435
|
-
PostField.location: location,
|
|
436
|
-
PostField.push_number: push_number,
|
|
437
|
-
PostField.full_content: origin_post,
|
|
438
|
-
PostField.is_unconfirmed: api.Unconfirmed, })
|
|
318
|
+
ip, location = _parse_ip_location(origin_post_lines)
|
|
439
319
|
|
|
320
|
+
if api.config.host == data_type.HOST.PTT1 and ip is None:
|
|
321
|
+
log.logger.info(i18n.substandard_post, ip)
|
|
440
322
|
return post
|
|
323
|
+
post[PostField.ip] = ip
|
|
324
|
+
post[PostField.location] = location
|
|
325
|
+
log.logger.debug('IP', ip)
|
|
441
326
|
|
|
442
|
-
|
|
327
|
+
post[PostField.comments] = _parse_comments(api, origin_post_lines)
|
|
328
|
+
post[PostField.pass_format_check] = True
|
|
329
|
+
return post
|
|
443
330
|
|
|
444
|
-
|
|
331
|
+
|
|
332
|
+
def _parse_ip_location(lines: List[str]) -> Tuple[Optional[str], Optional[str]]:
|
|
333
|
+
"""Extract IP and optional location from ※ / ◆ info lines."""
|
|
334
|
+
ip = None
|
|
335
|
+
location = None
|
|
336
|
+
info_lines = [line for line in lines if line.startswith('※') or line.startswith('◆')]
|
|
445
337
|
|
|
446
338
|
pattern = re.compile(r'[\d]+\.[\d]+\.[\d]+\.[\d]+')
|
|
447
339
|
pattern_p2 = re.compile(r'[\d]+-[\d]+-[\d]+-[\d]+')
|
|
448
|
-
for line in reversed(info_lines):
|
|
449
340
|
|
|
341
|
+
for line in reversed(info_lines):
|
|
450
342
|
log.logger.debug('IP Line', line)
|
|
451
343
|
|
|
452
344
|
# type 1
|
|
@@ -470,46 +362,21 @@ def _get_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
470
362
|
location_temp = location_temp.replace('(', '')
|
|
471
363
|
location_temp = location_temp[:location_temp.rfind(')')]
|
|
472
364
|
location_temp = location_temp.strip()
|
|
473
|
-
# print(f'=>[{LocationTemp}]')
|
|
474
365
|
if ' ' not in location_temp and len(location_temp) > 0:
|
|
475
366
|
location = location_temp
|
|
476
|
-
|
|
477
367
|
log.logger.debug('Location', location)
|
|
478
368
|
break
|
|
479
369
|
|
|
480
370
|
pattern_result = pattern_p2.search(line)
|
|
481
371
|
if pattern_result is not None:
|
|
482
|
-
ip = pattern_result.group(0)
|
|
483
|
-
ip = ip.replace('-', '.')
|
|
484
|
-
# print(f'IP -> [{IP}]')
|
|
372
|
+
ip = pattern_result.group(0).replace('-', '.')
|
|
485
373
|
break
|
|
486
|
-
if api.config.host == data_type.HOST.PTT1:
|
|
487
|
-
if ip is None:
|
|
488
|
-
log.logger.info(i18n.substandard_post, ip)
|
|
489
|
-
|
|
490
|
-
post.update({
|
|
491
|
-
PostField.board: board,
|
|
492
|
-
PostField.aid: post_aid,
|
|
493
|
-
PostField.index: post_index,
|
|
494
|
-
PostField.author: post_author,
|
|
495
|
-
PostField.date: post_date,
|
|
496
|
-
PostField.title: post_title,
|
|
497
|
-
PostField.url: post_web,
|
|
498
|
-
PostField.money: post_money,
|
|
499
|
-
PostField.content: post_content,
|
|
500
|
-
PostField.ip: ip,
|
|
501
|
-
PostField.comments: push_list,
|
|
502
|
-
PostField.list_date: list_date,
|
|
503
|
-
PostField.has_control_code: has_control_code,
|
|
504
|
-
PostField.pass_format_check: False,
|
|
505
|
-
PostField.location: location,
|
|
506
|
-
PostField.push_number: push_number,
|
|
507
|
-
PostField.full_content: origin_post,
|
|
508
|
-
PostField.is_unconfirmed: api.Unconfirmed, })
|
|
509
374
|
|
|
510
|
-
|
|
511
|
-
|
|
375
|
+
return ip, location
|
|
376
|
+
|
|
512
377
|
|
|
378
|
+
def _parse_comments(api, origin_post_lines: List[str]) -> List[Dict]:
|
|
379
|
+
"""Parse push / boo / arrow comments from post lines."""
|
|
513
380
|
push_author_pattern = re.compile(r'[推|噓|→] [\w| ]+:')
|
|
514
381
|
push_date_pattern = re.compile(r'[\d]+/[\d]+ [\d]+:[\d]+')
|
|
515
382
|
push_ip_pattern = re.compile(r'[\d]+\.[\d]+\.[\d]+\.[\d]+')
|
|
@@ -531,7 +398,6 @@ def _get_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
531
398
|
# 不符合推文格式
|
|
532
399
|
continue
|
|
533
400
|
push_author = result.group(0)[2:-1].strip()
|
|
534
|
-
|
|
535
401
|
log.logger.debug(i18n.comment_id, push_author)
|
|
536
402
|
|
|
537
403
|
result = push_date_pattern.search(line)
|
|
@@ -547,7 +413,6 @@ def _get_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
547
413
|
log.logger.debug(f'{i18n.comment} ip', comment_ip)
|
|
548
414
|
|
|
549
415
|
push_content = line[line.find(push_author) + len(push_author):]
|
|
550
|
-
# PushContent = PushContent.replace(PushDate, '')
|
|
551
416
|
|
|
552
417
|
if api.config.host == data_type.HOST.PTT1:
|
|
553
418
|
push_content = push_content[:push_content.rfind(push_date)]
|
|
@@ -557,35 +422,14 @@ def _get_post(api, board: str, post_aid: Optional[str] = None, post_index: int =
|
|
|
557
422
|
if comment_ip is not None:
|
|
558
423
|
push_content = push_content.replace(comment_ip, '')
|
|
559
424
|
push_content = push_content[push_content.find(':') + 1:].strip()
|
|
560
|
-
|
|
561
425
|
log.logger.debug(i18n.comment_content, push_content)
|
|
562
426
|
|
|
563
|
-
|
|
427
|
+
push_list.append({
|
|
564
428
|
CommentField.type: comment_type,
|
|
565
429
|
CommentField.author: push_author,
|
|
566
430
|
CommentField.content: push_content,
|
|
567
431
|
CommentField.ip: comment_ip,
|
|
568
|
-
CommentField.time: push_date
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
post.update({
|
|
572
|
-
PostField.board: board,
|
|
573
|
-
PostField.aid: post_aid,
|
|
574
|
-
PostField.index: post_index,
|
|
575
|
-
PostField.author: post_author,
|
|
576
|
-
PostField.date: post_date,
|
|
577
|
-
PostField.title: post_title,
|
|
578
|
-
PostField.url: post_web,
|
|
579
|
-
PostField.money: post_money,
|
|
580
|
-
PostField.content: post_content,
|
|
581
|
-
PostField.ip: ip,
|
|
582
|
-
PostField.comments: push_list,
|
|
583
|
-
PostField.list_date: list_date,
|
|
584
|
-
PostField.has_control_code: has_control_code,
|
|
585
|
-
PostField.pass_format_check: True,
|
|
586
|
-
PostField.location: location,
|
|
587
|
-
PostField.push_number: push_number,
|
|
588
|
-
PostField.full_content: origin_post,
|
|
589
|
-
PostField.is_unconfirmed: api.Unconfirmed})
|
|
432
|
+
CommentField.time: push_date,
|
|
433
|
+
})
|
|
590
434
|
|
|
591
|
-
return
|
|
435
|
+
return push_list
|