PyPtt 2.3.15__tar.gz → 2.3.16__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.15 → pyptt-2.3.16}/PKG-INFO +1 -1
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/__init__.py +1 -1
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/connect_core.py +16 -16
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt.egg-info/PKG-INFO +1 -1
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt.egg-info/SOURCES.txt +2 -1
- pyptt-2.3.16/tests/test_websocket_endpoint.py +39 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/LICENSE +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/PTT.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_bucket.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_call_status.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_change_pw.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_comment.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_del_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_board_info.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_board_list.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_bottom_post_list.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_favourite_board.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_newest_index.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_post_index.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_post_list.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_time.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_user.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_get_waterball.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_give_money.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_has_new_mail.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_loginout.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_mail.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_mark_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_reply_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_search_user.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_set_board_title.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_set_signature_file.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_api_util.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_uao/LICENSE +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_uao/__init__.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_uao/b2u.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/_uao/u2b.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/api_server.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/check_value.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/command.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/config.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/data_type.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/exceptions.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/i18n.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/lang_en_US.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/lang_zh_TW.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/lib_util.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/log.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/screens.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/service.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt/ssl_config.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt.egg-info/dependency_links.txt +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt.egg-info/requires.txt +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/PyPtt.egg-info/top_level.txt +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/README.md +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/pyproject.toml +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/setup.cfg +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/setup.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_backup_effective.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_backup_param.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_board_setting_flow.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_bucket.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_change_pw.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_comment.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_connect_core_timeout.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_del_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_del_post_unit.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_exceptions.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_fast_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_board_info.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_board_info_parser.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_board_list.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_bottom_post_list.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_content_formula.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_favourite_boards.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_mail.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_newest_index.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_post_list.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_post_list_parser.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_post_parser.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_time.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_user.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_get_waterball.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_give_money.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_half_dead_connection.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_i18n.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_incremental_parity.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_init.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_logger.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_login_logout.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_mail_send_and_del.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_main_menu_status_bar.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_mark_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_new_screen_fixes.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_parse_query_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_post_anonymous.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_pyte_parity.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_reply_post.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_search_user.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_service.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_set_board_title.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_set_signature_file.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_status_line.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_uao_codec.py +0 -0
- {pyptt-2.3.15 → pyptt-2.3.16}/tests/test_vt100_parser.py +0 -0
|
@@ -179,6 +179,21 @@ class API(object):
|
|
|
179
179
|
asyncio.set_event_loop(self._loop)
|
|
180
180
|
return self._loop
|
|
181
181
|
|
|
182
|
+
def _websocket_endpoint(self) -> tuple[str, str]:
|
|
183
|
+
# PTT1 started answering wss://ws.ptt.cc/bbs/ (trailing slash) with HTTP 404 on
|
|
184
|
+
# 2026-11-06; term.ptt.cc connects to /bbs, so use the slash-less path everywhere
|
|
185
|
+
if self.config.host == data_type.HOST.PTT1:
|
|
186
|
+
return 'wss://ws.ptt.cc/bbs', 'https://term.ptt.cc'
|
|
187
|
+
if self.config.host == data_type.HOST.PTT2:
|
|
188
|
+
return 'wss://ws.ptt2.cc/bbs', 'https://term.ptt2.cc'
|
|
189
|
+
if self.config.host == data_type.HOST.LOCALHOST:
|
|
190
|
+
# ponytail: local pttbbs docker image (bbsdocker/imageptt) serves plain ws
|
|
191
|
+
# (no TLS) on 48763; config.port defaults to 23 (telnet default) so treat
|
|
192
|
+
# that as "not set" and fall back to imageptt's default port
|
|
193
|
+
port = self.config.port if self.config.port != 23 else 48763
|
|
194
|
+
return f'ws://localhost:{port}/bbs', 'http://localhost'
|
|
195
|
+
return f'wss://{self.config.host}', 'https://term.ptt.cc'
|
|
196
|
+
|
|
182
197
|
def connect(self) -> None:
|
|
183
198
|
def _wait():
|
|
184
199
|
for i in range(self.config.retry_wait_time):
|
|
@@ -198,22 +213,7 @@ class API(object):
|
|
|
198
213
|
|
|
199
214
|
self.current_encoding = 'big5uao'
|
|
200
215
|
|
|
201
|
-
|
|
202
|
-
websocket_host = 'wss://ws.ptt.cc/bbs/'
|
|
203
|
-
websocket_origin = 'https://term.ptt.cc'
|
|
204
|
-
elif self.config.host == data_type.HOST.PTT2:
|
|
205
|
-
websocket_host = 'wss://ws.ptt2.cc/bbs/'
|
|
206
|
-
websocket_origin = 'https://term.ptt2.cc'
|
|
207
|
-
elif self.config.host == data_type.HOST.LOCALHOST:
|
|
208
|
-
# ponytail: local pttbbs docker image (bbsdocker/imageptt) serves plain ws
|
|
209
|
-
# (no TLS) on 48763; config.port defaults to 23 (telnet default) so treat
|
|
210
|
-
# that as "not set" and fall back to imageptt's default port
|
|
211
|
-
port = self.config.port if self.config.port != 23 else 48763
|
|
212
|
-
websocket_host = f'ws://localhost:{port}/bbs'
|
|
213
|
-
websocket_origin = 'http://localhost'
|
|
214
|
-
else:
|
|
215
|
-
websocket_host = f'wss://{self.config.host}'
|
|
216
|
-
websocket_origin = 'https://term.ptt.cc'
|
|
216
|
+
websocket_host, websocket_origin = self._websocket_endpoint()
|
|
217
217
|
|
|
218
218
|
self._pending = []
|
|
219
219
|
connect_success = False
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""No-network regression test for the WebSocket endpoint connect_core dials.
|
|
2
|
+
|
|
3
|
+
On 2026-11-06 PTT1's gateway started answering wss://ws.ptt.cc/bbs/ (with a
|
|
4
|
+
trailing slash) with HTTP 404, so login died with ConnectError before reaching
|
|
5
|
+
any screen. term.ptt.cc connects to wss://ws.ptt.cc/bbs, which still works.
|
|
6
|
+
"""
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from PyPtt import config, connect_core, data_type
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FakeApi:
|
|
13
|
+
def __init__(self, host, port=23):
|
|
14
|
+
self.config = config.Config()
|
|
15
|
+
self.config.host = host
|
|
16
|
+
self.config.port = port
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _endpoint(host, port=23):
|
|
20
|
+
return connect_core.API(FakeApi(host, port))._websocket_endpoint()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.parametrize('host, url, origin', [
|
|
24
|
+
(data_type.HOST.PTT1, 'wss://ws.ptt.cc/bbs', 'https://term.ptt.cc'),
|
|
25
|
+
(data_type.HOST.PTT2, 'wss://ws.ptt2.cc/bbs', 'https://term.ptt2.cc'),
|
|
26
|
+
(data_type.HOST.LOCALHOST, 'ws://localhost:48763/bbs', 'http://localhost'),
|
|
27
|
+
])
|
|
28
|
+
def test_known_hosts(host, url, origin):
|
|
29
|
+
assert _endpoint(host) == (url, origin)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@pytest.mark.parametrize('host', [data_type.HOST.PTT1, data_type.HOST.PTT2, data_type.HOST.LOCALHOST])
|
|
33
|
+
def test_no_trailing_slash(host):
|
|
34
|
+
url, _ = _endpoint(host)
|
|
35
|
+
assert not url.endswith('/')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_localhost_custom_port():
|
|
39
|
+
assert _endpoint(data_type.HOST.LOCALHOST, port=12345)[0] == 'ws://localhost:12345/bbs'
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|