diff --git a/src/account_x.ts b/src/account_x.ts index 99ff26d1..74382c06 100644 --- a/src/account_x.ts +++ b/src/account_x.ts @@ -23,6 +23,7 @@ import { XRateLimitInfo, emptyXRateLimitInfo, XIndexMessagesStartResponse, XDeleteTweetsStartResponse, + XDeleteRetweetsStartResponse, XProgressInfo, emptyXProgressInfo, ResponseData, // XTweet @@ -1182,7 +1183,7 @@ export class XAccountController { return true; } - // When you start deleting tweets, retur a list of tweetIDs to delete + // When you start deleting tweets, return a list of tweetIDs to delete async deleteTweetsStart(): Promise { if (!this.db) { this.initDB(); @@ -1248,6 +1249,35 @@ export class XAccountController { return true; } + // When you start deleting retweets, return a list of tweetIDs to delete + async deleteRetweetsStart(): Promise { + if (!this.db) { + this.initDB(); + } + + if (!this.account) { + throw new Error("Account not found"); + } + + // Select just the retweets that need to be deleted based on the settings + const daysOldTimestamp = getTimestampDaysAgo(this.account.deleteRetweetsDaysOld); + const tweets: XTweetRow[] = exec( + this.db, + 'SELECT id, tweetID, username FROM tweet WHERE deletedAt IS NULL AND isRetweeted = ? AND username != ? AND createdAt <= ? ORDER BY createdAt DESC', + [1, this.account.username, daysOldTimestamp], + "all" + ) as XTweetRow[]; + + log.debug("XAccountController.deleteRetweetsStart", tweets); + return { + tweets: tweets.map((row) => ({ + id: row.id, + username: row.username, + tweetID: row.tweetID + })), + }; + } + async syncProgress(progressJSON: string) { this.progress = JSON.parse(progressJSON); } @@ -1623,4 +1653,13 @@ export const defineIPCX = () => { throw new Error(packageExceptionForReport(error as Error)); } }); + + ipcMain.handle('X:deleteRetweetsStart', async (_, accountID: number): Promise => { + try { + const controller = getXAccountController(accountID); + return await controller.deleteRetweetsStart(); + } catch (error) { + throw new Error(packageExceptionForReport(error as Error)); + } + }); }; \ No newline at end of file diff --git a/src/preload.ts b/src/preload.ts index a8f7d227..d91e6303 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -6,6 +6,7 @@ import { XArchiveStartResponse, XIndexMessagesStartResponse, XDeleteTweetsStartResponse, + XDeleteRetweetsStartResponse, XRateLimitInfo, XProgressInfo, ResponseData @@ -178,6 +179,9 @@ contextBridge.exposeInMainWorld('electron', { }, deleteTweet: (accountID: number, tweetID: string): Promise => { return ipcRenderer.invoke('X:deleteTweet', accountID, tweetID); - } + }, + deleteRetweetsStart: (accountID: number): Promise => { + return ipcRenderer.invoke('X:deleteRetweetsStart', accountID); + }, } }) \ No newline at end of file diff --git a/src/renderer/src/automation_errors.ts b/src/renderer/src/automation_errors.ts index 272ba349..87091a8c 100644 --- a/src/renderer/src/automation_errors.ts +++ b/src/renderer/src/automation_errors.ts @@ -31,6 +31,9 @@ export enum AutomationErrorType { x_runJob_deleteTweets_WaitForMenuFailed = "x_runJob_deleteTweets_WaitForMenuFailed", x_runJob_deleteTweets_WaitForDeleteConfirmationFailed = "x_runJob_deleteTweets_WaitForDeleteConfirmationFailed", x_runJob_deleteTweets_FailedToUpdateDeleteTimestamp = "x_runJob_deleteTweets_FailedToUpdateDeleteTimestamp", + x_runJob_deleteRetweets_FailedToStart = "x_runJob_deleteRetweets_FailedToStart", + x_runJob_deleteRetweets_WaitForMenuFailed = "x_runJob_deleteRetweets_WaitForMenuFailed", + x_runJob_deleteRetweets_FailedToUpdateDeleteTimestamp = "x_runJob_deleteRetweets_FailedToUpdateDeleteTimestamp", x_runJob_UnknownError = "x_runJob_UnknownError", x_runError = "x_runError", x_unknownError = "x_unknown", @@ -70,6 +73,9 @@ export const AutomationErrorTypeToMessage = { [AutomationErrorType.x_runJob_deleteTweets_WaitForMenuFailed]: "Failed to wait for menu while deleting tweets", [AutomationErrorType.x_runJob_deleteTweets_WaitForDeleteConfirmationFailed]: "Failed to wait for delete confirmation popup while deleting tweets", [AutomationErrorType.x_runJob_deleteTweets_FailedToUpdateDeleteTimestamp]: "Failed to update delete timestamp while deleting tweets", + [AutomationErrorType.x_runJob_deleteRetweets_FailedToStart]: "Failed to start deleting retweets", + [AutomationErrorType.x_runJob_deleteRetweets_WaitForMenuFailed]: "Failed to wait for unretweet menu while deleting retweets", + [AutomationErrorType.x_runJob_deleteRetweets_FailedToUpdateDeleteTimestamp]: "Failed to update delete timestamp while deleting retweets", [AutomationErrorType.x_runJob_UnknownError]: "An unknown error occured", [AutomationErrorType.x_runError]: "Error while in X run function", [AutomationErrorType.x_unknownError]: "An unknown error occured", diff --git a/src/renderer/src/components/XJobStatusComponent.vue b/src/renderer/src/components/XJobStatusComponent.vue index 4b3d0b51..3c6a0ea3 100644 --- a/src/renderer/src/components/XJobStatusComponent.vue +++ b/src/renderer/src/components/XJobStatusComponent.vue @@ -37,10 +37,11 @@ const getJobTypeText = (jobType: string) => { archiveTweets: 'Archiving tweets', archiveBuild: 'Building archive', deleteTweets: 'Deleting tweets', + deleteRetweets: 'Deleting retweets', deleteLikes: 'Deleting likes', deleteDMs: 'Deleting DMs' }; - return jobTypeTexts[jobType] || ''; + return jobTypeTexts[jobType] || jobType; }; const cycleRunningIcon = () => { diff --git a/src/renderer/src/components/XProgressComponent.vue b/src/renderer/src/components/XProgressComponent.vue index 1b52a43a..0a469d6f 100644 --- a/src/renderer/src/components/XProgressComponent.vue +++ b/src/renderer/src/components/XProgressComponent.vue @@ -153,6 +153,28 @@ onUnmounted(() => { + + +