Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions lib/artist/updateArtistSocials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { getSocialPlatformByLink } from "@/lib/artists/getSocialPlatformByLink";
import { getUsernameFromProfileUrl } from "@/lib/socials/getUsernameFromProfileUrl";
import { selectAccountSocials } from "@/lib/supabase/account_socials/selectAccountSocials";
import { deleteAccountSocial } from "@/lib/supabase/account_socials/deleteAccountSocial";
import { insertAccountSocial } from "@/lib/supabase/account_socials/insertAccountSocial";
import { selectSocials } from "@/lib/supabase/socials/selectSocials";
import { insertSocials } from "@/lib/supabase/socials/insertSocials";
import type { AccountSocialWithSocial } from "@/lib/supabase/account_socials/selectAccountSocials";

/**
* Updates artist socials by replacing existing socials for each platform type.
*
* @param artistId - The artist account ID
* @param profileUrls - Record mapping platform types to profile URLs
* @returns Array of updated account socials with joined social data
*/
export async function updateArtistSocials(
artistId: string,
profileUrls: Record<string, string>,
): Promise<AccountSocialWithSocial[]> {
// Get current account socials (with no limit to get all)
const accountSocials = await selectAccountSocials(artistId, 0, 10000);

// Process each platform type
const profilePromises = Object.entries(profileUrls).map(async ([type, value]) => {
const socials = value ? await selectSocials({ profile_url: value }) : null;
const social = socials && socials.length > 0 ? socials[0] : null;
const existingSocial = (accountSocials || []).find(
(account_social: AccountSocialWithSocial) =>
getSocialPlatformByLink(account_social.social?.profile_url || "") === type,
);

// Delete existing social for this platform type if it exists
if (existingSocial && existingSocial.social?.id) {
await deleteAccountSocial(artistId, existingSocial.social.id);
Comment thread
sweetmantech marked this conversation as resolved.
}

// Insert new social if URL provided
if (value) {
if (social) {
// Social already exists, check if account_social relationship exists
const existing = (accountSocials || []).find(
(as: AccountSocialWithSocial) => as.social_id === social.id,
);
if (!existing) {
await insertAccountSocial(artistId, social.id);
}
} else {
// Create new social record
const newSocials = await insertSocials([
{
username: getUsernameFromProfileUrl(value),
profile_url: value,
},
]);
if (newSocials.length > 0) {
await insertAccountSocial(artistId, newSocials[0].id);
}
}
}
});

await Promise.all(profilePromises);

// Return the updated account socials
const updated = await selectAccountSocials(artistId, 0, 10000);
return updated || [];
}
13 changes: 13 additions & 0 deletions lib/mcp/tools/artistSocials/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerGetArtistSocialsTool } from "./registerGetArtistSocialsTool";
import { registerUpdateArtistSocialsTool } from "./registerUpdateArtistSocialsTool";

/**
* Registers all artist socials-related MCP tools on the server.
*
* @param server - The MCP server instance to register tools on.
*/
export const registerAllArtistSocialsTools = (server: McpServer): void => {
registerGetArtistSocialsTool(server);
registerUpdateArtistSocialsTool(server);
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export function registerGetArtistSocialsTool(server: McpServer): void {
},
);
}

66 changes: 66 additions & 0 deletions lib/mcp/tools/artistSocials/registerUpdateArtistSocialsTool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { updateArtistSocials } from "@/lib/artist/updateArtistSocials";
import { getSocialPlatformByLink } from "@/lib/artists/getSocialPlatformByLink";
import { getToolResultSuccess } from "@/lib/mcp/getToolResultSuccess";
import { getToolResultError } from "@/lib/mcp/getToolResultError";

const updateArtistSocialsSchema = z.object({
artistId: z
.string()
.min(1, "Artist account ID is required")
.describe("The artist's account ID to update socials for."),
urls: z
.array(z.string())
.describe(
"An array of social profile URLs to associate with the artist. The platform will be inferred automatically.",
),
});

type UpdateArtistSocialsArgs = z.infer<typeof updateArtistSocialsSchema>;

/**
* Registers the "update_artist_socials" tool on the MCP server.
* Updates the artist_socials records for an artist.
*
* @param server - The MCP server instance to register the tool on.
*/
export function registerUpdateArtistSocialsTool(server: McpServer): void {
server.registerTool(
"update_artist_socials",
{
description: `Update the artist_socials records for an artist. Provide the artistId and an array of social profile URLs. The tool will infer the platform for each URL and update the artist's socials accordingly.`,
inputSchema: updateArtistSocialsSchema,
},
async (args: UpdateArtistSocialsArgs) => {
try {
// Map each URL to its platform type
const profileUrls: Record<string, string> = {};
for (const url of args.urls) {
const platform = getSocialPlatformByLink(url);
if (platform && platform !== "NONE") {
profileUrls[platform] = url;
}
}

const socials = await updateArtistSocials(args.artistId, profileUrls);

const response = {
success: true,
message: "Artist socials updated successfully.",
socials,
};

return getToolResultSuccess(response);
} catch (error) {
console.error("Error updating artist socials:", error);
const errorMessage =
error instanceof Error
? error.message
: "Failed to update artist socials.";
return getToolResultError(errorMessage);
}
},
);
}

4 changes: 2 additions & 2 deletions lib/mcp/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerGetArtistSocialsTool } from "./registerGetArtistSocialsTool";
import { registerGetLocalTimeTool } from "./registerGetLocalTimeTool";
import { registerAllTaskTools } from "./tasks";
import { registerAllImageTools } from "./images";
Expand All @@ -8,6 +7,7 @@ import { registerAllSora2Tools } from "./sora2";
import { registerAllSpotifyTools } from "./spotify";
import { registerContactTeamTool } from "./registerContactTeamTool";
import { registerUpdateAccountInfoTool } from "./registerUpdateAccountInfoTool";
import { registerAllArtistSocialsTools } from "./artistSocials";

/**
* Registers all MCP tools on the server.
Expand All @@ -16,7 +16,7 @@ import { registerUpdateAccountInfoTool } from "./registerUpdateAccountInfoTool";
* @param server - The MCP server instance to register tools on.
*/
export const registerAllTools = (server: McpServer): void => {
registerGetArtistSocialsTool(server);
registerAllArtistSocialsTools(server);
registerGetLocalTimeTool(server);
registerAllTaskTools(server);
registerAllImageTools(server);
Expand Down
23 changes: 23 additions & 0 deletions lib/supabase/account_socials/deleteAccountSocial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import supabase from "../serverClient";

/**
* Deletes an account_social record by account ID and social ID.
*
* @param accountId - The account ID
* @param socialId - The social ID
* @returns True if successful, false otherwise
*/
export async function deleteAccountSocial(accountId: string, socialId: string): Promise<boolean> {
const { error } = await supabase
.from("account_socials")
.delete()
.eq("account_id", accountId)
.eq("social_id", socialId);

if (error) {
console.error("[ERROR] deleteAccountSocial:", error);
return false;
}

return true;
}
27 changes: 27 additions & 0 deletions lib/supabase/account_socials/insertAccountSocial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import supabase from "../serverClient";
import type { Tables } from "@/types/database.types";

/**
* Inserts a new account_social record.
*
* @param accountId - The account ID
* @param socialId - The social ID
* @returns The inserted account_social record, or null if failed
*/
export async function insertAccountSocial(
accountId: string,
socialId: string,
): Promise<Tables<"account_socials"> | null> {
const { data, error } = await supabase
.from("account_socials")
.insert({ account_id: accountId, social_id: socialId })
.select("*")
.single();

if (error) {
console.error("[ERROR] insertAccountSocial:", error);
return null;
}

return data || null;
}
21 changes: 21 additions & 0 deletions lib/supabase/socials/insertSocials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import supabase from "../serverClient";
import type { Tables, TablesInsert } from "@/types/database.types";

/**
* Inserts one or more social records.
*
* @param socials - Array of social data to insert
* @returns Array of inserted social records
*/
export async function insertSocials(
socials: TablesInsert<"socials">[],
): Promise<Tables<"socials">[]> {
const { data, error } = await supabase.from("socials").insert(socials).select("*");

if (error) {
console.error("[ERROR] insertSocials:", error);
return [];
}

return data || [];
}
5 changes: 5 additions & 0 deletions lib/supabase/socials/selectSocials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import supabase from "../serverClient";

type SelectSocialsParams = {
id?: string;
profile_url?: string;
};

/**
Expand All @@ -21,6 +22,10 @@ export async function selectSocials(
query = query.eq("id", params.id);
}

if (params.profile_url) {
query = query.eq("profile_url", params.profile_url);
}

const { data, error } = await query;

if (error) {
Expand Down