chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:27:50 +08:00
commit cc8f841fc5
422 changed files with 70222 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
/**
* 压缩图片
* @param image 要压缩的图片
* @param mimeType mime类型
* @param quality 质量
*/
export default function compressImage(
image: HTMLImageElement,
mimeType: string,
quality = 1,
): Promise<Blob | null> {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.drawImage(image, 0, 0);
canvas.toBlob(resolve, mimeType, quality);
} else {
resolve(null);
}
});
}
+19
View File
@@ -0,0 +1,19 @@
/** 封禁后提示文案 */
export const SEAL_TEXT = '你已经被关进小黑屋中, 请反思后再试';
/** 封禁用户释放时间 */
export const SEAL_USER_TIMEOUT = 1000 * 60 * 10; // 10分钟
/** 封禁ip释放时间 */
export const SEAL_IP_TIMEOUT = 1000 * 60 * 60 * 6; // 6小时
/** 透明图 */
export const TRANSPARENT_IMAGE =
'data:image/png;base64,R0lGODlhFAAUAIAAAP///wAAACH5BAEAAAAALAAAAAAUABQAAAIRhI+py+0Po5y02ouz3rz7rxUAOw==';
/** 加密salt位数 */
export const SALT_ROUNDS = 10;
export const MB = 1024 * 1024;
export const NAME_REGEXP = /^([0-9a-zA-Z]{1,2}|[\u4e00-\u9eff]|[\u3040-\u309Fー]|[\u30A0-\u30FF]){1,8}$/;
+54
View File
@@ -0,0 +1,54 @@
import WuZeiNiangImage from '@fiora/assets/images/wuzeiniang.gif';
// function convertRobot10Message(message) {
// if (message.from._id === '5adad39555703565e7903f79') {
// try {
// const parseMessage = JSON.parse(message.content);
// message.from.tag = parseMessage.source;
// message.from.avatar = parseMessage.avatar;
// message.from.username = parseMessage.username;
// message.type = parseMessage.type;
// message.content = parseMessage.content;
// } catch (err) {
// console.warn('解析robot10消息失败', err);
// }
// }
// }
function convertSystemMessage(message: any) {
if (message.type === 'system') {
message.from._id = 'system';
message.from.originUsername = message.from.username;
message.from.username = '乌贼娘殿下';
message.from.avatar = WuZeiNiangImage;
message.from.tag = 'system';
const content = JSON.parse(message.content);
switch (content.command) {
case 'roll': {
message.content = `掷出了${content.value}点 (上限${content.top}点)`;
break;
}
case 'rps': {
message.content = `使出了 ${content.value}`;
break;
}
default: {
message.content = '不支持的指令';
}
}
} else if (message.deleted) {
message.type = 'system';
message.from._id = 'system';
message.from.originUsername = message.from.username;
message.from.username = '乌贼娘殿下';
message.from.avatar = WuZeiNiangImage;
message.from.tag = 'system';
message.content = `撤回了消息`;
}
}
export default function convertMessage(message: any) {
convertSystemMessage(message);
return message;
}
+54
View File
@@ -0,0 +1,54 @@
export default {
default: [
'呵呵',
'哈哈',
'吐舌',
'啊',
'酷',
'怒',
'开心',
'汗',
'泪',
'黑线',
'鄙视',
'不高兴',
'真棒',
'钱',
'疑问',
'阴险',
'吐',
'咦',
'委屈',
'花心',
'呼',
'笑眼',
'冷',
'太开心',
'滑稽',
'勉强',
'狂汗',
'乖',
'睡觉',
'惊哭',
'升起',
'惊讶',
'喷',
'爱心',
'心碎',
'玫瑰',
'礼物',
'星星月亮',
'太阳',
'音乐',
'灯泡',
'蛋糕',
'彩虹',
'钱币',
'咖啡',
'haha',
'胜利',
'大拇指',
'弱',
'ok',
],
};
+12
View File
@@ -0,0 +1,12 @@
/**
* Combina two users id as frind id
* The result has nothing to do with the order of the parameters
* @param userId1 user id
* @param userId2 user id
*/
export default function getFriendId(userId1: string, userId2: string) {
if (userId1 < userId2) {
return userId1 + userId2;
}
return userId2 + userId1;
}
+17
View File
@@ -0,0 +1,17 @@
const AvatarCount = 15;
const publicPath = process.env.PublicPath || '/';
/**
* 获取随机头像
*/
export default function getRandomAvatar() {
const number = Math.floor(Math.random() * AvatarCount);
return `${publicPath}avatar/${number}.jpg`;
}
/**
* 获取默认头像
*/
export function getDefaultAvatar() {
return `${publicPath}avatar/0.jpg`;
}
+36
View File
@@ -0,0 +1,36 @@
import randomColor from 'randomcolor';
type ColorMode = 'dark' | 'bright' | 'light' | 'random';
/**
* 获取随机颜色, 刷新页面不变
* @param seed when passed will cause randomColor to return the same color each time
*/
export function getRandomColor(seed: string, luminosity: ColorMode = 'dark') {
return randomColor({
luminosity,
seed,
});
}
type Cache = {
[key: string]: string;
};
const cache: Cache = {};
/**
* 获取随机颜色, 刷新页面后重新随机
* @param seed 随机种子
* @param luminosity 亮度
*/
export function getPerRandomColor(
seed: string,
luminosity: ColorMode = 'dark',
) {
if (cache[seed]) {
return cache[seed];
}
cache[seed] = randomColor({ luminosity });
return cache[seed];
}
+6
View File
@@ -0,0 +1,6 @@
import { getLogger } from 'log4js';
const logger = getLogger();
logger.level = process.env.NODE_ENV === 'development' ? 'trace' : 'info';
export default logger;
+18
View File
@@ -0,0 +1,18 @@
{
"name": "@fiora/utils",
"version": "1.0.0",
"license": "MIT",
"private": true,
"dependencies": {
"@fiora/assets": "^1.0.0",
"ali-oss": "^6.16.0",
"axios": "^0.21.1",
"log4js": "^6.3.0",
"randomcolor": "^0.6.2",
"socket.io": "^4.1.3",
"xss": "^1.0.9"
},
"devDependencies": {
"@types/randomcolor": "^0.5.6"
}
}
+5
View File
@@ -0,0 +1,5 @@
export default function sleep(duration = 200) {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
}
+9
View File
@@ -0,0 +1,9 @@
import { Socket } from 'socket.io';
export function getSocketIp(socket: Socket) {
return (
(socket.handshake.headers['x-real-ip'] as string) ||
socket.request.connection.remoteAddress ||
''
);
}
+10
View File
@@ -0,0 +1,10 @@
import getFriendId from '../getFriendId';
describe('utils/getFriendId.ts', () => {
it('should combina two users id as friend id', () => {
const user1 = '111';
const user2 = '222';
expect(getFriendId(user1, user2)).toBe('111222');
expect(getFriendId(user2, user1)).toBe('111222');
});
});
+23
View File
@@ -0,0 +1,23 @@
import { addParam } from '../url';
describe('utils/url.ts', () => {
it('should add ?key=value into url', () => {
const url = 'https://fiora.suisuijiang.com';
const key = 'key';
const value = 'value';
const params = {
[key]: value,
};
expect(addParam(url, params)).toBe(`${url}?${key}=${value}`);
});
it('should add &key=value into url', () => {
const url = 'https://fiora.suisuijiang.com?a=a';
const key = 'key';
const value = 'value';
const params = {
[key]: value,
};
expect(addParam(url, params)).toBe(`${url}&${key}=${value}`);
});
});
+28
View File
@@ -0,0 +1,28 @@
export default {
isToday(time1: Date, time2: Date) {
return (
time1.getFullYear() === time2.getFullYear() &&
time1.getMonth() === time2.getMonth() &&
time1.getDate() === time2.getDate()
);
},
isYesterday(time1: Date, time2: Date) {
const prevDate = new Date(time1);
prevDate.setDate(time1.getDate() - 1);
return (
prevDate.getFullYear() === time2.getFullYear() &&
prevDate.getMonth() === time2.getMonth() &&
prevDate.getDate() === time2.getDate()
);
},
getHourMinute(time: Date) {
const hours = time.getHours();
const minutes = time.getMinutes();
return `${hours < 10 ? `0${hours}` : hours}:${
minutes < 10 ? `0${minutes}` : minutes
}`;
},
getMonthDate(time: Date) {
return `${time.getMonth() + 1}/${time.getDate()}`;
},
};
+7
View File
@@ -0,0 +1,7 @@
const UA = window.navigator.userAgent;
export const isiOS = /iPhone/i.test(UA);
export const isAndroid = /android/i.test(UA);
export const isMobile = isiOS || isAndroid;
+16
View File
@@ -0,0 +1,16 @@
interface UrlParams {
[key: string]: string;
}
// eslint-disable-next-line import/prefer-default-export
export function addParam(url: string, params: UrlParams) {
let result = url;
Object.keys(params).forEach((key) => {
if (result.indexOf('?') === -1) {
result += `?${key}=${params[key]}`;
} else {
result += `&${key}=${params[key]}`;
}
});
return result;
}
+9
View File
@@ -0,0 +1,9 @@
import xss from 'xss';
/**
* xss防护
* @param text 要处理的文字
*/
export default function processXss(text: string) {
return xss(text);
}
File diff suppressed because it is too large Load Diff