diff --git a/src/commands/unfollow.ts b/src/commands/unfollow.ts new file mode 100644 index 0000000..f456312 --- /dev/null +++ b/src/commands/unfollow.ts @@ -0,0 +1,23 @@ +import Command from "#utils/Command.js" +import { ApplicationCommandOptionTypes } from "oceanic.js"; +import interactWithBlueskyNow from "#utils/interact-with-bluesky-now.js"; + +const unlikeNowCommand = new Command({ + name: "unfollow", + description: "Unfollow an account on Bluesky.", + options: [ + { + type: ApplicationCommandOptionTypes.STRING, + name: "handle", + description: "What's the name of the account?", + required: true + } + ], + async action(interaction) { + + return await interactWithBlueskyNow(interaction, "unfollow", "deleteFollow"); + + } +}); + +export default unlikeNowCommand; \ No newline at end of file diff --git a/src/utils/interact-with-bluesky-now.ts b/src/utils/interact-with-bluesky-now.ts index ae40e53..48f45cc 100644 --- a/src/utils/interact-with-bluesky-now.ts +++ b/src/utils/interact-with-bluesky-now.ts @@ -11,7 +11,7 @@ import { authenticator } from "otplib"; import IncorrectDecryptionKeyError from "./errors/IncorrectDecryptionKeyError.js"; import MFAIncorrectCodeError from "./errors/MFAIncorrectCodeError.js"; -async function interactWithBlueskyNow(interaction: CommandInteraction | ComponentInteraction | ModalSubmitInteraction, customIDPrefix: string, action: "follow" | "deleteRepost" | "like" | "deleteLike" | "repost") { +async function interactWithBlueskyNow(interaction: CommandInteraction | ComponentInteraction | ModalSubmitInteraction, customIDPrefix: string, action: "deleteFollow" | "follow" | "deleteRepost" | "like" | "deleteLike" | "repost") { const guildID = getGuildIDFromInteraction(interaction); @@ -26,7 +26,8 @@ async function interactWithBlueskyNow(interaction: CommandInteraction | Componen like: "💖", deleteLike: "💔", deleteRepost: "🗑️", - follow: "➕" + follow: "➕", + deleteFollow: "➖" }; await interaction.editOriginal({ content: responses[action], diff --git a/src/utils/interact-with-bluesky.ts b/src/utils/interact-with-bluesky.ts index dbd374c..a4b7d64 100644 --- a/src/utils/interact-with-bluesky.ts +++ b/src/utils/interact-with-bluesky.ts @@ -3,10 +3,11 @@ import blueskyClient from "./bluesky-client.js"; import { Agent } from "@atproto/api"; import { isThreadViewPost } from "@atproto/api/dist/client/types/app/bsky/feed/defs.js"; -async function interactWithBluesky(source: {interaction?: ModalSubmitInteraction | ComponentInteraction, rkey?: string, targetHandle?: string, actorDID?: string, guildID: string, decryptionKey?: string}, action: "follow" | "deletePost" | "deleteLike" | "like" | "deleteRepost" | "repost") { +async function interactWithBluesky(source: {interaction?: ModalSubmitInteraction | ComponentInteraction, rkey?: string, targetHandle?: string, actorDID?: string, guildID: string, decryptionKey?: string}, action: "deleteFollow" | "follow" | "deletePost" | "deleteLike" | "like" | "deleteRepost" | "repost") { let {interaction, rkey, targetHandle, actorDID} = source; + const isTargetAccount = action === "deleteFollow" || action === "follow"; if (interaction && !targetHandle) { // Get the rkey of the post. @@ -14,7 +15,7 @@ async function interactWithBluesky(source: {interaction?: ModalSubmitInteraction const originalEmbed = originalMessage.embeds[0]; const value = originalEmbed?.footer?.text; - if (action === "follow") { + if (isTargetAccount) { targetHandle = value; @@ -30,7 +31,7 @@ async function interactWithBluesky(source: {interaction?: ModalSubmitInteraction } - if (!targetHandle || !actorDID || (action !== "follow" && !rkey)) throw new Error(); + if (!targetHandle || !actorDID || (!isTargetAccount && !rkey)) throw new Error(); // Get the CID of the post if necessary. const session = await blueskyClient.restore(actorDID, "auto", {guildID: source.guildID, decryptionKey: source.decryptionKey}); @@ -39,10 +40,16 @@ async function interactWithBluesky(source: {interaction?: ModalSubmitInteraction let cid; let uri; if (!targetDID) throw new Error(); - if (action !== "follow") { + if (action === "deleteFollow") { + + const profileResponse = await agent.getProfile({actor: targetDID}); + uri = profileResponse.data.viewer?.following; + if (!uri) return; + + } else if (action !== "follow") { if (!rkey) throw new Error(); - + const recordResponse = await agent.com.atproto.repo.getRecord({ collection: "app.bsky.feed.post", repo: targetDID, @@ -52,20 +59,20 @@ async function interactWithBluesky(source: {interaction?: ModalSubmitInteraction cid = recordResponse.data.cid; if (!cid) throw new Error(); - - // Get the URI we need. uri = `at://${targetDID}/app.bsky.feed.post/${rkey}`; - if (action === "deleteLike" || action === "deleteRepost") { - - const response = await agent.getPostThread({uri}); - if (isThreadViewPost(response.data.thread)) { - - const possibleURI = response.data.thread.post.viewer?.[action === "deleteLike" ? "like" : "repost"]; - if (!possibleURI) return; - uri = possibleURI; - - } - + + } + + // Get the URI we need. + if (action === "deleteLike" || action === "deleteRepost") { + + const response = await agent.getPostThread({uri: uri!}); + if (isThreadViewPost(response.data.thread)) { + + const possibleURI = response.data.thread.post.viewer?.[action === "deleteLike" ? "like" : "repost"]; + if (!possibleURI) return; + uri = possibleURI; + } }