| | const { |
| | QuoteGenerate |
| | } = require('../utils') |
| | const { createCanvas, loadImage } = require('canvas') |
| | const sharp = require('sharp') |
| |
|
| | const normalizeColor = (color) => { |
| | const canvas = createCanvas(0, 0) |
| | const canvasCtx = canvas.getContext('2d') |
| |
|
| | canvasCtx.fillStyle = color |
| | color = canvasCtx.fillStyle |
| |
|
| | return color |
| | } |
| |
|
| | const colorLuminance = (hex, lum) => { |
| | hex = String(hex).replace(/[^0-9a-f]/gi, '') |
| | if (hex.length < 6) { |
| | hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] |
| | } |
| | lum = lum || 0 |
| |
|
| | |
| | let rgb = '#' |
| | let c |
| | let i |
| | for (i = 0; i < 3; i++) { |
| | c = parseInt(hex.substr(i * 2, 2), 16) |
| | c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16) |
| | rgb += ('00' + c).substr(c.length) |
| | } |
| |
|
| | return rgb |
| | } |
| |
|
| | const imageAlpha = (image, alpha) => { |
| | const canvas = createCanvas(image.width, image.height) |
| |
|
| | const canvasCtx = canvas.getContext('2d') |
| |
|
| | canvasCtx.globalAlpha = alpha |
| |
|
| | canvasCtx.drawImage(image, 0, 0) |
| |
|
| | return canvas |
| | } |
| |
|
| | module.exports = async (parm) => { |
| | |
| | if (!parm) return { error: 'query_empty' } |
| | if (!parm.messages || parm.messages.length < 1) return { error: 'messages_empty' } |
| |
|
| | let botToken = parm.botToken || process.env.BOT_TOKEN |
| |
|
| | const quoteGenerate = new QuoteGenerate(botToken) |
| |
|
| | const quoteImages = [] |
| |
|
| | let backgroundColor = parm.backgroundColor || '//#292232' |
| | let backgroundColorOne |
| | let backgroundColorTwo |
| |
|
| | const backgroundColorSplit = backgroundColor.split('/') |
| |
|
| | if (backgroundColorSplit && backgroundColorSplit.length > 1 && backgroundColorSplit[0] !== '') { |
| | backgroundColorOne = normalizeColor(backgroundColorSplit[0]) |
| | backgroundColorTwo = normalizeColor(backgroundColorSplit[1]) |
| | } else if (backgroundColor.startsWith('//')) { |
| | backgroundColor = normalizeColor(backgroundColor.replace('//', '')) |
| | backgroundColorOne = colorLuminance(backgroundColor, 0.35) |
| | backgroundColorTwo = colorLuminance(backgroundColor, -0.15) |
| | } else { |
| | backgroundColor = normalizeColor(backgroundColor) |
| | backgroundColorOne = backgroundColor |
| | backgroundColorTwo = backgroundColor |
| | } |
| |
|
| | for (const key in parm.messages) { |
| | const message = parm.messages[key] |
| |
|
| | if (message) { |
| | const canvasQuote = await quoteGenerate.generate( |
| | backgroundColorOne, |
| | backgroundColorTwo, |
| | message, |
| | parm.width, |
| | parm.height, |
| | parseFloat(parm.scale), |
| | parm.emojiBrand |
| | ) |
| |
|
| | quoteImages.push(canvasQuote) |
| | } |
| | } |
| |
|
| | if (quoteImages.length === 0) { |
| | return { |
| | error: 'empty_messages' |
| | } |
| | } |
| |
|
| | let canvasQuote |
| |
|
| | if (quoteImages.length > 1) { |
| | let width = 0 |
| | let height = 0 |
| |
|
| | for (let index = 0; index < quoteImages.length; index++) { |
| | if (quoteImages[index].width > width) width = quoteImages[index].width |
| | height += quoteImages[index].height |
| | } |
| |
|
| | const quoteMargin = 5 * parm.scale |
| |
|
| | const canvas = createCanvas(width, height + (quoteMargin * quoteImages.length)) |
| | const canvasCtx = canvas.getContext('2d') |
| |
|
| | let imageY = 0 |
| |
|
| | for (let index = 0; index < quoteImages.length; index++) { |
| | canvasCtx.drawImage(quoteImages[index], 0, imageY) |
| | imageY += quoteImages[index].height + quoteMargin |
| | } |
| | canvasQuote = canvas |
| | } else { |
| | canvasQuote = quoteImages[0] |
| | } |
| |
|
| | let quoteImage |
| |
|
| | let { type, format, ext } = parm |
| |
|
| | if (!type && ext) type = 'png' |
| | if (type !== 'image' && type !== 'stories' && canvasQuote.height > 1024 * 2) type = 'png' |
| |
|
| | if (type === 'quote') { |
| | const downPadding = 75 |
| | const maxWidth = 512 |
| | const maxHeight = 512 |
| |
|
| | const imageQuoteSharp = sharp(canvasQuote.toBuffer()) |
| |
|
| | if (canvasQuote.height > canvasQuote.width) imageQuoteSharp.resize({ height: maxHeight }) |
| | else imageQuoteSharp.resize({ width: maxWidth }) |
| |
|
| | const canvasImage = await loadImage(await imageQuoteSharp.toBuffer()) |
| |
|
| | const canvasPadding = createCanvas(canvasImage.width, canvasImage.height + downPadding) |
| | const canvasPaddingCtx = canvasPadding.getContext('2d') |
| |
|
| | canvasPaddingCtx.drawImage(canvasImage, 0, 0) |
| |
|
| | const imageSharp = sharp(canvasPadding.toBuffer()) |
| |
|
| | if (canvasPadding.height >= canvasPadding.width) imageSharp.resize({ height: maxHeight }) |
| | else imageSharp.resize({ width: maxWidth }) |
| |
|
| | if (format === 'png') quoteImage = await imageSharp.png().toBuffer() |
| | else quoteImage = await imageSharp.webp({ lossless: true, force: true }).toBuffer() |
| | } else if (type === 'image') { |
| | const heightPadding = 75 * parm.scale |
| | const widthPadding = 95 * parm.scale |
| |
|
| | const canvasImage = await loadImage(canvasQuote.toBuffer()) |
| |
|
| | const canvasPic = createCanvas(canvasImage.width + widthPadding, canvasImage.height + heightPadding) |
| | const canvasPicCtx = canvasPic.getContext('2d') |
| |
|
| | |
| | const gradient = canvasPicCtx.createRadialGradient( |
| | canvasPic.width / 2, |
| | canvasPic.height / 2, |
| | 0, |
| | canvasPic.width / 2, |
| | canvasPic.height / 2, |
| | canvasPic.width / 2 |
| | ) |
| |
|
| | const patternColorOne = colorLuminance(backgroundColorTwo, 0.15) |
| | const patternColorTwo = colorLuminance(backgroundColorOne, 0.15) |
| |
|
| | gradient.addColorStop(0, patternColorOne) |
| | gradient.addColorStop(1, patternColorTwo) |
| |
|
| | canvasPicCtx.fillStyle = gradient |
| | canvasPicCtx.fillRect(0, 0, canvasPic.width, canvasPic.height) |
| |
|
| | const canvasPatternImage = await loadImage('./assets/pattern_02.png') |
| | |
| |
|
| | const pattern = canvasPicCtx.createPattern(imageAlpha(canvasPatternImage, 0.3), 'repeat') |
| |
|
| | canvasPicCtx.fillStyle = pattern |
| | canvasPicCtx.fillRect(0, 0, canvasPic.width, canvasPic.height) |
| |
|
| | |
| | canvasPicCtx.shadowOffsetX = 8 |
| | canvasPicCtx.shadowOffsetY = 8 |
| | canvasPicCtx.shadowBlur = 13 |
| | canvasPicCtx.shadowColor = 'rgba(0, 0, 0, 0.5)' |
| |
|
| | |
| | canvasPicCtx.drawImage(canvasImage, widthPadding / 2, heightPadding / 2) |
| |
|
| | canvasPicCtx.shadowOffsetX = 0 |
| | canvasPicCtx.shadowOffsetY = 0 |
| | canvasPicCtx.shadowBlur = 0 |
| | canvasPicCtx.shadowColor = 'rgba(0, 0, 0, 0)' |
| |
|
| | |
| | canvasPicCtx.fillStyle = `rgba(0, 0, 0, 0.3)` |
| | canvasPicCtx.font = `${8 * parm.scale}px Noto Sans` |
| | canvasPicCtx.textAlign = 'right' |
| | canvasPicCtx.fillText('@QuotLyBot', canvasPic.width - 25, canvasPic.height - 25) |
| |
|
| | quoteImage = await sharp(canvasPic.toBuffer()).png({ lossless: true, force: true }).toBuffer() |
| | } else if (type === 'stories') { |
| | const canvasPic = createCanvas(720, 1280) |
| | const canvasPicCtx = canvasPic.getContext('2d') |
| |
|
| | |
| | const gradient = canvasPicCtx.createRadialGradient( |
| | canvasPic.width / 2, |
| | canvasPic.height / 2, |
| | 0, |
| | canvasPic.width / 2, |
| | canvasPic.height / 2, |
| | canvasPic.width / 2 |
| | ) |
| |
|
| | const patternColorOne = colorLuminance(backgroundColorTwo, 0.25) |
| | const patternColorTwo = colorLuminance(backgroundColorOne, 0.15) |
| |
|
| | gradient.addColorStop(0, patternColorOne) |
| | gradient.addColorStop(1, patternColorTwo) |
| |
|
| | canvasPicCtx.fillStyle = gradient |
| | canvasPicCtx.fillRect(0, 0, canvasPic.width, canvasPic.height) |
| |
|
| | const canvasPatternImage = await loadImage('./assets/pattern_02.png') |
| |
|
| | const pattern = canvasPicCtx.createPattern(imageAlpha(canvasPatternImage, 0.3), 'repeat') |
| |
|
| | canvasPicCtx.fillStyle = pattern |
| | canvasPicCtx.fillRect(0, 0, canvasPic.width, canvasPic.height) |
| |
|
| | |
| | canvasPicCtx.shadowOffsetX = 8 |
| | canvasPicCtx.shadowOffsetY = 8 |
| | canvasPicCtx.shadowBlur = 13 |
| | canvasPicCtx.shadowColor = 'rgba(0, 0, 0, 0.5)' |
| |
|
| | let canvasImage = await loadImage(canvasQuote.toBuffer()) |
| |
|
| | |
| | const minPadding = 110 |
| |
|
| | |
| | if (canvasImage.width > canvasPic.width - minPadding * 2 || canvasImage.height > canvasPic.height - minPadding * 2) { |
| | canvasImage = await sharp(canvasQuote.toBuffer()).resize({ |
| | width: canvasPic.width - minPadding * 2, |
| | height: canvasPic.height - minPadding * 2, |
| | fit: 'contain', |
| | background: { r: 0, g: 0, b: 0, alpha: 0 } |
| | }).toBuffer() |
| |
|
| | canvasImage = await loadImage(canvasImage) |
| | } |
| |
|
| | |
| | const imageX = (canvasPic.width - canvasImage.width) / 2 |
| | const imageY = (canvasPic.height - canvasImage.height) / 2 |
| |
|
| | canvasPicCtx.drawImage(canvasImage, imageX, imageY) |
| |
|
| | canvasPicCtx.shadowOffsetX = 0 |
| | canvasPicCtx.shadowOffsetY = 0 |
| | canvasPicCtx.shadowBlur = 0 |
| |
|
| | |
| | canvasPicCtx.fillStyle = `rgba(0, 0, 0, 0.4)` |
| | canvasPicCtx.font = `${16 * parm.scale}px Noto Sans` |
| | canvasPicCtx.textAlign = 'center' |
| | canvasPicCtx.translate(70, canvasPic.height / 2) |
| | canvasPicCtx.rotate(-Math.PI / 2) |
| | if (parm.watermark) canvasPicCtx.fillText(parm.watermark || '@QuotLyBot', 0, 0) |
| |
|
| | quoteImage = await sharp(canvasPic.toBuffer()).png({ lossless: true, force: true }).toBuffer() |
| | } else { |
| | quoteImage = canvasQuote.toBuffer() |
| | } |
| |
|
| | const imageMetadata = await sharp(quoteImage).metadata() |
| |
|
| | const width = imageMetadata.width |
| | const height = imageMetadata.height |
| |
|
| | let image |
| | if (ext) image = quoteImage |
| | else image = quoteImage.toString('base64') |
| |
|
| | return { |
| | image, |
| | type, |
| | width, |
| | height, |
| | ext |
| | } |
| | } |
| |
|