gs-x-parser 1.0.0

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/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # GS X Parser
2
+
3
+ [中文](README.zh.md)
4
+
5
+ ## Project Introduction
6
+
7
+ GS X Parser is a TypeScript library for parsing Twitter/X API responses, providing complete type definitions and parsing functionality, supporting the conversion of complex API responses into simple and easy-to-use formats.
8
+
9
+ ## Features
10
+
11
+ - Complete TypeScript type definitions based on Twitter/X API data structures
12
+ - Support for parsing multiple data types such as users, tweets, media, etc.
13
+ - Provide two parsing modes: simple mode and original mode
14
+ - Support data extraction from various API responses
15
+ - Type safety to ensure reliable data processing
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ # Using npm
21
+ npm install gs-x-parser
22
+
23
+ # Using yarn
24
+ yarn add gs-x-parser
25
+ ```
26
+
27
+ ## Usage Examples
28
+
29
+ ### Parse Simple Mode
30
+
31
+ ```typescript
32
+ import { XParser } from './src/parser/XParser';
33
+
34
+ // Parse API response
35
+ const data = {/* data from API */};
36
+ const result = XParser.parseSimple(data);
37
+
38
+ // Access parsed results
39
+ console.log(result.users); // Simple user list
40
+ console.log(result.tweets); // Simple tweet list
41
+ console.log(result.photos); // Tweets with photos
42
+ console.log(result.videos); // Tweets with videos
43
+ console.log(result.urls); // Tweets with URLs
44
+ ```
45
+
46
+ ### Parse Original Mode
47
+
48
+ ```typescript
49
+ import { XParser } from './src/parser/XParser';
50
+
51
+ // Parse API response
52
+ const data = {/* data from API */};
53
+ const result = XParser.parseOriginal(data);
54
+
55
+ // Access parsed results
56
+ console.log(result.users); // Original user list
57
+ console.log(result.tweets); // Original tweet list
58
+ console.log(result.photos); // Tweets with photos
59
+ console.log(result.videos); // Tweets with videos
60
+ console.log(result.urls); // Tweets with URLs
61
+ ```
62
+
63
+ ## Type Definitions
64
+
65
+ The project provides complete type definitions in the `src/type` directory, including:
66
+
67
+ - `IUser` - User type
68
+ - `ITweet` - Tweet type
69
+ - `IMediaEntity` - Media entity type
70
+ - `ISimpleUser` - Simple user type
71
+ - `ISimpleTweet` - Simple tweet type
72
+ - `ISimpleResult` - Simple result type
73
+ - `IOriginalResult` - Original result type
74
+
75
+ ## License
76
+
77
+ This project uses the MIT license. For details, please see the [LICENSE](LICENSE) file.
package/README.zh.md ADDED
@@ -0,0 +1,77 @@
1
+ # GS X Parser
2
+
3
+ [English](README.md)
4
+
5
+ ## 项目简介
6
+
7
+ GS X Parser 是一个用于解析 Twitter/X API 响应的 TypeScript 库,提供了完整的类型定义和解析功能,支持将复杂的 API 响应转换为简单易用的格式。
8
+
9
+ ## 功能特性
10
+
11
+ - 完整的 TypeScript 类型定义,基于 Twitter/X API 数据结构
12
+ - 支持解析用户、推文、媒体等多种数据类型
13
+ - 提供简单模式和原始模式两种解析方式
14
+ - 支持从各种 API 响应中提取数据
15
+ - 类型安全,确保数据处理的可靠性
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ # 使用 npm
21
+ npm install gs-x-parser
22
+
23
+ # 使用 yarn
24
+ yarn add gs-x-parser
25
+ ```
26
+
27
+ ## 使用示例
28
+
29
+ ### 解析简单模式
30
+
31
+ ```typescript
32
+ import { XParser } from './src/parser/XParser';
33
+
34
+ // 解析 API 响应
35
+ const data = {/* 从 API 获取的数据 */};
36
+ const result = XParser.parseSimple(data);
37
+
38
+ // 访问解析结果
39
+ console.log(result.users); // 简单用户列表
40
+ console.log(result.tweets); // 简单推文列表
41
+ console.log(result.photos); // 包含图片的推文
42
+ console.log(result.videos); // 包含视频的推文
43
+ console.log(result.urls); // 包含链接的推文
44
+ ```
45
+
46
+ ### 解析原始模式
47
+
48
+ ```typescript
49
+ import { XParser } from './src/parser/XParser';
50
+
51
+ // 解析 API 响应
52
+ const data = {/* 从 API 获取的数据 */};
53
+ const result = XParser.parseOriginal(data);
54
+
55
+ // 访问解析结果
56
+ console.log(result.users); // 原始用户列表
57
+ console.log(result.tweets); // 原始推文列表
58
+ console.log(result.photos); // 包含图片的推文
59
+ console.log(result.videos); // 包含视频的推文
60
+ console.log(result.urls); // 包含链接的推文
61
+ ```
62
+
63
+ ## 类型定义
64
+
65
+ 项目在 `src/type` 目录下提供了完整的类型定义,包括:
66
+
67
+ - `IUser` - 用户类型
68
+ - `ITweet` - 推文类型
69
+ - `IMediaEntity` - 媒体实体类型
70
+ - `ISimpleUser` - 简单用户类型
71
+ - `ISimpleTweet` - 简单推文类型
72
+ - `ISimpleResult` - 简单结果类型
73
+ - `IOriginalResult` - 原始结果类型
74
+
75
+ ## 许可证
76
+
77
+ 本项目使用 MIT 许可证,详情请查看 [LICENSE](LICENSE) 文件。
package/lib/index.cjs ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var type = require('./type.cjs'), parser = require('./parser.cjs');
3
+ Object.keys(type).forEach(function(k) {
4
+ k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k) && Object.defineProperty(exports, k, {
5
+ enumerable: !0,
6
+ get: function() {
7
+ return type[k];
8
+ }
9
+ });
10
+ }), Object.keys(parser).forEach(function(k) {
11
+ k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k) && Object.defineProperty(exports, k, {
12
+ enumerable: !0,
13
+ get: function() {
14
+ return parser[k];
15
+ }
16
+ });
17
+ });
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './type.d.ts';
2
+ export * from './parser.d.ts';
package/lib/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ export * from './type.mjs';
2
+ export * from './parser.mjs';
package/lib/parser.cjs ADDED
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ class XParser {
3
+ /**
4
+ * 解析任意对象为ISimpleResult
5
+ * @param data 任意对象
6
+ * @returns ISimpleResult
7
+ */
8
+ static parseSimple(data) {
9
+ const result = {}, users = this.#extractUsers(data);
10
+ users.length > 0 && (result.users = users.map((user) => this.convertToSimpleUser(user)));
11
+ const tweets = this.#extractTweets(data);
12
+ return tweets.length > 0 && (result.tweets = tweets.map((tweet) => this.convertToSimpleTweet(tweet)), result.photos = result.tweets.filter((tweet) => tweet.photos && tweet.photos.length > 0), result.videos = result.tweets.filter((tweet) => tweet.videos && tweet.videos.length > 0), result.urls = result.tweets.filter((tweet) => tweet.urls && tweet.urls.length > 0)), this.#extractCursors(data, result), result;
13
+ }
14
+ /**
15
+ * 解析任意对象为IOriginalResult
16
+ * @param data 任意对象
17
+ * @returns IOriginalResult
18
+ */
19
+ static parseOriginal(data) {
20
+ const result = {}, users = this.#extractUsers(data);
21
+ users.length > 0 && (result.users = users);
22
+ const tweets = this.#extractTweets(data);
23
+ return tweets.length > 0 && (result.tweets = tweets, result.photos = result.tweets.filter((tweet) => {
24
+ const legacy = tweet.legacy;
25
+ return legacy && legacy.extended_entities && legacy.extended_entities.media && legacy.extended_entities.media.some((media) => media.type === "photo");
26
+ }), result.videos = result.tweets.filter((tweet) => {
27
+ const legacy = tweet.legacy;
28
+ return legacy && legacy.extended_entities && legacy.extended_entities.media && legacy.extended_entities.media.some((media) => media.type === "video" || media.type === "animated_gif");
29
+ }), result.urls = result.tweets.filter((tweet) => {
30
+ const legacy = tweet.legacy;
31
+ return legacy && legacy.entities && legacy.entities.urls && legacy.entities.urls.length > 0;
32
+ })), this.#extractCursors(data, result), result;
33
+ }
34
+ /**
35
+ * 将IUser转换为ISimpleUser
36
+ * @param user IUser对象
37
+ * @returns ISimpleUser
38
+ */
39
+ static convertToSimpleUser(user) {
40
+ return {
41
+ rest_id: user.rest_id,
42
+ name: user.legacy?.name || "",
43
+ screen_name: user.legacy?.screen_name || "",
44
+ profile_image_url_https: user.legacy?.profile_image_url_https || "",
45
+ verified: user.legacy?.verified,
46
+ followers_count: user.legacy?.followers_count,
47
+ friends_count: user.legacy?.friends_count,
48
+ statuses_count: user.legacy?.statuses_count,
49
+ description: user.legacy?.description,
50
+ location: user.legacy?.location,
51
+ url: user.legacy?.url,
52
+ userLabelType: user.affiliates_highlighted_label?.label?.userLabelType,
53
+ verified_type: user.legacy?.verified_type
54
+ };
55
+ }
56
+ /**
57
+ * 将ITweet转换为ISimpleTweet
58
+ * @param tweet ITweet对象
59
+ * @returns ISimpleTweet
60
+ */
61
+ static convertToSimpleTweet(tweet) {
62
+ const legacy = tweet.legacy, simpleTweet = {
63
+ rest_id: tweet.rest_id,
64
+ full_text: legacy?.full_text || "",
65
+ created_at: legacy?.created_at || "",
66
+ user_id: legacy?.user_id_str || "",
67
+ user_screen_name: "",
68
+ // 需要从用户信息中获取
69
+ retweet_count: legacy?.retweet_count,
70
+ favorite_count: legacy?.favorite_count,
71
+ reply_count: legacy?.reply_count,
72
+ quote_count: legacy?.quote_count,
73
+ lang: legacy?.lang,
74
+ conversation_id: legacy?.conversation_id_str,
75
+ possibly_sensitive: legacy?.possibly_sensitive,
76
+ is_retweet: !!legacy?.retweeted_status_id_str,
77
+ retweeted_status_id: legacy?.retweeted_status_id_str,
78
+ quoted_status_id: legacy?.quoted_status_id_str
79
+ };
80
+ if (legacy?.extended_entities?.media) {
81
+ const photos = [], videos = [];
82
+ legacy.extended_entities.media.forEach((media) => {
83
+ if (media.type === "photo") {
84
+ let simpleSizes;
85
+ media.sizes && (simpleSizes = {}, media.sizes.thumb && (simpleSizes.thumb = {
86
+ w: media.sizes.thumb.w || 0,
87
+ h: media.sizes.thumb.h || 0,
88
+ resize: media.sizes.thumb.resize || "fit"
89
+ }), media.sizes.small && (simpleSizes.small = {
90
+ w: media.sizes.small.w || 0,
91
+ h: media.sizes.small.h || 0,
92
+ resize: media.sizes.small.resize || "fit"
93
+ }), media.sizes.medium && (simpleSizes.medium = {
94
+ w: media.sizes.medium.w || 0,
95
+ h: media.sizes.medium.h || 0,
96
+ resize: media.sizes.medium.resize || "fit"
97
+ }), media.sizes.large && (simpleSizes.large = {
98
+ w: media.sizes.large.w || 0,
99
+ h: media.sizes.large.h || 0,
100
+ resize: media.sizes.large.resize || "fit"
101
+ }));
102
+ let simpleOriginalInfo;
103
+ media.original_info && (simpleOriginalInfo = {
104
+ width: media.original_info.width || 0,
105
+ height: media.original_info.height || 0
106
+ }), photos.push({
107
+ media_key: media.media_key || "",
108
+ type: "photo",
109
+ media_url_https: media.media_url_https || "",
110
+ display_url: media.display_url || "",
111
+ expanded_url: media.expanded_url || "",
112
+ sizes: simpleSizes,
113
+ original_info: simpleOriginalInfo
114
+ });
115
+ } else if (media.type === "video" || media.type === "animated_gif") {
116
+ let simpleVideoInfo;
117
+ media.video_info && (simpleVideoInfo = {
118
+ aspect_ratio: media.video_info.aspect_ratio,
119
+ duration_millis: media.video_info.duration_millis,
120
+ variants: media.video_info.variants?.map((variant) => ({
121
+ bitrate: variant.bitrate,
122
+ content_type: variant.content_type || "",
123
+ url: variant.url || ""
124
+ }))
125
+ }), videos.push({
126
+ media_key: media.media_key || "",
127
+ type: media.type,
128
+ media_url_https: media.media_url_https || "",
129
+ display_url: media.display_url || "",
130
+ expanded_url: media.expanded_url || "",
131
+ video_info: simpleVideoInfo
132
+ });
133
+ }
134
+ }), photos.length > 0 && (simpleTweet.photos = photos), videos.length > 0 && (simpleTweet.videos = videos);
135
+ }
136
+ if (legacy?.entities?.urls) {
137
+ const urls = legacy.entities.urls.map((url) => ({
138
+ url: url.url || "",
139
+ expanded_url: url.expanded_url || "",
140
+ display_url: url.display_url || ""
141
+ }));
142
+ urls.length > 0 && (simpleTweet.urls = urls);
143
+ }
144
+ return simpleTweet;
145
+ }
146
+ /**
147
+ * 从数据中提取用户
148
+ * @param data 任意对象
149
+ * @returns IUser数组
150
+ */
151
+ static #extractUsers(data) {
152
+ const users = [], searchUsers = (obj) => {
153
+ if (obj && typeof obj == "object") {
154
+ obj.__typename === "User" && obj.rest_id && users.push(obj);
155
+ for (const key in obj)
156
+ obj.hasOwnProperty(key) && searchUsers(obj[key]);
157
+ } else Array.isArray(obj) && obj.forEach((item) => searchUsers(item));
158
+ };
159
+ return searchUsers(data), users;
160
+ }
161
+ /**
162
+ * 从数据中提取推文
163
+ * @param data 任意对象
164
+ * @returns ITweet数组
165
+ */
166
+ static #extractTweets(data) {
167
+ const tweets = [], searchTweets = (obj) => {
168
+ if (obj && typeof obj == "object") {
169
+ obj.__typename === "Tweet" && obj.rest_id && tweets.push(obj);
170
+ for (const key in obj)
171
+ obj.hasOwnProperty(key) && searchTweets(obj[key]);
172
+ } else Array.isArray(obj) && obj.forEach((item) => searchTweets(item));
173
+ };
174
+ return searchTweets(data), tweets;
175
+ }
176
+ /**
177
+ * 从数据中提取游标
178
+ * @param data 任意对象
179
+ * @param result 结果对象
180
+ */
181
+ static #extractCursors(data, result) {
182
+ const searchCursors = (obj) => {
183
+ if (obj && typeof obj == "object") {
184
+ obj.cursor_top && (result.cursor_top = obj.cursor_top), obj.cursor_bottom && (result.cursor_bottom = obj.cursor_bottom), obj.next_cursor && (result.next_cursor = obj.next_cursor), obj.next_cursor_str && (result.next_cursor_str = obj.next_cursor_str), obj.previous_cursor && (result.previous_cursor = obj.previous_cursor), obj.previous_cursor_str && (result.previous_cursor_str = obj.previous_cursor_str), obj.entryId && (obj.entryId.startsWith("cursor-top-") || obj.entryId.startsWith("cursor-bottom-")) && (obj.entryId.startsWith("cursor-top-") ? result.cursor_top = obj.entryId.replace("cursor-top-", "") : result.cursor_bottom = obj.entryId.replace("cursor-bottom-", ""));
185
+ for (const key in obj)
186
+ obj.hasOwnProperty(key) && searchCursors(obj[key]);
187
+ } else Array.isArray(obj) && obj.forEach((item) => searchCursors(item));
188
+ };
189
+ searchCursors(data);
190
+ }
191
+ }
192
+ exports.XParser = XParser;
@@ -0,0 +1,35 @@
1
+ import { ISimpleResult, IOriginalResult, IUser, ISimpleUser, ITweet, ISimpleTweet } from './type.d.ts';
2
+
3
+ /**
4
+ * XParser 静态类,用于解析Twitter API响应
5
+ */
6
+
7
+ declare class XParser {
8
+ #private;
9
+ /**
10
+ * 解析任意对象为ISimpleResult
11
+ * @param data 任意对象
12
+ * @returns ISimpleResult
13
+ */
14
+ static parseSimple(data: any): ISimpleResult;
15
+ /**
16
+ * 解析任意对象为IOriginalResult
17
+ * @param data 任意对象
18
+ * @returns IOriginalResult
19
+ */
20
+ static parseOriginal(data: any): IOriginalResult;
21
+ /**
22
+ * 将IUser转换为ISimpleUser
23
+ * @param user IUser对象
24
+ * @returns ISimpleUser
25
+ */
26
+ static convertToSimpleUser(user: IUser): ISimpleUser;
27
+ /**
28
+ * 将ITweet转换为ISimpleTweet
29
+ * @param tweet ITweet对象
30
+ * @returns ISimpleTweet
31
+ */
32
+ static convertToSimpleTweet(tweet: ITweet): ISimpleTweet;
33
+ }
34
+
35
+ export { XParser };
package/lib/parser.mjs ADDED
@@ -0,0 +1,193 @@
1
+ class XParser {
2
+ /**
3
+ * 解析任意对象为ISimpleResult
4
+ * @param data 任意对象
5
+ * @returns ISimpleResult
6
+ */
7
+ static parseSimple(data) {
8
+ const result = {}, users = this.#extractUsers(data);
9
+ users.length > 0 && (result.users = users.map((user) => this.convertToSimpleUser(user)));
10
+ const tweets = this.#extractTweets(data);
11
+ return tweets.length > 0 && (result.tweets = tweets.map((tweet) => this.convertToSimpleTweet(tweet)), result.photos = result.tweets.filter((tweet) => tweet.photos && tweet.photos.length > 0), result.videos = result.tweets.filter((tweet) => tweet.videos && tweet.videos.length > 0), result.urls = result.tweets.filter((tweet) => tweet.urls && tweet.urls.length > 0)), this.#extractCursors(data, result), result;
12
+ }
13
+ /**
14
+ * 解析任意对象为IOriginalResult
15
+ * @param data 任意对象
16
+ * @returns IOriginalResult
17
+ */
18
+ static parseOriginal(data) {
19
+ const result = {}, users = this.#extractUsers(data);
20
+ users.length > 0 && (result.users = users);
21
+ const tweets = this.#extractTweets(data);
22
+ return tweets.length > 0 && (result.tweets = tweets, result.photos = result.tweets.filter((tweet) => {
23
+ const legacy = tweet.legacy;
24
+ return legacy && legacy.extended_entities && legacy.extended_entities.media && legacy.extended_entities.media.some((media) => media.type === "photo");
25
+ }), result.videos = result.tweets.filter((tweet) => {
26
+ const legacy = tweet.legacy;
27
+ return legacy && legacy.extended_entities && legacy.extended_entities.media && legacy.extended_entities.media.some((media) => media.type === "video" || media.type === "animated_gif");
28
+ }), result.urls = result.tweets.filter((tweet) => {
29
+ const legacy = tweet.legacy;
30
+ return legacy && legacy.entities && legacy.entities.urls && legacy.entities.urls.length > 0;
31
+ })), this.#extractCursors(data, result), result;
32
+ }
33
+ /**
34
+ * 将IUser转换为ISimpleUser
35
+ * @param user IUser对象
36
+ * @returns ISimpleUser
37
+ */
38
+ static convertToSimpleUser(user) {
39
+ return {
40
+ rest_id: user.rest_id,
41
+ name: user.legacy?.name || "",
42
+ screen_name: user.legacy?.screen_name || "",
43
+ profile_image_url_https: user.legacy?.profile_image_url_https || "",
44
+ verified: user.legacy?.verified,
45
+ followers_count: user.legacy?.followers_count,
46
+ friends_count: user.legacy?.friends_count,
47
+ statuses_count: user.legacy?.statuses_count,
48
+ description: user.legacy?.description,
49
+ location: user.legacy?.location,
50
+ url: user.legacy?.url,
51
+ userLabelType: user.affiliates_highlighted_label?.label?.userLabelType,
52
+ verified_type: user.legacy?.verified_type
53
+ };
54
+ }
55
+ /**
56
+ * 将ITweet转换为ISimpleTweet
57
+ * @param tweet ITweet对象
58
+ * @returns ISimpleTweet
59
+ */
60
+ static convertToSimpleTweet(tweet) {
61
+ const legacy = tweet.legacy, simpleTweet = {
62
+ rest_id: tweet.rest_id,
63
+ full_text: legacy?.full_text || "",
64
+ created_at: legacy?.created_at || "",
65
+ user_id: legacy?.user_id_str || "",
66
+ user_screen_name: "",
67
+ // 需要从用户信息中获取
68
+ retweet_count: legacy?.retweet_count,
69
+ favorite_count: legacy?.favorite_count,
70
+ reply_count: legacy?.reply_count,
71
+ quote_count: legacy?.quote_count,
72
+ lang: legacy?.lang,
73
+ conversation_id: legacy?.conversation_id_str,
74
+ possibly_sensitive: legacy?.possibly_sensitive,
75
+ is_retweet: !!legacy?.retweeted_status_id_str,
76
+ retweeted_status_id: legacy?.retweeted_status_id_str,
77
+ quoted_status_id: legacy?.quoted_status_id_str
78
+ };
79
+ if (legacy?.extended_entities?.media) {
80
+ const photos = [], videos = [];
81
+ legacy.extended_entities.media.forEach((media) => {
82
+ if (media.type === "photo") {
83
+ let simpleSizes;
84
+ media.sizes && (simpleSizes = {}, media.sizes.thumb && (simpleSizes.thumb = {
85
+ w: media.sizes.thumb.w || 0,
86
+ h: media.sizes.thumb.h || 0,
87
+ resize: media.sizes.thumb.resize || "fit"
88
+ }), media.sizes.small && (simpleSizes.small = {
89
+ w: media.sizes.small.w || 0,
90
+ h: media.sizes.small.h || 0,
91
+ resize: media.sizes.small.resize || "fit"
92
+ }), media.sizes.medium && (simpleSizes.medium = {
93
+ w: media.sizes.medium.w || 0,
94
+ h: media.sizes.medium.h || 0,
95
+ resize: media.sizes.medium.resize || "fit"
96
+ }), media.sizes.large && (simpleSizes.large = {
97
+ w: media.sizes.large.w || 0,
98
+ h: media.sizes.large.h || 0,
99
+ resize: media.sizes.large.resize || "fit"
100
+ }));
101
+ let simpleOriginalInfo;
102
+ media.original_info && (simpleOriginalInfo = {
103
+ width: media.original_info.width || 0,
104
+ height: media.original_info.height || 0
105
+ }), photos.push({
106
+ media_key: media.media_key || "",
107
+ type: "photo",
108
+ media_url_https: media.media_url_https || "",
109
+ display_url: media.display_url || "",
110
+ expanded_url: media.expanded_url || "",
111
+ sizes: simpleSizes,
112
+ original_info: simpleOriginalInfo
113
+ });
114
+ } else if (media.type === "video" || media.type === "animated_gif") {
115
+ let simpleVideoInfo;
116
+ media.video_info && (simpleVideoInfo = {
117
+ aspect_ratio: media.video_info.aspect_ratio,
118
+ duration_millis: media.video_info.duration_millis,
119
+ variants: media.video_info.variants?.map((variant) => ({
120
+ bitrate: variant.bitrate,
121
+ content_type: variant.content_type || "",
122
+ url: variant.url || ""
123
+ }))
124
+ }), videos.push({
125
+ media_key: media.media_key || "",
126
+ type: media.type,
127
+ media_url_https: media.media_url_https || "",
128
+ display_url: media.display_url || "",
129
+ expanded_url: media.expanded_url || "",
130
+ video_info: simpleVideoInfo
131
+ });
132
+ }
133
+ }), photos.length > 0 && (simpleTweet.photos = photos), videos.length > 0 && (simpleTweet.videos = videos);
134
+ }
135
+ if (legacy?.entities?.urls) {
136
+ const urls = legacy.entities.urls.map((url) => ({
137
+ url: url.url || "",
138
+ expanded_url: url.expanded_url || "",
139
+ display_url: url.display_url || ""
140
+ }));
141
+ urls.length > 0 && (simpleTweet.urls = urls);
142
+ }
143
+ return simpleTweet;
144
+ }
145
+ /**
146
+ * 从数据中提取用户
147
+ * @param data 任意对象
148
+ * @returns IUser数组
149
+ */
150
+ static #extractUsers(data) {
151
+ const users = [], searchUsers = (obj) => {
152
+ if (obj && typeof obj == "object") {
153
+ obj.__typename === "User" && obj.rest_id && users.push(obj);
154
+ for (const key in obj)
155
+ obj.hasOwnProperty(key) && searchUsers(obj[key]);
156
+ } else Array.isArray(obj) && obj.forEach((item) => searchUsers(item));
157
+ };
158
+ return searchUsers(data), users;
159
+ }
160
+ /**
161
+ * 从数据中提取推文
162
+ * @param data 任意对象
163
+ * @returns ITweet数组
164
+ */
165
+ static #extractTweets(data) {
166
+ const tweets = [], searchTweets = (obj) => {
167
+ if (obj && typeof obj == "object") {
168
+ obj.__typename === "Tweet" && obj.rest_id && tweets.push(obj);
169
+ for (const key in obj)
170
+ obj.hasOwnProperty(key) && searchTweets(obj[key]);
171
+ } else Array.isArray(obj) && obj.forEach((item) => searchTweets(item));
172
+ };
173
+ return searchTweets(data), tweets;
174
+ }
175
+ /**
176
+ * 从数据中提取游标
177
+ * @param data 任意对象
178
+ * @param result 结果对象
179
+ */
180
+ static #extractCursors(data, result) {
181
+ const searchCursors = (obj) => {
182
+ if (obj && typeof obj == "object") {
183
+ obj.cursor_top && (result.cursor_top = obj.cursor_top), obj.cursor_bottom && (result.cursor_bottom = obj.cursor_bottom), obj.next_cursor && (result.next_cursor = obj.next_cursor), obj.next_cursor_str && (result.next_cursor_str = obj.next_cursor_str), obj.previous_cursor && (result.previous_cursor = obj.previous_cursor), obj.previous_cursor_str && (result.previous_cursor_str = obj.previous_cursor_str), obj.entryId && (obj.entryId.startsWith("cursor-top-") || obj.entryId.startsWith("cursor-bottom-")) && (obj.entryId.startsWith("cursor-top-") ? result.cursor_top = obj.entryId.replace("cursor-top-", "") : result.cursor_bottom = obj.entryId.replace("cursor-bottom-", ""));
184
+ for (const key in obj)
185
+ obj.hasOwnProperty(key) && searchCursors(obj[key]);
186
+ } else Array.isArray(obj) && obj.forEach((item) => searchCursors(item));
187
+ };
188
+ searchCursors(data);
189
+ }
190
+ }
191
+ export {
192
+ XParser
193
+ };
package/lib/type.cjs ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ const USER_TYPENAMES = ["User", "UserUnavailable"], TWEET_TYPENAMES = ["Tweet", "TextTombstone", "TweetTombstone", "TweetWithVisibilityResults", "ContextualTweetInterstitial"], COMMUNITY_TYPENAMES = ["Community", "CommunityDeleteActionUnavailable", "CommunityUserDefaultModerationState", "CommunityJoinRequestsUnavailable"], TIMELINE_TYPENAMES = ["TimelineTimelineItem", "TimelineMessagePrompt"], TIMELINE_ITEM_TYPENAMES = ["TimelineTweet", "TimelineMessagePrompt"], TIMELINE_INSTRUCTION_TYPES = ["TimelineAddEntries"], TIMELINE_ENTRY_TYPES = ["TimelineTimelineItem", "TimelineTimelineModule", "TimelineTimelineCursor"], TIMELINE_ITEM_TYPES = ["TimelineTweet", "TimelineMessagePrompt"], MEDIA_TYPES = ["photo", "video", "animated_gif"], ELIGIBILITY_TYPES = ["Eligible", "IneligibleUserUnauthorized"], DISPLAY_TYPES = ["Carousel", "Vertical", "VerticalConversation", "Classic", "EntireTweet"], TWEET_DISPLAY_TYPES = ["CondensedTweet", "Tweet", "SelfThread"], USER_DISPLAY_TYPES = ["SubscribableUser", "User"], INJECTION_TYPES = ["ForYouInNetwork", "ForYouPromoted", "WhoToFollow", "creators-only-connect-tab", "CommunityToJoin"], TRANSPARENT_GUIDE_DETAIL_TYPES = ["TimelineEventUrtMetadata"], ELEMENT_TYPES = ["tweet", "user", "event", "feedback", "trend", "users_followed_you", "generic_report_received", "users_liked_your_tweet", "user_quoted_your_tweet", "user_replied_to_your_tweet", "users_retweeted_your_tweet", "user_mentioned_you"], TWEET_QUALITIES = ["HighQuality", "LowQuality", "AbusiveQuality", "RelatedTweet"], COMPONENT_VALUES = ["url", "tweet", "trends", "unified_events", "related_tweet", "suggest_who_to_follow", "for_you_in_network", "for-you-promoted", "alt-text-prompt-injection", "ads-sharing-x-premium-upsell-candidate"], ITEM_TYPES = ["TimelineTweet", "TimelineUser", "TimelineEventSummary", "TimelineMessagePrompt", "TimelineCommunity", "CommunityPinnedTimeline"], CURSOR_TYPES = ["Top", "Bottom", "ShowMore", "ShowMoreThreads"], INSTRUCTION_TYPES = ["TimelineClearCache", "TimelineAddEntries", "TimelineTerminateTimeline", "TimelineShowAlert", "TimelineShowCover"], CONTENT_TYPES = ["TimelineTweetMedia", "TimelineUrl"], VIDEO_CONTENT_TYPES = ["application/x-mpegURL", "video/mp4"], MESSAGE_INBOX_TIMELINE_INDEXES = ["trusted", "untrusted", "untrusted_low_quality"], NOTIFICATION_KEYS = ["clearCache", "addEntries", "clearEntriesUnreadState", "markEntriesUnreadGreaterThanSortIndex"], VERIFIED_TYPES = ["Business", "Government", "Blue", "None"], USER_LABEL_TYPES = ["BusinessLabel", "AutomatedLabel"], URL_TYPES = ["ExternalUrl", "DeepLink", "UrtEndpoint"], VIEW_STATES = ["EnabledWithCount", "Enabled"], LEGACY_CARD_BINDING_KEY_TYPES = ["thumbnail_image", "description", "domain", "thumbnail_image_large", "thumbnail_image_original", "thumbnail_image_small", "thumbnail_image_x_large", "thumbnail_image_color", "summary_photo_image", "summary_photo_image_small", "summary_photo_image_large", "summary_photo_image_x_large", "summary_photo_image_original", "summary_photo_image_color", "photo_image_full_size_color", "vanity_url", "title", "card_url", "creator", "site", "player_image", "player_image_small", "player_image_large", "player_image_x_large", "player_image_original", "player_image_color", "player_url", "player_width", "player_height", "app_name", "app_is_free", "app_star_rating", "app_num_ratings", "app_price_currency", "unified_card", "app_price_amount"], LEGACY_CARD_BINDING_VALUE_TYPES = ["STRING", "IMAGE_COLOR", "IMAGE", "USER"];
3
+ var IdPrefixes = /* @__PURE__ */ ((IdPrefixes2) => (IdPrefixes2.MessagePrompt = "messageprompt-", IdPrefixes2.NormalTweet = "tweet-", IdPrefixes2.PromotedTweet = "promoted-tweet-", IdPrefixes2.User = "user-", IdPrefixes2.CommunityToJoin = "community-to-join-", IdPrefixes2.HomeConversation = "home-conversation-", IdPrefixes2.Conversationthread = "conversationthread-", IdPrefixes2.CommunityConversation = "community-conversation-", IdPrefixes2.CommunityGrid = "communities-grid-", IdPrefixes2.PinnedTweets = "pinned-tweets-", IdPrefixes2.WhoToFollow = "who-to-follow-", IdPrefixes2.CreatorsOnlyConnectTab = "creators-only-connect-tab-", IdPrefixes2.Label = "label-", IdPrefixes2.Guide = "Guide-", IdPrefixes2.ProfileGrid = "profile-grid-", IdPrefixes2.Notification = "notification-", IdPrefixes2.Cursor = "cursor-", IdPrefixes2.CursorTop = "cursor-top-", IdPrefixes2.CursorBottom = "cursor-bottom-", IdPrefixes2.TweetDetailRelatedTweets = "tweetdetailrelatedtweets-", IdPrefixes2.Trends = "trends-", IdPrefixes2))(IdPrefixes || {});
4
+ exports.COMMUNITY_TYPENAMES = COMMUNITY_TYPENAMES, exports.COMPONENT_VALUES = COMPONENT_VALUES, exports.CONTENT_TYPES = CONTENT_TYPES, exports.CURSOR_TYPES = CURSOR_TYPES, exports.DISPLAY_TYPES = DISPLAY_TYPES, exports.ELEMENT_TYPES = ELEMENT_TYPES, exports.ELIGIBILITY_TYPES = ELIGIBILITY_TYPES, exports.INJECTION_TYPES = INJECTION_TYPES, exports.INSTRUCTION_TYPES = INSTRUCTION_TYPES, exports.ITEM_TYPES = ITEM_TYPES, exports.IdPrefixes = IdPrefixes, exports.LEGACY_CARD_BINDING_KEY_TYPES = LEGACY_CARD_BINDING_KEY_TYPES, exports.LEGACY_CARD_BINDING_VALUE_TYPES = LEGACY_CARD_BINDING_VALUE_TYPES, exports.MEDIA_TYPES = MEDIA_TYPES, exports.MESSAGE_INBOX_TIMELINE_INDEXES = MESSAGE_INBOX_TIMELINE_INDEXES, exports.NOTIFICATION_KEYS = NOTIFICATION_KEYS, exports.TIMELINE_ENTRY_TYPES = TIMELINE_ENTRY_TYPES, exports.TIMELINE_INSTRUCTION_TYPES = TIMELINE_INSTRUCTION_TYPES, exports.TIMELINE_ITEM_TYPENAMES = TIMELINE_ITEM_TYPENAMES, exports.TIMELINE_ITEM_TYPES = TIMELINE_ITEM_TYPES, exports.TIMELINE_TYPENAMES = TIMELINE_TYPENAMES, exports.TRANSPARENT_GUIDE_DETAIL_TYPES = TRANSPARENT_GUIDE_DETAIL_TYPES, exports.TWEET_DISPLAY_TYPES = TWEET_DISPLAY_TYPES, exports.TWEET_QUALITIES = TWEET_QUALITIES, exports.TWEET_TYPENAMES = TWEET_TYPENAMES, exports.URL_TYPES = URL_TYPES, exports.USER_DISPLAY_TYPES = USER_DISPLAY_TYPES, exports.USER_LABEL_TYPES = USER_LABEL_TYPES, exports.USER_TYPENAMES = USER_TYPENAMES, exports.VERIFIED_TYPES = VERIFIED_TYPES, exports.VIDEO_CONTENT_TYPES = VIDEO_CONTENT_TYPES, exports.VIEW_STATES = VIEW_STATES;