feat: new variables parser

This commit is contained in:
diced 2022-12-11 15:29:57 -08:00
parent 6349503b00
commit 91341e2d21
No known key found for this signature in database
GPG key ID: 370BD1BA142842D1
6 changed files with 193 additions and 63 deletions

View file

@ -2,53 +2,21 @@ import { Image, Url, User } from '@prisma/client';
import config from 'lib/config';
import { ConfigDiscordContent } from 'lib/config/Config';
import Logger from './logger';
// [user, image, url, route (ex. https://example.com/r/something.png)]
export type Args = [User, Image?, Url?, string?];
import { parseString, ParseValue } from './utils/parser';
const logger = Logger.get('discord');
function parse(str: string, args: Args) {
if (!str) return null;
str = str
.replace(/{user\.admin}/gi, args[0].administrator ? 'yes' : 'no')
.replace(/{user\.id}/gi, args[0].id.toString())
.replace(/{user\.name}/gi, args[0].username)
.replace(/{link}/gi, args[3]);
if (args[1])
str = str
.replace(/{file\.id}/gi, args[1].id.toString())
.replace(/{file\.mime}/gi, args[1].mimetype)
.replace(/{file\.file}/gi, args[1].file)
.replace(/{file\.created_at.full_string}/gi, args[1].created_at.toLocaleString())
.replace(/{file\.created_at.time_string}/gi, args[1].created_at.toLocaleTimeString())
.replace(/{file\.created_at.date_string}/gi, args[1].created_at.toLocaleDateString());
if (args[2])
str = str
.replace(/{url\.id}/gi, args[2].id.toString())
.replace(/{url\.vanity}/gi, args[2].vanity ? args[2].vanity : 'none')
.replace(/{url\.destination}/gi, args[2].destination)
.replace(/{url\.created_at.full_string}/gi, args[2].created_at.toLocaleString())
.replace(/{url\.created_at.time_string}/gi, args[2].created_at.toLocaleTimeString())
.replace(/{url\.created_at.date_string}/gi, args[2].created_at.toLocaleDateString());
return str;
}
export function parseContent(
content: ConfigDiscordContent,
args: Args
args: ParseValue
): ConfigDiscordContent & { url: string } {
return {
content: parse(content.content, args),
content: parseString(content.content, args),
embed: content.embed
? {
title: parse(content.embed.title, args),
description: parse(content.embed.description, args),
footer: parse(content.embed.footer, args),
title: parseString(content.embed.title, args),
description: parseString(content.embed.description, args),
footer: parseString(content.embed.footer, args),
color: content.embed.color,
thumbnail: content.embed.thumbnail,
timestamp: content.embed.timestamp,
@ -59,10 +27,16 @@ export function parseContent(
};
}
export async function sendUpload(user: User, image: Image, host: string) {
export async function sendUpload(user: User, image: Image, raw_link: string, link: string) {
if (!config.discord.upload) return;
const parsed = parseContent(config.discord.upload, [user, image, null, host]);
const parsed = parseContent(config.discord.upload, {
file: image,
user,
link,
raw_link,
});
const isImage = image.mimetype.startsWith('image/');
const body = JSON.stringify({
@ -118,10 +92,14 @@ export async function sendUpload(user: User, image: Image, host: string) {
return;
}
export async function sendShorten(user: User, url: Url, host: string) {
export async function sendShorten(user: User, url: Url, link: string) {
if (!config.discord.shorten) return;
const parsed = parseContent(config.discord.shorten, [user, null, url, host]);
const parsed = parseContent(config.discord.shorten, {
url,
user,
link,
});
const body = JSON.stringify({
username: config.discord.username,

View file

@ -1,4 +1,3 @@
import type { Image, User } from '@prisma/client';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import dayjsRelativeTime from 'dayjs/plugin/relativeTime';
@ -6,21 +5,6 @@ import ms, { StringValue } from 'ms';
dayjs.extend(duration);
dayjs.extend(dayjsRelativeTime);
export function parse(str: string, image: Image, user: User) {
if (!str) return null;
return str
.replace(/{user\.admin}/gi, user.administrator ? 'yes' : 'no')
.replace(/{user\.id}/gi, user.id.toString())
.replace(/{user\.name}/gi, user.username)
.replace(/{image\.id}/gi, image.id.toString())
.replace(/{image\.mime}/gi, image.mimetype)
.replace(/{image\.file}/gi, image.file)
.replace(/{image\.created_at.full_string}/gi, image.created_at.toLocaleString())
.replace(/{image\.created_at.time_string}/gi, image.created_at.toLocaleTimeString())
.replace(/{image\.created_at.date_string}/gi, image.created_at.toLocaleDateString());
}
export function randomChars(length: number) {
const charset = 'QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm1234567890';

143
src/lib/utils/parser.ts Normal file
View file

@ -0,0 +1,143 @@
import type { Image, User, Url } from '@prisma/client';
export type ParseValue = {
file?: Image;
url?: Url;
user?: User;
link?: string;
raw_link?: string;
};
export function parseString(str: string, value: ParseValue) {
str = str.replace(/\{link\}/gi, value.link).replace(/\{raw_link\}/gi, value.raw_link);
const re = /\{(?<type>file|url|user)\.(?<prop>\w+)(::(?<mod>\w+))?\}/gi;
let matches: RegExpMatchArray;
while ((matches = re.exec(str))) {
const getV = value[matches.groups.type];
if (!getV) {
str = replaceCharsFromString(str, '{unknown_type}', matches.index, re.lastIndex);
re.lastIndex = matches.index;
continue;
}
if (matches.groups.prop in ['password', 'avatar']) {
str = replaceCharsFromString(str, '{unknown_property}', matches.index, re.lastIndex);
re.lastIndex = matches.index;
continue;
}
const v = getV[matches.groups.prop];
if (v === undefined) {
str = replaceCharsFromString(str, '{unknown_property}', matches.index, re.lastIndex);
re.lastIndex = matches.index;
continue;
}
if (matches.groups.mod) {
str = replaceCharsFromString(str, modifier(matches.groups.mod, v), matches.index, re.lastIndex);
re.lastIndex = matches.index;
continue;
}
str = replaceCharsFromString(str, v, matches.index, re.lastIndex);
re.lastIndex = matches.index;
}
return str;
}
function modifier(mod: string, value: any): string {
mod = mod.toLowerCase();
if (value instanceof Date) {
switch (mod) {
case 'locale':
return value.toLocaleString();
case 'time':
return value.toLocaleTimeString();
case 'date':
return value.toLocaleDateString();
case 'unix':
return Math.floor(value.getTime() / 1000).toString();
case 'iso':
return value.toISOString();
case 'utc':
return value.toUTCString();
case 'year':
return value.getFullYear().toString();
case 'month':
return (value.getMonth() + 1).toString();
case 'day':
return value.getDate().toString();
case 'hour':
return value.getHours().toString();
case 'minute':
return value.getMinutes().toString();
case 'second':
return value.getSeconds().toString();
default:
return '{unknown_date_modifier}';
}
} else if (typeof value === 'string') {
switch (mod) {
case 'upper':
return value.toUpperCase();
case 'lower':
return value.toLowerCase();
case 'title':
return value.charAt(0).toUpperCase() + value.slice(1);
case 'length':
return value.length.toString();
case 'reverse':
return value.split('').reverse().join('');
case 'base64':
return btoa(value);
case 'hex':
return toHex(value);
default:
return '{unknown_str_modifier}';
}
} else if (typeof value === 'number') {
switch (mod) {
case 'comma':
return value.toLocaleString();
case 'hex':
return value.toString(16);
case 'octal':
return value.toString(8);
case 'binary':
return value.toString(2);
default:
return '{unknown_int_modifier}';
}
} else if (typeof value === 'boolean') {
switch (mod) {
case 'yesno':
return value ? 'Yes' : 'No';
case 'onoff':
return value ? 'On' : 'Off';
case 'truefalse':
return value ? 'True' : 'False';
default:
return '{unknown_bool_modifier}';
}
}
return '{unknown_modifier}';
}
function replaceCharsFromString(str: string, replace: string, start: number, end: number): string {
return str.slice(0, start) + replace + str.slice(end);
}
function toHex(str: string): string {
let hex = '';
for (let i = 0; i < str.length; i++) {
hex += '' + str.charCodeAt(i).toString(16);
}
return hex;
}

View file

@ -191,7 +191,10 @@ async function handler(req: NextApiReq, res: NextApiRes) {
file,
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}/r/${
invis ? invis.invis : file.file
}`
}`,
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}${
zconfig.uploader.route === '/' ? '' : zconfig.uploader.route
}/${invis ? invis.invis : file.file}`
);
}
@ -325,7 +328,10 @@ async function handler(req: NextApiReq, res: NextApiRes) {
image,
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}/r/${
invis ? invis.invis : image.file
}`
}`,
`${zconfig.core.return_https ? 'https' : 'http'}://${req.headers.host}${
zconfig.uploader.route === '/' ? '' : zconfig.uploader.route
}/${invis ? invis.invis : image.file}`
);
}

View file

@ -1,7 +1,7 @@
import { Box, Button, Modal, PasswordInput } from '@mantine/core';
import exts from 'lib/exts';
import prisma from 'lib/prisma';
import { parse } from 'lib/utils/client';
import { parseString } from 'lib/utils/parser';
import { GetServerSideProps } from 'next';
import Head from 'next/head';
import { useRouter } from 'next/router';
@ -63,9 +63,14 @@ export default function EmbeddedFile({ image, user, pass, prismRender }) {
{image.embed && (
<>
{user.embedSiteName && (
<meta property='og:site_name' content={parse(user.embedSiteName, image, user)} />
<meta
property='og:site_name'
content={parseString(user.embedSiteName, { file: image, user })}
/>
)}
{user.embedTitle && (
<meta property='og:title' content={parseString(user.embedTitle, { file: image, user })} />
)}
{user.embedTitle && <meta property='og:title' content={parse(user.embedTitle, image, user)} />}
<meta property='theme-color' content={user.embedColor} />
</>
)}

View file

@ -21,6 +21,7 @@ import rawRoute from './routes/raw';
import uploadsRoute, { uploadsRouteOnResponse } from './routes/uploads';
import urlsRoute, { urlsRouteOnResponse } from './routes/urls';
import { IncomingMessage } from 'http';
import { parseString } from '../lib/utils/parser';
const dev = process.env.NODE_ENV === 'development';
const logger = Logger.get('server');
@ -38,6 +39,19 @@ start();
async function start() {
logger.debug('Starting server');
// const a = parseString(
// '{file.name::upper} {file.mimetype} {file.test} {file.created_at::unix} {file.v::yesno} {file.int::hex}',
// // @ts-ignore
// {
// name: 'test',
// mimetype: 'image/png',
// created_at: new Date(),
// int: 123123123123123,
// v: false,
// }
// );
// console.log(a);
// plugins
server
.register(loggerPlugin)