Comparing version 6.0.4 to 6.0.5-dev.1733492009-c2f8775.0
{ | ||
"name": "canvacard", | ||
"version": "6.0.4", | ||
"version": "6.0.5-dev.1733492009-c2f8775.0", | ||
"description": "Powerful image manipulation package for beginners.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -6,2 +6,3 @@ const { createCanvas, loadImage } = require("@napi-rs/canvas"); | ||
const parseSvg = require("./utils/parseSvg.utils"); | ||
const formatAndValidateHex = require("./utils/formatAndValidateHex.utils"); | ||
@@ -33,6 +34,7 @@ /** | ||
constructor() { | ||
this.token = "f4a26b940ef54a9a4238cef040bd08fa9001cd6c"; | ||
this.token = ""; // Requiere token válido | ||
this.textHeader = "FORTNITE ITEMS SHOP"; | ||
this.textFooter = "Generated with canvascard"; | ||
this.options = { lang: "es", dateFormat: "dddd, MMMM Do YYYY" }; | ||
this.rows = 8; | ||
} | ||
@@ -55,2 +57,16 @@ | ||
/** | ||
* @method setRows | ||
* @name setRows | ||
* @description Set the number of rows for the Fortnite Shop card | ||
* @param {number} value Number of rows to set for the card | ||
* @returns {FortniteShop} The current instance of FortniteShop | ||
* @throws {APIError} If the value is not a number | ||
*/ | ||
setRows(value) { | ||
if (!value || typeof value !== "number") throw new APIError("Please provide a valid number of rows for fortnite-api.com!"); | ||
this.rows = value; | ||
return this; | ||
} | ||
/** | ||
* @method setText | ||
@@ -81,3 +97,3 @@ * @name setText | ||
const shopRequest = await fetch("https://fortnite-api.com/v2/shop/br/combined?language=es", { | ||
const shopRequest = await fetch("https://fortnite-api.com/v2/shop?language=es", { | ||
headers: { "x-api-key": this.token } | ||
@@ -89,234 +105,211 @@ }); | ||
const shopData = await shopRequest.json(); | ||
const entries = shopData.data.featured.entries; | ||
const entries = shopData.data.entries || []; | ||
// Create a new arrangement for the items | ||
// Process items same as before | ||
let items = []; | ||
const existingItemsSet = new Set(); // Use Set to avoid duplicates | ||
for (const entry of entries) { | ||
if (entry.bundle) { | ||
// If it's a bundle, get the first item in the bundle for the rarity | ||
const bundle = entry.bundle; | ||
const firstItem = entry.items[0]; // Get the first item | ||
// Add the batch to the array, using the rarity of the first item | ||
items.push({ | ||
name: bundle.name, | ||
type: bundle.info, | ||
images: bundle.image, | ||
regularPrice: entry.regularPrice, | ||
finalPrice: entry.finalPrice, | ||
rarity: firstItem?.rarity || { backendValue: 'EFortRarity::Common' } // Add first item rarity or default value | ||
type: "bundle", | ||
name: entry.bundle.name, | ||
image: entry.bundle.image, | ||
price: entry.finalPrice, | ||
section: entry.section?.name || "Featured", | ||
colors: entry.colors // Colors for gradient | ||
}); | ||
} else { | ||
// If it is a loose item, add only the item | ||
entry.items.forEach(item => { | ||
if (!existingItemsSet.has(item.name)) { | ||
existingItemsSet.add(item.name); | ||
items.push({ | ||
name: item.name, | ||
type: "Article", | ||
rarity: item.rarity, | ||
images: item.images?.icon || item.images?.smallIcon || item.images?.featured, | ||
finalPrice: entry.finalPrice // Add price to item object | ||
}); | ||
} | ||
}); | ||
} else if (entry.brItems) { | ||
const itemData = entry.brItems.map(item => ({ | ||
type: "item", | ||
name: item.name, | ||
description: item.description, | ||
rarity: item.rarity?.backendValue || "Common", | ||
image: item.images?.featured || item.images?.icon || item.images?.smallIcon || "", | ||
price: entry.finalPrice, | ||
section: entry.section?.name || "Daily", | ||
colors: item?.series?.colors // Array of colors for gradient | ||
})); | ||
items = items.concat(itemData); | ||
} | ||
} | ||
// Sort items by finalPrice (lowest to highest) | ||
items.sort((a, b) => b.finalPrice - a.finalPrice); | ||
// Group items by section | ||
const sections = {}; | ||
items.forEach(item => { | ||
if (!sections[item.section]) sections[item.section] = []; | ||
sections[item.section].push(item); | ||
}); | ||
const itemsPerRow = 8; // Maximum 8 items per row | ||
const maxRows = 10; // Maximum 10 rows | ||
const itemWidth = 384; // Width of each item | ||
const itemHeight = 384; // Height of each item | ||
const padding = 15; // Space between items | ||
const canvasWidth = itemsPerRow * itemWidth + (itemsPerRow - 1) * padding; | ||
const canvasHeight = maxRows * itemHeight + (maxRows - 1) * padding + 300; // Extra adjustment for header | ||
// Canvas setup with new dimensions | ||
const itemsPerRow = this.rows; | ||
const itemWidth = 512; | ||
const itemHeight = 512; | ||
const padding = 20; | ||
const headerHeight = 100; | ||
const sectionPadding = 60; | ||
// Calculate total width based on sections | ||
const canvasWidth = itemsPerRow * (itemWidth + padding) + padding; | ||
// Calculate total height based on sections | ||
const rows = Math.ceil(items.filter(i => i.type === "item").length / itemsPerRow) + Math.ceil(items.filter(i => i.type === "bundle").length / itemsPerRow); | ||
const canvasHeight = rows * (itemHeight + padding) + (headerHeight + sectionPadding) + 300; | ||
const canvas = createCanvas(canvasWidth, canvasHeight); | ||
const ctx = canvas.getContext("2d"); | ||
// Create a blue circular radial gradient | ||
const gradient = ctx.createRadialGradient( | ||
canvasWidth / 2, // X center of gradient | ||
canvasHeight / 2, // Y center of gradient | ||
0, // Start radius (0 to start at a point) | ||
canvasWidth / 2, // X center of gradient | ||
canvasHeight / 2, // Y center of gradient | ||
canvasWidth // Final radius set to canvas width | ||
); | ||
gradient.addColorStop(0, '#0064BD'); // Light Blue (SkyBlue) | ||
gradient.addColorStop(1, '#001E8E'); // Dark blue | ||
// Apply gradient as background | ||
// Create gradient background | ||
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); | ||
gradient.addColorStop(0, '#1e3c72'); | ||
gradient.addColorStop(1, '#2a5298'); | ||
ctx.fillStyle = gradient; | ||
ctx.fillRect(0, 0, canvasWidth, canvasHeight); | ||
ctx.fillRect(0, 0, canvas.width, canvas.height); | ||
// Draw header | ||
ctx.fillStyle = "#ffffff"; | ||
ctx.font = `70px ${font}`; | ||
ctx.textAlign = "center"; | ||
ctx.fillText(this.textHeader, canvas.width / 2, 100); | ||
ctx.fillStyle = '#ffffff'; | ||
ctx.font = `bold 60px ${font}`; | ||
ctx.textAlign = 'center'; | ||
ctx.fillText(this.textHeader.toUpperCase(), canvas.width / 2, 70); | ||
// Initialize position variables | ||
let currentRow = 0; | ||
let currentColumn = 0; | ||
// Draw date | ||
const date = new Date().toLocaleDateString('en-US', { | ||
weekday: 'long', | ||
month: 'long', | ||
day: 'numeric', | ||
year: 'numeric' | ||
}); | ||
ctx.font = `30px ${font}`; | ||
ctx.fillText(date, canvas.width / 2, 110); | ||
// Iterate over the items and draw them on the canvas | ||
for (const item of items) { | ||
try { | ||
const { name, type, rarity, images, finalPrice } = item; | ||
// Draw sections | ||
let currentY = headerHeight + padding; | ||
// Calculate item position based on current column and row | ||
const x = currentColumn * (itemWidth + padding); | ||
const y = 200 + currentRow * (itemHeight + padding); | ||
for (const [sectionName, sectionItems] of Object.entries(sections)) { | ||
// Section header | ||
ctx.font = `bold 40px ${font}`; | ||
ctx.fillStyle = '#ffffff'; | ||
ctx.textAlign = 'left'; | ||
ctx.fillText(sectionName.toUpperCase(), padding, currentY + 40); | ||
currentY += 60; | ||
// Get colors by rarity | ||
const rarityColors = this.getRarityColors(rarity.backendValue || 'EFortRarity::Common'); | ||
// Draw items in grid | ||
let currentX = padding; | ||
let maxRowHeight = 0; | ||
// Draw the item background | ||
ctx.fillStyle = rarityColors.colorCenter; | ||
for (let i = 0; i < sectionItems.length; i++) { | ||
const item = sectionItems[i]; | ||
ctx.fillRect(x, y, itemWidth, itemHeight); // Background for the article | ||
if (currentX + itemWidth > canvas.width - padding) { | ||
currentX = padding; | ||
currentY += maxRowHeight + padding; | ||
maxRowHeight = 0; | ||
} | ||
ctx.beginPath(); | ||
// Draw item card | ||
await this.drawItemCard(ctx, item, currentX, currentY, itemWidth, itemHeight, font); | ||
// Upload and draw the image of the item or lot | ||
const itemIcon = await loadImage(images); | ||
currentX += itemWidth + padding; | ||
maxRowHeight = Math.max(maxRowHeight, itemHeight); | ||
// If it is a loose item | ||
ctx.drawImage(itemIcon, x, y, itemWidth, itemHeight); | ||
ctx.globalAlpha = 1; | ||
ctx.closePath(); | ||
ctx.save(); | ||
ctx.beginPath(); | ||
const overlay = await loadImage(`${__dirname}/../assets/images/fortnite/shop/SmallOverlay.png`); | ||
ctx.drawImage(overlay, x, y, itemWidth, itemHeight); | ||
ctx.closePath(); | ||
ctx.save(); | ||
ctx.globalAlpha = 1; | ||
// Adjust the item name (lot or article) | ||
this.drawItemName(ctx, name, x + 196, y + 310, 375, font); | ||
// Adjust the price within the card area | ||
await this.drawItemPrice(ctx, finalPrice, x + 192, y + 340, 200, font); | ||
currentColumn++; | ||
// If we reach the maximum number of columns, move to the next row | ||
if (currentColumn >= itemsPerRow) { | ||
currentColumn = 0; | ||
currentRow++; | ||
// If we reach the maximum number of rows, we are done. | ||
if (currentRow >= maxRows) break; | ||
if (i === sectionItems.length - 1) { | ||
currentY += maxRowHeight + padding; | ||
} | ||
} catch (error) { | ||
console.error("Error al procesar el ítem:", item, error); | ||
// Skip the item that has an error and continue with the next one | ||
continue; | ||
} | ||
currentY += sectionPadding; | ||
} | ||
// Footer | ||
ctx.font = `50px ${font}`; | ||
ctx.fillText(this.textFooter, canvas.width / 2, canvas.height - 50); | ||
// Draw footer | ||
ctx.fillStyle = '#ffffff'; | ||
ctx.font = `30px ${font}`; | ||
ctx.textAlign = 'center'; | ||
ctx.fillText(this.textFooter, canvas.width / 2, canvas.height - 30); | ||
// Save Image | ||
const outputPath = `${__dirname}/../assets/images/fortnite/shop/output.png`; | ||
const buffer = canvas.toBuffer("image/png"); | ||
fs.writeFileSync(outputPath, buffer); | ||
return canvas.toBuffer("image/png"); | ||
} | ||
// Function to obtain a numerical value according to the rarity of the item | ||
getRarityValue(rarity) { | ||
const rarityValues = { | ||
"EFortRarity::Legendary": 5, | ||
"EFortRarity::Epic": 4, | ||
"EFortRarity::Rare": 3, | ||
"EFortRarity::Uncommon": 2, | ||
"EFortRarity::Common": 1 | ||
}; | ||
return rarityValues[rarity] || 0; // Use 0 by default if not found | ||
// New method to draw colored rounded rectangles | ||
getItemColors(item) { | ||
if (item.type === "bundle") { | ||
// Validar los colores a hexadecimal | ||
const color1 = formatAndValidateHex(item.colors.color1); | ||
const color2 = formatAndValidateHex(item.colors.color2); | ||
return [color1, color2]; | ||
} else if (item.type === "item") { | ||
if (item.colors) { | ||
// Si tiene colores, toma los dos primeros y los valida | ||
const color1 = formatAndValidateHex(item.colors[0]); | ||
const color2 = formatAndValidateHex(item.colors[1]); | ||
return [color1, color2]; | ||
} else { | ||
// Si no tiene series.colors, obtiene los colores por rareza | ||
return this.getRarityColors(item.rarity); | ||
} | ||
} | ||
// Si no hay colores definidos, usa un gradiente por defecto | ||
return ["#ffffff", "#000000"]; | ||
} | ||
// Function to get colors based on item rarity | ||
getRarityColors(rarity) { | ||
const rarities = { | ||
"EFortRarity::Legendary": { colorBorder: "#e98d4b", colorCenter: "#ea8d23" }, | ||
"EFortRarity::Epic": { colorBorder: "#e95eff", colorCenter: "#c359ff" }, | ||
"EFortRarity::Rare": { colorBorder: "#37d1ff", colorCenter: "#2cc1ff" }, | ||
"EFortRarity::Uncommon": { colorBorder: "#87e339", colorCenter: "#69bb1e" }, | ||
"EFortRarity::Common": { colorBorder: "#b1b1b1", colorCenter: "#bebebe" } | ||
}; | ||
return rarities[rarity] || rarities.common; // Use common by default | ||
} | ||
// New method to draw individual item cards | ||
async drawItemCard(ctx, item, x, y, width, height, font) { | ||
// Function to split the name into two lines if necessary | ||
drawItemName(ctx, text, x, y, maxWidth, font) { | ||
ctx.font = `30px ${font}`; | ||
ctx.fillStyle = "#ffffff"; | ||
const colors = this.getItemColors(item); | ||
const words = text.split(' '); | ||
let line = ''; | ||
let lineHeight = 30; | ||
let lineCount = 0; | ||
const maxLines = 2; // Maximum 2 lines | ||
// Draw card background with gradient | ||
const cardGradient = ctx.createLinearGradient(x, y, x, y + height); | ||
cardGradient.addColorStop(0, colors[0]); | ||
cardGradient.addColorStop(1, colors[1]); | ||
for (let i = 0; i < words.length; i++) { | ||
const testLine = line + words[i] + ' '; | ||
const metrics = ctx.measureText(testLine); | ||
const testWidth = metrics.width; | ||
// Card shadow | ||
ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'; | ||
ctx.shadowBlur = 15; | ||
ctx.shadowOffsetX = 0; | ||
ctx.shadowOffsetY = 5; | ||
if (testWidth > maxWidth && lineCount < maxLines - 1) { | ||
ctx.fillText(line, x, y); | ||
line = words[i] + ' '; | ||
y += lineHeight; | ||
lineCount++; | ||
} else { | ||
line = testLine; | ||
} | ||
} | ||
// Draw the last line | ||
ctx.fillText(line, x, y); | ||
} | ||
// Draw rounded rectangle | ||
ctx.beginPath(); | ||
ctx.roundRect(x, y, width, height, 15); | ||
ctx.fillStyle = cardGradient; | ||
ctx.fill(); | ||
// Function to adjust the price within the card | ||
async drawItemPrice(ctx, price, x, y, maxWidth, font) { | ||
const vbuckIcon = await loadImage(parseSvg(`${__dirname}/../assets/images/fortnite/shop/vBucks.svg`)); | ||
const iconSize = 20; | ||
// Reset shadow | ||
ctx.shadowColor = 'transparent'; | ||
ctx.shadowBlur = 0; | ||
// Draw the V-Bucks icon | ||
ctx.drawImage(vbuckIcon, x - 40, y + 10, iconSize, iconSize); | ||
// Draw item image | ||
try { | ||
const image = await loadImage(item.image); | ||
const imageSize = Math.min(width - 40, height - 100); | ||
const imageX = x + (width - imageSize) / 2; | ||
const imageY = y + 20; | ||
// Draw the price adjusted to the available width | ||
ctx.font = `30px ${font}`; | ||
ctx.fillStyle = "#ffffff"; | ||
let priceText = `${price}`; | ||
const priceWidth = ctx.measureText(priceText).width; | ||
// If the price exceeds the available space, adjust the font size | ||
if (priceWidth > maxWidth - iconSize - 10) { | ||
let fontSize = 30; | ||
do { | ||
fontSize -= 1; | ||
ctx.font = `${fontSize}px ${font}`; | ||
} while (ctx.measureText(priceText).width > maxWidth - iconSize - 10); | ||
ctx.drawImage(image, imageX, imageY, imageSize, imageSize); | ||
} catch (error) { | ||
console.error(`Error loading image for ${item.name}:`, error); | ||
} | ||
// Draw the price text | ||
ctx.fillText(priceText, x + iconSize, y + 30); | ||
// Draw item name | ||
ctx.fillStyle = '#ffffff'; | ||
ctx.font = `bold 20px ${font}`; | ||
ctx.textAlign = 'center'; | ||
ctx.fillText(item.name, x + width / 2, y + height - 60, width - 20); | ||
// Draw price with V-Bucks icon | ||
ctx.font = `bold 24px ${font}`; | ||
ctx.fillText(`${item.price} V-Bucks`, x + width / 2, y + height - 20); | ||
} | ||
// Updated rarity colors method | ||
getRarityColors(rarity) { | ||
const rarities = { | ||
"EFortRarity::Legendary": ['#ea8d23', 'rgba(233, 141, 75, 0.9)'], | ||
"EFortRarity::Epic": ['#c359ff', 'rgba(233, 94, 255, 0.9)'], | ||
"EFortRarity::Rare": ['#2cc1ff', 'rgba(55, 209, 255, 0.9)'], | ||
"EFortRarity::Uncommon": ['#69bb1e', 'rgba(135, 227, 57, 0.9)'], | ||
"EFortRarity::Common": ['#bebebe', 'rgba(177, 177, 177, 0.9)'], | ||
}; | ||
return rarities[rarity] || rarities["EFortRarity::Uncommon"]; | ||
} | ||
} | ||
module.exports = FortniteShop; |
@@ -16,9 +16,11 @@ const APIError = require("./error.utils"); | ||
// Validar longitud y expandir si es necesario | ||
if (hex.length === 3) { | ||
// Si el código hexadecimal es menor a 6 caracteres, duplicar los caracteres | ||
if (hex.length < 6) { | ||
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; | ||
} | ||
// Validar que el código tenga 6 caracteres | ||
if (hex.length !== 6) return alt; | ||
// Si el código hexadecimal es mayor a 6 caracteres, cortar a 6 caracteres | ||
if (hex.length > 6) { | ||
hex = hex.slice(0, 6); | ||
} | ||
@@ -25,0 +27,0 @@ // Regex para validar caracteres válidos |
@@ -31,9 +31,9 @@ import Canvacard = require("./src/Canvacard"); | ||
export declare namespace MSX { | ||
let Brightness: (img: any, amount: any) => Promise<Buffer>; | ||
let Convolute: (img: any, matrix: any, opaque: any, lvl: any) => Promise<Buffer>; | ||
let Darkness: (img: any, amount: any) => Promise<Buffer>; | ||
let Greyscale: (img: any) => Promise<Buffer>; | ||
let Invert: (img: any) => Promise<Buffer>; | ||
let Sepia: (img: any) => Promise<Buffer>; | ||
let Threshold: (img: any, amount?: number) => Promise<Buffer>; | ||
let Brightness: (img: any, amount: any) => Promise<Buffer<ArrayBufferLike>>; | ||
let Convolute: (img: any, matrix: any, opaque: any, lvl: any) => Promise<Buffer<ArrayBufferLike>>; | ||
let Darkness: (img: any, amount: any) => Promise<Buffer<ArrayBufferLike>>; | ||
let Greyscale: (img: any) => Promise<Buffer<ArrayBufferLike>>; | ||
let Invert: (img: any) => Promise<Buffer<ArrayBufferLike>>; | ||
let Sepia: (img: any) => Promise<Buffer<ArrayBufferLike>>; | ||
let Threshold: (img: any, amount?: number) => Promise<Buffer<ArrayBufferLike>>; | ||
let Trigger: (image: any, TRIGGERED: any) => Promise<any>; | ||
@@ -40,0 +40,0 @@ } |
@@ -1,3 +0,3 @@ | ||
declare function _exports(img: any, amount: any): Promise<Buffer>; | ||
declare function _exports(img: any, amount: any): Promise<Buffer<ArrayBufferLike>>; | ||
export = _exports; | ||
//# sourceMappingURL=Brightness.d.ts.map |
@@ -1,3 +0,3 @@ | ||
declare function _exports(img: any, matrix: any, opaque: any, lvl: any): Promise<Buffer>; | ||
declare function _exports(img: any, matrix: any, opaque: any, lvl: any): Promise<Buffer<ArrayBufferLike>>; | ||
export = _exports; | ||
//# sourceMappingURL=Convolute.d.ts.map |
@@ -1,3 +0,3 @@ | ||
declare function _exports(img: any, amount: any): Promise<Buffer>; | ||
declare function _exports(img: any, amount: any): Promise<Buffer<ArrayBufferLike>>; | ||
export = _exports; | ||
//# sourceMappingURL=Darkness.d.ts.map |
@@ -1,3 +0,3 @@ | ||
declare function _exports(img: any): Promise<Buffer>; | ||
declare function _exports(img: any): Promise<Buffer<ArrayBufferLike>>; | ||
export = _exports; | ||
//# sourceMappingURL=Greyscale.d.ts.map |
@@ -1,3 +0,3 @@ | ||
declare function _exports(img: any): Promise<Buffer>; | ||
declare function _exports(img: any): Promise<Buffer<ArrayBufferLike>>; | ||
export = _exports; | ||
//# sourceMappingURL=Invert.d.ts.map |
@@ -1,3 +0,3 @@ | ||
declare function _exports(img: any): Promise<Buffer>; | ||
declare function _exports(img: any): Promise<Buffer<ArrayBufferLike>>; | ||
export = _exports; | ||
//# sourceMappingURL=Sepia.d.ts.map |
@@ -1,3 +0,3 @@ | ||
declare function _exports(img: any, amount?: number): Promise<Buffer>; | ||
declare function _exports(img: any, amount?: number): Promise<Buffer<ArrayBufferLike>>; | ||
export = _exports; | ||
//# sourceMappingURL=Threshold.d.ts.map |
@@ -33,2 +33,3 @@ export = FortniteShop; | ||
}; | ||
rows: number; | ||
/** | ||
@@ -44,2 +45,11 @@ * @method setToken | ||
/** | ||
* @method setRows | ||
* @name setRows | ||
* @description Set the number of rows for the Fortnite Shop card | ||
* @param {number} value Number of rows to set for the card | ||
* @returns {FortniteShop} The current instance of FortniteShop | ||
* @throws {APIError} If the value is not a number | ||
*/ | ||
setRows(value: number): FortniteShop; | ||
/** | ||
* @method setText | ||
@@ -62,7 +72,6 @@ * @name setText | ||
build(font?: string): Promise<Buffer>; | ||
getRarityValue(rarity: any): any; | ||
getItemColors(item: any): any; | ||
drawItemCard(ctx: any, item: any, x: any, y: any, width: any, height: any, font: any): Promise<void>; | ||
getRarityColors(rarity: any): any; | ||
drawItemName(ctx: any, text: any, x: any, y: any, maxWidth: any, font: any): void; | ||
drawItemPrice(ctx: any, price: any, x: any, y: any, maxWidth: any, font: any): Promise<void>; | ||
} | ||
//# sourceMappingURL=FortniteShop.d.ts.map |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
AI-detected possible typosquat
Supply chain riskAI has identified this package as a potential typosquat of a more popular package. This suggests that the package may be intentionally mimicking another package's name, description, or other metadata.
Found 1 instance in 1 package
291355
5556
4
1