Source

lib/develop/configureItem.js

// Includes
const http = require('../util/http.js').func
const getGeneralToken = require('../util/getGeneralToken.js').func

// Args
exports.required = ['id', 'name', 'description']
exports.optional = ['enableComments', 'sellForRobux', 'genreSelection', 'jar']

// Docs
/**
 * 🔐 Configure an asset.
 * @category Develop
 * @alias configureItem
 * @param {number} assetId - The id of the asset.
 * @param {string} name - The new name of the asset.
 * @param {string} description - The new description of the asset.
 * @param {boolean=} enableComments - Enable comments on your asset.
 * @param {number|boolean=} [sellForRobux=false] - The amount of robux to sell for; use a number for sellable assets, boolean for copyable assets
 * @param {string=} [genreSelection="All"] - The genre of your asset.
 * @returns {Promise<ConfigureItemResponse>}
 * @example const noblox = require("noblox.js")
 * // Login using your cookie
 * noblox.configureItem(1117747196, "Item", "A cool item.", false, 100)
**/

// Define
function configure (jar, token, id, name, description, enableComments, sellForRobux, genreSelection, sellingPrice) {
  return http({
    url: '//develop.roblox.com/v1/assets/' + id,
    options: {
      method: 'PATCH',
      jar,
      headers: {
        'X-CSRF-TOKEN': token
      },
      json: {
        name,
        description,
        enableComments,
        genres: genreSelection || ['All'],
        isCopyingAllowed: typeof (sellForRobux) === 'boolean' ? sellForRobux : null
      }
    }
  }).then(function (json) {
    if (!json.errors) {
      const response = {
        name,
        description,
        assetId: id
      }
      if (typeof sellForRobux === 'boolean') {
        response.isCopyingAllowed = sellForRobux
      } else {
        response.price = sellingPrice || 0
      }
      return response
    } else {
      if (json.errors[0].code === 13) { // "Only a marketplace asset can be updated with IsCopyingAllowed."
        throw new Error('Attempting to make a sellable asset copyable; it must be sold for robux. (Use a number for sellForRobux.)')
      }
      throw new Error(json.errors[0].message)
    }
  })
}

function configureRobux (args) {
  return http({
    url: '//itemconfiguration.roblox.com/v1/assets/' + args.id + '/release',
    options: {
      method: 'POST',
      jar: args.jar,
      headers: {
        'X-CSRF-TOKEN': args.token
      },
      json: {
        saleStatus: args.sellForRobux ? 'OnSale' : 'OffSale',
        priceConfiguration: {
          priceInRobux: args.sellForRobux || 0
        }
      }
    }
  })
    .then(function (json) {
      if (!json.errors) {
        return configure(args.jar, args.token, args.id, args.name, args.description, args.enableComments, args.sellForRobux, args.genreSelection, args.sellForRobux)
      } else {
        // Code 6: "Asset is released"; caused when changing the price of an on-sale asset from non-zero to non-zero
        if (json.errors[0].code === 6) {
          return http({
            url: '//itemconfiguration.roblox.com/v1/assets/' + args.id + '/update-price',
            options: {
              method: 'POST',
              jar: args.jar,
              headers: {
                'X-CSRF-TOKEN': args.token
              },
              json: {
                priceConfiguration: {
                  priceInRobux: args.sellForRobux
                }
              }
            }
          }).then((err) => {
            if (!err.errors) {
              return configure(args.jar, args.token, args.id, args.name, args.description, args.enableComments, args.sellForRobux, args.genreSelection, args.sellForRobux)
            } else {
              throw new Error(json.errors[0].message)
            }
          })
        } else if (json.errors[0].code === 3) { // "Cannot release the associated asset type" - caused by copyable asset using sellForRobux: 2 or greater
          // Throw an error as the developer may have intended to sell the asset for robux instead of making it free.
          throw new Error('Attempting to sell a copyable asset for robux; it can only be made free. (Use true for sellForRobux.)')
        } else if (json.errors[0].code === 20) { // "Cannot set the associated asset type to remove-from-release" - caused by copyable asset using sellForRobux: 0
          // Continue and ignore the error as the intended outcome makes the asset private; set sellForRobux from 0 to false
          return configure(args.jar, args.token, args.id, args.name, args.description, args.enableComments, args.sellForRobux, args.genreSelection, !!args.sellForRobux)
        } else if (json.errors[0].code === 37) { // "AssetIsLimited" - Use the newer endpoint to update price and sale status
          return http({
            url: '//catalog.roblox.com/v1/catalog/items/details',
            options: {
              method: 'POST',
              jar: args.jar,
              json: {
                items: [
                  {
                    id: args.id,
                    itemType: 1
                  }
                ]
              },
              resolveWithFullResponse: true
            }
          }).then((response) => {
            if (!response.ok) throw new Error(response.body)

            const { collectibleItemId, price } = JSON.parse(response.body)

            if (!collectibleItemId) throw new Error(`The publishing fee for asset ${args.id} has not been paid, you must do this in order to change or set the price.`)

            return http({
              url: `//itemconfiguration.roblox.com/v1/collectibles/${collectibleItemId}`,
              options: {
                method: 'PATCH',
                jar: args.jar,
                headers: {
                  'X-CSRF-Token': args.token
                },
                json: {
                  isFree: false,
                  priceInRobux: args.sellForRobux || price,
                  priceOffset: 0,
                  quantityLimitPerUser: 0,
                  resaleRestriction: 2,
                  saleLocationConfiguration: {
                    places: [],
                    saleLocationType: 1
                  },
                  saleStatus: args.sellForRobux > 0 ? 0 : 1
                },
                resolveWithFullResponse: true
              }
            }).then((response) => {
              if (!response.ok) throw new Error(response.body)

              return {
                name: args.name,
                assetId: args.id,
                description: args.description,
                price: args.sellForRobux,
                isCopyingAllowed: null
              }
            })
          })
        } else if (json.errors[0].code === 40) { // "Use collecibles publishing endpoint." - Publishing fee has not been paid for this asset
          throw new Error(`The publishing fee for asset ${args.id} has not been paid, you must pay the fee before setting or updating the price.`)
        }
        throw new Error(`An unknown error occurred: [${json.errors[0].code}] ${json.errors[0].message}`)
      }
    })
}

function runWithToken (args) {
  const jar = args.jar
  return getGeneralToken({
    jar
  })
    .then(function (token) {
      if (typeof (args.sellForRobux) === 'number') {
        if (args.sellForRobux < 2 && args.sellForRobux !== 0) {
          throw new Error('Assets cannot be sold for less than 2R.')
        }
        args.token = token
        return configureRobux(args)
      } else {
        return configure(args.jar, token, args.id, args.name, args.description, args.enableComments, args.sellForRobux, args.genreSelection)
      }
    })
}

exports.func = function (args) {
  return runWithToken(args)
}