微信公众号应用开发,支持微信公众号,下发指令生图,和网页版

This commit is contained in:
严浪
2024-04-21 17:24:39 +08:00
parent 396dd1a2cb
commit 1ec31bc90d
19 changed files with 14182 additions and 5 deletions
+2
View File
@@ -16,3 +16,5 @@ unpackage/dist/dev/
*.pyc
config/baidu.json
ckpts/*
config/lamWeChat.db
config/weChat.yaml
+116
View File
@@ -0,0 +1,116 @@
wechat:
appid: wxa50ed345b253964d #公众号appid
secret: 327ac9ba06d214d2fcfe2e9ed0520684 #公众号秘钥
serverAddress: http://139.9.53.208/ #服务器地址 微信公众配置地址的前缀
freeSize: 3 #免费使用次数
isEnterprise: true #是否为企业微信
adminNo: b0pOVFM2dnRsZkdLeWl2Zlk2bG9MTFNjUTNGUQ== #管理员微信编号启动会后可发送我的编号获取
adminWeChat: yanlang123456 #管理员微信号
access_token: '' #不用填系统用于保存临时值
access_token_expires_at: 0 #默认0
commands: #功能列表
图生视频:
filename: img2video.json #功能流程图json文件
params: #参数列表
image: #参数名称(几个固定参数名称必须如下:图片:image 随机种子:seed 正向提示词:prompt 反向提示词:negative)
isRequired: true #是否必填
keys: #参数对应json中对应的key
- '38'
- inputs
- image_path
zhName: 图片 #参数中文名称
motion_bucket:
default: 127
isRequired: true
keys:
- '12'
- inputs
- motion_bucket_id
max: 200
min: 0
type: number
zhName: 运动量
seed:
default: -1
isRequired: true
keys:
- '3'
- inputs
- seed
type: text
zhName: 随机种子
replyText: 请发送您的图片发送“ok”开始任务,如:“ok” #指令说明回复
文生图:
filename: text2img.json
params:
ckpt:
isRequired: false
keys:
- '4'
- inputs
- ckpt_name
options: #选项参数
冷却混合料SD1.5: chilloutmix_NiPrunedFp32Fix.safetensors #中文名称:英文名称
卡通动画片SD1.5: yamer_Cartoon_xenoArcadiaCD.safetensors
无法控制的SDXL: juggernautXL_version6Rundiffusion.safetensors
梦想塑造者SD1.5: dreamshaper_8.safetensors
zhName: 基础模型
height:
default: 512 #默认值
isRequired: false
keys:
- '14'
- inputs
- height
max: 1024 #最大值
min: 512 #最小值
type: number #类型(输入型参数目前只支持 number/text)
zhName: 高度
lora:
isRequired: false
keys:
- '10'
- inputs
- lora_name
options:
SD1.5-LCM加速: lcm-lora-sdv1-5.safetensors
SDXL-LCM加速: lcm-lora-sdxl.safetensors
zhName: LoRA
negative:
isRequired: false
keys:
- '7'
- inputs
- text
zhName: 反向提示词
prompt:
isRequired: true
keys:
- '20'
- inputs
- text_trans
zhName: 正向提示词
seed:
default: -1
isRequired: true
keys:
- '3'
- inputs
- seed
type: text
zhName: 随机种子
width:
default: 512
isRequired: false
keys:
- '14'
- inputs
- width
max: 1024
min: 512
type: number
zhName: 宽度
replyText: 请发送您的中文或英文提示词用“ok”结束,如:“一只小狗ok”
query_commands: #查询指令(固定)
- 查询排队情况
- 我的编号
+142
View File
@@ -0,0 +1,142 @@
{
"3": {
"inputs": {
"seed": 801899397770458,
"steps": 4,
"cfg": 1,
"sampler_name": "lcm",
"scheduler": "karras",
"denoise": 1,
"model": [
"14",
0
],
"positive": [
"12",
0
],
"negative": [
"12",
1
],
"latent_image": [
"12",
2
]
},
"class_type": "KSampler"
},
"8": {
"inputs": {
"samples": [
"3",
0
],
"vae": [
"15",
2
]
},
"class_type": "VAEDecode"
},
"12": {
"inputs": {
"width": [
"30",
0
],
"height": [
"30",
1
],
"video_frames": 25,
"motion_bucket_id": 127,
"fps": 6,
"augmentation_level": 0,
"clip_vision": [
"15",
1
],
"init_image": [
"38",
0
],
"vae": [
"15",
2
]
},
"class_type": "SVD_img2vid_Conditioning"
},
"14": {
"inputs": {
"min_cfg": 1,
"model": [
"33",
0
]
},
"class_type": "VideoLinearCFGGuidance"
},
"15": {
"inputs": {
"ckpt_name": "AnimateLCM-SVD-xt-1-1_fp16_comfy.safetensors"
},
"class_type": "ImageOnlyCheckpointLoader"
},
"25": {
"inputs": {
"frame_rate": 8,
"loop_count": 0,
"filename_prefix": "AnimateDiff",
"format": "video/h265-mp4",
"pingpong": false,
"save_image": true,
"Sync playback": null,
"images": [
"8",
0
]
},
"class_type": "VHS_VideoCombine"
},
"30": {
"inputs": {
"image": [
"31",
0
]
},
"class_type": "Get image size"
},
"31": {
"inputs": {
"upscale_method": "nearest-exact",
"scale_by": 0.7000000000000001,
"image": [
"38",
0
]
},
"class_type": "ImageScaleBy"
},
"33": {
"inputs": {
"lora_name": "lcm-lora-sdv1-5.safetensors",
"strength_model": 1,
"model": [
"15",
0
]
},
"class_type": "LoraLoaderModelOnly"
},
"38": {
"inputs": {
"image_path": "http://127.0.0.1:8188/view?filename=ComfyUI_00402_.png&type=input&subfolder=&rand=0.9232966295130483",
"RGBA": "false",
"filename_text_extension": "true"
},
"class_type": "LamLoadPathImage"
}
}
+125
View File
@@ -0,0 +1,125 @@
{
"3": {
"inputs": {
"seed": 1068764177227380,
"steps": 5,
"cfg": 1,
"sampler_name": "lcm",
"scheduler": "normal",
"denoise": 1,
"model": [
"10",
0
],
"positive": [
"6",
0
],
"negative": [
"7",
0
],
"latent_image": [
"5",
0
]
},
"class_type": "KSampler"
},
"4": {
"inputs": {
"ckpt_name": "dreamshaper_8.safetensors"
},
"class_type": "CheckpointLoaderSimple"
},
"5": {
"inputs": {
"width": [
"14",
0
],
"height": [
"14",
1
],
"batch_size": 1
},
"class_type": "EmptyLatentImage"
},
"6": {
"inputs": {
"text": [
"20",
0
],
"clip": [
"10",
1
]
},
"class_type": "CLIPTextEncode"
},
"7": {
"inputs": {
"text": "undefined",
"clip": [
"4",
1
]
},
"class_type": "CLIPTextEncode"
},
"8": {
"inputs": {
"samples": [
"3",
0
],
"vae": [
"4",
2
]
},
"class_type": "VAEDecode"
},
"9": {
"inputs": {
"filename_prefix": "ComfyUI",
"images": [
"8",
0
]
},
"class_type": "SaveImage"
},
"10": {
"inputs": {
"lora_name": "lcm-lora-sdv1-5.safetensors",
"strength_model": 1,
"strength_clip": 1,
"model": [
"4",
0
],
"clip": [
"4",
1
]
},
"class_type": "LoraLoader"
},
"14": {
"inputs": {
"aspect_ratio": "1152×896 ∣ 9:7",
"width": 512,
"height": 512
},
"class_type": "AspectRatio"
},
"20": {
"inputs": {
"text_trans": "一只小狗"
},
"class_type": "ZhPromptTranslator"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+1
View File
@@ -292,6 +292,7 @@ app.registerExtension({
if(cat_values.join(',')!=Object.keys(pb_cache[prompt_type.value]).join(',')){
cat_values=Object.keys(pb_cache[prompt_type.value])
cat_value=''
category.value=cat_values[0]
}
return cat_values;
}
File diff suppressed because it is too large Load Diff
+67
View File
@@ -0,0 +1,67 @@
Date.prototype.format = function (format) {
var args = {
"M+": this.getMonth() + 1,
"d+": this.getDate(),
"h+": this.getHours(),
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"q+": Math.floor((this.getMonth() + 3) / 3), //quarter
"S": this.getMilliseconds()
};
if (/(y+)/.test(format))
format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var i in args) {
var n = args[i];
if (new RegExp("(" + i + ")").test(format))
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? n : ("00" + n).substr(("" + n).length));
}
return format;
};
//生成指定长度的随机数字字符
function randomString(length) {
const str = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
let result = '';
for(let i = 0; i<length; i++)
{
let id = Math.floor(Math.random() * str.length);
result += str[id];
}
return result;
}
// 获取当前时间
function NowTime() {
var time = new Date();
var year = time.getFullYear();// 获取年
var month = time.getMonth() + 1;// 或者月
var day = time.getDate();// 或者天
var hour = time.getHours();// 获取小时
var minu = time.getMinutes();// 获取分钟
var second = time.getSeconds();// 或者秒
var data = year + "-";
if (month < 10) {
data += "0";
}
data += month + "-";
if (day < 10) {
data += "0"
}
data += day + " ";
if (hour < 10) {
data += "0"
}
data += hour + ":";
if (minu < 10) {
data += "0"
}
data += minu + ":";
if (second < 10) {
data += "0"
}
data += second;
return data;
}
File diff suppressed because it is too large Load Diff
+809
View File
@@ -0,0 +1,809 @@
<!DOCTYPE>
<!--解决idea thymeleaf 表达式模板报红波浪线-->
<!--suppress ALL -->
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<title>欢迎{{openId}}</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<!-- jquery在线版本 -->
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="../extensions/ComfyUI_Lam/static/js/qn-rtplayer-web.js"></script>
<!--引入样式-->
<link rel="stylesheet" href="../extensions/ComfyUI_Lam/static/css/app.css" />
<style>
:where(.css-3m4nqy).ant-select-single .ant-select-selector .ant-select-selection-search {
position: absolute;
top: 0;
inset-inline-start: 11px px;
inset-inline-end: 11px;
bottom: 0;
}
:where(.css-3m4nqy).ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
width: 100%;
height: 32px;
padding: 0 11px;
}
:where(.css-3m4nqy).ant-select:not(.ant-select-customize-input) .ant-select-selector {
position: relative;
background-color: #ffffff;
border: 1px solid #d9d9d9;
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
:where(.css-3m4nqy).ant-select-single .ant-select-selector {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.88);
font-size: 14px;
line-height: 1.5714285714285714;
list-style: none;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
display: flex;
border-radius: 6px;
}
:where(.css-3m4nqy)[class^="ant-select"] [class^="ant-select"],
:where(.css-3m4nqy)[class*=" ant-select"] [class^="ant-select"],
:where(.css-3m4nqy)[class^="ant-select"] [class*=" ant-select"],
:where(.css-3m4nqy)[class*=" ant-select"] [class*=" ant-select"] {
box-sizing: border-box;
}
:where(.css-3m4nqy).ant-select-dropdown .ant-select-item {
position: relative;
display: block;
min-height: 32px;
padding: 5px 12px;
color: rgba(250, 250, 250, 0.88);
font-weight: normal;
font-size: 14px;
line-height: 1.5714285714285714;
box-sizing: border-box;
cursor: pointer;
transition: background 0.3s ease;
border-radius: 4px;
}
:where(.css-3m4nqy)[class^="ant-input"] [class^="ant-input"],
:where(.css-3m4nqy)[class*=" ant-input"] [class^="ant-input"],
:where(.css-3m4nqy)[class^="ant-input"] [class*=" ant-input"],
:where(.css-3m4nqy)[class*=" ant-input"] [class*=" ant-input"] {
box-sizing: border-box;
}
:where(.css-3m4nqy).ant-input-number .ant-input-number-input {
width: 100%;
height: 30px;
padding: 0 11px;
text-align: start;
background-color: transparent;
border: 0;
border-radius: 6px;
outline: 0;
transition: all 0.2s linear;
appearance: textfield;
color: rgba(0, 0, 0, 0.88);
font-size: inherit;
vertical-align: top;
}
:where(.css-3m4nqy).ant-select .ant-select-arrow {
display: flex;
align-items: center;
color: rgba(0, 0, 0, 0.25);
font-style: normal;
line-height: 1;
text-align: center;
text-transform: none;
vertical-align: -0.125em;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
position: absolute;
top: 50%;
inset-inline-start: auto;
inset-inline-end: 11px;
height: 12px;
margin-top: -6px;
font-size: 12px;
pointer-events: none;
}
:where(.css-3m4nqy)[class^="ant-select"] [class^="ant-select"],
:where(.css-3m4nqy)[class*=" ant-select"] [class^="ant-select"],
:where(.css-3m4nqy)[class^="ant-select"] [class*=" ant-select"],
:where(.css-3m4nqy)[class*=" ant-select"] [class*=" ant-select"] {
box-sizing: border-box;
}
:where(.css-3m4nqy).ant-popover {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.88);
font-size: 14px;
line-height: 1.5714285714285714;
list-style: none;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
position: absolute;
top: 0;
left: 0;
z-index: 1030;
font-weight: normal;
white-space: normal;
text-align: start;
cursor: auto;
user-select: text;
--antd-arrow-background-color: #ffffff;
}
</style>
</head>
<body>
<div id="app" data-v-app="">
<section class="aigc-layout">
<div class="aigc-head">
<div style="color: aliceblue;font-size: x-large;">降维AI|创作平台</div>
<div class="aigc-head__left-block"></div>
<div class="aigc-head__right-block">
<div style="color: aliceblue;" id="contact_administrator"
class="aigc-head__right-block--qun aigc-head__right-block--commonbtn aigc-head__right-block--hover">
联系管理员 </div><!---->
<div class="aigc-head__right-block--avatarbox"><img class="aigc-head__right-block--avatar"
src="https://himg.bdimg.com/sys/portraith/item/public.1.e8294af1.YF07P1v3sOMXm56wbOMh9w">
</div><!---->
</div>
</div>
<div>
<div><!---->
<div class="ant-popover pop-com soci css-3m4nqy ant-popover-placement-bottomRight"
style="left: 968px; top: 43px; transform-origin: 203px 0px;display: none;" id="contact_administrator_popover">
<div class="ant-popover-content">
<div class="ant-popover-arrow"><span class="ant-popover-arrow-content"></span></div>
<div class="ant-popover-inner" role="tooltip"><!---->
<div class="ant-popover-inner-content">
<div class="add-soci">
<div class="add-text">扫码添加管理员微信</div><img class="qr-code"
src="/lam/getImage?type=wechat&&name=admin_qr_code.jpg">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div data-v-ada5c28e="" class="aigc-tab" style="--688ac256: 0px;" id="comand_div_id">
<!-- 功能菜单 -->
</div>
<section data-v-d1097eb1="" class="aigc-main">
<div data-v-d1097eb1="" class="aigc-main__left-content">
<div data-v-d1097eb1="" class="aigc-main__left-content--top-wrap">
<div data-v-d1097eb1="" class="aigc-main__left-content--top-left" id="upload_image_div_id"
style="display: none;">
<div data-v-23e1e235="" data-v-d1097eb1="" class="aigc-upload step_1"><!---->
<span data-v-23e1e235="" class="ant-upload-wrapper aigc-upload__ant-upload css-3m4nqy"
id="upload_image_btn_parent_id">
<div class="ant-upload ant-upload-drag aigc-upload__ant-upload css-3m4nqy"
style="padding-top: 150px;" id="upload_image_btn_id"><span
class="ant-upload ant-upload-btn" role="button" tabindex="0">
<input type="file" accept="" id="upload_image_input_id"
style="display: none;">
<div data-v-23e1e235="" class="aigc-upload__not-upload"
onclick="clickUpload()">
<div data-v-23e1e235="" style="color: aliceblue;"
class="aigc-upload__icon-coll--guide-btn">上传图片
</div>
</div>
</span>
</div><!---->
<div class="aigc-loading aigc-main__left-content--loading"
id="upload_image_loading_id" style="display: none;">
<div data-v-e4fb7b31="" class="aigc-loading__mask">
<div data-v-e4fb7b31="" role="progressbar"
class="ant-progress ant-progress-circle ant-progress-status-normal ant-progress-24 css-3m4nqy">
<div class="ant-progress-inner"
style="width: 24px; height: 24px; font-size: 9.6px;"><svg
class="ant-progress-circle" viewBox="0 0 100 100"><!---->
<path d="M 50,50 m 0,-46 a 46,46 0 1 1 0,92 a 46,46 0 1 1 0,-92"
stroke="#d8d8d8" stroke-linecap="round" stroke-width="8"
fill-opacity="0" class="ant-progress-circle-trail"
style="stroke: rgb(216, 216, 216); stroke-dasharray: 289.027px, 289.027px; stroke-dashoffset: 0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s ease 0s, stroke-width 0.06s ease 0.3s, opacity 0.3s ease 0s;">
</path>
</svg><!---->
</div>
</div><!---->
<div data-v-e4fb7b31="" class="aigc-loading__describe">上传中</div>
</div>
</div>
</span>
<section data-v-e19f253b="" data-v-23e1e235="" class="aigc-preview"
id="upload_image_preview_parend_id" style="display: none;">
<div data-v-e19f253b="" onclick="clickUpload()" class="aigc-preview__img"
id="upload_image_preview_id"
style="background-image: url(&quot;http://127.0.0.1:8189/view?filename=ComfyUI_00819_.png&type=input&subfolder=&rand=0.9293840121020174&quot;);">
<!----><!----><!---->
</div>
</section>
</div>
</div>
<div data-v-d1097eb1="" class="aigc-main__left-content--top-right step_5 step_7">
<div data-v-d1097eb1="" class="aigc-main__left-content--nodata" id="no_add_task_div">
<p data-v-d1097eb1="">生成预览区</p>
</div><!---->
<div data-v-e4fb7b31="" data-v-d1097eb1=""
class="aigc-loading aigc-main__left-content--loading" style="display: none;" id="yes_add_task_div">
<div data-v-e4fb7b31="" class="aigc-loading__mask">
<div data-v-e4fb7b31="" role="progressbar"
class="ant-progress ant-progress-circle ant-progress-status-normal ant-progress-24 css-3m4nqy">
<div class="ant-progress-inner"
style="width: 24px; height: 24px; font-size: 9.6px;"><svg
class="ant-progress-circle" viewBox="0 0 100 100"><!---->
<path d="M 50,50 m 0,-46 a 46,46 0 1 1 0,92 a 46,46 0 1 1 0,-92"
stroke="#d8d8d8" stroke-linecap="round" stroke-width="8"
fill-opacity="0" class="ant-progress-circle-trail"
style="stroke: rgb(216, 216, 216); stroke-dasharray: 289.027px, 289.027px; stroke-dashoffset: 0px; transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s ease 0s, stroke-width 0.06s ease 0.3s, opacity 0.3s ease 0s;">
</path>
</svg><!---->
</div>
</div><!---->
<div data-v-e4fb7b31="" class="aigc-loading__describe">效果绘制中</div>
</div>
</div>
<section data-v-e19f253b="" data-v-d1097eb1="" class="aigc-preview" style="display: none;" id="wcomplete_add_task_div">
<div data-v-e19f253b="" onclick="openViewNow(this)" class="aigc-preview__img" id="wcomplete_add_task_img_div"
style="background-image: url(&quot;https://pcsdata.baidu.com/thumbnail/6a9bc2ee8fb8cf53f0d23d6fa4b31143?fid=3878105166-16051585-1026667930322733&amp;time=1713502800&amp;rt=pr&amp;sign=FDTAER-yUdy3dSFZ0SVxtzShv1zcMqd-oTE9DBFqjwgK6eAiBoZIZhTdKU0%3D&amp;expires=48h&amp;chkv=0&amp;chkbd=0&amp;chkpc=&amp;dp-logid=235267649399325916&amp;dp-callid=0&amp;size=c1600_u1600&amp;quality=100&amp;vuk=&amp;ft=video&amp;imgtimes=1713505888030&quot;);">
</div>
</section>
</div>
</div>
<div data-v-d1097eb1="" class="aigc-main__left-content--bottom-wrap step_2_empty">
<div data-v-622eff0c="" data-v-d1097eb1="" class="aigc-container">
<div data-v-622eff0c="" class="aigc-container__formula">
<div data-v-622eff0c="" class="aigc-container__tabs-box">
<div data-v-622eff0c="" class="aigc-container__sub-tab active-sub-tab">历史记录</div>
</div>
<div data-v-622eff0c="" class="aigc-container__recomm-fml" id="historys_div_id">
</div><!---->
</div><!----><!----><!----><!---->
</div>
</div>
</div>
<div data-v-d1097eb1="" class="aigc-main__right-params">
<div data-v-fe0dcf7c="" data-v-d1097eb1="" class="aigc-layright step_3" id="rightBoxRef">
<div data-v-fe0dcf7c="" class="aigc-layright__top">
<div data-v-d25ec045="" data-v-fe0dcf7c="" class="aigc-top">
<div id="select_container_div">
</div>
<div class="aigc-prompt" id="prompt-container" style="display: none;">
<div class="aigc-prompt__head">
<div class="aigc-prompt__head--text">正向提示词</div>
<div class="aigc-prompt__head--clear"
onclick="$('#prompt').html('');userParams['prompt']='';$('#prompt').attr('placeholder',$('#prompt').html().length>0?'':'输入中文或英文提示词');"> 清除 </div>
</div>
<div class="aigc-prompt__base--textarea">
<div class="textarea" id="prompt" contenteditable="true"
oninput="userParams['prompt']=$(event.target).html();$(event.target).attr('placeholder',$(event.target).html().length>0?'':'输入中文或英文提示词');"
placeholder="输入中文或英文提示词" style="height: 130px;"></div>
</div>
</div>
<div class="aigc-prompt" id="negative-container" style="display: none;">
<div class="aigc-prompt__head">
<div class="aigc-prompt__head--text">反向提示词</div>
<div class="aigc-prompt__head--clear"
onclick="$('#negative').html('');userParams['negative']='';$('#negative').attr('placeholder',$('#negative').html().length>0?'':'输入中文或英文提示词');"> 清除
</div>
</div>
<div class="aigc-prompt__base--textarea">
<div class="textarea" id="negative" contenteditable="true"
oninput="userParams['negative']=$(event.target).html();$(event.target).attr('placeholder',$(event.target).html().length>0?'':'输入中文或英文提示词');"
placeholder="输入中文或英文提示词" style="height: 130px;"></div>
</div>
</div>
<div data-v-fe0dcf7c="" class="aigc-layright__bottom" id="other_params_parent_div"
style="display: none;">
<div data-v-fea6429a="" data-v-fe0dcf7c="" class="aigc-advanced">
<div data-v-fea6429a="" class="aigc-advanced__mid--text"
style="font-weight: bold;"> 其他参数 </div>
<div data-v-fea6429a="" class="aigc-advanced__midexpend"
id="other_params_div_id">
<div data-v-fea6429a="" class="aigc-advanced__midexpend--process"> 生成步数 <div
data-v-fea6429a=""
class="ant-input-number aigc-advanced__midexpend--input css-3m4nqy">
<div class="ant-input-number-input-wrap"><input autocomplete="off"
role="spinbutton" aria-valuenow="30" step="1" type="number"
value="30" class="ant-input-number-input"></div>
</div>
</div>
<div data-v-fea6429a="" class="aigc-advanced__midexpend--process"> 提示词引导系数
<div data-v-fea6429a=""
class="ant-input-number aigc-advanced__midexpend--inputshort css-3m4nqy"
style="margin-left: 16px;">
<div class="ant-input-number-input-wrap"><input autocomplete="off"
role="spinbutton" aria-valuemin="1" aria-valuemax="20"
aria-valuenow="7" step="1" type="number" value="0"
class="ant-input-number-input"></div>
</div>
</div>
<div data-v-fea6429a="" style="white-space: nowrap;"
class="aigc-advanced__midexpend--process"> 随机种子
<input data-v-fea6429a="" type="text"
class="ant-input css-3m4nqy aigc-advanced__midexpend--input">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div data-v-d1097eb1="" class="aigc-main__btn-box step_4">
<div data-v-d1097eb1="" class="aigc-main__btn-box--merge" id="add_task_btn_id" onclick="addTask(this)"><!---->
<div data-v-d1097eb1="" class="text"><span data-v-d1097eb1="" class="upper">立即生成</span>
</div>
</div>
</div>
</div><!---->
<div data-v-51fd3d7a="" data-v-d1097eb1="" class="modal" style="display:none;" id="pop_frame_div_parent_id">
<div data-v-51fd3d7a="" class="content-wrap">
<img data-v-51fd3d7a="" class="cross-icon" id="pop_frame_div_id" onclick="$('#pop_frame_div_parent_id').hide()"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGkSURBVHgB7dptbcNADMZxQyiEQCqEMBiEjcHGYGMSCIUQCIHgXaRY8rSmeff5zs9fivrtbP1UVY0SIoQQQgghhBBCCAWJmd/T1aXrO10NOSvtdEvX17TjG1mWBn7y33pPSOMu0066D7IqDRv4fy6QZnDGBrIqDXvw87IivcAZe5BVadid5+tzIC3gjN3JsjSw9YK0AqelHHlAcoujFsyG5B5HyoFUDI5kiVQcjmSBVCyOdCVS8TjSFUjV4EhnIlWHI52BVC2OdASpehxpD1IYHGkLUjgcaQ1SWBxpBVIfFkdaQIqNI21EailiK5Faihov/yCPDwcaitgKHKkPh7QBJx7SDpw4SCtwWnb0tMQ03vAPORwS77h9CIPEB+6tqkc6gqPOqBPpDBx1Vl1IZ+KoM+tAugJHnV020pU4akaZSBY4alZZSJY4amYZSDlw1GzfSDlx1A4+kTzgqF18IXnCUTv5QUrDfjzhqL1eIXVkFT9/kTwrjtptDsn0RfLOI440g2T6DdK/QYMnHGlCkh3Hz4asm6Bu5Dj2euuBEEIIIYQQitwvJD8kAp6CtMQAAAAASUVORK5CYII=">
<div data-v-d1097eb1="" class="title" id="pop_frame_div_title_id"></div>
<div data-v-d1097eb1="" class="desc" id="pop_frame_div_desc_id">
</div>
<div data-v-51fd3d7a="" class="bottom-box" id="pop_frame_div_btn_id">
<div data-v-51fd3d7a="" class="cancel-btn btn" id="pop_frame_div_btn_cancel_id">取消</div>
<div data-v-51fd3d7a="" class="confirm-btn btn" id="pop_frame_div_btn_ok_id">确认</div>
</div>
</div>
</div>
</section><!---->
</section>
</div>
</body>
<script src="../extensions/ComfyUI_Lam/static/js/app.js"></script>
<script>
var openId = '{{openId}}';
var websocket = null;
// 判断当前浏览器是否支持WebSocket, springboot是项目名
if ('WebSocket' in window) {
var host = window.location.host;
websocket = new WebSocket("ws://" + host + '/ws?clientId=' + openId);
} else {
console.error("不支持WebSocket");
}
// 连接发生错误的回调方法
websocket.onerror = function (e) {
console.error("WebSocket连接发生错误");
};
// 连接成功建立的回调方法
websocket.onopen = function () {
// 获取所有在线用户
console.log("WebSocket连接成功");
//var username = randomString(10);
//sendIssue({ "userName": username, "comType": "login" });
//$("#talks").html(username);
}
// 接收到消息的回调方法
websocket.onmessage = async function (event) {
if (event.data == null || event.data == "" || event.data instanceof Blob) {
return;
}
var messageJson = eval("(" + event.data + ")");
if(messageJson['type']=='executing'){
$("#add_task_btn_id").removeClass("disabled");
getHistorys()
}
console.log(new Date().format("yyyy-MM-dd hh:mm:ss"), messageJson);
}
// 连接关闭的回调方法
websocket.onclose = function () {
// alert("WebSocket连接关闭");
}
// 发送消息
function sendIssue(msg) {
websocket.send(JSON.stringify(msg));
}
var userParams = {}
var comands = {}
$(function () {
$('#contact_administrator').mouseenter(function(event) {
var mouseX = event.pageX; // 鼠标相对于页面的X坐标
var mouseY = event.pageY; // 鼠标相对于页面的Y坐标
mouseX=mouseX-$('#contact_administrator_popover').width()
$('#contact_administrator_popover').css({
'top': mouseY + 'px',
'left': mouseX + 'px'
});
$('#contact_administrator_popover').show()
}).mouseleave(function() {
// 鼠标离开时的操作
$('#contact_administrator_popover').hide()
});
$.get('/wechatauth/getCommands', {}, function (res) {
comands = res;
$("#comand_div_id").html('')
for (var key of Object.keys(res)) {
$("#comand_div_id").append('<div id="'+btoa(encodeURIComponent(key))+'" data-v-ada5c28e="" class="aigc-tab__item " onclick="setComand(\'' + key + '\')"><span data-v-ada5c28e="">' + key + '</span></div>')
}
if (Object.keys(res).length > 0) {
$("#comand_div_id").children().eq(0).click()
}
})
getHistorys()
})
var historyObj={}
function getHistorys(){
$.get('/wechatauth/getHistorys', {'openId':openId}, function (res) {
console.log(res)
var historyHtml='';
for(var i = 0; i < res.length; i++){
var command=JSON.parse(res[i][2])
if (res[i][4] == 'waiting'){
setComand(command['command'],command)
continue
}
var outputs=JSON.parse(res[i][7])
historyObj[command['prompt_id']]=command
for(var node_id of Object.keys(outputs)){
node_output = outputs[node_id]
if(Object.keys(node_output).indexOf("images") >= 0){
for(var j = 0; j < node_output.images.length; j++){
var img=node_output.images[j]
var url = '/view?filename=' + img.filename + '&type=' + img.type + '&subfolder='+ img.subfolder
historyHtml+=`<div data-v-ae8b2cf0="" data-v-622eff0c="" onclick="openView('image','`+url+`','`+command['prompt_id']+`')"
class="item-box step_2 aigc-container__fml-item"><img data-v-ae8b2cf0=""
class="cover-img"
src="`+url+`">
<div data-v-ae8b2cf0="" class="bottom">
<div data-v-ae8b2cf0="" title="`+command['command']+`" class="title">`+command['command']+`</div>
</div>
</div>`
if(userParams['prompt_id']==command['prompt_id']){
$("#no_add_task_div").hide()
$("#yes_add_task_div").hide()
$("#wcomplete_add_task_img_div").css("background-image", "url(" + url + ")")
$("#wcomplete_add_task_img_div").html('');
$("#wcomplete_add_task_img_div").attr('url',url);
$("#wcomplete_add_task_img_div").attr('type','image');
$("#wcomplete_add_task_div").show()
}
}
}else if(Object.keys(node_output).indexOf("gifs") >= 0){
for(var j = 0; j < node_output.gifs.length; j++){
var video=node_output.gifs[j]
var url = '/view?filename=' + video.filename + '&type=' + video.type + '&subfolder='+ video.subfolder
var imgUrl='/lam/getImage?type=wechat&&name=video_play.jpeg'
var backgroundImg=''
if(Object.keys(command).indexOf('image')>=0){
if(command['image'].indexOf('view?filename')>=0){
backgroundImg='/view?'+command['image'].split('?')[1]
}
}
if(userParams['prompt_id']==command['prompt_id']){
$("#no_add_task_div").hide()
$("#yes_add_task_div").hide()
//修改背景图片
$("#wcomplete_add_task_img_div").css("background-image", "url(" + backgroundImg + ")")
$("#wcomplete_add_task_img_div").html('<img src="'+imgUrl+'" style="width: auto;height: 100%;">')
$("#wcomplete_add_task_img_div").attr('url',url);
$("#wcomplete_add_task_img_div").attr('type','video');
$("#wcomplete_add_task_div").show()
}
historyHtml+=`<div data-v-ae8b2cf0="" data-v-622eff0c="" style="background-image: url(&quot;`+backgroundImg+`&quot;);background-size: cover;background-position: center;"
class="item-box step_2 aigc-container__fml-item" onclick="openView('video','`+url+`','`+command['prompt_id']+`')" ><img data-v-ae8b2cf0=""
class="cover-img" data-video="`+url+`"
src="`+imgUrl+`">
<div data-v-ae8b2cf0="" class="bottom">
<div data-v-ae8b2cf0="" title="`+command['command']+`" class="title">`+command['command']+`</div>
</div>
</div>`
}
}
}
}
$("#historys_div_id").html(historyHtml);
})
}
function setComand(name,comand) {
userParams = comand?comand:{ 'openId': openId }
userParams['command'] = name
$("#"+btoa(encodeURIComponent(name))).addClass("active").siblings().removeClass("active")
if ("prompt" in comands[name].params) {
if(comand &&'prompt' in comand){
$("#prompt").html(comand['prompt'])
}else{
userParams['prompt'] = ''
}
$('#prompt').attr('placeholder',$('#prompt').html().length>0?'':'输入中文或英文提示词');
$("#prompt-container").show()
} else {
$("#prompt-container").hide()
}
if ("negative" in comands[name].params) {
if(comand &&'negative' in comand){
$("#negative").html(comand['negative'])
}else{
userParams['negative'] = ''
}
$('#negative').attr('placeholder',$('#negative').html().length>0?'':'输入中文或英文提示词');
$("#negative-container").show()
} else {
$("#negative-container").hide()
}
if ('image' in comands[name].params) {
if(comand &&'image' in comand){
if(comand['image'].indexOf('view?filename')>=0){
url='/view?'+comand['image'].split('?')[1]
$("#upload_image_btn_parent_id").hide()
$("#upload_image_preview_parend_id").show()
//修改div的背景图片
$("#upload_image_preview_id").css("background-image", "url(" + url + ")")
var host = window.location.host;
imageUrl = 'http://' + host + url
userParams['image'] = imageUrl
}else{
userParams['image'] = ''
}
}else{
userParams['image'] = ''
}
$("#upload_image_div_id").show()
} else {
$("#upload_image_div_id").hide()
}
var comandHtml = ''
var otherHtml = ''
console.log(comands[name].params)
var paichuKeys = ['prompt', 'negative', 'image']
for (var key of Object.keys(comands[name].params)) {
if (paichuKeys.indexOf(key) >= 0) {
continue
}
if ('options' in comands[name].params[key]) {
var selName=''
if(comand && key in comand){
selName= Object.keys(comands[name].params[key]["options"]).find(opName => comands[name].params[key]["options"][opName] == comand[key]);
}else{
selName= Object.keys(comands[name].params[key]["options"])[0]
userParams[key] = comands[name].params[key]['options'][selName]
}
comandHtml += `<div data-v-d25ec045="" class="aigc-top__base">
<div data-v-d25ec045="" class="aigc-top__base--text" style="font-weight: bold;">
`+ comands[name].params[key]['zhName'] + ` </div>
<div data-v-d25ec045=""
class="ant-select css-3m4nqy aigc-top__base--select ant-select-single ant-select-show-arrow"
style="width: 100%;"><!---->
<div class="ant-select-selector" onclick="$('#params_`+ key + `_options').toggle()">
<span class="ant-select-selection-search" style="position: unset;" id="params_`+ key + `" >
`+ selName + `
</span>
</div>
</div>
<div>
<div><!---->
<div class="ant-select-dropdown css-3m4nqy ant-select-dropdown-placement-bottomLeft"
style="min-width: 268px; width: 268px; left: 0px; top: 36px;display: none;" id="params_`+ key + `_options">
<div>
<div class="rc-virtual-list" style="position: relative;">
<div class="rc-virtual-list-holder"
style="max-height: 256px; overflow-y: hidden; overflow-anchor: none;">
<div><div class="rc-virtual-list-holder-inner"
style="display: flex; flex-direction: column;">`
for (var option of Object.keys(comands[name].params[key]["options"])) {
classStr = comands[name].params[key]["options"][option] == userParams[key] ? 'ant-select-item-option-selected' : ''
comandHtml += `<div aria-selected="true"
class="ant-select-item ant-select-item-option `+ classStr + `" onclick="setOption(this,'` + comands[name].params[key]['options'][option] + `','` + key + `','` + option + `')">
<div class="ant-select-item-option-content">
<div data-v-d25ec045="">`+ option + `</div>
</div>
</div>
`
}
comandHtml += ` </div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`
} else if ('type' in comands[name].params[key]) {
if (comands[name].params[key]['type'] == 'number') {
if(!comand||!(key in comand)){
userParams[key] = comands[name].params[key]['default']
}
otherHtml += `
<div data-v-fea6429a="" class="aigc-advanced__midexpend--process">`+ comands[name].params[key]['zhName'] + `<div
data-v-fea6429a=""
class="ant-input-number aigc-advanced__midexpend--input css-3m4nqy">
<div class="ant-input-number-input-wrap">
<input autocomplete="off" oninput="userParams['`+ key + `']=event.target.value"
role="spinbutton" step="1" type="number" aria-valuemin="`+ comands[name].params[key]['min'] + `" aria-valuemax="` + comands[name].params[key]['max'] + `"
value="`+ userParams[key] + `" class="ant-input-number-input"></div>
</div>
</div>
`
} else {
if(!comand||!(key in comand)){
userParams[key] = comands[name].params[key]['default']
}
otherHtml += `
<div data-v-fea6429a="" style="white-space: nowrap;" class="aigc-advanced__midexpend--process"> `+ comands[name].params[key]['zhName'] + `
<input data-v-fea6429a="" type="text" value="`+ userParams[key] + `"
oninput="userParams['`+ key + `']=event.target.value" class="ant-input css-3m4nqy aigc-advanced__midexpend--input">
</div>
`
}
}
}
$("#select_container_div").html(comandHtml)
if (otherHtml != '') {
$("#other_params_div_id").html(otherHtml)
$("#other_params_parent_div").show();
} else {
$("#other_params_parent_div").hide()
}
//console.log(comands[name].params)
}
function setOption(thi,val, key, option) {
userParams[key] = val
$("#params_" + key).html(option)
$(thi).addClass("ant-select-item-option-selected").siblings().removeClass("ant-select-item-option-selected")
$("#params_" + key + "_options").hide()
}
function clickUpload() {
const myFile = $("#upload_image_input_id")
// 触发
myFile.click()
// 监听change事件
myFile[0].addEventListener('change', function (e) {
var filesList = document.querySelector('#upload_image_input_id').files;
if (filesList.length == 0) { //如果取消上传,则上传文件的长度为0
console.log("没有上传任何文件");
return;
} else {
//判断div是否显示
$("#upload_image_btn_parent_id").show()
$("#upload_image_btn_id").hide()
$("#upload_image_loading_id").show()
//如果有文件上传,这在这里面进行
uploadImage(filesList[0])
}
});
}
function uploadImage(image) {
//上传文件
let formData = new FormData();
formData.append("image", image, image.name);
formData.append("overwrite", "true");
formData.append("type", "input");
//发起jquery ajax请求
$.ajax({
method: 'post',
url: '/upload/image',
data: formData,
processData: false,
contentType: false,
success: function (res) {
$("#upload_image_btn_parent_id").hide()
$("#upload_image_preview_parend_id").show()
//修改div的背景图片
var url = '/view?filename=' + res.name + '&type=' + res.type + '&subfolder='
$("#upload_image_preview_id").css("background-image", "url(" + url + ")")
var host = window.location.host;
imageUrl = 'http://' + host + url
userParams['image'] = imageUrl
console.log(imageUrl);
}
})
}
function openViewNow(thi){
var url=$(thi).attr('url')
var type=$(thi).attr('type')
var options={'url':url}
alertWindow(type,options)
}
function openView(type,url,prompt_id){
var options={'url':url,'okName':'应用参数'}
alertWindow(type,options,function(){
if(prompt_id){
setComand(historyObj[prompt_id]['command'],historyObj[prompt_id])
//getHistorys()
}
})
}
function alertWindow(type,options,okFun,cancelFun){
$("#pop_frame_div_title_id").html('')
$("#pop_frame_div_desc_id").html('')
if(type=='image'){
$("#pop_frame_div_desc_id").html('<img src="'+options.url+'" style="width:100%;height:100%;">')
}else if(type=='video'){
$("#pop_frame_div_desc_id").html('<video src="'+options.url+'" style="width:100%;height:100%;" controls="controls"></video>')
}else if(type=='confirm'){
$("#pop_frame_div_title_id").html(options.title)
$("#pop_frame_div_desc_id").html(options.desc)
}
if('okName' in options){
$("#pop_frame_div_btn_ok_id").html(options.okName)
$("#pop_frame_div_btn_ok_id").show();
$("#pop_frame_div_btn_ok_id").click(function(){
$("#pop_frame_div_parent_id").hide();
if(okFun){
okFun();
}
})
}else{
$("#pop_frame_div_btn_ok_id").hide();
}
if('cancelName' in options){
$("#pop_frame_div_btn_cancel_id").html(options.cancelName)
$("#pop_frame_div_btn_cancel_id").show();
$("#pop_frame_div_btn_cancel_id").click(function(){
$("#pop_frame_div_parent_id").hide();
if(cancelFun){
cancelFun();
}
})
}else{
$("#pop_frame_div_btn_cancel_id").hide();
}
$("#pop_frame_div_parent_id").show();
}
function addTask(thi){ //提交任务
$(thi).addClass('disable');
let formData = new FormData();
for (const key in userParams) {
if (Object.hasOwnProperty.call(userParams, key)) {
const element = userParams[key];
if(element){
formData.append(key, element)
}
}
}
$.ajax({
method: 'post',
url: '/wechatauth/addTask',
data: formData,
processData: false,
contentType: false,
success: function (res) {
console.log(res)
if(res.success){
userParams['prompt_id']=res.prompt_id
$("#no_add_task_div").hide()
$("#wcomplete_add_task_div").hide()
$("#yes_add_task_div").show()
}else{
alertWindow('confirm',{'title':'提示','desc':res.msg,'okName':'确定'})
}
}
})
}
</script>
</html>
+89
View File
@@ -0,0 +1,89 @@
from PIL import Image, ImageFilter, ImageEnhance, ImageOps, ImageDraw, ImageChops, ImageFont
import folder_paths
import os
import numpy as np
import torch
import random
import requests
import io
class LamLoadPathImage:
def __init__(self):
self.input_dir = folder_paths.input_directory
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image_path": ("STRING", {"default": './ComfyUI/input/example.png', "multiline": False}),
"RGBA": (["false","true"],),
},
"optional": {
"filename_text_extension": (["true", "false"],),
}
}
RETURN_TYPES = ("IMAGE", "MASK", "STRING")
RETURN_NAMES = ("image", "mask", "filename_text")
FUNCTION = "load_image"
CATEGORY = "lam"
def load_image(self, image_path, RGBA='false', filename_text_extension="true"):
RGBA = (RGBA == 'true')
if image_path.startswith('http'):
from io import BytesIO
i = self.download_image(image_path)
else:
try:
i = Image.open(image_path)
except OSError:
print(f"The image `{image_path.strip()}` specified doesn't exist!")
i = Image.new(mode='RGB', size=(512, 512), color=(0, 0, 0))
if not i:
return
image = i
if not RGBA:
image = image.convert('RGB')
image = np.array(image).astype(np.float32) / 255.0
image = torch.from_numpy(image)[None,]
if 'A' in i.getbands():
mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
mask = 1. - torch.from_numpy(mask)
else:
mask = torch.zeros((64, 64), dtype=torch.float32, device="cpu")
if filename_text_extension == "true":
filename = os.path.basename(image_path)
else:
filename = os.path.splitext(os.path.basename(image_path))[0]
return (image, mask, filename)
def download_image(self, url):
try:
response = requests.get(url)
response.raise_for_status()
img = Image.open(io.BytesIO(response.content))
return img
except requests.exceptions.HTTPError as errh:
print(f"HTTP Error: ({url}): {errh}")
except requests.exceptions.ConnectionError as errc:
print(f"Connection Error: ({url}): {errc}")
except requests.exceptions.Timeout as errt:
print(f"Timeout Error: ({url}): {errt}")
except requests.exceptions.RequestException as err:
print(f"Request Exception: ({url}): {err}")
NODE_CLASS_MAPPINGS = {
"LamLoadPathImage": LamLoadPathImage
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LamLoadPathImage": "加载网络图片或本地图片"
}
+427
View File
@@ -0,0 +1,427 @@
from server import PromptServer
from aiohttp import web
import os
import json
from .src.wechat.WechatAuthUtils import *
from .src.wechat.DataBaseUtil import DataBaseUtil
import xml.etree.ElementTree as ET
import time
import logging
import queue
import random
import types
import execution
import uuid
import folder_paths
def send_sync(self, event, data, sid=None): #继承父类的send_sync方法
if sid and hasattr(self, "user_command") and sid in getattr(self,'user_command'):
if event == "executing" and data['node'] is None and data['prompt_id'] == self.user_command[sid]['prompt_id']:
history=self.prompt_queue.get_history(prompt_id=data['prompt_id'])[data['prompt_id']]
#print("历史记录======:",history)
if Config().wechat['isEnterprise'] and ('isWeb' not in self.user_command[sid] or self.user_command[sid]['isWeb']==False):
imagePaths=[]
videoPaths=[]
for node_id in history['outputs']:
node_output = history['outputs'][node_id]
if 'images' in node_output:
for image in node_output['images']:
basePath=''
if 'output' == image['type']:
basePath=folder_paths.get_output_directory()
elif 'temp' == image['type']:
basePath=folder_paths.get_temp_directory()
elif 'input' == image['type']:
basePath=folder_paths.get_input_directory()
if image['subfolder']:
basePath=os.path.join(basePath,image['subfolder'])
imagePaths.append(os.path.join(basePath,image['filename']))
if 'gifs' in node_output:
for image in node_output['gifs']:
basePath=''
if 'output' in image['type']:
basePath=folder_paths.get_output_directory()
elif 'temp' in image['type']:
basePath=folder_paths.get_temp_directory()
elif 'input' in image['type']:
basePath=folder_paths.get_input_directory()
if image['subfolder']:
basePath=os.path.join(basePath,image['subfolder'])
if 'format' in image:
if image['format'].startswith('video'):
videoPaths.append(os.path.join(basePath,image['filename']))
continue
imagePaths.append(os.path.join(basePath,image['filename']))
logging.info("imagePaths: " + str(imagePaths)+" videoPaths: " + str(videoPaths))
for imagePath in imagePaths:
mediaId=getMediaId(imagePath,'image')
sendServiceImageMessge(mediaId,sid)
for videoPath in videoPaths:
mediaId=getMediaId(videoPath,'video')
sendServiceVideoMessge(mediaId,'AI生成','AI生成视频',sid)
self.user_command[sid]['status']='wcomplete'
now = time.localtime()
end_time = time.strftime("%Y-%m-%d %H:%M:%S", now)
db=DataBaseUtil()
db.update_data('wcomplete', end_time, json.dumps(history['outputs']),data['prompt_id'])
db.close_con()
self.loop.call_soon_threadsafe(
self.messages.put_nowait, (event, data, sid))
def update_dict(dictionary, keys, value):
if len(keys) == 1:
dictionary[keys[0]] = value
else:
key = keys.pop(0)
if isinstance(dictionary[key], dict):
update_dict(dictionary[key], keys, value)
elif isinstance(dictionary[key], list):
for item in dictionary[key]:
update_dict(item, keys[:], value)
else:
raise ValueError("Invalid data structure")
def setPost(self,FromUserName):
params=self.user_command[FromUserName]
logging.info(str(params))
basePath=folder_paths.folder_names_and_paths['custom_nodes'][0][0]
comand=Config().wechat['commands'][params['command']]
filePath = os.path.join(basePath,'ComfyUI_Lam','config','workflow',comand['filename'])
if os.path.exists(filePath)==False:
logging.warning("文件不存在:"+filePath)
return None
f = open(filePath,'r', encoding='utf-8')
json_data = json.load(f)
f.close()
for key in comand['params']:
keys=comand['params'][key]['keys'][:]
if key in params:
update_dict(json_data,keys,params[key])
json_data={"prompt":json_data,"client_id":params['openId']}
logging.info(json_data)
json_data = self.trigger_on_prompt(json_data)
if "number" in json_data:
number = float(json_data['number'])
else:
number = self.number
if "front" in json_data:
if json_data['front']:
number = -number
self.number += 1
if "prompt" in json_data:
prompt = json_data["prompt"]
valid = execution.validate_prompt(prompt)
extra_data = {}
if "extra_data" in json_data:
extra_data = json_data["extra_data"]
if "client_id" in json_data:
extra_data["client_id"] = json_data["client_id"]
if valid[0]:
prompt_id = str(uuid.uuid4())
outputs_to_execute = valid[2]
self.prompt_queue.put((number, prompt_id, prompt, extra_data, outputs_to_execute))
now = time.localtime()
start_time = time.strftime("%Y-%m-%d %H:%M:%S", now)
self.user_command[FromUserName]['status']='waiting' #prepare:准备 waiting:待执行 wcomplete完成
self.user_command[FromUserName]['prompt_id']=prompt_id
db=DataBaseUtil()
db.insert_data( params['openId'], json.dumps(params), prompt_id,'waiting',start_time, '', '')
db.close_con()
return prompt_id
else:
logging.warning("invalid prompt: {}".format(valid[1]))
return None
def getTaskRanking(self,FromUserName):
if 'prompt_id' not in self.user_command[FromUserName]:
return '您还没有排队,请先发送您的指令'
prompt_id=self.user_command[FromUserName]['prompt_id']
current_queue = self.prompt_queue.get_current_queue()
print(current_queue)
for i in range(len(current_queue[0])):
if current_queue[0][i][1] == prompt_id:
return '您的任务正在执行。'
for i in range(len(current_queue[1])):
if current_queue[1][i][1] == prompt_id:
return '当前排队人数:'+str(len(current_queue[1]))+'人,您当前的位置在'+current_queue[1][i][0]+'号。'
return '您还没有排队,请先发送您的指令'
@PromptServer.instance.routes.get("/wechatauth/getHistorys")
async def getHistorys(request):
if "openId" in request.rel_url.query:
openId=request.rel_url.query['openId']
db=DataBaseUtil()
datas=db.get_many_data(openId)
db.close_con()
return web.Response(text=json.dumps(datas), content_type='application/json')
else:
return web.Response(status=404)
@PromptServer.instance.routes.post("/wechatauth/addTask")
async def addTask(request):
post = await request.post()
print(post.keys())
command = post.get("command")
if command == None:
msg = '指令不能为空!'
data={'msg':msg,'success':False}
return web.Response(text=json.dumps(data), content_type='application/json')
openId = post.get("openId")
if openId == None:
msg = '用户编码不能为空!'
data={'msg':msg,'success':False}
return web.Response(text=json.dumps(data), content_type='application/json')
if hasattr(PromptServer.instance,'user_command') and openId in PromptServer.instance.user_command and PromptServer.instance.user_command[openId]['status']=='waiting':
msg = '您已经在队列中,请勿重复提交!'
data={'msg':msg,'success':False}
return web.Response(text=json.dumps(data), content_type='application/json')
adminNo=base64_decode(Config().wechat['adminNo'])
if adminNo!=openId:
db=DataBaseUtil()
data=db.get_user_frequency(openId)
if data[0]==None:
db.user_recharge(openId,Config().wechat['freeSize'])
data=db.get_user_frequency(openId)
countd=db.get_user_task_count(openId)
db.close_con()
if countd[0] >=data[0]:
msg='非常抱歉,您的免费使用次数已用完,如需继续使用,请扫描右上角二维码,联系管理员。'
data={'msg':msg,'success':False}
return web.Response(text=json.dumps(data), content_type='application/json')
params=Config().wechat['commands'][command]['params']
paramName=''
userData={'openId':openId,'command':command,'status':'prepare','isWeb':True}
for param in params:
val=post.get(param)
if params[param]['isRequired'] and val==None:
paramName= params[param]['zhName']
break
if val:
userData[param]=val
if paramName:
msg = '参数"'+paramName+'"不能为空!'
data={'msg':msg,'success':False}
return web.Response(text=json.dumps(data), content_type='application/json')
if 'seed' in userData:
if userData['seed'].isdigit()==False or int(userData['seed'])==-1:
userData['seed']=''.join(random.sample('123456789012345678901234567890',14))
if hasattr(PromptServer.instance,"user_command")==False:
setattr(PromptServer.instance,"user_command",{})
PromptServer.instance.user_command[openId]=userData
resp=setPost(PromptServer.instance,openId)
if resp!=None:
data={'msg':'任务已加入队列请等待...','prompt_id':resp,'success':True}
return web.Response(text=json.dumps(data), content_type='application/json')
@PromptServer.instance.routes.get("/wechatauth/getCommands")
async def getCommands(request):
commands=Config().wechat['commands']
return web.Response(text=json.dumps(commands), content_type='application/json')
@PromptServer.instance.routes.get("/wechatauth/app")
async def app(request):
if "openId" in request.rel_url.query:
openId=request.rel_url.query['openId']
openId=base64_decode(openId)
basePath = folder_paths.folder_names_and_paths['custom_nodes'][0][0]
htmlPtah = os.path.join(basePath, 'ComfyUI_Lam', 'pages','app.html')
# 打开文件
with open(htmlPtah, 'r', encoding='utf-8') as file:
# 读取文件内容
html_content = file.read()
html_content = html_content.replace('{{openId}}', openId)
return web.Response(text=html_content, content_type='text/html')
#return web.FileResponse(htmlPtah)
else:
return web.Response(status=404)
@PromptServer.instance.routes.get("/wechatauth/handleMessage")
async def handleMessageGet(request):
if "echostr" in request.rel_url.query:
echostr=request.rel_url.query['echostr']
return web.Response(text=echostr)
else:
return web.Response(text="121212")
@PromptServer.instance.routes.post("/wechatauth/handleMessage")
async def handleMessagePost(request):
# 读取请求体数据
body = await request.read()
# 将字节转换为字符串
data_str = body.decode('utf-8')
data = ET.fromstring(data_str)
ToUserName = data.find('ToUserName').text
FromUserName = data.find('FromUserName').text
MsgType = data.find('MsgType').text
logging.info(data_str)
CreateTime = int(time.time())
if hasattr(PromptServer.instance,"user_command")==False:
setattr(PromptServer.instance,"user_command",{})
#判断是否为准备状态
isPrepare=FromUserName in PromptServer.instance.user_command and PromptServer.instance.user_command[FromUserName]['status']=='prepare'
isWaiting=FromUserName in PromptServer.instance.user_command and PromptServer.instance.user_command[FromUserName]['status']=='waiting'
# 如果发送的是消息请求,
if MsgType == 'text': #语音屏蔽or MsgType == 'voice'
try:
MsgId = data.find("MsgId").text
if MsgType == 'text':
Content = data.find('Content').text # 文本消息内容
elif MsgType == 'voice':
Content = data.find('Recognition').text # 语音识别结果,UTF8编码
# 调用回复函数判断接受的信息,然后返回对应的内容
reply_content,otherName=receive_msg(Content,isPrepare,isWaiting)
logging.info("reply_content:"+reply_content+',otherName:'+str(otherName))
if reply_content=='menu' and otherName:
if Config().wechat['isEnterprise']:
sendServiceMenuMessage('您可以执行以下操作:','欢迎再次光临',otherName, FromUserName)
else:
msgStr='您可以执行以下操作指令:\n '
for i in range(len(otherName)):
msgStr+=otherName[i]['content']+'\n'
out = reply_text(FromUserName, ToUserName, CreateTime, msg)
return web.Response(text=out, content_type='application/xml')
elif reply_content=='command' and otherName:
adminNo=base64_decode(Config().wechat['adminNo'])
msg=''
isAdopt=True
if adminNo!=FromUserName:
db=DataBaseUtil()
data=db.get_user_frequency(FromUserName)
if data[0]==None:
db.user_recharge(FromUserName,Config().wechat['freeSize'])
data=db.get_user_frequency(FromUserName)
countd=db.get_user_task_count(FromUserName)
db.close_con()
if countd[0] >=data[0]:
isAdopt=False
msg='非常抱歉,您的免费使用次数已用完,如需继续使用,请联系管理员。微信号:\n'+Config().wechat['adminWeChat']
if isAdopt:
#添加指令的代码
PromptServer.instance.user_command[FromUserName]={'openId':FromUserName,'status':'prepare','command':otherName,'seed':''.join(random.sample('123456789012345678901234567890',14))}
msg,comlist=getCommandMsg(Config().wechat['commands'][otherName],Config().wechat['isEnterprise'])
if comlist:
sendServiceMenuMessage(msg,Config().wechat['commands'][otherName]['replyText'],comlist, FromUserName)
return web.Response(status=200)
out = reply_text(FromUserName, ToUserName, CreateTime, msg)
return web.Response(text=out, content_type='application/xml')
elif reply_content=='query' and otherName:
if otherName=='查询排队情况':
msg=''
if isWaiting:
msg=getTaskRanking(PromptServer.instance,FromUserName)
else:
msg='您还没有排队,请先发送您的指令'
out = reply_text(FromUserName, ToUserName, CreateTime, msg)
return web.Response(text=out, content_type='application/xml')
elif otherName == '我的编号':
out = reply_text(FromUserName, ToUserName, CreateTime, base64_encode(FromUserName))
return web.Response(text=out, content_type='application/xml')
elif reply_content=='ok':
if otherName and len(otherName)>0:
PromptServer.instance.user_command[FromUserName]['prompt']=PromptServer.instance.user_command[FromUserName]['prompt']+otherName if 'prompt' in PromptServer.instance.user_command[FromUserName] else otherName
params=Config().wechat['commands'][PromptServer.instance.user_command[FromUserName]['command']]['params']
paramName=''
for param in params:
if params[param]['isRequired'] and param not in PromptServer.instance.user_command[FromUserName]:
paramName= params[param]['zhName']
break
if paramName!='':
out = reply_text(FromUserName, ToUserName, CreateTime, '任务缺乏参数“'+paramName+'”请补充后再提交')
return web.Response(text=out, content_type='application/xml')
resp=setPost(PromptServer.instance,FromUserName)
if resp!=None:
serverAddress=Config().wechat['serverAddress']+'wechatauth/app?openId='+base64_encode(FromUserName)
imgUrl='http://mmbiz.qpic.cn/sz_mmbiz_jpg/EpicUicwgk0IGpbYZicuGSXUBaIFWbuDZmBbmDO9PleONOz3FJ3eANEmRicD0eR7mF6PCkxSPxVicbDclQldFKDlHRA/0'
out = reply_news(FromUserName, ToUserName, CreateTime, '任务加入队列提醒','任务已加入队列请等待。或可点击访问详情,使用更多功能!',imgUrl,serverAddress)
#out = reply_text(FromUserName, ToUserName, CreateTime, '任务已加入队列请等待,详情请访问:\n'+serverAddress)
return web.Response(text=out, content_type='application/xml')
elif reply_content=='recharge' and otherName:
adminNo=base64_decode(Config().wechat['adminNo'])
msg=''
if adminNo==FromUserName:
data=otherName.split('加')
openId=base64_decode(data[0])
tount=int(data[1])
if openId and tount>0:
db=DataBaseUtil()
fdata=db.get_user_frequency(openId)
if fdata[0] and fdata[0]>0:
db.user_recharge(openId,tount)
msg='次数加入成功'
else:
msg='用户不存在'
db.close_con()
else:
msg='用户编码错误'
else:
msg='无效的指令'
out = reply_text(FromUserName, ToUserName, CreateTime, msg)
return web.Response(text=out, content_type='application/xml')
elif isPrepare:
msg=''
if reply_content and otherName!=None:
commandName=PromptServer.instance.user_command[FromUserName]['command']
if reply_content in Config().wechat['commands'][commandName]['params']:
if 'options' in Config().wechat['commands'][commandName]['params'][reply_content]:
PromptServer.instance.user_command[FromUserName][reply_content]=Config().wechat['commands'][commandName]['params'][reply_content]['options'][otherName]
else:
PromptServer.instance.user_command[FromUserName][reply_content]=otherName
msg='已填选'+Config().wechat['commands'][commandName]['params'][reply_content]['zhName']+':'+otherName
else:
PromptServer.instance.user_command[FromUserName]['prompt']=PromptServer.instance.user_command[FromUserName]['prompt']+reply_content if 'prompt' in PromptServer.instance.user_command[FromUserName] else reply_content
msg='已填入提示词:'+reply_content
out = reply_text(FromUserName, ToUserName, CreateTime, msg)
return web.Response(text=out, content_type='application/xml')
else:
out = reply_text(FromUserName, ToUserName, CreateTime, reply_content)
logging.info(out)
return web.Response(text=out, content_type='application/xml')
except Exception as e:
logging.error('消息处理异常:'+str(e))
elif MsgType == 'image': #图片
if isPrepare:
PicUrl=data.find('PicUrl').text
PromptServer.instance.user_command[FromUserName]['image']=PicUrl
else:
out = reply_text(FromUserName, ToUserName, CreateTime, '请先选择指令')
return web.Response(text=out, content_type='application/xml')
elif MsgType == 'event': #事件
try:
Event = data.find('Event').text
Event_key = data.find('EventKey').text
CreateTime = int(time.time())
# 判断事件,并返回内容
reply_content = receive_event(Event,Event_key)
if reply_content:
out = reply_text(FromUserName, ToUserName, CreateTime, reply_content)
return web.Response(text=out, content_type='application/xml')
except Exception as e:
logging.error('事件处理异常:'+str(e))
return web.Response(status=200)
PromptServer.instance.send_sync=types.MethodType(send_sync,PromptServer.instance)
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
+9 -5
View File
@@ -24,8 +24,8 @@ if not os.path.exists(dir):
# Directory node save settings
CHUNK_SIZE = 1024
dir_painter_node = os.path.dirname(__file__)
extension_path = os.path.join(os.path.abspath(dir_painter_node))
basePath = folder_paths.folder_names_and_paths['custom_nodes'][0][0]
extension_path = os.path.join(basePath, 'ComfyUI_Lam', 'config')
file_settings_path = os.path.join(extension_path,"settings_nodes.json")
# Function create file json file
@@ -379,9 +379,13 @@ class openPoseEditorPlus:
image_path = folder_paths.get_annotated_filepath(image)
i = Image.open(image_path)
i = ImageOps.exif_transpose(i)
painterImage = i.convert("RGB")
if os.path.exists(image_path):
i = Image.open(image_path)
i = ImageOps.exif_transpose(i)
painterImage = i.convert("RGB")
else:
painterImage = np.zeros(shape=(height, width, 3), dtype=np.uint8)
painterImage = np.array(painterImage).astype(np.float32) / 255.0
painterImage = torch.from_numpy(painterImage)[None,]
+219
View File
@@ -0,0 +1,219 @@
# -*- coding: utf-8 -*-
import sys
import os
import sqlite3
import folder_paths
import time
class DataBaseUtil():
"""
简单sqlite数据库工具类
编写这个类主要是为了封装sqlite,继承此类复用方法
"""
def __init__(self, dbName="lamWeChat.db"):
"""
初始化连接——使用完需关闭连接
:param dbName: 连接库的名字,注意,以'.db'结尾
"""
basePath = folder_paths.folder_names_and_paths['custom_nodes'][0][0]
self.db_path = os.path.join(basePath, 'ComfyUI_Lam', 'config', dbName)
isNew=os.path.exists(self.db_path)
# 连接数据库
self._conn = sqlite3.connect(self.db_path)
# 创建游标
self._cur = self._conn.cursor()
if isNew==False:
# 创建数据表 用户任务表
users_tb_sql ='''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
openId TEXT NOT NULL,
command TEXT NOT NULL,
prompt_id TEXT NOT NULL,
status TEXT NOT NULL,
start_time TEXT NOT NULL,
end_time TEXT,
outputs TEXT
)
'''
self.create_tabel(users_tb_sql)
# 用户充值记录表
user_recharge_sql ='''
CREATE TABLE IF NOT EXISTS user_recharge (
id INTEGER PRIMARY KEY,
openId TEXT NOT NULL,
frequency INTEGER NOT NULL,
recharge_time TEXT NOT NULL
)
'''
self.create_tabel(user_recharge_sql)
def close_con(self):
"""关闭连接对象——主动调用
:return:
"""
self._cur.close()
self._conn.close()
# 创建数据表
def create_tabel(self, sql: str):
"""
创建表
:param sql: create sql语句
:return: True表示创建表成功
"""
try:
self._cur.execute(sql)
self._conn.commit()
print("[create table success]")
return True
except Exception as e:
print("[create table error]", e)
# 删除数据表
def drop_table(self, sql: str):
"""
删除表
:param sql: drop sql语句
:return: True表示删除成功
"""
try:
self._cur.execute(sql)
self._conn.commit()
return True
except Exception as e:
print("[drop table error]", e)
return False
# 插入或更新表数据,一次插入或更新一条数据
def operate_one(self, sql: str, value: tuple):
"""
插入或更新单条表记录
:param sql: insert语句或update语句
:param value: 插入或更新的值,形如()
:return: True表示插入或更新成功
"""
try:
self._cur.execute(sql, value)
self._conn.commit()
if 'INSERT' in sql.upper():
print("[insert one record success]")
if 'UPDATE' in sql.upper():
print("[update one record success]")
return True
except Exception as e:
print("[insert/update one record error]", e)
self._conn.rollback()
return False
# 插入或更新表数据,一次插入或更新多条数据
def operate_many(self, sql: str, value: list):
"""
插入或更新多条表记录
:param sql: insert语句或update语句
:param value: 插入或更新的字段的具体值,列表形式为list:[(),()]
:return: True表示插入或更新成功
"""
try:
# 调用executemany()方法
self._cur.executemany(sql, value)
self._conn.commit()
if 'INSERT' in sql.upper():
print("[insert many records success]")
if 'UPDATE' in sql.upper():
print("[update many records success]")
return True
except Exception as e:
print("[insert/update many records error]", e)
self._conn.rollback()
return False
# 删除表数据
def delete_record(self, sql: str):
"""
删除表记录
:param sql: 删除记录SQL语句
:return: True表示删除成功
"""
try:
if 'DELETE' in sql.upper():
self._cur.execute(sql)
self._conn.commit()
#print("[detele record success]")
return True
else:
print("[sql is not delete]")
return False
except Exception as e:
print("[detele record error]", e)
return False
# 查询一条数据
def query_one(self, sql: str, params=None):
"""
查询单条数据
:param sql: select语句
:param params: 查询参数,形如()
:return: 语句查询单条结果
"""
try:
if params:
self._cur.execute(sql, params)
else:
self._cur.execute(sql)
# 调用fetchone()方法
r = self._cur.fetchone()
#print("[select one record success]")
return r
except Exception as e:
print("[select one record error]", e)
# 查询多条数据
def query_many(self, sql: str, params=None):
"""
查询多条数据
:param sql: select语句
:param params: 查询参数,形如()
:return: 语句查询多条结果
"""
try:
if params:
self._cur.execute(sql, params)
else:
self._cur.execute(sql)
# 调用fetchall()方法
r = self._cur.fetchall()
#print("[select many records success]")
return r
except Exception as e:
print("[select many records error]", e)
def insert_data(self, openId, command, prompt_id,status,start_time, end_time, outputs):
self.operate_one("INSERT INTO users (openId, command,prompt_id,status, start_time, end_time, outputs) VALUES (?, ?,?, ?, ?, ?,?)", (
openId, command, prompt_id,status,start_time, end_time, outputs))
def get_data(self, openId,prompt_id):
return self.query_one("SELECT * FROM users WHERE openId = ? and prompt_id=? ", (openId,prompt_id))
def update_data(self, status, end_time, outputs,prompt_id):
self.operate_one("UPDATE users SET status=?,end_time = ?, outputs = ? WHERE prompt_id = ?",
(status, end_time, outputs, prompt_id))
def get_many_data(self, openId):
return self.query_many("SELECT * FROM users WHERE openId=? ", (openId,))
def user_recharge(self,openId,frequency):
now = time.localtime()
recharge_time = time.strftime("%Y-%m-%d %H:%M:%S", now)
self.operate_one("INSERT INTO user_recharge (openId,frequency,recharge_time) VALUES (?, ?,?)", (openId, frequency, recharge_time))
def get_user_frequency(self,openId):
return self.query_one("SELECT sum(frequency) FROM user_recharge WHERE openId=? ", (openId,))
def get_user_task_count(self,openId):
return self.query_one("SELECT count(*) FROM users WHERE openId=? ", (openId,))
#db=DataBaseUtil()
#db.delete_record('DELETE FROM users where command="图生视频"')
#data=db.get_user_task_count('oJNTS6vtlfGKyivfY6loLLScQ3FQ')
#data=db.get_data('oJNTS6vtlfGKyivfY6loLLScQ3FQ','02b73c61-6f18-4432-837a-dd64d9424ed7')
#print(data)
+263
View File
@@ -0,0 +1,263 @@
import requests
import hashlib
import io
import cv2
import json
from .config import Config
import time
import logging
# APPID = 'wx1c2b19f7af1d6241'
# SECRET = '915d587b5ae4e148e100429e11ca4d60'
#APPID = 'wxa50ed345b253964d'
#SECRET = "327ac9ba06d214d2fcfe2e9ed0520684"
# APPID = 'wx2693b75459c2f339'
# SECRET = 'f5e8741764bbb0a8b8b2d7d26a17ea62'
import base64
def base64_encode(text):
encoded_text = base64.b64encode(text.encode('utf-8')).decode('utf-8')
return encoded_text
def base64_decode(encoded_text):
decoded_text = base64.b64decode(encoded_text).decode('utf-8')
return decoded_text
"""这是一个处理客户发送信息的文件"""
def reply_text(FromUserName, ToUserName, CreateTime, Content):
"""回复文本消息模板"""
textTpl = """<xml> <ToUserName><![CDATA[%s]]></ToUserName> <FromUserName><![CDATA[%s]]></FromUserName> <CreateTime>%s</CreateTime> <MsgType><![CDATA[%s]]></MsgType> <Content><![CDATA[%s]]></Content></xml>"""
out = textTpl % (FromUserName, ToUserName, CreateTime, 'text', Content)
return out
"""图片文消息"""
def reply_news(FromUserName, ToUserName, CreateTime, Title, Description, PicUrl, Url):
textTpl = """
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
</Articles>
</xml>
"""
out = textTpl % (FromUserName, ToUserName, CreateTime,
Title, Description, PicUrl, Url)
return out
def getCommandMsg(command, isEnterprise):
keyNames = ['image', 'seed', 'prompt', 'negative']
if isEnterprise:
comList = []
names = []
otherParamsStr=''
index = 0
for key in command['params']:
if key in keyNames:
continue
requiredText='(必填)' if command['params'][key]['isRequired'] else '(选填)'
if 'options' in command['params'][key]:
names.append(command['params'][key]['zhName']+requiredText)
for option in command['params'][key]['options']:
comList.append({"id": index, "content": key+':'+option})
index += 1
else:
otherParamsStr+='\n参数“'+command['params'][key]['zhName']+requiredText+'”指令=》'+key+':'+str(command['params'][key]['default'])
if len(names) > 0 and len(comList) > 0:
return '您可以选择以下指令来选择('+','.join(names)+')'+otherParamsStr, comList
else:
if len(otherParamsStr)>0:
return '您可以输入以下指令:'+otherParamsStr+'\n'+command['replyText'], None
return command['replyText'], None
else:
names = []
comList = []
otherParamsStr=''
for key in command['params']:
if key in keyNames:
continue
requiredText='(必填)' if command['params'][key]['isRequired'] else '(选填)'
if 'options' in command['params'][key]:
names.append(command['params'][key]['zhName']+requiredText)
for option in command['params'][key]['options']:
comList.append(key+':'+option)
else:
otherParamsStr+='\n参数“'+command['params'][key]['zhName']+requiredText+'”命令=》'+key+':'+str(command['params'][key]['default'])
return '您可以执行以下命令:\n'+'\n'.join(comList)+'\n,选择》'+','.join(names)+otherParamsStr+'\n'+command['replyText'], None
def receive_msg(msg, isPrepare=False, isWaiting=False):
# 这是一个将疑问改成成熟句子的函数,例如:你好吗 公众号回复:你好
commands = Config().wechat['commands']
isEnterprise = Config().wechat['isEnterprise']
query_commands = Config().wechat['query_commands']
if msg == u'帮助':
comList = []
index = 0
for key in commands:
comList.append({"id": index, "content": key})
index += 1
for key in query_commands:
comList.append({"id": index, "content": key})
index += 1
if isEnterprise == False:
comNames = [x['content'] for x in comList]
return '您可以执行以下操作:\n'+'\n'.join(comNames), None
return 'menu', comList
elif isWaiting == False and msg in commands:
return 'command', msg
elif msg in query_commands:
return 'query', msg
elif isPrepare:
if msg.find(':') != -1:
return tuple(msg.split(':'))
elif (msg.lower().endswith(u'ok') or msg.lower() == 'ok'):
return 'ok', msg[:-2]
else:
return msg, None
elif msg.find('加') != -1 and len(msg.split('加')) == 2:
return 'recharge', msg
elif isWaiting:
return '任务已加入队列请等待...', None
else:
return "您可以发送“帮助”查看使用说明", None
def receive_event(event, key):
# 如果是关注公众号事件
if event == 'subscribe':
return "感谢关注!您可以发送“帮助”查看使用说明"
# 如果是点击菜单拉取消息事件
elif event == 'CLICK':
# 接下来就是根据你点击不同的菜单拉去不同的消息啦
# 我为了省事,不进行判断啦,如果需要判断请根据 key进行判断
return "你点击了菜单"+key
# 如果是点击菜单跳转Url事件,不做任何处理因为微信客户端会自行处理
elif event == 'VIEW':
return None
def getAccessToken():
if Config().wechat['access_token_expires_at'] <= 0 or time.time() >= Config().wechat['access_token_expires_at']:
APPID = Config().wechat['appid']
SECRET = Config().wechat['secret']
token_url = f'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={APPID}&secret={SECRET}'
resResult = requests.get(token_url)
data = resResult.json()
if 'access_token' in data:
access_token = data['access_token']
Config().wechat['access_token'] = access_token
Config().wechat['access_token_expires_at'] = time.time(
)+data['expires_in']-60
Config().save_config()
return access_token
else:
logging.info(token_url)
logging.error('获取access_token失败'+str(data))
else:
return Config().wechat['access_token']
def sendServiceImageMessge(media_id, touser): # 发送图片
message = {
"touser": touser,
"msgtype": 'image',
"image": {
"media_id": media_id,
}
}
return sendServiceMessage(message)
def sendServiceVideoMessge(media_id, title, desc, touser): # 发送视频
message = {
"touser": touser,
"msgtype": "video",
"video": {
"media_id": media_id,
"title": title,
"description": desc,
}
}
return sendServiceMessage(message)
# 发送菜单 menu_list = [{"id":"1","content":"菜单1"},{"id":"2","content":"菜单2"}]
def sendServiceMenuMessage(head_content, tail_content, menu_list, touser):
message = {
"touser": touser,
"msgtype": "msgmenu",
"msgmenu": {
"head_content": head_content,
"list": menu_list,
"tail_content": tail_content
}
}
return sendServiceMessage(message)
def sendServiceMessage(message):
access_token = getAccessToken()
url = f'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={access_token}'
data = json.dumps(message, ensure_ascii=False).encode('utf-8')
resResult = requests.post(url, data=data)
data = resResult.json()
if data['errcode'] == 0:
return True
else:
logging.error('发送消息失败'+str(data))
return False
def sendServiceTyping(touser): # 下发正在输入状态
accessToken = getAccessToken()
url = f'https://api.weixin.qq.com/cgi-bin/message/custom/typing?access_token={accessToken}'
message = {"touser": touser, "command": "Typing"}
data = json.dumps(message, ensure_ascii=False).encode('utf-8')
resResult = requests.post(url, data=data)
data = resResult.json()
if data['errcode'] == 0:
return True
else:
logging.error('正在输入状态失败'+str(data))
return False
# 上传临时/永久素材
# 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
def getMediaId(image_path, type, isTemporary=True):
access_token = getAccessToken()
if(isTemporary):
url = f"https://api.weixin.qq.com/cgi-bin/media/upload?access_token={access_token}&type={type}"
else:
url = f"https://api.weixin.qq.com/cgi-bin/material/add_material?access_token={access_token}&type={type}"
files = {'media': open(image_path, 'rb')}
resResult = requests.post(url, files=files)
data = resResult.json()
if 'media_id' in data:
return data['media_id']
else:
logging.error('素材上传失败'+str(data))
return None
+65
View File
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
import os
import shutil
import functools
import yaml
import folder_paths
def singleton(cls):
"""
将一个类作为单例
来自 https://wiki.python.org/moin/PythonDecoratorLibrary#Singleton
"""
cls.__new_original__ = cls.__new__
@functools.wraps(cls.__new__)
def singleton_new(cls, *args, **kw):
it = cls.__dict__.get('__it__')
if it is not None:
return it
cls.__it__ = it = cls.__new_original__(cls, *args, **kw)
it.__init_original__(*args, **kw)
return it
cls.__new__ = singleton_new
cls.__init_original__ = cls.__init__
cls.__init__ = object.__init__
return cls
@singleton
class Config(object):
def __init__(self) -> None:
basePath=folder_paths.folder_names_and_paths['custom_nodes'][0][0]
self.pwd = os.path.join(basePath,'ComfyUI_Lam','config')
self.reload()
def _load_config(self) -> dict:
try:
with open(f"{self.pwd}/weChat.yaml", "rb") as fp:
yconfig = yaml.safe_load(fp)
except FileNotFoundError:
shutil.copyfile(f"{self.pwd}/weChat.yaml.template", f"{self.pwd}/weChat.yaml")
with open(f"{self.pwd}/weChat.yaml", "rb") as fp:
yconfig = yaml.safe_load(fp)
return yconfig
def save_config(self):
yconfig = self._load_config()
yconfig["wechat"] = self.wechat
with open(f"{self.pwd}/weChat.yaml", 'w', encoding='utf-8') as file:
yaml.dump(yconfig, file,sort_keys=False,default_flow_style=False,allow_unicode=True, encoding='utf-8')
def reload(self) -> None:
yconfig = self._load_config()
self.wechat = yconfig.get("wechat", {})
#self.comfyui = yconfig.get("comfyui", {})
#self.EMAIL= yconfig.get("email", {})
#self.OPENAI= yconfig.get("openai", {})
#self.GLM4= yconfig.get("glm4", {})
#self.COMFY_UI=yconfig.get("comfyUI", {})