twitter-web-exporter
twitter-web-exporter copied to clipboard
导出 Following 数据时,如果有 Suspended 用户,会中止,提示 Error: Cannot read properties of undefined (reading 'screen_name')
来自 Twitter 的粉丝,非常感谢作者能开发这个工具。
直接导出时卡住,Console 有输出错误。
错误信息如下:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'created_at')
at VMalzsdyq82vt.columnHelper.accessor.id [as accessorFn] (Twitter Web Exporter.user.js:2764:71)
at Object.getValue (Twitter Web Exporter.user.js:19:46141)
at Object.getValue (Twitter Web Exporter.user.js:19:45538)
at onExport (Twitter Web Exporter.user.js:1299:30)
at HTMLButtonElement.T (Twitter Web Exporter.user.js:3:4503)
相同界面,分页浏览 Following 数据时出现的错误
错误提示截图:
错误文本:
Something went wrong.
Error: Cannot read properties of undefined (reading 'screen_name')
原因分析
原因是 Twitter 返回的数据中,混有 Suspended
用户。
Suspended 用户数据示例如下:
...
},
{
"entryId": "user-1723415153759150080",
"sortIndex": "1788044944628252472",
"content": {
"entryType": "TimelineTimelineItem",
"__typename": "TimelineTimelineItem",
"itemContent": {
"itemType": "TimelineUser",
"__typename": "TimelineUser",
"user_results": {
"result": {
"__typename": "UserUnavailable",
"message": "User is suspended",
"reason": "Suspended"
}
},
"userDisplayType": "User"
},
"clientEventInfo": {
"component": "FollowingSgs",
"element": "user"
}
}
},
{
...
处理建议
浏览或导出时可能需要忽略 Suspended 用户数据。
找到问题就发出来了,完全不懂暴力猴工程,接下来可能会尝试修正一下,成功了来 PR 😌
在 GPT 的帮助下,修改文件 src/modules/following/api.ts
:
diff --git a/src/modules/following/api.ts b/src/modules/following/api.ts
index 1e3184a..a7142a8 100644
--- a/src/modules/following/api.ts
+++ b/src/modules/following/api.ts
@@ -31,12 +31,15 @@ export const FollowingInterceptor: Interceptor = (req, res) => {
}
try {
- const newData = extractDataFromResponse<FollowingResponse, User>(
+ const extractedData = extractDataFromResponse<FollowingResponse, User>(
res,
(json) => json.data.user.result.timeline.timeline.instructions,
(entry) => entry.content.itemContent.user_results.result,
);
+ // 使用 Array.filter() 方法过滤掉 __typename 不是 'User' 的数据项
+ const newData = extractedData.filter(user => user.__typename === 'User');
+
// Add captured data to the global store.
followingSignal.value = [...followingSignal.value, ...newData];
有效,但还是不要 PR 了,可能这不是最好的方法 ☹️
哦哦,我没有考虑到这种情况,感谢你的反馈!
我后面有时间会针对这种情况修复一下。如果你愿意,也可以参考以下流程发个 PR,我很欢迎~(如果要 PR 的话,麻烦你基于 idb 分支修改,目前我是在这个分支上开发的)
你上面的修改只针对 Following 数据导出有效,但这个问题在导出其他用户列表时应该也可能会出现。所以更通用的解决方法是这样修改(所有用到了 extractDataFromResponse
的用户模块都需要修改):
// src/modules/following/api.ts
const newData = extractDataFromResponse<FollowingResponse, User>(
res,
(json) => json.data.user.result.timeline.timeline.instructions,
- (entry) => entry.content.itemContent.user_results.result,
+ (entry) => extractTimelineUser(entry.content.itemContent),
);
然后在 src/utils/api.ts
中 extractTimelineTweet
方法的下面,类似地添加一个 extractTimelineUser
方法:
/**
* Extract the user object from the timeline entry, ignoring unavailable users.
*/
export function extractTimelineUser(itemContent: TimelineUser): User | null {
const user = itemContent.user_results.result;
if (!user || user.__typename !== 'User') {
logger.warn(
"TimelineUser is empty. This could happen when the user's account is suspended or deleted.",
itemContent,
);
return null;
}
return user;
}
最后,完善类型定义 src/types/user.ts
:
export interface TimelineUser {
itemType: 'TimelineUser';
__typename: 'TimelineUser';
user_results: {
- result: User;
+ result: User | UserUnavailable;
};
userDisplayType: string;
}
+export interface UserUnavailable {
+ __typename: 'UserUnavailable';
+ message: string;
+ reason: string;
+}
此问题已在最新测试版中修复,如有问题欢迎反馈。
https://github.com/prinsss/twitter-web-exporter/releases/download/nightly/twitter-web-exporter.user.js