@@ -11,4 +11,13 @@ tmp | |||||
plugins.json | plugins.json | ||||
itchat.pkl | itchat.pkl | ||||
*.log | *.log | ||||
user_datas.pkl | |||||
user_datas.pkl | |||||
plugins/**/ | |||||
!plugins/bdunit | |||||
!plugins/dungeon | |||||
!plugins/finish | |||||
!plugins/godcmd | |||||
!plugins/tool | |||||
!plugins/banwords | |||||
!plugins/hello | |||||
!plugins/role |
@@ -1,71 +0,0 @@ | |||||
{ | |||||
"start":{ | |||||
"host" : "127.0.0.1", | |||||
"port" : 7860, | |||||
"use_https" : false | |||||
}, | |||||
"defaults": { | |||||
"params": { | |||||
"sampler_name": "DPM++ 2M Karras", | |||||
"steps": 20, | |||||
"width": 512, | |||||
"height": 512, | |||||
"cfg_scale": 7, | |||||
"prompt":"masterpiece, best quality", | |||||
"negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", | |||||
"enable_hr": false, | |||||
"hr_scale": 2, | |||||
"hr_upscaler": "Latent", | |||||
"hr_second_pass_steps": 15, | |||||
"denoising_strength": 0.7 | |||||
}, | |||||
"options": { | |||||
"sd_model_checkpoint": "perfectWorld_v2Baked" | |||||
} | |||||
}, | |||||
"rules": [ | |||||
{ | |||||
"keywords": [ | |||||
"横版", | |||||
"壁纸" | |||||
], | |||||
"params": { | |||||
"width": 640, | |||||
"height": 384 | |||||
}, | |||||
"desc": "分辨率会变成640x384" | |||||
}, | |||||
{ | |||||
"keywords": [ | |||||
"竖版" | |||||
], | |||||
"params": { | |||||
"width": 384, | |||||
"height": 640 | |||||
} | |||||
}, | |||||
{ | |||||
"keywords": [ | |||||
"高清" | |||||
], | |||||
"params": { | |||||
"enable_hr": true, | |||||
"hr_scale": 1.6 | |||||
}, | |||||
"desc": "出图分辨率长宽都会提高1.6倍" | |||||
}, | |||||
{ | |||||
"keywords": [ | |||||
"二次元" | |||||
], | |||||
"params": { | |||||
"negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", | |||||
"prompt": "masterpiece, best quality" | |||||
}, | |||||
"options": { | |||||
"sd_model_checkpoint": "meinamix_meinaV8" | |||||
}, | |||||
"desc": "使用二次元风格模型出图" | |||||
} | |||||
] | |||||
} |
@@ -1,91 +0,0 @@ | |||||
## 插件描述 | |||||
本插件用于将画图请求转发给stable diffusion webui。 | |||||
## 环境要求 | |||||
使用前先安装stable diffusion webui,并在它的启动参数中添加 "--api"。 | |||||
具体信息,请参考[文章](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/API)。 | |||||
部署运行后,保证主机能够成功访问http://127.0.0.1:7860/docs | |||||
请**安装**本插件的依赖包```webuiapi``` | |||||
``` | |||||
pip install webuiapi | |||||
``` | |||||
## 使用说明 | |||||
请将`config.json.template`复制为`config.json`,并修改其中的参数和规则。 | |||||
PS: 如果修改了webui的`host`和`port`,也需要在配置文件中更改启动参数, 更多启动参数参考:https://github.com/mix1009/sdwebuiapi/blob/a1cb4c6d2f39389d6e962f0e6436f4aa74cd752c/webuiapi/webuiapi.py#L114 | |||||
### 画图请求格式 | |||||
用户的画图请求格式为: | |||||
``` | |||||
<画图触发词><关键词1> <关键词2> ... <关键词n>:<prompt> | |||||
``` | |||||
- 本插件会对画图触发词后的关键词进行逐个匹配,如果触发了规则中的关键词,则会在画图请求中重载对应的参数。 | |||||
- 规则的匹配顺序参考`config.json`中的顺序,每个关键词最多被匹配到1次,如果多个关键词触发了重复的参数,重复参数以最后一个关键词为准。 | |||||
- 关键词中包含`help`或`帮助`,会打印出帮助文档。 | |||||
第一个"**:**"号之后的内容会作为附加的**prompt**,接在最终的prompt后 | |||||
例如: 画横版 高清 二次元:cat | |||||
会触发三个关键词 "横版", "高清", "二次元",prompt为"cat" | |||||
若默认参数是: | |||||
```json | |||||
"width": 512, | |||||
"height": 512, | |||||
"enable_hr": false, | |||||
"prompt": "8k" | |||||
"negative_prompt": "nsfw", | |||||
"sd_model_checkpoint": "perfectWorld_v2Baked" | |||||
``` | |||||
"横版"触发的规则参数为: | |||||
```json | |||||
"width": 640, | |||||
"height": 384, | |||||
``` | |||||
"高清"触发的规则参数为: | |||||
```json | |||||
"enable_hr": true, | |||||
"hr_scale": 1.6, | |||||
``` | |||||
"二次元"触发的规则参数为: | |||||
```json | |||||
"negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", | |||||
"steps": 20, | |||||
"prompt": "masterpiece, best quality", | |||||
"sd_model_checkpoint": "meinamix_meinaV8" | |||||
``` | |||||
以上这些规则的参数会和默认参数合并。第一个":"后的内容cat会连接在prompt后。 | |||||
得到最终参数为: | |||||
```json | |||||
"width": 640, | |||||
"height": 384, | |||||
"enable_hr": true, | |||||
"hr_scale": 1.6, | |||||
"negative_prompt": "(low quality, worst quality:1.4),(bad_prompt:0.8), (monochrome:1.1), (greyscale)", | |||||
"steps": 20, | |||||
"prompt": "masterpiece, best quality, cat", | |||||
"sd_model_checkpoint": "meinamix_meinaV8" | |||||
``` | |||||
PS: 实际参数分为两部分: | |||||
- 一部分是`params`,为画画的参数;参数名**必须**与webuiapi包中[txt2img api](https://github.com/mix1009/sdwebuiapi/blob/fb2054e149c0a4e25125c0cd7e7dca06bda839d4/webuiapi/webuiapi.py#L163)的参数名一致 | |||||
- 另一部分是`options`,指sdwebui的设置,使用的模型和vae需写在里面。它和(http://127.0.0.1:7860/sdapi/v1/options )所返回的键一致。 |
@@ -1,123 +0,0 @@ | |||||
# encoding:utf-8 | |||||
import json | |||||
import os | |||||
from bridge.context import ContextType | |||||
from bridge.reply import Reply, ReplyType | |||||
from config import conf | |||||
import plugins | |||||
from plugins import * | |||||
from common.log import logger | |||||
import webuiapi | |||||
import io | |||||
@plugins.register(name="sdwebui", desc="利用stable-diffusion webui来画图", version="2.0", author="lanvent") | |||||
class SDWebUI(Plugin): | |||||
def __init__(self): | |||||
super().__init__() | |||||
curdir = os.path.dirname(__file__) | |||||
config_path = os.path.join(curdir, "config.json") | |||||
try: | |||||
with open(config_path, "r", encoding="utf-8") as f: | |||||
config = json.load(f) | |||||
self.rules = config["rules"] | |||||
defaults = config["defaults"] | |||||
self.default_params = defaults["params"] | |||||
self.default_options = defaults["options"] | |||||
self.start_args = config["start"] | |||||
self.api = webuiapi.WebUIApi(**self.start_args) | |||||
self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context | |||||
logger.info("[SD] inited") | |||||
except Exception as e: | |||||
if isinstance(e, FileNotFoundError): | |||||
logger.warn(f"[SD] init failed, {config_path} not found, ignore or see https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/sdwebui .") | |||||
else: | |||||
logger.warn("[SD] init failed, ignore or see https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/sdwebui .") | |||||
raise e | |||||
def on_handle_context(self, e_context: EventContext): | |||||
if e_context['context'].type != ContextType.IMAGE_CREATE: | |||||
return | |||||
channel = e_context['channel'] | |||||
if ReplyType.IMAGE in channel.NOT_SUPPORT_REPLYTYPE: | |||||
return | |||||
logger.debug("[SD] on_handle_context. content: %s" %e_context['context'].content) | |||||
logger.info("[SD] image_query={}".format(e_context['context'].content)) | |||||
reply = Reply() | |||||
try: | |||||
content = e_context['context'].content[:] | |||||
# 解析用户输入 如"横版 高清 二次元:cat" | |||||
if ":" in content: | |||||
keywords, prompt = content.split(":", 1) | |||||
else: | |||||
keywords = content | |||||
prompt = "" | |||||
keywords = keywords.split() | |||||
if "help" in keywords or "帮助" in keywords: | |||||
reply.type = ReplyType.INFO | |||||
reply.content = self.get_help_text(verbose = True) | |||||
else: | |||||
rule_params = {} | |||||
rule_options = {} | |||||
for keyword in keywords: | |||||
matched = False | |||||
for rule in self.rules: | |||||
if keyword in rule["keywords"]: | |||||
for key in rule["params"]: | |||||
rule_params[key] = rule["params"][key] | |||||
if "options" in rule: | |||||
for key in rule["options"]: | |||||
rule_options[key] = rule["options"][key] | |||||
matched = True | |||||
break # 一个关键词只匹配一个规则 | |||||
if not matched: | |||||
logger.warning("[SD] keyword not matched: %s" % keyword) | |||||
params = {**self.default_params, **rule_params} | |||||
options = {**self.default_options, **rule_options} | |||||
params["prompt"] = params.get("prompt", "")+f", {prompt}" | |||||
if len(options) > 0: | |||||
logger.info("[SD] cover options={}".format(options)) | |||||
self.api.set_options(options) | |||||
logger.info("[SD] params={}".format(params)) | |||||
result = self.api.txt2img( | |||||
**params | |||||
) | |||||
reply.type = ReplyType.IMAGE | |||||
b_img = io.BytesIO() | |||||
result.image.save(b_img, format="PNG") | |||||
reply.content = b_img | |||||
e_context.action = EventAction.BREAK_PASS # 事件结束后,跳过处理context的默认逻辑 | |||||
except Exception as e: | |||||
reply.type = ReplyType.ERROR | |||||
reply.content = "[SD] "+str(e) | |||||
logger.error("[SD] exception: %s" % e) | |||||
e_context.action = EventAction.CONTINUE # 事件继续,交付给下个插件或默认逻辑 | |||||
finally: | |||||
e_context['reply'] = reply | |||||
def get_help_text(self, verbose = False, **kwargs): | |||||
if not conf().get('image_create_prefix'): | |||||
return "画图功能未启用" | |||||
else: | |||||
trigger = conf()['image_create_prefix'][0] | |||||
help_text = "利用stable-diffusion来画图。\n" | |||||
if not verbose: | |||||
return help_text | |||||
help_text += f"使用方法:\n使用\"{trigger}[关键词1] [关键词2]...:提示语\"的格式作画,如\"{trigger}横版 高清:cat\"\n" | |||||
help_text += "目前可用关键词:\n" | |||||
for rule in self.rules: | |||||
keywords = [f"[{keyword}]" for keyword in rule['keywords']] | |||||
help_text += f"{','.join(keywords)}" | |||||
if "desc" in rule: | |||||
help_text += f"-{rule['desc']}\n" | |||||
else: | |||||
help_text += "\n" | |||||
return help_text |
@@ -16,9 +16,6 @@ pysilk_mod>=1.6.0 # needed by send voice | |||||
# wechatmp | # wechatmp | ||||
web.py | web.py | ||||
# sdwebui plugin | |||||
webuiapi>=0.6.2 | |||||
# chatgpt-tool-hub plugin | # chatgpt-tool-hub plugin | ||||
--extra-index-url https://pypi.python.org/simple | --extra-index-url https://pypi.python.org/simple | ||||
chatgpt_tool_hub>=0.3.5 | chatgpt_tool_hub>=0.3.5 |