diff --git a/backend/package-lock.json b/backend/package-lock.json index 78f6735d..948aea87 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -3033,6 +3033,21 @@ "resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz", "integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=" }, + "parse-color": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-color/-/parse-color-1.0.0.tgz", + "integrity": "sha1-e3SLlag/A/FqlPU15S1/PZRlhhk=", + "requires": { + "color-convert": "~0.5.0" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + } + } + }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index c7af1a1f..08212866 100644 --- a/backend/package.json +++ b/backend/package.json @@ -49,6 +49,7 @@ "lodash.pick": "^4.4.0", "moment-timezone": "^0.5.21", "mysql": "^2.16.0", + "parse-color": "^1.0.0", "passport": "^0.4.0", "passport-custom": "^1.0.5", "passport-oauth2": "^1.5.0", diff --git a/backend/src/plugins/Post/commands/EditEmbedCmd.ts b/backend/src/plugins/Post/commands/EditEmbedCmd.ts index 9cfdf357..584f780c 100644 --- a/backend/src/plugins/Post/commands/EditEmbedCmd.ts +++ b/backend/src/plugins/Post/commands/EditEmbedCmd.ts @@ -4,6 +4,8 @@ import { sendErrorMessage, sendSuccessMessage } from "src/pluginUtils"; import { Embed } from "eris"; import { trimLines } from "src/utils"; import { formatContent } from "../util/formatContent"; +import { parseColor } from "../../../utils/parseColor"; +import { rgbToInt } from "../../../utils/rgbToInt"; const COLOR_MATCH_REGEX = /^#?([0-9a-f]{6})$/; @@ -31,13 +33,13 @@ export const EditEmbedCmd = postCmd({ let color = null; if (args.color) { - const colorMatch = args.color.match(COLOR_MATCH_REGEX); - if (!colorMatch) { - sendErrorMessage(pluginData, msg.channel, "Invalid color specified, use hex colors"); + const colorRgb = parseColor(args.color); + if (colorRgb) { + color = rgbToInt(colorRgb); + } else { + sendErrorMessage(pluginData, msg.channel, "Invalid color specified"); return; } - - color = parseInt(colorMatch[1], 16); } const embed: Embed = savedMessage.data.embeds[0] as Embed; diff --git a/backend/src/plugins/Post/commands/PostEmbedCmd.ts b/backend/src/plugins/Post/commands/PostEmbedCmd.ts index 707b97f5..1fc64571 100644 --- a/backend/src/plugins/Post/commands/PostEmbedCmd.ts +++ b/backend/src/plugins/Post/commands/PostEmbedCmd.ts @@ -5,8 +5,8 @@ import { sendErrorMessage } from "src/pluginUtils"; import { Embed } from "eris"; import { isValidEmbed, trimLines } from "src/utils"; import { formatContent } from "../util/formatContent"; - -const COLOR_MATCH_REGEX = /^#?([0-9a-f]{6})$/; +import { parseColor } from "../../../utils/parseColor"; +import { rgbToInt } from "../../../utils/rgbToInt"; export const PostEmbedCmd = postCmd({ trigger: "post_embed", @@ -37,13 +37,13 @@ export const PostEmbedCmd = postCmd({ let color = null; if (args.color) { - const colorMatch = args.color.toLowerCase().match(COLOR_MATCH_REGEX); - if (!colorMatch) { - sendErrorMessage(pluginData, msg.channel, "Invalid color specified, use hex colors"); + const colorRgb = parseColor(args.color); + if (colorRgb) { + color = rgbToInt(colorRgb); + } else { + sendErrorMessage(pluginData, msg.channel, "Invalid color specified"); return; } - - color = parseInt(colorMatch[1], 16); } let embed: Embed = { type: "rich" }; diff --git a/backend/src/utils/parseColor.ts b/backend/src/utils/parseColor.ts new file mode 100644 index 00000000..9a91bab2 --- /dev/null +++ b/backend/src/utils/parseColor.ts @@ -0,0 +1,39 @@ +import _parseColor from "parse-color"; + +// Accepts 100,100,100 and 100 100 100 +const isRgb = /^(\d{1,3})\D+(\d{1,3})\D+(\d{1,3})$/; + +const isPartialHex = /^([0-9a-f]{3}|[0-9a-f]{6})$/i; + +/** + * Parses a color from the input string. The following formats are accepted: + * - any CSS color format (hex, rgb(), color names, etc.) + * - rrr, ggg, bbb + * - rrr ggg bbb + * @return Parsed color as `[r, g, b]` or `null` if no color could be parsed + */ +export function parseColor(input: string): null | [number, number, number] { + const rgbMatch = input.match(isRgb); + if (rgbMatch) { + const r = parseInt(rgbMatch[1], 10); + const g = parseInt(rgbMatch[2], 10); + const b = parseInt(rgbMatch[3], 10); + + if (r > 255 || g > 255 || b > 255) { + return null; + } + + return [r, g, b]; + } + + if (input.match(isPartialHex)) { + input = `#${input}`; + } + + const cssColorMatch = _parseColor(input); + if (cssColorMatch.rgb) { + return cssColorMatch.rgb; + } + + return null; +} diff --git a/backend/src/utils/rgbToInt.ts b/backend/src/utils/rgbToInt.ts new file mode 100644 index 00000000..9b0e8235 --- /dev/null +++ b/backend/src/utils/rgbToInt.ts @@ -0,0 +1,3 @@ +export function rgbToInt(rgb: [number, number, number]) { + return (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]; +}