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.
Files changed (80) hide show
  1. {pyptt-2.0.2 → pyptt-2.0.4}/PKG-INFO +2 -2
  2. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/PTT.py +45 -4
  3. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/__init__.py +1 -1
  4. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_bottom_post_list.py +2 -6
  5. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_newest_index.py +11 -2
  6. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_post.py +117 -273
  7. pyptt-2.0.4/PyPtt/_api_get_waterball.py +149 -0
  8. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_util.py +28 -8
  9. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/data_type.py +16 -6
  10. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/lang_en_US.py +1 -0
  11. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/lang_zh_TW.py +1 -0
  12. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/screens.py +4 -0
  13. pyptt-2.0.4/PyPtt/ssl_config.py +24 -0
  14. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/PKG-INFO +2 -2
  15. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/SOURCES.txt +4 -0
  16. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/requires.txt +1 -1
  17. {pyptt-2.0.2 → pyptt-2.0.4}/pyproject.toml +1 -1
  18. pyptt-2.0.4/tests/test_get_post_parser.py +514 -0
  19. pyptt-2.0.4/tests/test_get_waterball.py +33 -0
  20. pyptt-2.0.4/tests/test_parse_query_post.py +242 -0
  21. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_service.py +23 -7
  22. pyptt-2.0.2/PyPtt/ssl_config.py +0 -24
  23. {pyptt-2.0.2 → pyptt-2.0.4}/LICENSE +0 -0
  24. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_bucket.py +0 -0
  25. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_call_status.py +0 -0
  26. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_change_pw.py +0 -0
  27. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_comment.py +0 -0
  28. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_del_post.py +0 -0
  29. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_board_info.py +0 -0
  30. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_board_list.py +0 -0
  31. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_favourite_board.py +0 -0
  32. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_post_index.py +0 -0
  33. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_post_list.py +0 -0
  34. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_time.py +0 -0
  35. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_get_user.py +0 -0
  36. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_give_money.py +0 -0
  37. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_has_new_mail.py +0 -0
  38. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_loginout.py +0 -0
  39. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_mail.py +0 -0
  40. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_mark_post.py +0 -0
  41. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_post.py +0 -0
  42. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_reply_post.py +0 -0
  43. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_search_user.py +0 -0
  44. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/_api_set_board_title.py +0 -0
  45. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/api_server.py +0 -0
  46. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/check_value.py +0 -0
  47. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/command.py +0 -0
  48. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/config.py +0 -0
  49. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/connect_core.py +0 -0
  50. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/exceptions.py +0 -0
  51. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/i18n.py +0 -0
  52. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/lib_util.py +0 -0
  53. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/log.py +0 -0
  54. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt/service.py +0 -0
  55. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/dependency_links.txt +0 -0
  56. {pyptt-2.0.2 → pyptt-2.0.4}/PyPtt.egg-info/top_level.txt +0 -0
  57. {pyptt-2.0.2 → pyptt-2.0.4}/README.md +0 -0
  58. {pyptt-2.0.2 → pyptt-2.0.4}/setup.cfg +0 -0
  59. {pyptt-2.0.2 → pyptt-2.0.4}/setup.py +0 -0
  60. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_change_pw.py +0 -0
  61. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_comment.py +0 -0
  62. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_del_post.py +0 -0
  63. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_exceptions.py +0 -0
  64. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_board_info.py +0 -0
  65. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_board_list.py +0 -0
  66. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_bottom_post_list.py +0 -0
  67. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_favourite_boards.py +0 -0
  68. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_mail.py +0 -0
  69. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_newest_index.py +0 -0
  70. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_post.py +0 -0
  71. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_post_list.py +0 -0
  72. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_time.py +0 -0
  73. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_get_user.py +0 -0
  74. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_give_money.py +0 -0
  75. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_i18n.py +0 -0
  76. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_init.py +0 -0
  77. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_logger.py +0 -0
  78. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_post.py +0 -0
  79. {pyptt-2.0.2 → pyptt-2.0.4}/tests/test_reply_post.py +0 -0
  80. {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.2
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.32.4
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
- search_list (List[str]): 搜尋清單。
300
- query (bool): 是否為查詢模式。
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
  更改密碼。
@@ -1,4 +1,4 @@
1
- __version__ = '2.0.2'
1
+ __version__ = '2.0.4'
2
2
 
3
3
  from .PTT import API
4
4
  from .data_type import *
@@ -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
- lock_post, post_author, post_title, post_aid, post_web, post_money, list_date, push_number, post_index = \
55
- _api_util.parse_query_post(
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
- cursor_line = [x for x in last_screen.split('\n') if x.strip().startswith(api.cursor)][0]
169
+ cursor_lines = [x for x in last_screen.split('\n') if x.strip().startswith(api.cursor)]
170
170
 
171
- list_index = int(re.compile(r'(\d+)').search(cursor_line).group(0))
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
- log.logger.debug(i18n.post_deleted)
161
- log.logger.debug('OriScreen', last_screen)
162
-
163
- cursor_line = [line for line in last_screen.split(
164
- '\n') if line.startswith(api.cursor)]
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
- if len(cursor_line) != 1:
167
- raise exceptions.UnknownError(last_screen)
178
+ if query:
179
+ post[PostField.pass_format_check] = True
180
+ return post
168
181
 
169
- cursor_line = cursor_line[0]
170
- log.logger.debug('CursorLine', cursor_line)
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
- pattern = re.compile(r'[\d]+\/[\d]+')
173
- pattern_result = pattern.search(cursor_line)
174
- if pattern_result is None:
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
- pattern = re.compile(r'\[[\w]+\]')
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
- # > 79843 9/11 - □ (本文已被吃掉)<
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
- elif index == 0:
213
-
214
- lock_post, post_author, post_title, post_aid, post_web, post_money, list_date, push_number, post_index = \
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 query:
235
- post.update({
236
- PostField.board: board,
237
- PostField.aid: post_aid,
238
- PostField.index: post_index,
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
- origin_post, has_control_code = _api_util.get_content(api)
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
- if origin_post is None:
251
- log.logger.info(i18n.post_deleted)
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
- post.update({
254
- PostField.board: board,
255
- PostField.aid: post_aid,
256
- PostField.index: post_index,
257
- PostField.author: post_author,
258
- PostField.title: post_title,
259
- PostField.url: post_web,
260
- PostField.money: post_money,
261
- PostField.list_date: list_date,
262
- PostField.has_control_code: has_control_code,
263
- PostField.pass_format_check: False,
264
- PostField.push_number: push_number,
265
- PostField.is_unconfirmed: api.Unconfirmed
266
- })
267
- return post
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
- post_date = None
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 = post_author = pattern_result.group(0)
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
- post_title = post_title[4:].strip()
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
- post_date = post_date[4:].strip()
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 not in origin_post:
397
- # print('Type 1')
398
- content_fail = True
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
- post.update({
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
- log.logger.debug(i18n.content, post_content)
327
+ post[PostField.comments] = _parse_comments(api, origin_post_lines)
328
+ post[PostField.pass_format_check] = True
329
+ return post
443
330
 
444
- info_lines = [line for line in origin_post_lines if line.startswith('※') or line.startswith('◆')]
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
- return post
511
- log.logger.debug('IP', ip)
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
- current_push = {
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
- push_list.append(current_push)
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 post
435
+ return push_list