diff --git a/package.json b/package.json index 5616cf0b..6498cbf0 100644 --- a/package.json +++ b/package.json @@ -82,8 +82,8 @@ "cmdk": "^1.1.1", "cookies-next": "^6.0.0", "date-fns": "^4.1.0", - "etsy-ts": "^4.2.0", "discord-api-types": "^0.38.17", + "etsy-ts": "^5.0.0", "exa-js": "^1.8.8", "fast-deep-equal": "^3.1.3", "googleapis": "^150.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9cafcd36..9c2330ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -179,12 +179,12 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 - etsy-ts: - specifier: ^4.2.0 - version: 4.2.0 discord-api-types: specifier: ^0.38.17 version: 0.38.18 + etsy-ts: + specifier: ^5.0.0 + version: 5.0.0 exa-js: specifier: ^1.8.8 version: 1.8.8(encoding@0.1.13)(ws@8.18.3)(zod@3.25.56) @@ -3194,8 +3194,8 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - etsy-ts@4.2.0: - resolution: {integrity: sha512-YrVSiIP1s1FE7/isCnnkUR86te4+UABHfW7gWUsAHUDtPZj2NOw4VswabcfWxbtis+8pmZm7XQO0cQs8LZL+Fw==} + etsy-ts@5.0.0: + resolution: {integrity: sha512-Bh5Y3QJby9a22VOThJrIPBHEQDT7ixPK67m3EGiQUxRRywK1GWqbGAQi8E63yWGJKF41yO81E83JSBfJ/1ZypA==} event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} @@ -8670,11 +8670,11 @@ snapshots: etag@1.8.1: {} - etsy-ts@4.2.0: + etsy-ts@5.0.0: dependencies: axios: 1.7.7 axios-auth-refresh: 3.3.6(axios@1.7.7) - form-data: 4.0.3 + form-data: 4.0.4 tslib: 2.8.1 transitivePeerDependencies: - debug diff --git a/src/server/auth/custom-providers/etsy.ts b/src/server/auth/custom-providers/etsy.ts index 2358607b..20e3e3e2 100644 --- a/src/server/auth/custom-providers/etsy.ts +++ b/src/server/auth/custom-providers/etsy.ts @@ -10,7 +10,8 @@ export interface EtsyProfile { image_url_75x75?: string | null; } -export const etsyScopes = "email_r shops_r listings_r"; +export const etsyScopes = + "address_r address_w billing_r cart_r cart_w email_r favorites_r favorites_w feedback_r listings_d listings_r listings_w profile_r profile_w recommend_r recommend_w shops_r shops_w transactions_r transactions_w"; export default function EtsyProvider
( options: OAuthUserConfig
,
diff --git a/src/toolkits/toolkits/etsy/base.ts b/src/toolkits/toolkits/etsy/base.ts
index d585bf7d..31ee13ac 100644
--- a/src/toolkits/toolkits/etsy/base.ts
+++ b/src/toolkits/toolkits/etsy/base.ts
@@ -4,6 +4,8 @@ import { EtsyTools } from "./tools/tools";
import { getListings } from "@/toolkits/toolkits/etsy/tools/get-listings/base";
+import { createDraftListing } from "@/toolkits/toolkits/etsy/tools/create-draft-listing/base";
+
import type { ToolkitConfig } from "@/toolkits/types";
export const etsyParameters = z.object({});
@@ -14,6 +16,7 @@ export const baseEtsyToolkitConfig: ToolkitConfig<
> = {
tools: {
[EtsyTools.getListings]: getListings,
+ [EtsyTools.createDraftListing]: createDraftListing,
},
parameters: etsyParameters,
};
diff --git a/src/toolkits/toolkits/etsy/client.tsx b/src/toolkits/toolkits/etsy/client.tsx
index dc79b09b..1274e57f 100644
--- a/src/toolkits/toolkits/etsy/client.tsx
+++ b/src/toolkits/toolkits/etsy/client.tsx
@@ -10,6 +10,8 @@ import { createClientToolkit } from "@/toolkits/create-toolkit";
import { getListingsClientConfig } from "@/toolkits/toolkits/etsy/tools/get-listings/client";
+import { createDraftListingClientConfig } from "@/toolkits/toolkits/etsy/tools/create-draft-listing/client";
+
import { ToolkitGroups } from "@/toolkits/types";
import { EtsyTools } from "./tools/tools";
@@ -17,7 +19,8 @@ export const etsyClientToolkit = createClientToolkit(
baseEtsyToolkitConfig,
{
name: "Etsy Toolkit",
- description: "Etsy toolkit for fetching listing details.",
+ description:
+ "Etsy toolkit for Listing management, Payment management, Receipt management, Shipping management, Shop management and more!",
icon: SiEtsy,
form: null,
type: ToolkitGroups.DataSource,
@@ -37,5 +40,6 @@ export const etsyClientToolkit = createClientToolkit(
},
{
[EtsyTools.getListings]: getListingsClientConfig,
+ [EtsyTools.createDraftListing]: createDraftListingClientConfig,
},
);
diff --git a/src/toolkits/toolkits/etsy/server.ts b/src/toolkits/toolkits/etsy/server.ts
index a2cc94f5..9985fc3e 100644
--- a/src/toolkits/toolkits/etsy/server.ts
+++ b/src/toolkits/toolkits/etsy/server.ts
@@ -10,11 +10,13 @@ import { EtsyTools } from "./tools/tools";
import { EtsySecurityDataStorage } from "./security-data-storage";
import { getListingsServerConfig } from "@/toolkits/toolkits/etsy/tools/get-listings/server";
+import { createDraftListingServerConfig } from "@/toolkits/toolkits/etsy/tools/create-draft-listing/server";
export const etsyToolkitServer = createServerToolkit(
baseEtsyToolkitConfig,
"You have access to the Etsy toolkit for general account management. Currently, this toolkit provides:\n" +
- "- **Get Listings**: Retrieves all listings and their image URLs associated with the shop associated with the signed-in user.\n\n",
+ "- **Get Listings By Shop**: Retrieves listings associated with the shop owned by authenticated user. Has the ability to fetch associations relating to each listing as well.\n" +
+ "- **Create Draft Listing**: Creates a new draft listing in the shop owned by authenticated user. Accepts a variety of inputs to assign to listing.\n",
async () => {
const account = await api.accounts.getAccountByProvider("etsy");
@@ -32,7 +34,14 @@ export const etsyToolkitServer = createServerToolkit(
});
return {
- [EtsyTools.getListings]: getListingsServerConfig(etsy),
+ [EtsyTools.getListings]: getListingsServerConfig(
+ etsy,
+ account.providerAccountId,
+ ),
+ [EtsyTools.createDraftListing]: createDraftListingServerConfig(
+ etsy,
+ account.providerAccountId,
+ ),
};
},
);
diff --git a/src/toolkits/toolkits/etsy/tools/create-draft-listing/base.ts b/src/toolkits/toolkits/etsy/tools/create-draft-listing/base.ts
new file mode 100644
index 00000000..603e2866
--- /dev/null
+++ b/src/toolkits/toolkits/etsy/tools/create-draft-listing/base.ts
@@ -0,0 +1,299 @@
+import { z } from "zod";
+import { createBaseTool } from "@/toolkits/create-tool";
+import type { IShopListing } from "etsy-ts";
+
+export const createDraftListing = createBaseTool({
+ description:
+ "Creates a new draft listing in the Etsy shop associated with the authenticated user." +
+ "Requires input: quantity, title, description, price, who_made, when_made, is_supply, and taxonomy_id." +
+ "Optional inputs include: (if not specified, the default value is null" +
+ "shipping_profile_id — ID of the shipping profile to associate with the listing. REQUIRED if listing type is physical." +
+ "return_policy_id - the numeric ID of the Return Policy." +
+ "materials - A list of material strings for materials used in the product. Valid materials strings contain only letters, numbers, and whitespace characters. (regex: /[^\\p{L}\\p{Nd}\\p{Zs}]/u)." +
+ "shop_section_id - The numeric ID of the shop section for this listing." +
+ "processing_min - The minimum number of days it takes to produce the item." +
+ "processing_max - The maximum number of days it takes to produce the item." +
+ "readiness_state_id - The numeric ID of the processing profile associated with the listing. Required when type is physical." +
+ "tags - A comma-separated list of tag strings for the listing. When creating or updating a listing, valid tag strings contain only letters, numbers, whitespace characters, -, ', ™, ©, and ®. (regex: /[^\\p{L}\\p{Nd}\\p{Zs}-'™©®]/u)." +
+ 'styles - An array of style strings for this listing, each of which is free-form text string such as "Formal", or "Steampunk". When creating or updating a listing, the listing may have up to two styles. Valid style strings contain only letters, numbers, and whitespace characters. (regex: /[^\\p{L}\\p{Nd}\\p{Zs}]/u).' +
+ "item_weight - The numeric weight of the product measured in units set in 'item_weight_unit'. If set, the values must be greater than 0." +
+ "item_length - The numeric length of the product measured in units set in 'item_dimensions_unit'. If set, the values must be greater than 0." +
+ "item_width - The numeric width of the product measured in units set in 'item_dimensions_unit'. If set, the values must be greater than 0." +
+ "item_height - The numeric height of the product measured in units set in 'item_dimensions_unit'. If set, the values must be greater than 0." +
+ "item_weight_unit - The unit of measurement for the weight of the product. Valid values are 'g' (grams), 'kg' (kilograms), 'oz' (ounces), and 'lb' (pounds). Required if 'item_weight' is provided." +
+ "item_dimensions_unit - The unit of measurement for the dimensions of the product. Valid values are 'mm' (millimeters), 'cm' (centimeters), 'm' (meters), 'in' (inches), and 'ft' (feet). Required if any of 'item_length', 'item_width', or 'item_height' is provided." +
+ "is_personalizable - When ture, indicates that the listing is personalizable." +
+ "personalization_is_required - When true, indicates that personalization is required for the listing. Will only change if _is_personalizable is true" +
+ "personalization_char_count_max - The maximum number of characters allowed for personalization. Will only change if _is_personalizable is true." +
+ "personalization_instructions - Instructions for personalization. Will only change if _is_personalizable is true." +
+ "production_partner_ids - An array of unique IDs of production partner ids." +
+ "image_ids - An array of numeric image IDs of the images in a listing, which can include up to 10 images." +
+ "is_customizable - When true, a buyer may contact the seller for a customized order. The default value is true when a shop accepts custom orders. Does not apply to shops that do not accept custom orders." +
+ "should_auto_renew - When true, renews a listing for four months upon expiration." +
+ "is_taxable - When true, applicable shop tax rates apply to this listing at checkout" +
+ "type - An enumerated type string that indicates whether the listing is physical or a digital download or both.",
+ inputSchema: z.object({
+ title: z
+ .string()
+ .min(1)
+ .max(140)
+ .describe(
+ "The title of the listing. Can only contain letters, numbers, punctuation marks, mathematical symbols, whitespace characters",
+ ),
+ description: z
+ .string()
+ .min(1)
+ .max(1000)
+ .describe("The description of the listing."),
+ price: z
+ .number()
+ .positive()
+ .describe(
+ "The price of the item. Must be a positive value with up to two decimal places.",
+ ),
+ quantity: z
+ .number()
+ .min(1)
+ .describe(
+ "The quantity of items available in this listing. Must be at least 1.",
+ ),
+ who_made: z
+ .enum(["i_did", "collective", "someone_else"])
+ .describe("Who made the item."),
+ when_made: z
+ .enum([
+ "made_to_order",
+ "2020_2025",
+ "2010_2019",
+ "2006_2009",
+ "before_2006",
+ "2000_2005",
+ "1990s",
+ "1980s",
+ "1970s",
+ "1960s",
+ "1950s",
+ "1940s",
+ "1930s",
+ "1920s",
+ "1910s",
+ "1900s",
+ "1800s",
+ "1700s",
+ "before_1700",
+ ])
+ .describe(
+ "An enumerated string for the era in which the maker made the product in this listing. Helps buyers locate the listing under the Vintage heading.",
+ ),
+ taxonomy_id: z
+ .number()
+ .int()
+ .positive()
+ .describe("The taxonomy ID for the category of the listing."),
+ is_supply: z
+ .boolean()
+ .describe(
+ "When true, tags the listing as a supply product, else indicates that it's a finished product.",
+ ),
+ shipping_profile_id: z
+ .number()
+ .int()
+ .positive()
+ .optional()
+ .describe(
+ "ID of the shipping profile to associate with the listing. REQUIRED if listing type is physical.",
+ ),
+ return_policy_id: z
+ .number()
+ .int()
+ .positive()
+ .optional()
+ .describe("The numeric ID of the Return Policy."),
+ materials: z
+ .array(
+ z
+ .string()
+ .regex(/[^\p{L}\p{Nd}\p{Zs}]/u, {
+ message:
+ "Valid materials strings contain only letters, numbers, and whitespace characters.",
+ })
+ .min(1)
+ .max(25)
+ .describe(
+ "A material string for materials used in the product. Valid materials strings contain only letters, numbers, and whitespace characters.",
+ ),
+ )
+ .max(25),
+ shop_section_id: z
+ .number()
+ .int()
+ .positive()
+ .optional()
+ .describe("The numeric ID of the shop section for this listing."),
+ processing_min: z
+ .number()
+ .int()
+ .min(0)
+ .optional()
+ .describe("The minimum number of days it takes to produce the item."),
+ processing_max: z
+ .number()
+ .int()
+ .min(0)
+ .optional()
+ .describe("The maximum number of days it takes to produce the item."),
+ readiness_state_id: z
+ .number()
+ .int()
+ .positive()
+ .optional()
+ .describe(
+ "The numeric ID of the processing profile associated with the listing. Required when type is physical.",
+ ),
+ tags: z
+ .array(
+ z
+ .string()
+ .regex(/^[\p{L}\p{Nd}\p{Zs}'™©®-]+$/u, {
+ message:
+ "Valid tag strings contain only letters, numbers, whitespace characters, -, ', ™, ©, and ®.",
+ })
+ .min(1)
+ .max(25)
+ .describe(
+ "A tag string for the listing. When creating or updating a listing, valid tag strings contain only letters, numbers, whitespace characters, -, ', ™, ©, and ®.",
+ ),
+ )
+ .max(13)
+ .describe("A list of tag strings for the listing."),
+ styles: z
+ .array(
+ z
+ .string()
+ .regex(/[^\p{L}\p{Nd}\p{Zs}]/u, {
+ message:
+ "Valid style strings contain only letters, numbers, and whitespace characters.",
+ })
+ .min(1)
+ .max(25)
+ .describe(
+ 'A style string for this listing, such as "Formal", or "Steampunk". When creating or updating a listing, the listing may have up to two styles. Valid style strings contain only letters, numbers, and whitespace characters.',
+ ),
+ )
+ .max(2)
+ .describe("An array of style strings for this listing."),
+ item_weight: z
+ .number()
+ .positive()
+ .optional()
+ .describe(
+ "The numeric weight of the product measured in units set in 'item_weight_unit'. If set, the values must be greater than 0.",
+ ),
+ item_length: z
+ .number()
+ .positive()
+ .optional()
+ .describe(
+ "The numeric length of the product measured in units set in 'item_dimensions_unit'. If set, the values must be greater than 0.",
+ ),
+ item_width: z
+ .number()
+ .positive()
+ .optional()
+ .describe(
+ "The numeric width of the product measured in units set in 'item_dimensions_unit'. If set, the values must be greater than 0.",
+ ),
+ item_height: z
+ .number()
+ .positive()
+ .optional()
+ .describe(
+ "The numeric height of the product measured in units set in 'item_dimensions_unit'. If set, the values must be greater than 0.",
+ ),
+ item_weight_unit: z
+ .enum(["g", "kg", "oz", "lb"])
+ .optional()
+ .describe(
+ "The unit of measurement for the weight of the product. Valid values are 'g' (grams), 'kg' (kilograms), 'oz' (ounces), and 'lb' (pounds). Required if 'item_weight' is provided.",
+ ),
+ item_dimensions_unit: z
+ .enum(["mm", "cm", "m", "in", "ft"])
+ .optional()
+ .describe(
+ "The unit of measurement for the dimensions of the product. Valid values are 'mm' (millimeters), 'cm' (centimeters), 'm' (meters), 'in' (inches), and 'ft' (feet). Required if any of 'item_length', 'item_width', or 'item_height' is provided.",
+ ),
+ is_personalizable: z
+ .boolean()
+ .optional()
+ .describe("When true, indicates that the listing is personalizable."),
+ personalization_is_required: z
+ .boolean()
+ .optional()
+ .describe(
+ "When true, indicates that personalization is required for the listing. Will only change if _is_personalizable is true",
+ ),
+ personalization_char_count_max: z
+ .number()
+ .int()
+ .min(1)
+ .max(250)
+ .optional()
+ .describe(
+ "The maximum number of characters allowed for personalization. Will only change if _is_personalizable is true.",
+ ),
+ personalization_instructions: z
+ .string()
+ .max(500)
+ .optional()
+ .describe(
+ "Instructions for personalization. Will only change if _is_personalizable is true.",
+ ),
+ production_partner_ids: z
+ .array(
+ z
+ .number()
+ .int()
+ .positive()
+ .describe("A unique ID of a production partner."),
+ )
+ .optional()
+ .describe("An array of unique IDs of production partner ids."),
+ image_ids: z
+ .array(
+ z
+ .number()
+ .int()
+ .positive()
+ .describe("A numeric image ID of an image in a listing."),
+ )
+ .max(10)
+ .optional()
+ .describe(
+ "An array of numeric image IDs of the images in a listing, which can include up to 10 images.",
+ ),
+ is_customizable: z
+ .boolean()
+ .optional()
+ .describe(
+ "When true, a buyer may contact the seller for a customized order. The default value is true when a shop accepts custom orders. Does not apply to shops that do not accept custom orders.",
+ ),
+ should_auto_renew: z
+ .boolean()
+ .optional()
+ .describe("When true, renews a listing for four months upon expiration."),
+ is_taxable: z
+ .boolean()
+ .optional()
+ .describe(
+ "When true, applicable shop tax rates apply to this listing at checkout",
+ ),
+ type: z
+ .enum(["physical", "download", "both"])
+ .describe(
+ "An enumerated type string that indicates whether the listing is physical or a digital download or both.",
+ ),
+ }),
+ outputSchema: z.object({
+ result: z.customListing
+