@fg-lab/amadeus-qq 1.2.1 → 1.2.2
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.
- package/LICENSE +21 -21
- package/README.md +55 -53
- package/README.zh-CN.md +51 -51
- package/REFERENCE.md +49 -49
- package/cordis.patch.yml +6 -6
- package/docs/DATA_MIGRATION.md +26 -26
- package/docs/INSTALL_V1_2.md +22 -22
- package/docs/PUBLISHING.md +28 -26
- package/docs/RELEASE_CHECKLIST.md +23 -23
- package/docs/RELEASE_NOTES.md +38 -38
- package/docs/UPGRADE_POLICY.md +26 -26
- package/docs/verification/IMAGE_READ_FIX_2026-11-07.md +22 -0
- package/docs/verification/LIFE_QQ_BOUNDARY_2026-11-07.md +20 -0
- package/lib/client.js +372 -372
- package/package.json +94 -91
- package/preset.patch.yml +63 -0
- package/scripts/symlink-security.mjs +1 -1
- package/src/account-profile.js +223 -223
- package/src/admin-http.js +361 -361
- package/src/audio.js +428 -428
- package/src/capabilities.js +785 -785
- package/src/client/factory.template.js +82 -82
- package/src/client/logic.js +290 -290
- package/src/config.js +2 -0
- package/src/console/console.css +28 -28
- package/src/console/console.js +3 -0
- package/src/console/index.html +67 -67
- package/src/core.js +21 -4
- package/src/files.js +230 -230
- package/src/index.js +135 -8
- package/src/lease.js +42 -42
- package/src/media.js +66 -3
- package/src/napcat-action-names.js +186 -186
- package/src/native-asr.js +112 -112
- package/src/onebot.js +378 -378
- package/src/outbound-media.js +309 -309
- package/src/owner-workspace.js +394 -394
- package/src/permissions.js +20 -0
- package/src/preset-guard.mjs +16 -2
- package/src/qzone-provider.js +628 -628
- package/src/session-provenance.js +74 -74
- package/src/setup.js +255 -255
- package/src/web-console.js +9 -9
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
// API identifiers inventoried from the installed local NapCat bundle on 2026-10-26.
|
|
2
|
-
// Presence in the bundle does not prove that the currently running backend supports an action.
|
|
3
|
-
export const NAPCAT_ACTION_NAMES = Object.freeze([
|
|
4
|
-
"_del_group_notice",
|
|
5
|
-
"_get_group_notice",
|
|
6
|
-
"_get_model_show",
|
|
7
|
-
"_mark_all_as_read",
|
|
8
|
-
"_send_group_notice",
|
|
9
|
-
"_set_model_show",
|
|
10
|
-
".get_word_slices",
|
|
11
|
-
".handle_quick_operation",
|
|
12
|
-
".ocr_image",
|
|
13
|
-
"add_custom_face",
|
|
14
|
-
"ArkShareGroup",
|
|
15
|
-
"ArkSharePeer",
|
|
16
|
-
"bot_exit",
|
|
17
|
-
"can_send_image",
|
|
18
|
-
"can_send_record",
|
|
19
|
-
"cancel_group_album_media_like",
|
|
20
|
-
"cancel_group_todo",
|
|
21
|
-
"cancel_online_file",
|
|
22
|
-
"check_url_safely",
|
|
23
|
-
"clean_cache",
|
|
24
|
-
"clean_stream_temp_file",
|
|
25
|
-
"click_inline_keyboard_button",
|
|
26
|
-
"complete_group_todo",
|
|
27
|
-
"create_collection",
|
|
28
|
-
"create_flash_task",
|
|
29
|
-
"create_group_file_folder",
|
|
30
|
-
"del_group_album_media",
|
|
31
|
-
"delete_custom_face",
|
|
32
|
-
"delete_essence_msg",
|
|
33
|
-
"delete_friend",
|
|
34
|
-
"delete_group_file",
|
|
35
|
-
"delete_group_folder",
|
|
36
|
-
"delete_msg",
|
|
37
|
-
"delete_qzone_msg",
|
|
38
|
-
"do_group_album_comment",
|
|
39
|
-
"download_file",
|
|
40
|
-
"download_file_image_stream",
|
|
41
|
-
"download_file_record_stream",
|
|
42
|
-
"download_file_stream",
|
|
43
|
-
"download_fileset",
|
|
44
|
-
"fetch_custom_face",
|
|
45
|
-
"fetch_custom_face_detail",
|
|
46
|
-
"fetch_emoji_like",
|
|
47
|
-
"fetch_ptt_text",
|
|
48
|
-
"forward_friend_single_msg",
|
|
49
|
-
"forward_group_single_msg",
|
|
50
|
-
"friend_poke",
|
|
51
|
-
"get_ai_characters",
|
|
52
|
-
"get_ai_record",
|
|
53
|
-
"get_clientkey",
|
|
54
|
-
"get_collection_list",
|
|
55
|
-
"get_cookies",
|
|
56
|
-
"get_credentials",
|
|
57
|
-
"get_csrf_token",
|
|
58
|
-
"get_doubt_friends_add_request",
|
|
59
|
-
"get_emoji_likes",
|
|
60
|
-
"get_essence_msg_list",
|
|
61
|
-
"get_file",
|
|
62
|
-
"get_fileset_id",
|
|
63
|
-
"get_fileset_info",
|
|
64
|
-
"get_flash_file_list",
|
|
65
|
-
"get_flash_file_url",
|
|
66
|
-
"get_forward_msg",
|
|
67
|
-
"get_friend_list",
|
|
68
|
-
"get_friend_msg_history",
|
|
69
|
-
"get_friends_with_category",
|
|
70
|
-
"get_group_album_media_list",
|
|
71
|
-
"get_group_at_all_remain",
|
|
72
|
-
"get_group_detail_info",
|
|
73
|
-
"get_group_file_system_info",
|
|
74
|
-
"get_group_file_url",
|
|
75
|
-
"get_group_files_by_folder",
|
|
76
|
-
"get_group_honor_info",
|
|
77
|
-
"get_group_ignore_add_request",
|
|
78
|
-
"get_group_ignored_notifies",
|
|
79
|
-
"get_group_info",
|
|
80
|
-
"get_group_info_ex",
|
|
81
|
-
"get_group_list",
|
|
82
|
-
"get_group_member_info",
|
|
83
|
-
"get_group_member_list",
|
|
84
|
-
"get_group_msg_history",
|
|
85
|
-
"get_group_root_files",
|
|
86
|
-
"get_group_share_link",
|
|
87
|
-
"get_group_shut_list",
|
|
88
|
-
"get_group_signed_list",
|
|
89
|
-
"get_group_system_msg",
|
|
90
|
-
"get_guild_list",
|
|
91
|
-
"get_guild_service_profile",
|
|
92
|
-
"get_image",
|
|
93
|
-
"get_login_info",
|
|
94
|
-
"get_mini_app_ark",
|
|
95
|
-
"get_msg",
|
|
96
|
-
"get_online_clients",
|
|
97
|
-
"get_online_file_msg",
|
|
98
|
-
"get_private_file_url",
|
|
99
|
-
"get_profile_like",
|
|
100
|
-
"get_qun_album_list",
|
|
101
|
-
"get_recent_contact",
|
|
102
|
-
"get_record",
|
|
103
|
-
"get_rkey",
|
|
104
|
-
"get_rkey_server",
|
|
105
|
-
"get_robot_uin_range",
|
|
106
|
-
"get_share_link",
|
|
107
|
-
"get_status",
|
|
108
|
-
"get_stranger_info",
|
|
109
|
-
"get_unidirectional_friend_list",
|
|
110
|
-
"get_version_info",
|
|
111
|
-
"group_poke",
|
|
112
|
-
"mark_group_msg_as_read",
|
|
113
|
-
"mark_msg_as_read",
|
|
114
|
-
"mark_private_msg_as_read",
|
|
115
|
-
"move_group_file",
|
|
116
|
-
"nc_get_packet_status",
|
|
117
|
-
"nc_get_rkey",
|
|
118
|
-
"nc_get_user_status",
|
|
119
|
-
"ocr_image",
|
|
120
|
-
"receive_online_file",
|
|
121
|
-
"refuse_online_file",
|
|
122
|
-
"rename_group_file",
|
|
123
|
-
"send_ark_share",
|
|
124
|
-
"send_flash_msg",
|
|
125
|
-
"send_forward_msg",
|
|
126
|
-
"send_group_ai_record",
|
|
127
|
-
"send_group_ark_share",
|
|
128
|
-
"send_group_forward_msg",
|
|
129
|
-
"send_group_msg",
|
|
130
|
-
"send_group_sign",
|
|
131
|
-
"send_like",
|
|
132
|
-
"send_msg",
|
|
133
|
-
"send_online_file",
|
|
134
|
-
"send_online_folder",
|
|
135
|
-
"send_packet",
|
|
136
|
-
"send_poke",
|
|
137
|
-
"send_private_forward_msg",
|
|
138
|
-
"send_private_msg",
|
|
139
|
-
"send_qzone_msg",
|
|
140
|
-
"set_custom_face_desc",
|
|
141
|
-
"set_diy_online_status",
|
|
142
|
-
"set_doubt_friends_add_request",
|
|
143
|
-
"set_essence_msg",
|
|
144
|
-
"set_friend_add_request",
|
|
145
|
-
"set_friend_remark",
|
|
146
|
-
"set_group_add_option",
|
|
147
|
-
"set_group_add_request",
|
|
148
|
-
"set_group_admin",
|
|
149
|
-
"set_group_album_media_like",
|
|
150
|
-
"set_group_ban",
|
|
151
|
-
"set_group_card",
|
|
152
|
-
"set_group_kick",
|
|
153
|
-
"set_group_kick_members",
|
|
154
|
-
"set_group_leave",
|
|
155
|
-
"set_group_member_invite_policy",
|
|
156
|
-
"set_group_member_permissions",
|
|
157
|
-
"set_group_name",
|
|
158
|
-
"set_group_new_member_history_visibility",
|
|
159
|
-
"set_group_portrait",
|
|
160
|
-
"set_group_remark",
|
|
161
|
-
"set_group_robot_add_option",
|
|
162
|
-
"set_group_search",
|
|
163
|
-
"set_group_sign",
|
|
164
|
-
"set_group_special_title",
|
|
165
|
-
"set_group_todo",
|
|
166
|
-
"set_group_whole_ban",
|
|
167
|
-
"set_input_status",
|
|
168
|
-
"set_msg_emoji_like",
|
|
169
|
-
"set_online_status",
|
|
170
|
-
"set_qq_avatar",
|
|
171
|
-
"set_qq_profile",
|
|
172
|
-
"set_restart",
|
|
173
|
-
"set_self_longnick",
|
|
174
|
-
"test_auto_register_01",
|
|
175
|
-
"test_auto_register_02",
|
|
176
|
-
"test_download_stream",
|
|
177
|
-
"trans_group_file",
|
|
178
|
-
"translate_en2zh",
|
|
179
|
-
"unknown",
|
|
180
|
-
"upload_file_stream",
|
|
181
|
-
"upload_group_file",
|
|
182
|
-
"upload_image_to_qun_album",
|
|
183
|
-
"upload_images_to_qun_album",
|
|
184
|
-
"upload_private_file",
|
|
185
|
-
"upload_video_to_qun_album",
|
|
186
|
-
])
|
|
1
|
+
// API identifiers inventoried from the installed local NapCat bundle on 2026-10-26.
|
|
2
|
+
// Presence in the bundle does not prove that the currently running backend supports an action.
|
|
3
|
+
export const NAPCAT_ACTION_NAMES = Object.freeze([
|
|
4
|
+
"_del_group_notice",
|
|
5
|
+
"_get_group_notice",
|
|
6
|
+
"_get_model_show",
|
|
7
|
+
"_mark_all_as_read",
|
|
8
|
+
"_send_group_notice",
|
|
9
|
+
"_set_model_show",
|
|
10
|
+
".get_word_slices",
|
|
11
|
+
".handle_quick_operation",
|
|
12
|
+
".ocr_image",
|
|
13
|
+
"add_custom_face",
|
|
14
|
+
"ArkShareGroup",
|
|
15
|
+
"ArkSharePeer",
|
|
16
|
+
"bot_exit",
|
|
17
|
+
"can_send_image",
|
|
18
|
+
"can_send_record",
|
|
19
|
+
"cancel_group_album_media_like",
|
|
20
|
+
"cancel_group_todo",
|
|
21
|
+
"cancel_online_file",
|
|
22
|
+
"check_url_safely",
|
|
23
|
+
"clean_cache",
|
|
24
|
+
"clean_stream_temp_file",
|
|
25
|
+
"click_inline_keyboard_button",
|
|
26
|
+
"complete_group_todo",
|
|
27
|
+
"create_collection",
|
|
28
|
+
"create_flash_task",
|
|
29
|
+
"create_group_file_folder",
|
|
30
|
+
"del_group_album_media",
|
|
31
|
+
"delete_custom_face",
|
|
32
|
+
"delete_essence_msg",
|
|
33
|
+
"delete_friend",
|
|
34
|
+
"delete_group_file",
|
|
35
|
+
"delete_group_folder",
|
|
36
|
+
"delete_msg",
|
|
37
|
+
"delete_qzone_msg",
|
|
38
|
+
"do_group_album_comment",
|
|
39
|
+
"download_file",
|
|
40
|
+
"download_file_image_stream",
|
|
41
|
+
"download_file_record_stream",
|
|
42
|
+
"download_file_stream",
|
|
43
|
+
"download_fileset",
|
|
44
|
+
"fetch_custom_face",
|
|
45
|
+
"fetch_custom_face_detail",
|
|
46
|
+
"fetch_emoji_like",
|
|
47
|
+
"fetch_ptt_text",
|
|
48
|
+
"forward_friend_single_msg",
|
|
49
|
+
"forward_group_single_msg",
|
|
50
|
+
"friend_poke",
|
|
51
|
+
"get_ai_characters",
|
|
52
|
+
"get_ai_record",
|
|
53
|
+
"get_clientkey",
|
|
54
|
+
"get_collection_list",
|
|
55
|
+
"get_cookies",
|
|
56
|
+
"get_credentials",
|
|
57
|
+
"get_csrf_token",
|
|
58
|
+
"get_doubt_friends_add_request",
|
|
59
|
+
"get_emoji_likes",
|
|
60
|
+
"get_essence_msg_list",
|
|
61
|
+
"get_file",
|
|
62
|
+
"get_fileset_id",
|
|
63
|
+
"get_fileset_info",
|
|
64
|
+
"get_flash_file_list",
|
|
65
|
+
"get_flash_file_url",
|
|
66
|
+
"get_forward_msg",
|
|
67
|
+
"get_friend_list",
|
|
68
|
+
"get_friend_msg_history",
|
|
69
|
+
"get_friends_with_category",
|
|
70
|
+
"get_group_album_media_list",
|
|
71
|
+
"get_group_at_all_remain",
|
|
72
|
+
"get_group_detail_info",
|
|
73
|
+
"get_group_file_system_info",
|
|
74
|
+
"get_group_file_url",
|
|
75
|
+
"get_group_files_by_folder",
|
|
76
|
+
"get_group_honor_info",
|
|
77
|
+
"get_group_ignore_add_request",
|
|
78
|
+
"get_group_ignored_notifies",
|
|
79
|
+
"get_group_info",
|
|
80
|
+
"get_group_info_ex",
|
|
81
|
+
"get_group_list",
|
|
82
|
+
"get_group_member_info",
|
|
83
|
+
"get_group_member_list",
|
|
84
|
+
"get_group_msg_history",
|
|
85
|
+
"get_group_root_files",
|
|
86
|
+
"get_group_share_link",
|
|
87
|
+
"get_group_shut_list",
|
|
88
|
+
"get_group_signed_list",
|
|
89
|
+
"get_group_system_msg",
|
|
90
|
+
"get_guild_list",
|
|
91
|
+
"get_guild_service_profile",
|
|
92
|
+
"get_image",
|
|
93
|
+
"get_login_info",
|
|
94
|
+
"get_mini_app_ark",
|
|
95
|
+
"get_msg",
|
|
96
|
+
"get_online_clients",
|
|
97
|
+
"get_online_file_msg",
|
|
98
|
+
"get_private_file_url",
|
|
99
|
+
"get_profile_like",
|
|
100
|
+
"get_qun_album_list",
|
|
101
|
+
"get_recent_contact",
|
|
102
|
+
"get_record",
|
|
103
|
+
"get_rkey",
|
|
104
|
+
"get_rkey_server",
|
|
105
|
+
"get_robot_uin_range",
|
|
106
|
+
"get_share_link",
|
|
107
|
+
"get_status",
|
|
108
|
+
"get_stranger_info",
|
|
109
|
+
"get_unidirectional_friend_list",
|
|
110
|
+
"get_version_info",
|
|
111
|
+
"group_poke",
|
|
112
|
+
"mark_group_msg_as_read",
|
|
113
|
+
"mark_msg_as_read",
|
|
114
|
+
"mark_private_msg_as_read",
|
|
115
|
+
"move_group_file",
|
|
116
|
+
"nc_get_packet_status",
|
|
117
|
+
"nc_get_rkey",
|
|
118
|
+
"nc_get_user_status",
|
|
119
|
+
"ocr_image",
|
|
120
|
+
"receive_online_file",
|
|
121
|
+
"refuse_online_file",
|
|
122
|
+
"rename_group_file",
|
|
123
|
+
"send_ark_share",
|
|
124
|
+
"send_flash_msg",
|
|
125
|
+
"send_forward_msg",
|
|
126
|
+
"send_group_ai_record",
|
|
127
|
+
"send_group_ark_share",
|
|
128
|
+
"send_group_forward_msg",
|
|
129
|
+
"send_group_msg",
|
|
130
|
+
"send_group_sign",
|
|
131
|
+
"send_like",
|
|
132
|
+
"send_msg",
|
|
133
|
+
"send_online_file",
|
|
134
|
+
"send_online_folder",
|
|
135
|
+
"send_packet",
|
|
136
|
+
"send_poke",
|
|
137
|
+
"send_private_forward_msg",
|
|
138
|
+
"send_private_msg",
|
|
139
|
+
"send_qzone_msg",
|
|
140
|
+
"set_custom_face_desc",
|
|
141
|
+
"set_diy_online_status",
|
|
142
|
+
"set_doubt_friends_add_request",
|
|
143
|
+
"set_essence_msg",
|
|
144
|
+
"set_friend_add_request",
|
|
145
|
+
"set_friend_remark",
|
|
146
|
+
"set_group_add_option",
|
|
147
|
+
"set_group_add_request",
|
|
148
|
+
"set_group_admin",
|
|
149
|
+
"set_group_album_media_like",
|
|
150
|
+
"set_group_ban",
|
|
151
|
+
"set_group_card",
|
|
152
|
+
"set_group_kick",
|
|
153
|
+
"set_group_kick_members",
|
|
154
|
+
"set_group_leave",
|
|
155
|
+
"set_group_member_invite_policy",
|
|
156
|
+
"set_group_member_permissions",
|
|
157
|
+
"set_group_name",
|
|
158
|
+
"set_group_new_member_history_visibility",
|
|
159
|
+
"set_group_portrait",
|
|
160
|
+
"set_group_remark",
|
|
161
|
+
"set_group_robot_add_option",
|
|
162
|
+
"set_group_search",
|
|
163
|
+
"set_group_sign",
|
|
164
|
+
"set_group_special_title",
|
|
165
|
+
"set_group_todo",
|
|
166
|
+
"set_group_whole_ban",
|
|
167
|
+
"set_input_status",
|
|
168
|
+
"set_msg_emoji_like",
|
|
169
|
+
"set_online_status",
|
|
170
|
+
"set_qq_avatar",
|
|
171
|
+
"set_qq_profile",
|
|
172
|
+
"set_restart",
|
|
173
|
+
"set_self_longnick",
|
|
174
|
+
"test_auto_register_01",
|
|
175
|
+
"test_auto_register_02",
|
|
176
|
+
"test_download_stream",
|
|
177
|
+
"trans_group_file",
|
|
178
|
+
"translate_en2zh",
|
|
179
|
+
"unknown",
|
|
180
|
+
"upload_file_stream",
|
|
181
|
+
"upload_group_file",
|
|
182
|
+
"upload_image_to_qun_album",
|
|
183
|
+
"upload_images_to_qun_album",
|
|
184
|
+
"upload_private_file",
|
|
185
|
+
"upload_video_to_qun_album",
|
|
186
|
+
])
|
package/src/native-asr.js
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
import { normalizeMessageId } from './storage.js'
|
|
2
|
-
import { sanitizeInboundSegments } from './onebot.js'
|
|
3
|
-
|
|
4
|
-
const MAX_TRANSCRIPT_CHARS = 4000
|
|
5
|
-
const MAX_RESPONSE_BYTES = 64 * 1024
|
|
6
|
-
const PLATFORM_TIMEOUT_MS = 10_000
|
|
7
|
-
|
|
8
|
-
function asrError(code) {
|
|
9
|
-
const error = new Error(code)
|
|
10
|
-
error.code = code
|
|
11
|
-
return error
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function readPermissionError(changed = false) {
|
|
15
|
-
return asrError(changed ? 'QQ_PERMISSION_CHANGED: qq.read' : 'QQ_PERMISSION_DENIED: qq.read')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function assertRead(core, sessionId, changed = false) {
|
|
19
|
-
let allowed = false
|
|
20
|
-
try {
|
|
21
|
-
allowed = core.authorizeSessionCapability(sessionId, 'qq.read') === true
|
|
22
|
-
} catch {
|
|
23
|
-
allowed = false
|
|
24
|
-
}
|
|
25
|
-
if (!allowed) throw readPermissionError(changed)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function authorizeScene(core, sessionId) {
|
|
29
|
-
try {
|
|
30
|
-
return core.authorizedScene(sessionId)
|
|
31
|
-
} catch (error) {
|
|
32
|
-
const changed = String(error?.message ?? '').startsWith('QQ_PERMISSION_CHANGED')
|
|
33
|
-
throw readPermissionError(changed)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function boundedText(value) {
|
|
38
|
-
if (typeof value !== 'string') throw asrError('QQ_NATIVE_ASR_INVALID_RESPONSE')
|
|
39
|
-
const text = value
|
|
40
|
-
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, '')
|
|
41
|
-
.trim()
|
|
42
|
-
if (text.length === 0) throw asrError('QQ_NATIVE_ASR_EMPTY')
|
|
43
|
-
if (text.length > MAX_TRANSCRIPT_CHARS) throw asrError('QQ_NATIVE_ASR_TOO_LONG')
|
|
44
|
-
return text
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Use NapCat/QQ's native PTT transcript for one stored record message. The
|
|
49
|
-
* model supplies only messageId; Scene, sender and transcript storage target
|
|
50
|
-
* all come from the current authorized Session.
|
|
51
|
-
*/
|
|
52
|
-
export async function transcribeMessageRecordNative({
|
|
53
|
-
core,
|
|
54
|
-
store,
|
|
55
|
-
transport,
|
|
56
|
-
sessionId,
|
|
57
|
-
messageId,
|
|
58
|
-
}) {
|
|
59
|
-
if (!transport || typeof transport.action !== 'function') throw asrError('QQ_NATIVE_ASR_UNAVAILABLE')
|
|
60
|
-
if (transport.status !== undefined && transport.status !== 'connected') throw asrError('QQ_NATIVE_ASR_UNAVAILABLE')
|
|
61
|
-
|
|
62
|
-
let id
|
|
63
|
-
try {
|
|
64
|
-
id = normalizeMessageId(messageId, 'messageId')
|
|
65
|
-
} catch {
|
|
66
|
-
throw asrError('QQ_NATIVE_ASR_MESSAGE_INVALID')
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const scene = authorizeScene(core, sessionId)
|
|
70
|
-
assertRead(core, sessionId)
|
|
71
|
-
|
|
72
|
-
const row = store.getScene(scene)
|
|
73
|
-
const message = row?.messages?.find(item => item.messageId === id)
|
|
74
|
-
if (!message) throw asrError('QQ_MESSAGE_SCOPE')
|
|
75
|
-
|
|
76
|
-
let segments
|
|
77
|
-
try {
|
|
78
|
-
segments = sanitizeInboundSegments(message.segments)
|
|
79
|
-
} catch {
|
|
80
|
-
throw asrError('QQ_NATIVE_ASR_MESSAGE_INVALID')
|
|
81
|
-
}
|
|
82
|
-
if (!segments.some(segment => segment.type === 'record')) throw asrError('QQ_NATIVE_ASR_NOT_RECORD')
|
|
83
|
-
|
|
84
|
-
let response
|
|
85
|
-
try {
|
|
86
|
-
response = await transport.action('fetch_ptt_text', { message_id: id }, PLATFORM_TIMEOUT_MS, MAX_RESPONSE_BYTES)
|
|
87
|
-
} catch (error) {
|
|
88
|
-
throw asrError(error?.code === 'ONEBOT_RESPONSE_TOO_LARGE'
|
|
89
|
-
? 'QQ_NATIVE_ASR_RESPONSE_TOO_LARGE'
|
|
90
|
-
: 'QQ_NATIVE_ASR_UNAVAILABLE')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
assertRead(core, sessionId, true)
|
|
94
|
-
if (!response || typeof response !== 'object' || Array.isArray(response)) {
|
|
95
|
-
throw asrError('QQ_NATIVE_ASR_INVALID_RESPONSE')
|
|
96
|
-
}
|
|
97
|
-
const text = boundedText(response.text)
|
|
98
|
-
assertRead(core, sessionId, true)
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
store.setMessageTranscript(scene, id, text)
|
|
102
|
-
} catch {
|
|
103
|
-
throw asrError('QQ_NATIVE_ASR_STORAGE_FAILED')
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
text,
|
|
108
|
-
messageId: id,
|
|
109
|
-
senderId: message.senderId,
|
|
110
|
-
scene: { botId: scene.botId, kind: scene.kind, peerId: scene.peerId },
|
|
111
|
-
}
|
|
112
|
-
}
|
|
1
|
+
import { normalizeMessageId } from './storage.js'
|
|
2
|
+
import { sanitizeInboundSegments } from './onebot.js'
|
|
3
|
+
|
|
4
|
+
const MAX_TRANSCRIPT_CHARS = 4000
|
|
5
|
+
const MAX_RESPONSE_BYTES = 64 * 1024
|
|
6
|
+
const PLATFORM_TIMEOUT_MS = 10_000
|
|
7
|
+
|
|
8
|
+
function asrError(code) {
|
|
9
|
+
const error = new Error(code)
|
|
10
|
+
error.code = code
|
|
11
|
+
return error
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readPermissionError(changed = false) {
|
|
15
|
+
return asrError(changed ? 'QQ_PERMISSION_CHANGED: qq.read' : 'QQ_PERMISSION_DENIED: qq.read')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function assertRead(core, sessionId, changed = false) {
|
|
19
|
+
let allowed = false
|
|
20
|
+
try {
|
|
21
|
+
allowed = core.authorizeSessionCapability(sessionId, 'qq.read') === true
|
|
22
|
+
} catch {
|
|
23
|
+
allowed = false
|
|
24
|
+
}
|
|
25
|
+
if (!allowed) throw readPermissionError(changed)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function authorizeScene(core, sessionId) {
|
|
29
|
+
try {
|
|
30
|
+
return core.authorizedScene(sessionId)
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const changed = String(error?.message ?? '').startsWith('QQ_PERMISSION_CHANGED')
|
|
33
|
+
throw readPermissionError(changed)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function boundedText(value) {
|
|
38
|
+
if (typeof value !== 'string') throw asrError('QQ_NATIVE_ASR_INVALID_RESPONSE')
|
|
39
|
+
const text = value
|
|
40
|
+
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, '')
|
|
41
|
+
.trim()
|
|
42
|
+
if (text.length === 0) throw asrError('QQ_NATIVE_ASR_EMPTY')
|
|
43
|
+
if (text.length > MAX_TRANSCRIPT_CHARS) throw asrError('QQ_NATIVE_ASR_TOO_LONG')
|
|
44
|
+
return text
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Use NapCat/QQ's native PTT transcript for one stored record message. The
|
|
49
|
+
* model supplies only messageId; Scene, sender and transcript storage target
|
|
50
|
+
* all come from the current authorized Session.
|
|
51
|
+
*/
|
|
52
|
+
export async function transcribeMessageRecordNative({
|
|
53
|
+
core,
|
|
54
|
+
store,
|
|
55
|
+
transport,
|
|
56
|
+
sessionId,
|
|
57
|
+
messageId,
|
|
58
|
+
}) {
|
|
59
|
+
if (!transport || typeof transport.action !== 'function') throw asrError('QQ_NATIVE_ASR_UNAVAILABLE')
|
|
60
|
+
if (transport.status !== undefined && transport.status !== 'connected') throw asrError('QQ_NATIVE_ASR_UNAVAILABLE')
|
|
61
|
+
|
|
62
|
+
let id
|
|
63
|
+
try {
|
|
64
|
+
id = normalizeMessageId(messageId, 'messageId')
|
|
65
|
+
} catch {
|
|
66
|
+
throw asrError('QQ_NATIVE_ASR_MESSAGE_INVALID')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const scene = authorizeScene(core, sessionId)
|
|
70
|
+
assertRead(core, sessionId)
|
|
71
|
+
|
|
72
|
+
const row = store.getScene(scene)
|
|
73
|
+
const message = row?.messages?.find(item => item.messageId === id)
|
|
74
|
+
if (!message) throw asrError('QQ_MESSAGE_SCOPE')
|
|
75
|
+
|
|
76
|
+
let segments
|
|
77
|
+
try {
|
|
78
|
+
segments = sanitizeInboundSegments(message.segments)
|
|
79
|
+
} catch {
|
|
80
|
+
throw asrError('QQ_NATIVE_ASR_MESSAGE_INVALID')
|
|
81
|
+
}
|
|
82
|
+
if (!segments.some(segment => segment.type === 'record')) throw asrError('QQ_NATIVE_ASR_NOT_RECORD')
|
|
83
|
+
|
|
84
|
+
let response
|
|
85
|
+
try {
|
|
86
|
+
response = await transport.action('fetch_ptt_text', { message_id: id }, PLATFORM_TIMEOUT_MS, MAX_RESPONSE_BYTES)
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw asrError(error?.code === 'ONEBOT_RESPONSE_TOO_LARGE'
|
|
89
|
+
? 'QQ_NATIVE_ASR_RESPONSE_TOO_LARGE'
|
|
90
|
+
: 'QQ_NATIVE_ASR_UNAVAILABLE')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
assertRead(core, sessionId, true)
|
|
94
|
+
if (!response || typeof response !== 'object' || Array.isArray(response)) {
|
|
95
|
+
throw asrError('QQ_NATIVE_ASR_INVALID_RESPONSE')
|
|
96
|
+
}
|
|
97
|
+
const text = boundedText(response.text)
|
|
98
|
+
assertRead(core, sessionId, true)
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
store.setMessageTranscript(scene, id, text)
|
|
102
|
+
} catch {
|
|
103
|
+
throw asrError('QQ_NATIVE_ASR_STORAGE_FAILED')
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
text,
|
|
108
|
+
messageId: id,
|
|
109
|
+
senderId: message.senderId,
|
|
110
|
+
scene: { botId: scene.botId, kind: scene.kind, peerId: scene.peerId },
|
|
111
|
+
}
|
|
112
|
+
}
|