99AI/dist/modules/mj/mj.service.js
2024-01-17 09:22:28 +08:00

576 lines
27 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MjService = void 0;
const globalConfig_service_1 = require("./../globalConfig/globalConfig.service");
const upload_service_1 = require("./../upload/upload.service");
const common_1 = require("@nestjs/common");
const axios_1 = require("axios");
const chatLog_service_1 = require("../chatLog/chatLog.service");
const balance_constant_1 = require("../../common/constants/balance.constant");
const utils_1 = require("../../common/utils");
const chatLog_entity_1 = require("../chatLog/chatLog.entity");
const typeorm_1 = require("typeorm");
const typeorm_2 = require("@nestjs/typeorm");
const balance_entity_1 = require("../userBalance/balance.entity");
const fanyi_service_1 = require("../fanyi/fanyi.service");
const badwords_service_1 = require("../badwords/badwords.service");
let MjService = class MjService {
constructor(chatLogEntity, balanceEntity, uploadService, chatLogService, globalConfigService, fanyiService, badwordsService) {
this.chatLogEntity = chatLogEntity;
this.balanceEntity = balanceEntity;
this.uploadService = uploadService;
this.chatLogService = chatLogService;
this.globalConfigService = globalConfigService;
this.fanyiService = fanyiService;
this.badwordsService = badwordsService;
this.rateLimits = {};
this.drawWorking = [];
this.enlargeWorking = [];
this.queueCount = 0;
this.freeQueueUsers = {};
}
async mjDraw(data) {
const { jobId, prompt, startTime, userId } = data;
console.log('绘画任务开始', 'mjservice');
await new Promise((resolve) => setTimeout(resolve, 5000));
return { a: 1, b: 2 };
}
async draw(body, req) {
await this.checkAuth(req);
await this.badwordsService.checkBadWords(body.prompt, req.user.id);
const basicPrompt = body.prompt;
let fyPrompt = body.prompt;
const { baiduFanyiAppId, baiduFanyiSecret } = await this.globalConfigService.getConfigs(['baiduFanyiAppId', 'baiduFanyiSecret']);
if (baiduFanyiAppId && baiduFanyiSecret) {
fyPrompt = await this.fanyiService.convertToEnglish(basicPrompt);
}
const randomId = `[${(0, utils_1.createRandomUid)()}]`;
const prompt = `${randomId} ${fyPrompt}`;
console.log('randomId: ', randomId);
console.log('prompt --------> ', prompt);
const isWorking = this.drawWorking.find((item) => item.includes(body.prompt));
if (isWorking) {
throw new common_1.HttpException('当前提示词已经在任务队列中了、请勿重复提交。。。', common_1.HttpStatus.BAD_REQUEST);
}
if (this.queueCount >= 3) {
throw new common_1.HttpException('当前绘图任务满载、请排队等候、队列任务完成后即可开始您的任务...', common_1.HttpStatus.BAD_REQUEST);
}
await this.checkRateLimit(req);
this.queueCount++;
console.log(`开始请求用户${req.user.id} 队列+1: `, this.queueCount);
try {
const historyDraw = await this.chatLogEntity.find({ where: { prompt: (0, typeorm_1.Like)(`%${prompt}%`) } });
const histroyMessageIds = historyDraw.map((item) => item.message_id);
this.drawWorking.push(prompt);
let drawDetail;
const sendRes = await this.sendDrawInteractions(prompt, histroyMessageIds, randomId);
if (sendRes) {
console.log(`历史中存在当前图片、直接获取!`);
drawDetail = sendRes;
}
else {
drawDetail = await this.pollForResult(prompt, histroyMessageIds, randomId);
}
this.queueCount--;
this.queueCount < 0 && (this.queueCount = 0);
console.log('绘制图片任务结束 队列-1: ', this.queueCount);
const { id, content, channel_id, attachments = [], timestamp } = drawDetail;
if (!attachments.length || !attachments[0].url) {
throw new common_1.HttpException('绘画失败', common_1.HttpStatus.BAD_REQUEST);
}
const { filename, url, width, height, size } = attachments[0];
console.log('拿到了远程地址: ', url);
const mjNotSaveImg = this.globalConfigService.getConfigs(['mjNotSaveImg']);
let cosUrl = '';
if (!Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0) {
cosUrl = await this.uploadService.uploadFileFromUrl({ filename, url });
console.log('存入图片完成: ', cosUrl);
}
const logInfo = {
curIp: (0, utils_1.getClientIp)(req),
userId: req.user.id,
type: balance_constant_1.DeductionKey.PAINT_TYPE,
prompt,
answer: cosUrl,
model: 'mj',
extend: this.removeEmoji(JSON.stringify(drawDetail)),
message_id: id,
variationId: id,
upscaleId: id,
group: 1,
isSaveImg: !Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0,
fileInfo: JSON.stringify({ width, height, size, filename, cosUrl }),
};
await this.chatLogService.saveChatLog(logInfo);
await this.deductBalance(req);
this.drawWorking = this.drawWorking.filter((item) => item !== body.prompt);
return cosUrl;
}
catch (error) {
this.queueCount--;
this.queueCount < 0 && (this.queueCount = 0);
console.log('绘制图片任务异常中断 队列-1: ', this.queueCount);
this.drawWorking = this.drawWorking.filter((item) => item !== body.prompt);
throw new common_1.HttpException(error.response, common_1.HttpStatus.BAD_REQUEST);
}
}
async upscaleSingleImg(body, req) {
if (this.queueCount >= 3) {
throw new common_1.HttpException('当前绘图任务满载、请排队等候、队列任务完成后即可开始您的任务...', common_1.HttpStatus.BAD_REQUEST);
}
this.queueCount++;
console.log(`用户${req.user.id}开始请求放大图片 队列+1: `, this.queueCount);
const { message_id, orderId } = body;
try {
const historyLog = await this.chatLogEntity.findOne({ where: { message_id } });
if (!historyLog) {
throw new common_1.HttpException('历史记录中不存在当前图片、请确认您放大的图片是否存在', common_1.HttpStatus.BAD_REQUEST);
}
const isAreadlyEnlarge = await this.chatLogEntity.findOne({ where: { upscaleId: message_id, action: 'enlarge', orderId } });
if (isAreadlyEnlarge) {
throw new common_1.HttpException('当前图片已经放大过了、请勿重复放大!', common_1.HttpStatus.BAD_REQUEST);
}
const { prompt, extend } = historyLog;
let historyDetailDrawInfo = null;
try {
historyDetailDrawInfo = JSON.parse(extend);
}
catch (error) {
historyDetailDrawInfo = [];
}
const { components = [] } = historyDetailDrawInfo;
if (!components.length) {
throw new common_1.HttpException('当前图片没有绘画信息、无法放大!', common_1.HttpStatus.BAD_REQUEST);
}
const currentImgComponent = components[0]['components'][orderId - 1];
const { custom_id } = currentImgComponent;
console.log('放大custom_id: ', custom_id);
const params = { message_id, custom_id, prompt, orderId };
await this.sendSmInteractions(params);
console.log('发送放大指令成功');
const historyDraw = await this.chatLogEntity.find({ where: { prompt: (0, typeorm_1.Like)(`%${prompt}%`) } });
const histroyMessageIds = historyDraw.map((item) => item.message_id);
console.log('历史这些id已经被获取过了 不能拿了: ', histroyMessageIds);
const enlargeImgInfo = await this.pollForUpscaleResult(params, histroyMessageIds);
this.queueCount--;
this.queueCount < 0 && (this.queueCount = 0);
console.log('放大图片任务结束 队列-1: ', this.queueCount);
const { id, content, channel_id, attachments = [], timestamp } = enlargeImgInfo;
if (!attachments.length || !attachments[0].url) {
throw new common_1.HttpException('放大当前图片失败', common_1.HttpStatus.BAD_REQUEST);
}
const { filename, url, width, height, size } = attachments[0];
const mjNotSaveImg = this.globalConfigService.getConfigs(['mjNotSaveImg']);
let cosUrl = '';
if (!Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0) {
cosUrl = await this.uploadService.uploadFileFromUrl({ filename, url });
console.log('存入图片完成: ', cosUrl);
}
const logInfo = {
curIp: (0, utils_1.getClientIp)(req),
userId: req.user.id,
type: balance_constant_1.DeductionKey.PAINT_TYPE,
prompt,
answer: cosUrl,
model: 'mj',
extend: this.removeEmoji(JSON.stringify(enlargeImgInfo)),
message_id,
upscaleId: id,
variationId: id,
action: 'enlarge',
orderId: params.orderId,
isSaveImg: !Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0,
fileInfo: JSON.stringify({ width, height, size, filename, cosUrl }),
};
await this.chatLogService.saveChatLog(logInfo);
return cosUrl;
}
catch (error) {
console.log('error: ', error);
this.queueCount--;
this.queueCount < 0 && (this.queueCount = 0);
console.log('放大图片任务异常中断 队列-1: ', this.queueCount);
throw new common_1.HttpException(error.response, common_1.HttpStatus.BAD_REQUEST);
}
}
async variationSingleImg(body, req) {
if (this.queueCount >= 3) {
throw new common_1.HttpException('当前绘图任务满载、请排队等候、队列任务完成后即可开始您的任务...', common_1.HttpStatus.BAD_REQUEST);
}
await this.checkAuth(req);
await this.checkRateLimit(req);
this.queueCount++;
console.log(`用户${req.user.id}开始请求变换图片 队列+1: `, this.queueCount);
const { message_id, orderId } = body;
try {
const historyLog = await this.chatLogEntity.findOne({ where: { message_id } });
if (!historyLog) {
throw new common_1.HttpException('历史记录中不存在当前图片、请确认您需要变换的图片是否存在', common_1.HttpStatus.BAD_REQUEST);
}
const { prompt, extend } = historyLog;
let historyDetailDrawInfo = null;
try {
historyDetailDrawInfo = JSON.parse(extend);
}
catch (error) {
historyDetailDrawInfo = [];
}
const { components = [] } = historyDetailDrawInfo;
if (!components.length) {
throw new common_1.HttpException('当前图片没有绘画信息、无法变体!', common_1.HttpStatus.BAD_REQUEST);
}
const currentImgComponent = components[1]['components'][orderId - 1];
const { custom_id } = currentImgComponent;
const historyVariationLog = await this.chatLogEntity.find({ where: { variationId: (0, typeorm_1.Not)((0, typeorm_1.IsNull)()), prompt: (0, typeorm_1.Like)(`%${prompt}%`) } });
const historyVariationIds = historyVariationLog.map((item) => item.variationId);
const params = { message_id, custom_id, prompt, orderId };
await this.sendSmInteractions(params);
const variationImgInfo = await this.pollForVariationResult(params, historyVariationIds);
this.queueCount--;
this.queueCount < 0 && (this.queueCount = 0);
console.log('变换图片任务结束 队列-1: ', this.queueCount);
const { id, content, channel_id, attachments = [], timestamp } = variationImgInfo;
if (!attachments.length || !attachments[0].url) {
throw new common_1.HttpException('变换当前图片失败', common_1.HttpStatus.BAD_REQUEST);
}
const { filename, url, width, height, size } = attachments[0];
const mjNotSaveImg = this.globalConfigService.getConfigs(['mjNotSaveImg']);
let cosUrl = '';
if (!Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0) {
cosUrl = await this.uploadService.uploadFileFromUrl({ filename, url });
console.log('存入图片完成: ', cosUrl);
}
const logInfo = {
curIp: (0, utils_1.getClientIp)(req),
userId: req.user.id,
type: balance_constant_1.DeductionKey.PAINT_TYPE,
prompt,
answer: cosUrl,
model: 'mj',
group: 1,
extend: this.removeEmoji(JSON.stringify(variationImgInfo)),
message_id: id,
upscaleId: id,
variationId: id,
action: 'enlarge',
orderId: params.orderId,
isSaveImg: !Number(mjNotSaveImg) || Number(mjNotSaveImg) === 0,
fileInfo: JSON.stringify({ width, height, size, filename, cosUrl }),
};
await this.chatLogService.saveChatLog(logInfo);
return cosUrl;
}
catch (error) {
console.log('error: ', error);
this.queueCount--;
this.queueCount < 0 && (this.queueCount = 0);
console.log('变化图片任务异常中断 队列-1: ', this.queueCount);
throw new common_1.HttpException(error.response, common_1.HttpStatus.BAD_REQUEST);
}
}
async sendSmInteractions(params) {
const { message_id, custom_id } = params;
const { application_id, guild_id, channel_id, session_id, version, id, authorization, mjProxy } = await this.getMjDefaultParams();
const url = mjProxy == 1 ? `http://172.247.48.137:8000/mj/draw` : 'https://discord.com/api/v9/interactions';
const headers = { authorization };
const body = {
type: 3,
guild_id,
channel_id,
message_flags: 0,
message_id,
application_id,
session_id,
data: {
component_type: 2,
custom_id,
},
};
try {
await axios_1.default.post(url, body, { headers });
console.log('绘图指令完成');
}
catch (error) {
console.log('error: ', error);
throw new common_1.HttpException('放大单张图片请求失败...', common_1.HttpStatus.BAD_REQUEST);
}
}
async pollForUpscaleResult(params, histroyMessageIds) {
const { message_id, custom_id, prompt, orderId } = params;
let enlargeImgDetail = null;
let pollingCount = 0;
while (!enlargeImgDetail && pollingCount < 10) {
try {
const startTime = Date.now();
const messageList = await this.queryMessageList();
console.log(`${pollingCount + 1} 次开始查询 => 当前查询结果:${messageList.length}`);
if (messageList && messageList.length) {
enlargeImgDetail = await this.findCurrentEnlargeImgResult(messageList, params, histroyMessageIds);
}
const elapsedTime = Date.now() - startTime;
const nextPollingDelay = 3000;
await this.sleep(Math.max(nextPollingDelay - elapsedTime, 0));
pollingCount++;
}
catch (error) {
console.error(`查询期间出现错误:${error.message}`);
}
}
return enlargeImgDetail;
}
async pollForVariationResult(params, historyVariationIds) {
const { message_id, custom_id, prompt, orderId } = params;
console.log('开始轮询单张变换图片结果');
let variationImgDetail = null;
let pollingCount = 0;
while (!variationImgDetail && pollingCount < 10) {
try {
console.log(`${pollingCount + 1} 次开始查询[变换图片]`);
const startTime = Date.now();
const messageList = await this.queryMessageList();
if (messageList && messageList.length) {
variationImgDetail = await this.findCurrentVariationImgResult(messageList, params, historyVariationIds);
}
const elapsedTime = Date.now() - startTime;
const nextPollingDelay = 8000;
await this.sleep(Math.max(nextPollingDelay - elapsedTime, 0));
pollingCount++;
}
catch (error) {
console.error(`查询期间出现错误:${error.message}`);
}
}
if (!variationImgDetail) {
throw new common_1.HttpException('变换当前图片超时!', common_1.HttpStatus.BAD_REQUEST);
}
return variationImgDetail;
}
async findCurrentEnlargeImgResult(messageList, params, histroyMessageIds) {
const { message_id, custom_id, prompt, orderId } = params;
const randomId = prompt.substring(0, 12);
console.log('本次放大图片的id: ', randomId);
const enlargeImgDetail = messageList.find((item) => {
const { content } = item;
if (!this.extractContent(content))
return false;
const { prompt, order } = this.extractContent(content);
return prompt.includes(randomId) && params.orderId === order && !histroyMessageIds.includes(item.id);
});
return enlargeImgDetail;
}
async findCurrentVariationImgResult(messageList, params, historyVariationIds) {
const { message_id, custom_id, prompt, orderId } = params;
const randomId = prompt.substring(0, 12);
const variationImgDetail = messageList.find((item) => {
const { content } = item;
const promptMatch = content.match(/\*\*(.+?)\*\*/);
const prompt = promptMatch ? promptMatch[1] : '';
if (!prompt)
return false;
return prompt.includes(randomId) && !historyVariationIds.includes(item.id);
});
return variationImgDetail;
}
async sendDrawInteractions(prompt, histroyMessageIds, randomId) {
const messageList = await this.queryMessageList();
const drawDetail = await this.findCurrentPromptResult(messageList, randomId, histroyMessageIds);
if (drawDetail) {
console.log('有历史信息之间返回: ', drawDetail);
return drawDetail;
}
const { application_id, guild_id, channel_id, session_id, version, id, authorization, mjProxy } = await this.getMjDefaultParams();
const payloadJson = {
type: 2,
application_id,
guild_id,
channel_id,
session_id,
data: { version, id, name: 'imagine', type: 1, options: [{ type: 3, name: 'prompt', value: prompt }], attachments: [] },
};
try {
const url = mjProxy == 1 ? `http://172.247.48.137:8000/mj/draw` : 'https://discord.com/api/v9/interactions';
const headers = { authorization };
const res = await axios_1.default.post(url, payloadJson, { headers });
console.log('发送绘画指令结果: ', res.data);
return false;
}
catch (error) {
console.log('axios: ', error);
throw new common_1.HttpException('绘画请求失败、当前使用人数过多、请稍后试试吧、排队中...', common_1.HttpStatus.BAD_REQUEST);
}
}
async pollForResult(prompt, histroyMessageIds, randomId) {
console.log('开始查询绘画结果轮询');
const startTime = Date.now();
try {
const MAX_POLLING_COUNT = 13;
const SHORT_INTERVAL = 12000;
const LONG_INTERVAL = 5000;
const TIME_THRESHOLD = 60 * 1000;
let pollingCount = 0;
let isLongInterval = false;
let drawDetail = null;
while (!drawDetail && pollingCount < MAX_POLLING_COUNT) {
console.log(`${pollingCount + 1} 次开始查询`);
if (Date.now() - startTime >= TIME_THRESHOLD) {
isLongInterval = true;
}
await this.sleep(isLongInterval ? LONG_INTERVAL : SHORT_INTERVAL);
const messageList = await this.queryMessageList();
drawDetail = await this.findCurrentPromptResult(messageList, randomId, histroyMessageIds);
pollingCount++;
}
if (!drawDetail) {
throw new common_1.HttpException('绘画超时,请稍后再试!', common_1.HttpStatus.BAD_REQUEST);
}
const endTime = Date.now();
console.log(`本次绘图耗时: ${Math.floor((endTime - startTime) / 1000)} S`);
return drawDetail;
}
catch (err) {
console.error(err.message);
throw new common_1.HttpException('网络连接失败,请稍后再试!', common_1.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async findCurrentPromptResult(data, randomId, histroyMessageIds) {
if (!data || !data.length)
return;
console.log('本次比对的随机ID: ', randomId);
const matchingItem = data.find((item) => {
const { attachments = [], content, edited_timestamp } = item;
return content.includes(randomId) && attachments.length > 0 && !edited_timestamp && !histroyMessageIds.includes(item.id);
});
return matchingItem || null;
}
async queryMessageList() {
try {
const { application_id, guild_id, channel_id, session_id, version, id, authorization, mjProxy } = await this.getMjDefaultParams();
const url = mjProxy == 1
? `http://172.247.48.137:8000/mj/list?channel_id=${channel_id}`
: `https://discord.com/api/v9/channels/${channel_id}/messages?limit=50`;
const headers = { authorization };
const response = await axios_1.default.get(url, { headers });
return response.data;
}
catch (error) {
console.log('axios get: ', error);
throw new common_1.HttpException('查询绘制结果失败...', common_1.HttpStatus.BAD_REQUEST);
}
}
async sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
extractContent(str) {
const promptMatch = str.match(/\*\*(.+?)\*\*/);
const orderMatch = str.match(/- Image #(\d+)/);
if (!promptMatch || !orderMatch) {
return null;
}
const prompt = promptMatch[1];
const order = parseInt(orderMatch[1]);
return { prompt, order };
}
async getMjDefaultParams() {
const configs = await this.globalConfigService.getConfigs([
'mjId',
'mjApplicationId',
'mjGuildId',
'mjChannelId',
'mjSessionId',
'mjVersion',
'mjAuthorization',
'mjRateLimit',
'mjProxy',
]);
const params = {
application_id: configs.mjApplicationId,
guild_id: configs.mjGuildId,
channel_id: configs.mjChannelId,
session_id: configs.mjSessionId,
version: configs.mjVersion,
id: configs.mjId,
authorization: configs.mjAuthorization,
mjRateLimit: configs.mjRateLimit,
mjProxy: configs.mjProxy || 0,
};
return params;
}
removeEmoji(str) {
const regex = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
return str.replace(regex, '');
}
async checkAuth(req) {
const m = await this.balanceEntity.findOne({ where: { userId: req.user.id } });
const { id, balance } = m;
if (!balance || (m === null || m === void 0 ? void 0 : m.balance) < 1) {
throw new common_1.HttpException('您当前暂无MJ绘画余额', common_1.HttpStatus.BAD_REQUEST);
}
}
async checkFree(req) {
const { id, role } = req.user;
if (!this.freeQueueUsers[id]) {
this.freeQueueUsers[id] = 1;
}
else {
this.freeQueueUsers[id] = this.freeQueueUsers[id] + 1;
}
console.log(`当前用户${id}使用的次数:`, this.freeQueueUsers[id]);
}
async checkRateLimit(req) {
const { id, role } = req.user;
if (['admin', 'super'].includes(role))
return true;
const { mjRateLimit } = await this.getMjDefaultParams();
if (this.rateLimits[id]) {
const val = this.rateLimits[id];
if (val > Date.now()) {
console.log(`当前用户 ${id} 请求过于频繁!`);
throw new common_1.HttpException(`由于速率限制、当前普通用户限制为${mjRateLimit}秒请求一次、请合理使用!`, common_1.HttpStatus.BAD_REQUEST);
}
else {
this.rateLimits[id] = Date.now() + Number(mjRateLimit) * 1000;
}
}
else {
const timeSpace = Date.now();
this.rateLimits[id] = timeSpace + 1000 * Number(mjRateLimit);
}
}
async deductBalance(req) {
await this.balanceEntity
.createQueryBuilder()
.update(balance_entity_1.BalanceEntity)
.set({ balance: () => 'balance - 1' })
.where('userId = :userId', { userId: req.user.id })
.execute();
}
async test() {
return 1;
}
};
MjService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_2.InjectRepository)(chatLog_entity_1.ChatLogEntity)),
__param(1, (0, typeorm_2.InjectRepository)(balance_entity_1.BalanceEntity)),
__metadata("design:paramtypes", [typeorm_1.Repository,
typeorm_1.Repository,
upload_service_1.UploadService,
chatLog_service_1.ChatLogService,
globalConfig_service_1.GlobalConfigService,
fanyi_service_1.FanyiService,
badwords_service_1.BadwordsService])
], MjService);
exports.MjService = MjService;