├── .idea ├── .gitignore ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── pictureChange.iml └── vcs.xml ├── LICENSE ├── README.md ├── __init__.py ├── __pycache__ ├── __init__.cpython-311.pyc ├── __init__.cpython-312.pyc ├── __init__.cpython-38.pyc ├── pictureChange.cpython-311.pyc ├── pictureChange.cpython-312.pyc └── pictureChange.cpython-38.pyc ├── adminService ├── README.md ├── __pycache__ │ └── admin_service.cpython-311.pyc ├── admin_service.py └── config.json ├── config.json ├── groupService ├── __pycache__ │ ├── change_config.cpython-311.pyc │ ├── config_cache.cpython-311.pyc │ ├── find_group.cpython-311.pyc │ ├── find_group.cpython-312.pyc │ └── find_group.cpython-38.pyc ├── change_config.py ├── config.json ├── config_cache.py └── find_group.py ├── message ├── __pycache__ │ ├── message_handle.cpython-311.pyc │ ├── message_handle.cpython-312.pyc │ ├── message_handle.cpython-38.pyc │ ├── message_limit.cpython-311.pyc │ ├── message_limit.cpython-312.pyc │ ├── message_limit.cpython-38.pyc │ ├── message_reply.cpython-311.pyc │ ├── message_reply.cpython-312.pyc │ ├── message_reply.cpython-38.pyc │ ├── message_sd_reply.cpython-311.pyc │ ├── message_sd_reply.cpython-312.pyc │ ├── message_sd_reply.cpython-38.pyc │ ├── message_type.cpython-311.pyc │ ├── message_type.cpython-312.pyc │ ├── message_type.cpython-38.pyc │ ├── music_handle.cpython-311.pyc │ ├── music_handle.cpython-312.pyc │ ├── music_handle.cpython-38.pyc │ ├── request_handle.cpython-311.pyc │ ├── request_handle.cpython-312.pyc │ └── request_handle.cpython-38.pyc ├── message_handle.py ├── message_limit.py ├── message_reply.py ├── message_sd_reply.py ├── message_type.py ├── music_handle.py ├── request_handle.py └── run.log ├── nohup.out ├── pictureChange.py ├── requirements.txt ├── util ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-311.pyc │ ├── __init__.cpython-312.pyc │ ├── __init__.cpython-38.pyc │ ├── baidu_image.cpython-311.pyc │ ├── baidu_image.cpython-312.pyc │ ├── baidu_image.cpython-38.pyc │ ├── file_handle.cpython-311.pyc │ ├── file_handle.cpython-312.pyc │ ├── file_handle.cpython-38.pyc │ ├── image_handle.cpython-311.pyc │ ├── image_handle.cpython-312.pyc │ ├── image_handle.cpython-38.pyc │ ├── translate_prompt.cpython-311.pyc │ ├── translate_prompt.cpython-312.pyc │ └── translate_prompt.cpython-38.pyc ├── baidu_image.py ├── file_handle.py ├── image_handle.py └── translate_prompt.py └── work ├── __init__.py ├── __pycache__ ├── __init__.cpython-311.pyc ├── __init__.cpython-312.pyc ├── __init__.cpython-38.pyc ├── common.cpython-311.pyc ├── common.cpython-312.pyc └── common.cpython-38.pyc └── common.py /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # 基于编辑器的 HTTP 客户端请求 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/pictureChange.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 17 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 插件描述 2 | 3 | - 支持运用百度AI进行图像处理 4 | - 支持运用stable diffusion webui进行图像处理 5 | - 支持运用stable diffusion webui画图,不影响dall-3画图 6 | - 支持运用suno音乐组合AI进行图生音,文生音 7 | - 支持运用AI进行文件总结,图片总结等 8 | - 支持多种stable diffusion模型 9 | - 支持管理员控制群聊图像,文件,音乐,更改群聊模型等功能 10 | - 支持并发控制,管理员参数控制 11 | - 支持stable diffusion图生图,文生图自定义模板 12 | - 支持企业微信,个人号,公众号部署 13 | 14 | 15 | ### 1.使用前先安装stable diffusion webui,并在它的启动参数中添加 "--api",并开启**种子数**。 16 | 17 | * 安装具体信息,请参考[视频](https://www.bilibili.com/video/BV1iM4y1y7oA/?spm_id_from=333.337.search-card.all.click)。 18 | * 部署运行后,保证主机能够成功访问http://127.0.0.1:7860/ 19 | * 如果是服务器部署则不必需要内网穿透,如果是本地电脑部署推荐[cpolar](https://dashboard.cpolar.com/signup)启动内网穿透 20 | 21 | #### **如下图** 22 | 23 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/7c58fde5-832c-4e8d-b822-0117cd77814f) 24 | 25 | #### **请确保图片的文件名字有种子数进行命名** 26 | 27 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/5e310369-011d-430c-a85b-ca95946af799) 28 | 29 | ### 2.**确保图片位置填写正确** 30 | 31 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/7736b31e-c58c-47b0-8f44-0dc167d43027) 32 | 33 | ``` 34 | "file_url": "file=D:/sd/sd-webui-aki/sd-webui-aki-v4.2/sd-webui-aki-v4.2/outputs/, #这个地址是你图片生成的地址" 35 | ``` 36 | 37 | ### 3. 请确保**安装**本插件的依赖包```pip3 install -r requireents.txt``` 38 | 39 | ``` 40 | pip3 install -r requirements.txt 41 | ``` 42 | 43 | 44 | ```config.json 45 | # config.json文件内容示例 46 | { 47 | # 是否使用pictureChange插件功能 48 | "use_pictureChange": true, 49 | # 是否使用SD功能 50 | "use_stable_diffusion": false, 51 | # 是否开启Suno音乐功能 52 | "use_music_handle": true, 53 | # 是否开启文件识别功能 54 | "use_file_handle": true, 55 | # 是否开启群聊管理功能 56 | "use_group_handle": true, 57 | # Stable Diffusion 最大并发数 58 | "max_number": 3, 59 | # 触发suno音乐关键词 60 | "music_create_prefix": [ 61 | "唱", 62 | "帮我唱" 63 | ], 64 | # 触发SD画图关键词 65 | "image_create_prefix": [ 66 | "SD画", 67 | "SD帮我画" 68 | ], 69 | # 是否使用翻译提示词,否的话使用当前对话机器人进行翻译 70 | "is_use_fanyi": false, 71 | # 百度翻译API密钥 72 | "baidu_api_key": "0fqG8crG04FDU", 73 | # 百度翻译API密钥 74 | "baidu_secret_key": "RQXZgD9j7hZqgse", 75 | # 外部识别图片和文件或者suno音乐的API BASE URL 76 | "openai_api_base": "https://xxx/v1", 77 | # 外部识别图片和文件或者suno音乐的API密钥 78 | "openai_api_key": "sk-8toj6An", 79 | "music_model": "suno-v3.5", 80 | # 识别图片的模型 81 | "image_recognize_model": "gpt-4o", 82 | # 识别文件的模型 83 | "file_recognize_model": "gpt-4o", 84 | # 识别图片的提示词 85 | "image_recognize_prompt": "请帮我描述这个图片的内容。【重要】1.请不要用markdown语法输出 2.请仔细阅读之后把你描述的内容分析给我!", 86 | # 识别文件的提示词 87 | "file_recognize_prompt": "请帮我描述这个文件的内容,【重要】1.请不要用markdown语法输出 2.请仔细阅读之后把你描述的内容分析给我!", 88 | # 识别图片的提示词 89 | "image_music_prompt": "请帮我描述这个图片的内容,将这段内容按照语义分成 Title(题目),Song Description(歌曲描述),Type of Music(音乐类型)。并根据用户的意图生成对应语言的歌曲提示词。请直接了当地输出Title(题目),Song Description(歌曲描述),Type of Music(音乐类型),[重点]不要超过100字!!!", 90 | # 图生图最大尺寸 91 | "max_size": 1024, 92 | # SD启动参数 93 | "start": { 94 | "host": "127.0.0.1", 95 | "port": 7860, 96 | "use_https": false 97 | }, 98 | # 本地SD图片存放位置 99 | "file_url": "file=G:/sd/sd-webui-aki-v4.8/outputs/", 100 | # 默认参数 101 | "defaults": { 102 | "params": { 103 | "sampler_name": "Euler a", 104 | "steps": 20, 105 | "width": 1024, 106 | "height": 1024, 107 | "cfg_scale": 7, 108 | "prompt": "absurdres, 8k", 109 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 110 | "enable_hr": false, 111 | "hr_scale": 2, 112 | "hr_upscaler": "Latent", 113 | "hr_second_pass_steps": 15, 114 | "denoising_strength": 0.7 115 | }, 116 | "options": { 117 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 118 | } 119 | }, 120 | "rules": [ 121 | { 122 | "keywords": [ 123 | "横版", 124 | "壁纸" 125 | ], 126 | "params": { 127 | "width": 640, 128 | "height": 384 129 | }, 130 | "desc": "分辨率会变成640x384" 131 | }, 132 | { 133 | "keywords": [ 134 | "竖版" 135 | ], 136 | "params": { 137 | "width": 384, 138 | "height": 640 139 | }, 140 | "desc": "分辨率会变成384x640" 141 | }, 142 | { 143 | "keywords": [ 144 | "高清" 145 | ], 146 | "params": { 147 | "enable_hr": true, 148 | "hr_scale": 1.4 149 | }, 150 | "desc": "出图分辨率长宽都会提高1.4倍" 151 | }, 152 | { 153 | "keywords": [ 154 | "二次元" 155 | ], 156 | # 切换模型 157 | "options": { 158 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 159 | }, 160 | "desc": "使用二次元风格模型出图" 161 | }, 162 | { 163 | "keywords": [ 164 | "现实" 165 | ], 166 | # 切换模型 167 | "options": { 168 | "sd_model_checkpoint": "absolutereality_v181.safetensors [463d6a9fe8]" 169 | }, 170 | "desc": "使用现实风格模型出图" 171 | } 172 | ], 173 | # 可自定义图生图角色 174 | "roles": [ 175 | { 176 | "title": "👧 可爱女生", 177 | "enable": false, 178 | "prompt": "multiple girls,Masterpiece,best quality, ", 179 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 180 | "denoising_strength": 0.5, 181 | "cfg_scale": 7.0, 182 | "options": { 183 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 184 | } 185 | }, 186 | { 187 | "title": "🧑 帅气男神", 188 | "enable": true, 189 | "prompt": "multiple boys, male focus, topless male, messy hair, looking at viewer, outdoors, beautiful lighting, deep shadow, best quality, masterpiece, ultra highres, photorealistic, blurry background,", 190 | "negative_prompt": "cartoon, anime, sketches,(worst quality, low quality), (deformed, distorted, disfigured), (bad eyes, wrong lips, weird mouth, bad teeth, mutated hands and fingers:1.2), bad anatomy, wrong anatomy, amputation, extra limb, missing limb, floating limbs, disconnected limbs, mutation, ugly, disgusting, (bad_pictures, negative_hand-neg:1.2)", 191 | "denoising_strength": 0.45, 192 | "cfg_scale": 8.0, 193 | "options": { 194 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 195 | } 196 | } 197 | ] 198 | } 199 | 200 | ``` 201 | 202 | 203 | - groupService/config.json 204 | 205 | ```groupService/config.json 206 | { 207 | # 控制群聊的功能 208 | "AllFunction": { 209 | # 是否进行图片处理 210 | "IS_IMAGE": "True", 211 | # 是否进行文件处理 212 | "IS_FILE": "True", 213 | # 是否进行音乐处理 214 | "IS_MUSIC": "True", 215 | # 群聊可用模型列表 216 | "MODEL": [ 217 | "gpt-4o", 218 | "glm-4", 219 | "abab6.5-chat", 220 | "abab6.5s-chat" 221 | ] 222 | }, 223 | "group": [ 224 | { 225 | # 群聊名称 226 | "name": "500 Not Found", 227 | "function": { 228 | "IMAGE": "True", 229 | "FILE": "True", 230 | "MUSIC": "True", 231 | "MODEL": "abab6.5s-chat", 232 | "发送消息": "True" 233 | } 234 | } 235 | ] 236 | } 237 | ``` 238 | 239 | - adminService/config.json 240 | ```adminService/config.json 241 | { 242 | "admin_id": [], 243 | "admin_password": "xxx" 244 | } 245 | ``` 246 | 247 |
248 | 249 | 250 | 使用实例 251 | 252 | 253 | 254 | #### 图生图 255 | 256 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/4d6f16b0-136a-48d6-991e-482ce3bbc701) 257 | 258 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/783d6f22-c18e-4368-bc91-aa62a24c0c78) 259 | 260 | ![9d3c926daec3b69ef1f2e1b38e4ad7e](https://github.com/Yanyutin753/pictureChange/assets/132346501/cc5f8843-6a42-422c-bbeb-1c073ffa651b) 261 | 262 | #### 画图 263 | 264 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/428a2333-1589-42fd-88df-a1b182f8b3f6) 265 | 266 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/89782b7c-8f28-42af-8c16-a588539219a3) 267 | 268 | #### 支持放大 变换操作 269 | 270 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/1ab662b2-bd3c-490f-9981-404dcb1ca0e3) 271 | 272 | ![image](https://github.com/Yanyutin753/pictureChange/assets/132346501/32949ac2-93fc-4b7c-8387-f5cd7b1cb139) 273 | 274 | #### suno音乐 275 | 276 | - 1. 文生音 277 | ![ff731f85a945cac88772af6f2cad790](https://github.com/Yanyutin753/pictureChange/assets/132346501/dde5488a-d9b2-4c71-b971-28074a477db1) 278 | 279 | - 2. 图生音 280 | ![d2f549aec32642e0ff2dbc9ad4f8956](https://github.com/Yanyutin753/pictureChange/assets/132346501/264ede7d-e162-423b-b9d3-2cb145fd4cd5) 281 | 282 | #### 文件识别 283 | ![dd0b8389d12a753f37781259c0f6ea7](https://github.com/Yanyutin753/pictureChange/assets/132346501/d973d5ce-b228-4d98-8771-7ffddccaecb1) 284 | 285 | #### 图片识别 286 | ![cf7e611c93f674098a3752f4f720a93](https://github.com/Yanyutin753/pictureChange/assets/132346501/94c171e4-2a96-4553-be82-998b39a962a5) 287 | 288 | #### 群聊管理和config管理 289 | ![8f911097129cb2585526e75556f4182](https://github.com/Yanyutin753/pictureChange/assets/132346501/1597cef7-d72c-4850-b3da-5978e759a21a) 290 | 291 | 292 |
293 | 294 | ### 贡献与支持 295 | 296 | - 欢迎贡献代码,提出问题和建议。如果你发现了bug或者有新的功能想法,请提交一个Issue让我知道。你也可以通过Fork项目并提交Pull 297 | Request来贡献代码。 如果你想部署这个项目,给我一个星星⭐,这是对我最大的支持! 298 | - 敲代码不易,希望客官给点赞助,让我更好修改代码! 299 | ![image](https://github.com/Yanyutin753/wechat_pictureChange/assets/132346501/713eb69e-6e00-46ad-bec5-0b3926305ef0) 300 | 301 | 302 | 303 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .pictureChange import * 2 | -------------------------------------------------------------------------------- /__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /__pycache__/pictureChange.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/__pycache__/pictureChange.cpython-311.pyc -------------------------------------------------------------------------------- /__pycache__/pictureChange.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/__pycache__/pictureChange.cpython-312.pyc -------------------------------------------------------------------------------- /__pycache__/pictureChange.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/__pycache__/pictureChange.cpython-38.pyc -------------------------------------------------------------------------------- /adminService/README.md: -------------------------------------------------------------------------------- 1 | # 管理员小工具 2 | 通过自身存储的账号与密码,实现对环境变量的配置。 3 | 请将config.json.template修改为config.json后再进行使用。调用请初始化adminService类 4 | 将此文件夹放入项目根目录下,调用时请使用相对路径 5 | ## 自身配置: 6 | 7 | ```json 8 | { 9 | "admin_id": [], # 管理员id,可以传入不同的特殊id作为表示符 10 | "admin_password": "", # 管理员密码 11 | } 12 | ``` 13 | 14 | ## 使用方法: 15 | ```python 16 | from adminService.adminService import adminService 17 | 18 | ad = adminService() 19 | 20 | # 认证管理员 21 | ad.verify_admin(admin_id, admin_password) 22 | 23 | # 判断管理员 24 | ad.is_admin(admin_id) 25 | 26 | # 修改管理员密码 27 | ad.change_password(admin_id, new_password) 28 | 29 | # 清空管理员名单 30 | ad.clear_admin(admin_id) 31 | 32 | # 添加项目json值,可变参数(config[key1][key2][key3]=value), value需要使用value=格式 33 | ad.append_json(admin_id, key1, key2, key3, value) 34 | 35 | # 修改项目json值,同上 36 | ad.update_json(admin_id, key1, key2, key3, value) 37 | 38 | ``` 39 | -------------------------------------------------------------------------------- /adminService/__pycache__/admin_service.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/adminService/__pycache__/admin_service.cpython-311.pyc -------------------------------------------------------------------------------- /adminService/admin_service.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import random 4 | import string 5 | 6 | from common.log import logger 7 | from plugins import PluginManager 8 | 9 | 10 | class admin_service: 11 | def __init__(self): 12 | # 存储的管理员及激活码 13 | self.admin_id = [] 14 | self.admin_password = [] 15 | # 读取配置文件 16 | config_path = os.path.join(os.path.dirname(__file__), "config.json") 17 | with open(config_path, "r", encoding="utf-8") as f: 18 | config = json.load(f) 19 | self.admin_id = config["admin_id"] 20 | self.admin_password = config["admin_password"] 21 | if self.admin_password == "": 22 | # 生成随机密码 23 | self.admin_password = ''.join(random.sample(string.ascii_letters + string.digits, 8)) 24 | logger.info( 25 | f"[adminService] 读取配置文件成功! admin_id: {self.admin_id}, admin_password: {self.admin_password}") 26 | 27 | # 认证管理员,然后写入config中 28 | def verify_admin(self, user_id: str, admin_password: str) -> bool: 29 | if admin_password != self.admin_password: 30 | return False 31 | config_path = os.path.join(os.path.dirname(__file__), "config.json") 32 | self.admin_id.append(user_id) 33 | with open(config_path, "r", encoding="utf-8") as f: 34 | config = json.load(f) 35 | config["admin_id"].append(user_id) 36 | with open(config_path, "w", encoding="utf-8") as f: 37 | json.dump(config, f, ensure_ascii=False, indent=4) 38 | return True 39 | 40 | def is_admin(self, user_id: str) -> bool: 41 | return user_id in self.admin_id 42 | 43 | # 修改管理员密码 44 | def update_password(self, user_id: str, admin_password: str) -> bool: 45 | if not self.is_admin(user_id): 46 | logger.info("False!") 47 | return False 48 | self.admin_password = admin_password 49 | # 保存配置文件, 修改配置文件为新密码 50 | config_path = os.path.join(os.path.dirname(__file__), "config.json") 51 | with open(config_path, "r", encoding="utf-8") as f: 52 | config = json.load(f) 53 | config["admin_password"] = admin_password 54 | logger.info(admin_password) 55 | with open(config_path, "w", encoding="utf-8") as f: 56 | json.dump(config, f, ensure_ascii=False, indent=4) 57 | logger.info(f"[adminService] 修改管理员密码成功! admin_password: {self.admin_password}") 58 | return True 59 | 60 | # 清空现有的管理员名单 61 | def clear_admin(self, user_id: str) -> bool: 62 | if not self.is_admin(user_id): 63 | return False 64 | config_path = os.path.join(os.path.dirname(__file__), "config.json") 65 | with open(config_path, "r", encoding="utf-8") as f: 66 | config = json.load(f) 67 | config["admin_id"] = [] 68 | config["admin_id"].append(user_id) 69 | with open(config_path, "w", encoding="utf-8") as f: 70 | json.dump(config, f, ensure_ascii=False, indent=4) 71 | logger.info(f"[adminService] 清空管理员成功!") 72 | return True 73 | 74 | # 修改插件目录下的json文件,不定参数代表传递几层,如config["start"]["host"] 75 | def update_json(self, user_id: str, target: str, *args, value: str) -> bool: 76 | if not self.is_admin(user_id): 77 | return False 78 | 79 | # 设定文件路径 80 | config_path = os.path.join(os.path.dirname(__file__), "../config.json") 81 | try: 82 | with open(config_path, "r", encoding="utf-8") as f: 83 | config = json.load(f) 84 | 85 | # 使用递归函数设置值 86 | def set_nested_value(d, keys, key_value): 87 | 88 | if len(keys) == 1: 89 | d[keys[0]] = key_value 90 | else: 91 | if keys[0] not in d: 92 | d[keys[0]] = {} 93 | set_nested_value(d[keys[0]], keys[1:], key_value) 94 | 95 | if not isinstance(value, bool): 96 | if value.isdigit(): 97 | value = int(value) 98 | 99 | # 调用递归函数 100 | set_nested_value(config, [target] + list(args), value) 101 | 102 | with open(config_path, "w", encoding="utf-8") as f: 103 | json.dump(config, f, ensure_ascii=False, indent=4) 104 | 105 | PluginManager().reload_plugin("pictureChange") 106 | logger.info(f"[adminService] 修改json成功! target: {target}, value: {value}") 107 | return True 108 | except Exception as e: 109 | logger.info(f"[adminService] 修改json失败! target: {target}, value: {value}, error: {str(e)}") 110 | return False 111 | 112 | # 添加元素 113 | def append_json(self, user_id: str, target: str, *args, value: str) -> bool: 114 | if not self.is_admin(user_id): 115 | return False 116 | 117 | # 设定文件路径 118 | config_path = os.path.join(os.path.dirname(__file__), "../config.json") 119 | try: 120 | with open(config_path, "r", encoding="utf-8") as f: 121 | config = json.load(f) 122 | 123 | # 使用递归函数设置值 124 | def set_nested_value(d, keys, key_value): 125 | if len(keys) == 1: 126 | d[keys[0]].append(key_value) 127 | else: 128 | if keys[0] not in d: 129 | d[keys[0]] = [] 130 | set_nested_value(d[keys[0]], keys[1:], key_value) 131 | 132 | # 调用递归函数 133 | set_nested_value(config, [target] + list(args), value) 134 | 135 | with open(config_path, "w", encoding="utf-8") as f: 136 | json.dump(config, f, ensure_ascii=False, indent=4) 137 | 138 | logger.info(f"[adminService] 修改json成功! target: {target}, value: {value}") 139 | return True 140 | except Exception as e: 141 | logger.info(f"[adminService] 修改json失败! target: {target}, value: {value}, error: {str(e)}") 142 | return False 143 | -------------------------------------------------------------------------------- /adminService/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "admin_id": [ 3 | "@19750fec00553c8d0ad6383a26c643ca5d68a52eebb26ba90f2a8af4975faf72" 4 | ], 5 | "admin_password": "20031028" 6 | } -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "use_pictureChange": true, 3 | "use_stable_diffusion": true, 4 | "use_music_handle": true, 5 | "use_file_handle": true, 6 | "use_group_handle": true, 7 | "max_number": 3, 8 | "music_create_prefix": [ 9 | "唱", 10 | "帮我唱" 11 | ], 12 | "image_create_prefix": [ 13 | "SD画", 14 | "SD帮我画" 15 | ], 16 | "is_use_fanyi": false, 17 | "file_url": "file=G:/sd/sd-webui-aki-v4.8/outputs/", 18 | "baidu_api_key": "0fqG8crG04FDU", 19 | "baidu_secret_key": "RQXZgD9j7hZqgse", 20 | "openai_api_base": "https://oneapi.clivia.fun/v1", 21 | "openai_api_key": "sk-8toj6AnHN1ASCAzs65FdE8D792E749", 22 | "music_model": "suno-v3.5", 23 | "image_recognize_model": "gpt-4o", 24 | "file_recognize_model": "gpt-4o", 25 | "image_recognize_prompt": "请帮我描述这个图片的内容。【重要】1.请不要用markdown语法输出 2.请仔细阅读之后把你描述的内容分析给我!", 26 | "file_recognize_prompt": "请帮我描述这个文件的内容,【重要】1.请不要用markdown语法输出 2.请仔细阅读之后把你描述的内容分析给我!", 27 | "image_music_prompt": "请帮我描述这个图片的内容,将这段内容按照语义分成 Title(题目),Song Description(歌曲描述),Type of Music(音乐类型)。并根据用户的意图生成对应语言的歌曲提示词。请直接了当地输出Title(题目),Song Description(歌曲描述),Type of Music(音乐类型),[重点]不要超过100字!!!", 28 | "use_group": [], 29 | "max_size": 1024, 30 | "start": { 31 | "host": "127.0.0.1", 32 | "port": 7860, 33 | "use_https": false 34 | }, 35 | "defaults": { 36 | "params": { 37 | "sampler_name": "Euler a", 38 | "steps": 20, 39 | "width": 1024, 40 | "height": 1024, 41 | "cfg_scale": 7, 42 | "prompt": "absurdres, 8k", 43 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 44 | "enable_hr": false, 45 | "hr_scale": 2, 46 | "hr_upscaler": "Latent", 47 | "hr_second_pass_steps": 15, 48 | "denoising_strength": 0.7 49 | }, 50 | "options": { 51 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 52 | } 53 | }, 54 | "rules": [ 55 | { 56 | "keywords": [ 57 | "横版", 58 | "壁纸" 59 | ], 60 | "params": { 61 | "width": 640, 62 | "height": 384 63 | }, 64 | "desc": "分辨率会变成640x384" 65 | }, 66 | { 67 | "keywords": [ 68 | "竖版" 69 | ], 70 | "params": { 71 | "width": 384, 72 | "height": 640 73 | }, 74 | "desc": "分辨率会变成384x640" 75 | }, 76 | { 77 | "keywords": [ 78 | "机甲少女" 79 | ], 80 | "params": { 81 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 82 | "prompt": "masterpiece, best quality," 83 | }, 84 | "desc": "输出机甲少女风格" 85 | }, 86 | { 87 | "keywords": [ 88 | "港风" 89 | ], 90 | "params": { 91 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 92 | "prompt": "masterpiece, best quality," 93 | }, 94 | "desc": "输出港风风格" 95 | }, 96 | { 97 | "keywords": [ 98 | "国风" 99 | ], 100 | "params": { 101 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 102 | "prompt": "masterpiece, best quality, " 103 | }, 104 | "desc": "输出国风风格" 105 | }, 106 | { 107 | "keywords": [ 108 | "高清" 109 | ], 110 | "params": { 111 | "enable_hr": true, 112 | "hr_scale": 1.4 113 | }, 114 | "desc": "出图分辨率长宽都会提高1.4倍" 115 | }, 116 | { 117 | "keywords": [ 118 | "二次元" 119 | ], 120 | "params": { 121 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 122 | "prompt": "masterpiece, best quality" 123 | }, 124 | "options": { 125 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 126 | }, 127 | "desc": "使用二次元风格模型出图" 128 | }, 129 | { 130 | "keywords": [ 131 | "现实" 132 | ], 133 | "params": { 134 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 135 | "prompt": "masterpiece, best quality" 136 | }, 137 | "options": { 138 | "sd_model_checkpoint": "absolutereality_v181.safetensors [463d6a9fe8]" 139 | }, 140 | "desc": "使用现实风格模型出图" 141 | }, 142 | { 143 | "keywords": [ 144 | "Q版" 145 | ], 146 | "params": { 147 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 148 | "prompt": "masterpiece, best quality" 149 | }, 150 | "options": { 151 | "sd_model_checkpoint": "QteaMix-fp16.safetensors [0c1efcbbd6]" 152 | }, 153 | "desc": "使用Q版风格模型出图" 154 | } 155 | ], 156 | "roles": [ 157 | { 158 | "title": "🌈 温柔女孩(强推)", 159 | "enable": false, 160 | "prompt": "multiple girls,masterpiece, best quality, ", 161 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 162 | "denoising_strength": 0.4, 163 | "cfg_scale": 7.0, 164 | "options": { 165 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 166 | } 167 | }, 168 | { 169 | "title": "👧 可爱女生", 170 | "enable": false, 171 | "prompt": "multiple girls,Masterpiece,best quality, ", 172 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 173 | "denoising_strength": 0.5, 174 | "cfg_scale": 7.0, 175 | "options": { 176 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 177 | } 178 | }, 179 | { 180 | "title": "🧑 帅气男神", 181 | "enable": true, 182 | "prompt": "multiple boys, male focus, topless male, messy hair, looking at viewer, outdoors, beautiful lighting, deep shadow, best quality, masterpiece, ultra highres, photorealistic, blurry background,", 183 | "negative_prompt": "cartoon, anime, sketches,(worst quality, low quality), (deformed, distorted, disfigured), (bad eyes, wrong lips, weird mouth, bad teeth, mutated hands and fingers:1.2), bad anatomy, wrong anatomy, amputation, extra limb, missing limb, floating limbs, disconnected limbs, mutation, ugly, disgusting, (bad_pictures, negative_hand-neg:1.2)", 184 | "denoising_strength": 0.45, 185 | "cfg_scale": 8.0, 186 | "options": { 187 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 188 | } 189 | }, 190 | { 191 | "title": "💑 动漫情侣(推荐)", 192 | "enable": false, 193 | "prompt": "couple,masterpiece, best quality, ", 194 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 195 | "denoising_strength": 0.4, 196 | "cfg_scale": 7.0, 197 | "options": { 198 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 199 | } 200 | }, 201 | { 202 | "title": "🦄 Q版可爱", 203 | "enable": false, 204 | "prompt": "masterpiece, best quality, ", 205 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 206 | "denoising_strength": 0.45, 207 | "cfg_scale": 7.0, 208 | "options": { 209 | "sd_model_checkpoint": "QteaMix-fp16.safetensors [0c1efcbbd6]" 210 | } 211 | }, 212 | { 213 | "title": "💖 Q版情侣", 214 | "enable": false, 215 | "prompt": "couple,masterpiece, best quality ", 216 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 217 | "denoising_strength": 0.4, 218 | "cfg_scale": 7.0, 219 | "options": { 220 | "sd_model_checkpoint": "QteaMix-fp16.safetensors [0c1efcbbd6]" 221 | } 222 | }, 223 | { 224 | "title": "🏎 机甲女孩", 225 | "enable": false, 226 | "prompt": "multiple girls,,masterpiece, best quality,girls", 227 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 228 | "denoising_strength": 0.5, 229 | "cfg_scale": 8.0, 230 | "options": { 231 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 232 | } 233 | }, 234 | { 235 | "title": "🌸 美丽女孩", 236 | "enable": false, 237 | "prompt": "multiple girls,,masterpiece, best quality", 238 | "negative_prompt": "(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), (low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2),facing away, looking away,tilted head, lowres,bad anatomy,bad hands, missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands,poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms,extra legs,malformed limbs,fused fingers,too many fingers,long neck,cross-eyed,mutated hands,polar lowres,bad body,bad proportions,gross proportions,missing arms,missing legs,extra digit, extra arms, extra leg, extra foot,teethcroppe,signature, watermark, username,blurry,cropped,jpeg artifacts,text,error,Lower body exposure", 239 | "denoising_strength": 0.45, 240 | "cfg_scale": 8.0, 241 | "options": { 242 | "sd_model_checkpoint": "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 243 | } 244 | } 245 | ] 246 | } -------------------------------------------------------------------------------- /groupService/__pycache__/change_config.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/groupService/__pycache__/change_config.cpython-311.pyc -------------------------------------------------------------------------------- /groupService/__pycache__/config_cache.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/groupService/__pycache__/config_cache.cpython-311.pyc -------------------------------------------------------------------------------- /groupService/__pycache__/find_group.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/groupService/__pycache__/find_group.cpython-311.pyc -------------------------------------------------------------------------------- /groupService/__pycache__/find_group.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/groupService/__pycache__/find_group.cpython-312.pyc -------------------------------------------------------------------------------- /groupService/__pycache__/find_group.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/groupService/__pycache__/find_group.cpython-38.pyc -------------------------------------------------------------------------------- /groupService/change_config.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | import threading 5 | 6 | from common.log import logger 7 | from plugins.pictureChange.groupService.config_cache import group_cache 8 | 9 | """ 10 | 11 | """ 12 | 13 | 14 | class group_config: 15 | def __init__(self): 16 | self.config = None 17 | self.config_file = os.path.join(os.path.dirname(__file__), "config.json") 18 | self.mode = None 19 | self.load_config() 20 | self.lock = threading.Lock() 21 | 22 | def load_config(self): 23 | try: 24 | with open(self.config_file, 'r', encoding='utf-8') as file: 25 | self.config = json.load(file) 26 | except FileNotFoundError: 27 | self.config = { 28 | "AllFunction": { 29 | "IS_IMAGE": True, 30 | "IS_FILE": True, 31 | "IS_MUSIC": True, 32 | "MODEL": ["GPT3-5", "GLM-4"] 33 | }, 34 | "group": [] 35 | } 36 | self.save_config() 37 | 38 | def save_config(self): 39 | with open(self.config_file, 'w', encoding='utf-8') as file: 40 | json.dump(self.config, file, ensure_ascii=False, indent=4) 41 | self.load_config() 42 | 43 | def ins_command(self, ins): 44 | with self.lock: # 使用线程锁确保同一时间只有一个线程访问 45 | if ins == "#help": 46 | self.mode = "help" 47 | return ("修改总功能,请使用#change [funct] to [state]来修改总功能\n" 48 | "修改已有群聊功能模式,请使用#change [funct] in [group_name] to [state]来修改群聊功能状态\n新增群聊模式,请使用#add [" 49 | "group_name]来新增群聊" 50 | "\n删除群聊模式,请使用#del [group_name]来删除群聊\n" 51 | "修改群聊模型模式,请使用#modify [group_name] to [model]来修改模型") 52 | if ins[:7] == "#change": 53 | self.mode = "modify_group_function" 54 | reply = self.change_command(ins) 55 | elif ins[:13] == "#change Model": 56 | self.mode = "modify_group_model" 57 | reply = self.change_command(ins) 58 | elif ins[:4] == "#add": 59 | self.mode = "add_group" 60 | reply = self.add_command(ins) 61 | elif ins[:4] == "#del": 62 | self.mode = "del_group" 63 | reply = self.del_command(ins) 64 | elif ins[:7] == "#modify": 65 | self.mode = "modify_group_model" 66 | reply = self.change_command(ins) 67 | else: 68 | self.mode = None 69 | reply = f"未知指令 {ins}" 70 | group_cache.load_from_json() 71 | logger.info("已将更新数据放入缓存") 72 | return reply 73 | 74 | def change_command(self, command): 75 | def normalize_state(value_state): 76 | return value_state.capitalize() 77 | 78 | change_pattern = re.compile(r'^#modify (\w+) to (\w+)$') 79 | group_change_pattern = re.compile(r'^#change (\w+) in (.+) to (\w+)$') 80 | model_change_pattern = re.compile(r'#modify (.+?) to (.+)') 81 | if self.mode == "all_function": 82 | change_match = change_pattern.match(command) 83 | if change_match: 84 | funct, state = change_match.groups() 85 | funct = funct.upper() 86 | state = normalize_state(state) 87 | # 将状态字符串转换为布尔类型 88 | if state.lower() == "true": 89 | state = True 90 | elif state.lower() == "false": 91 | state = False 92 | if funct in (key.upper() for key in self.config["AllFunction"]): 93 | self.config["AllFunction"][funct] = state 94 | self.save_config() 95 | return f"功能 {funct} 已修改为 {state}" 96 | else: 97 | return f"功能 {funct} 不存在于 AllFunction 中" 98 | else: 99 | return f"未知指令 {command}" 100 | 101 | elif self.mode == "modify_group_function": 102 | group_change_match = group_change_pattern.match(command) 103 | if group_change_match: 104 | funct, group_name, state = group_change_match.groups() 105 | funct = funct.upper() 106 | # 假设 normalize_state 已经处理好状态字符串 107 | state = normalize_state(state) 108 | # 将状态字符串转换为布尔类型 109 | if state.lower() == "true": 110 | state = True 111 | elif state.lower() == "false": 112 | state = False 113 | if funct == "MODEL": 114 | return "您没有对应权限" 115 | group_found = False 116 | for group in self.config["group"]: 117 | if group["name"] == group_name: 118 | if funct in (key.upper() for key in group["function"]): 119 | group["function"][funct] = state 120 | self.save_config() 121 | return f"群聊 {group_name} 的功能 {funct} 已修改为 {state}" 122 | else: 123 | return f"功能 {funct} 不存在于群聊 {group_name} 中" 124 | if not group_found: 125 | return f"没有找到对应群聊 {group_name}" 126 | else: 127 | return f"未知指令 {command}" 128 | 129 | elif self.mode == "modify_group_model": 130 | model_change_match = model_change_pattern.match(command) 131 | if model_change_match: 132 | group_name, model = model_change_match.groups() 133 | group_found = False 134 | for group in self.config["group"]: 135 | if group["name"] == group_name: 136 | if model in (m for m in self.config["AllFunction"]["MODEL"]): 137 | group["function"]["MODEL"] = model 138 | self.save_config() 139 | return f"群聊 {group_name} 的模型已修改为 {model}" 140 | else: 141 | return f"模型 {model} 不存在于可用模型列表中" 142 | if not group_found: 143 | return f"没有找到对应群聊 {group_name}" 144 | else: 145 | return f"未知指令 {command}" 146 | else: 147 | return f"当前模式不支持此指令 {command}" 148 | 149 | def add_command(self, command): 150 | add_pattern = re.compile(r'^#add (.+)$') 151 | add_match = add_pattern.match(command) 152 | if add_match: 153 | group_name = add_match.group(1) 154 | for group in self.config["group"]: 155 | if group["name"] == group_name: 156 | return f"群聊名 {group_name} 已经被使用" 157 | new_group = { 158 | "name": group_name, 159 | "function": { 160 | "IMAGE": self.config["AllFunction"]["IS_IMAGE"], 161 | "FILE": self.config["AllFunction"]["IS_FILE"], 162 | "MUSIC": self.config["AllFunction"]["IS_MUSIC"], 163 | "MODEL": self.config["AllFunction"]["MODEL"][0], 164 | "ENABLE": True, 165 | } 166 | } 167 | self.config["group"].append(new_group) 168 | self.save_config() 169 | return f"群聊 {group_name} 已成功新增" 170 | else: 171 | return f"未知指令 {command}" 172 | 173 | def del_command(self, command): 174 | del_pattern = re.compile(r'^#del (.+)$') 175 | del_match = del_pattern.match(command) 176 | if del_match: 177 | group_name = del_match.group(1) 178 | group_found = False 179 | for group in self.config["group"]: 180 | if group["name"] == group_name: 181 | self.config["group"].remove(group) 182 | self.save_config() 183 | return f"群聊 {group_name} 已成功删除" 184 | if not group_found: 185 | return f"没有找到对应群聊 {group_name}" 186 | else: 187 | return f"未知指令 {command}" 188 | 189 | # # 测试代码 190 | # manager = GroupConfig() 191 | # print(manager.config["group"]) 192 | # print(manager.ins_command("#modify 500 Not Found to abab6.5s-chat")) 193 | -------------------------------------------------------------------------------- /groupService/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "AllFunction": { 3 | "IS_IMAGE": true, 4 | "IS_FILE": true, 5 | "IS_MUSIC": true, 6 | "MODEL": [ 7 | "gpt-4o", 8 | "glm-4", 9 | "abab6.5-chat", 10 | "abab6.5s-chat" 11 | ] 12 | }, 13 | "group": [ 14 | { 15 | "name": "小羊一家🏡", 16 | "function": { 17 | "IMAGE": true, 18 | "FILE": true, 19 | "MUSIC": true, 20 | "MODEL": "abab6.5s-chat", 21 | "ENABLE": true 22 | } 23 | }, 24 | { 25 | "name": "小羊一家", 26 | "function": { 27 | "IMAGE": true, 28 | "FILE": true, 29 | "MUSIC": true, 30 | "MODEL": "gpt-4o", 31 | "ENABLE": true 32 | } 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /groupService/config_cache.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | 5 | class group_cache: 6 | _cached_data = None 7 | 8 | @staticmethod 9 | def load_from_json(): 10 | # 从JSON文件加载数据到静态缓存 11 | cursor = os.path.dirname(__file__) 12 | config_path = os.path.join(cursor, "config.json") 13 | if os.path.exists(config_path): 14 | with open(config_path, "r", encoding="utf-8") as f: 15 | group_cache._cached_data = json.load(f) 16 | else: 17 | raise FileNotFoundError(f"配置文件 {config_path} 未找到。") 18 | 19 | @staticmethod 20 | def get_cached_data(): 21 | # 获取缓存数据 22 | if group_cache._cached_data: 23 | return group_cache._cached_data 24 | else: 25 | group_cache.load_from_json() 26 | return group_cache._cached_data 27 | 28 | @staticmethod 29 | def refresh_cache(): 30 | # 强制刷新缓存数据 31 | group_cache.load_from_json() 32 | -------------------------------------------------------------------------------- /groupService/find_group.py: -------------------------------------------------------------------------------- 1 | from common.log import logger 2 | from plugins.pictureChange.groupService.config_cache import group_cache 3 | 4 | 5 | class find_group: 6 | @staticmethod 7 | def find_group(group_name): 8 | all_conf = group_cache().get_cached_data() # 从缓存获取内容 9 | # 获取群聊名称,要放到插件,将上面改成def find_group(self, e_context: EventContext),去掉下行注释 10 | # group_name = e_context["msg"].other_user_nickname 11 | if all_conf is None: 12 | logger.info("缓存内容不存在") 13 | 14 | if all_conf['group']: 15 | for group in all_conf["group"]: 16 | if group_name == group["name"]: 17 | funct_dic = group["function"] 18 | funct_values = list(funct_dic.values()) 19 | funct_keys = list(funct_dic.keys()) 20 | values = list(all_conf["AllFunction"].values()) 21 | keys = list(all_conf["AllFunction"].keys()) 22 | ans_dic = {} 23 | for i in range(len(keys)): 24 | if i == len(keys) - 1: # 保证最后一个列表项为model即可 25 | if funct_values[i] in values[i]: 26 | ans_dic[funct_keys[i]] = funct_values[i] 27 | continue 28 | if values[i] and funct_values[i]: 29 | ans_dic[funct_keys[i]] = True 30 | else: 31 | ans_dic[funct_keys[i]] = False 32 | 33 | if funct_dic["ENABLE"]: 34 | ans_dic["ENABLE"] = True 35 | else: 36 | ans_dic["ENABLE"] = False 37 | return ans_dic 38 | else: 39 | return None 40 | else: 41 | return None 42 | 43 | # print(find_group.find_group("小羊一家")) 44 | -------------------------------------------------------------------------------- /message/__pycache__/message_handle.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_handle.cpython-311.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_handle.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_handle.cpython-312.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_handle.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_handle.cpython-38.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_limit.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_limit.cpython-311.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_limit.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_limit.cpython-312.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_limit.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_limit.cpython-38.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_reply.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_reply.cpython-311.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_reply.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_reply.cpython-312.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_reply.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_reply.cpython-38.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_sd_reply.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_sd_reply.cpython-311.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_sd_reply.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_sd_reply.cpython-312.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_sd_reply.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_sd_reply.cpython-38.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_type.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_type.cpython-311.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_type.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_type.cpython-312.pyc -------------------------------------------------------------------------------- /message/__pycache__/message_type.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/message_type.cpython-38.pyc -------------------------------------------------------------------------------- /message/__pycache__/music_handle.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/music_handle.cpython-311.pyc -------------------------------------------------------------------------------- /message/__pycache__/music_handle.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/music_handle.cpython-312.pyc -------------------------------------------------------------------------------- /message/__pycache__/music_handle.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/music_handle.cpython-38.pyc -------------------------------------------------------------------------------- /message/__pycache__/request_handle.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/request_handle.cpython-311.pyc -------------------------------------------------------------------------------- /message/__pycache__/request_handle.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/request_handle.cpython-312.pyc -------------------------------------------------------------------------------- /message/__pycache__/request_handle.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/__pycache__/request_handle.cpython-38.pyc -------------------------------------------------------------------------------- /message/message_handle.py: -------------------------------------------------------------------------------- 1 | # 用于初始化消息 2 | from channel.chat_message import ChatMessage 3 | 4 | 5 | # 初始化消息 6 | def init_content(e_context): 7 | context = e_context['context'] 8 | msg: ChatMessage = context["msg"] 9 | msg.prepare() 10 | return context.content.strip() 11 | -------------------------------------------------------------------------------- /message/message_limit.py: -------------------------------------------------------------------------------- 1 | from plugins.pictureChange.message import message_reply as MessageReply 2 | 3 | 4 | class MessageLimit: 5 | use_number = 0 6 | wait_number = 0 7 | 8 | def __init__(self): 9 | pass 10 | 11 | @classmethod 12 | def isLimit(cls, max_number: int, e_context): 13 | if cls.use_number + 1 > max_number: 14 | cls.wait_number += 1 15 | replyText = f"🧸当前排队人数为 {str(cls.wait_number)}\n🚀 请耐心等待一至两分钟,再发送 '一张图片',让我为您进行图片操作" 16 | MessageReply.reply_Text_Message(True, replyText, e_context) 17 | return True 18 | return False 19 | 20 | @classmethod 21 | def success(cls, max_number: int): 22 | if cls.use_number > 0: 23 | cls.use_number -= 1 24 | if max_number > cls.use_number: 25 | cls.wait_number = 0 26 | if cls.wait_number > 0: 27 | cls.wait_number -= 1 28 | 29 | @classmethod 30 | def using(cls): 31 | cls.use_number += 1 32 | -------------------------------------------------------------------------------- /message/message_reply.py: -------------------------------------------------------------------------------- 1 | from bridge.reply import ReplyType, Reply 2 | from common.log import logger 3 | from plugins import EventAction 4 | 5 | 6 | # 根据reply_type,reply_content发送消息类型 7 | # 根据is_break决定是否结束会话 8 | def reply_message(is_break, reply_type, reply_content, e_context): 9 | reply = Reply() 10 | reply.type = reply_type 11 | reply.content = reply_content 12 | e_context["reply"] = reply 13 | e_context.action = EventAction.BREAK_PASS if is_break else EventAction.CONTINUE 14 | return e_context 15 | 16 | 17 | # 根据reply_content发送文本消息 18 | # 根据is_break决定是否结束会话 19 | def reply_Text_Message(is_break, reply_content, e_context): 20 | reply_message(is_break, ReplyType.TEXT, reply_content, e_context) 21 | 22 | 23 | # 根据reply_content发送错误文本消息 24 | # 根据is_break决定是否结束会话 25 | def reply_Error_Message(is_break, reply_content, e_context): 26 | logger.error(reply_content) 27 | reply_message(is_break, ReplyType.ERROR, reply_content, e_context) 28 | 29 | 30 | # 根据reply_content(image_url)获取图片,发送图片消息 31 | # 根据is_break决定是否结束会话 32 | def reply_Image_Url_Message(is_break, reply_content, e_context): 33 | reply_message(is_break, ReplyType.IMAGE_URL, reply_content, e_context) 34 | 35 | 36 | # 根据reply_content(图片文件二进制)发送音频消息 37 | # 根据is_break决定是否结束会话 38 | def reply_Image_Message(is_break, reply_content, e_context): 39 | reply_message(is_break, ReplyType.IMAGE, reply_content, e_context) 40 | 41 | 42 | # 根据reply_content(音频文件二进制)发送音频消息 43 | # 根据is_break决定是否结束会话 44 | def reply_Video_Message(is_break, reply_content, e_context): 45 | reply_message(is_break, ReplyType.VIDEO, reply_content, e_context) 46 | 47 | 48 | # 根据reply_content(video_url)获取音频,发送音频消息 49 | # 根据is_break决定是否结束会话 50 | def reply_Video_Url_Message(is_break, reply_content, e_context): 51 | reply_message(is_break, ReplyType.VIDEO_URL, reply_content, e_context) 52 | 53 | 54 | # 立即发送文本消息(reply_content),不结束会话 55 | def tem_reply_Text_Message(reply_content, e_context): 56 | reply = Reply(ReplyType.TEXT, reply_content) 57 | e_context['channel'].send(reply, e_context["context"]) 58 | 59 | 60 | # 立即发送图片消息(reply_content),不结束会话 61 | def tem_reply_Image_Message(reply_content, e_context): 62 | reply = Reply(ReplyType.IMAGE, reply_content) 63 | e_context['channel'].send(reply, e_context["context"]) 64 | 65 | 66 | # 立即发送图片消息(reply_content),不结束会话 67 | def tem_reply_Image_Url_Message(reply_content, e_context): 68 | reply = Reply(ReplyType.IMAGE_URL, reply_content) 69 | e_context['channel'].send(reply, e_context["context"]) 70 | 71 | 72 | # 立即发送音频消息(reply_content),不结束会话 73 | def tem_reply_Video_Message(reply_content, e_context): 74 | try: 75 | reply = Reply(ReplyType.FILE, reply_content) 76 | e_context['channel'].send(reply, e_context["context"]) 77 | except Exception as e: 78 | logger.error(f"Error sending video: {e}") 79 | 80 | 81 | # 立即发送音频消息(reply_content),不结束会话 82 | def tem_reply_Video_Url_Message(reply_content, e_context): 83 | reply = Reply(ReplyType.VIDEO_URL, reply_content) 84 | e_context['channel'].send(reply, e_context["context"]) 85 | -------------------------------------------------------------------------------- /message/message_sd_reply.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import time 4 | 5 | import requests 6 | import webuiapi 7 | from PIL import Image 8 | 9 | from common.log import logger 10 | from plugins.pictureChange.message import message_reply as message_reply, message_type 11 | from plugins.pictureChange.util import translate_prompt, image_handle, file_handle 12 | 13 | 14 | # 用于回复图片消息 15 | def reply_image(result, start_time, file_content, request_bot_name, modelName, image_type, is_wecom, e_context): 16 | # 发送图片 17 | b_img = io.BytesIO() 18 | result.image.save(b_img, format="PNG") 19 | message_reply.tem_reply_Image_Message(b_img, e_context) 20 | all_seeds = result.info['all_seeds'] 21 | imageMessage_reply(all_seeds, start_time, request_bot_name, modelName, image_type, is_wecom, e_context) 22 | file_handle.delete_file(file_content) 23 | 24 | 25 | # 用于图片信息文本回复 26 | def imageMessage_reply(all_seeds, start_time, request_bot_name, modelName, image_type, is_wecom, e_context): 27 | end_time = time.time() 28 | elapsed_time = end_time - start_time 29 | minutes = int(elapsed_time // 60) 30 | seconds = int(elapsed_time % 60) 31 | 32 | replyText = message_type.on_image_reply(request_bot_name, image_type, all_seeds, 33 | modelName, minutes, seconds, is_wecom) 34 | message_reply.reply_Text_Message(True, replyText, e_context) 35 | 36 | 37 | # 用于stable_diffusion创建图片 38 | def create_Image(content, is_use_fanyi, bot_prompt, rules, Model, 39 | request_bot_name, start_args, params, options, 40 | session_id, is_wecom, e_context): 41 | try: 42 | start_time = time.time() 43 | if ":" in content: 44 | keywords, prompt = content.split(":", 1) 45 | else: 46 | keywords = content 47 | prompt = "" 48 | keywords = keywords.split() 49 | unused_keywords = [] 50 | 51 | # Match keywords to rules 52 | for keyword in keywords: 53 | for rule in rules: 54 | if keyword in rule["keywords"]: 55 | logger.info("[SD] keyword matched: %s" % keyword) 56 | params.update(rule["params"]) 57 | if "options" in rule: 58 | options.update(rule["options"]) 59 | break 60 | else: 61 | unused_keywords.append(keyword) 62 | logger.info("[SD] keyword not matched: %s" % keyword) 63 | 64 | params["prompt"] = params.get("prompt", "") 65 | 66 | if unused_keywords: 67 | if prompt: 68 | prompt += f", {', '.join(unused_keywords)}" 69 | else: 70 | prompt = ', '.join(unused_keywords) 71 | translate_prompt.translatePrompt(is_use_fanyi, bot_prompt, prompt, params, session_id) 72 | api = webuiapi.WebUIApi(**start_args) 73 | if options: 74 | logger.info("[SD] cover options={}".format(options)) 75 | api.set_options(options) 76 | logger.info("[SD] params={}".format(params)) 77 | 78 | result = api.txt2img( 79 | batch_size=4, 80 | n_iter=1, 81 | do_not_save_samples=True, 82 | do_not_save_grid=True, 83 | save_images=True, 84 | **params 85 | ) 86 | # Process model name 87 | model = options["sd_model_checkpoint"] 88 | modelName = next((member.name for member in Model if model == member.value), model) 89 | logger.info(f"SD使用了其他模型:{modelName}") 90 | 91 | # Send image and additional instructions 92 | b_img = io.BytesIO() 93 | result.image.save(b_img, format="PNG") 94 | message_reply.tem_reply_Image_Message(b_img, e_context) 95 | all_seeds = result.info['all_seeds'] 96 | imageMessage_reply(all_seeds, start_time, request_bot_name, modelName, "txt2img-images", is_wecom, e_context) 97 | 98 | except Exception as e: 99 | logger.error(f"【SD】创作图片时出现错误:{e}") 100 | replyText = "hum......,请联系管理员或者稍后再试吧!" 101 | message_reply.reply_Error_Message(True, replyText, e_context) 102 | return 103 | 104 | 105 | # 用于stable_diffusion自定义图生图 106 | def custom_Image(content, is_use_fanyi, bot_prompt, Model, request_bot_name, start_args, session_id, 107 | negative_prompt, maxsize: int, is_wecom, e_context): 108 | start_time = time.time() 109 | start_index = content.find("tmp/") 110 | end_index = content.find(".png") 111 | file_content = content[start_index:end_index + 4] 112 | start_index = content.find("[关键词]") + 5 113 | keywords = content[start_index:].split() 114 | keywords_string = ' '.join(keywords) 115 | prompt = keywords_string 116 | prompt = translate_prompt.simple_translatePrompt(is_use_fanyi, bot_prompt, prompt, session_id) 117 | logger.info(f"[SD] prompt: {prompt}") 118 | 119 | images = [] 120 | logger.info(f"{file_content}") 121 | if os.path.isfile(file_content): 122 | try: 123 | # 从文件中读取数据 124 | with open(file_content, 'rb') as file: 125 | image_data = file.read() 126 | logger.info("图片读取成功") 127 | except Exception as e: 128 | logger.error(f"读取图片数据时出现错误:{e}") 129 | message_reply.reply_Error_Message(True, str(e), e_context) 130 | return 131 | 132 | try: 133 | image = Image.open(io.BytesIO(image_data)) 134 | images.append(image) 135 | width, height = image.size 136 | width, height = image_handle.adjust_image(width, height, maxsize) 137 | logger.info(f"width: {width} height: {height}") 138 | 139 | except Exception as e: 140 | message_reply.reply_Error_Message(True, str(e), e_context) 141 | return 142 | 143 | sdModel = getattr(Model, content.split()[3]).value 144 | default_options = { 145 | "sd_model_checkpoint": sdModel 146 | } 147 | api = webuiapi.WebUIApi(**start_args) 148 | api.set_options(default_options) 149 | # 调用img2img函数,并传递修改后的images列表作为参数 150 | result = api.img2img( 151 | images=images, 152 | steps=20, 153 | denoising_strength=0.70, 154 | cfg_scale=7.0, 155 | width=width, 156 | height=height, 157 | batch_size=4, 158 | n_iter=1, 159 | do_not_save_samples=True, 160 | do_not_save_grid=True, 161 | save_images=True, 162 | prompt=prompt, 163 | negative_prompt=negative_prompt, 164 | ) 165 | model = default_options["sd_model_checkpoint"] 166 | for member in Model: 167 | if model == member.value: 168 | modelName = member.name 169 | break 170 | else: 171 | modelName = model 172 | logger.info("使用了其他模型") 173 | # 发送图片 174 | reply_image(result, start_time, file_content, request_bot_name, modelName, "img2img-images", is_wecom, 175 | e_context) 176 | 177 | else: 178 | replyText = f"🥰请先发送图片给我,我将为您进行图片操作" 179 | message_reply.reply_Text_Message(True, replyText, e_context) 180 | 181 | 182 | # 用于stable_diffusion按照config.json变换图生图 183 | def change_Image(content, Model, request_bot_name, start_args, default_options, 184 | role_options, denoising_strength, cfg_scale, 185 | prompt, negative_prompt, title, maxsize: int, is_wecom, e_context): 186 | start_time = time.time() 187 | file_content = content.split()[2] 188 | images = [] 189 | logger.info(f"{file_content}") 190 | if os.path.isfile(file_content): 191 | try: 192 | # 从文件中读取数据 193 | with open(file_content, 'rb') as file: 194 | image_data = file.read() 195 | logger.info("图片读取成功") 196 | except Exception as e: 197 | logger.error(f"读取图片数据时出现错误:{e}") 198 | message_reply.reply_Error_Message(True, str(e), e_context) 199 | return 200 | 201 | image = Image.open(io.BytesIO(image_data)) 202 | width, height = image.size 203 | width, height = image_handle.adjust_image(width, height, int(maxsize)) 204 | logger.info(f"width: {width} height: {height}") 205 | images.append(image) 206 | 207 | options = {**default_options, **role_options} 208 | # 更改固定模型 209 | api = webuiapi.WebUIApi(**start_args) 210 | api.set_options(options) 211 | # 调用img2img函数,并传递修改后的images列表作为参数 212 | result = api.img2img( 213 | images=images, 214 | steps=20, 215 | denoising_strength=denoising_strength, 216 | cfg_scale=cfg_scale, 217 | width=width, 218 | height=height, 219 | batch_size=4, 220 | n_iter=1, 221 | do_not_save_samples=True, 222 | do_not_save_grid=True, 223 | save_images=True, 224 | prompt=prompt, 225 | negative_prompt=negative_prompt, 226 | ) 227 | 228 | model = options["sd_model_checkpoint"] 229 | for member in Model: 230 | if model == member.value: 231 | modelName = member.name 232 | break 233 | else: 234 | modelName = model 235 | logger.info("使用了其他模型") 236 | 237 | # 发送图片 238 | reply_image(result, start_time, file_content, request_bot_name, modelName, "img2img-images", is_wecom, 239 | e_context) 240 | 241 | else: 242 | replyText = f"🥰请先发送图片给我,我将为您进行{title}" 243 | message_reply.reply_Text_Message(True, replyText, e_context) 244 | 245 | 246 | # 用于stable_diffusion变换图生图 247 | def transform_Image(content, Model, request_bot_name, start_args, use_https, host, port, file_url, 248 | prompt, negative_prompt, maxsize: int, is_wecom, e_context): 249 | start_time = time.time() 250 | file_content = content.split()[2] 251 | images = [] 252 | logger.info(f"{file_content}") 253 | model = getattr(Model, content.split()[3]).value 254 | image_url = image_handle.format_image_url(use_https, host, port, file_url, file_content) 255 | # 发送 GET 请求获取图像数据 256 | response = requests.get(image_url) 257 | # 检查响应状态码是否为 200,表示请求成功 258 | if response.status_code == 200: 259 | # 获取图像的二进制数据 260 | image_data = response.content 261 | # 将二进制图像数据转换为 PIL Image 对象 262 | image = Image.open(io.BytesIO(image_data)) 263 | width, height = image.size 264 | width, height = image_handle.adjust_image(width, height, int(maxsize)) 265 | # 将PIL Image对象添加到images列表中 266 | images.append(image) 267 | default_options = { 268 | "sd_model_checkpoint": model 269 | } 270 | api = webuiapi.WebUIApi(**start_args) 271 | api.set_options(default_options) 272 | # 调用img2img函数,并传递修改后的images列表作为参数 273 | result = api.img2img( 274 | images=images, 275 | steps=20, 276 | denoising_strength=0.5, 277 | cfg_scale=7.0, 278 | batch_size=4, 279 | n_iter=1, 280 | do_not_save_samples=True, 281 | do_not_save_grid=True, 282 | save_images=True, 283 | width=width, 284 | height=height, 285 | prompt=prompt, 286 | negative_prompt=negative_prompt, 287 | ) 288 | # 发送图片 289 | for member in Model: 290 | if model == member.value: 291 | modelName = member.name 292 | break 293 | else: 294 | modelName = model 295 | logger.info("使用了其他模型") 296 | 297 | # 发送图片 298 | reply_image(result, start_time, file_content, request_bot_name, modelName, "img2img-images", is_wecom, 299 | e_context) 300 | 301 | else: 302 | replyText = f"🥰请先发送图片给我,我将为您进行图片操作" 303 | message_reply.reply_Text_Message(True, replyText, e_context) 304 | 305 | 306 | # 用于放大图片 307 | def large_Image(content, use_https, host, port, file_url, e_context): 308 | try: 309 | file_content = content.split()[2] 310 | logger.info(f"{file_content}") 311 | image_url = image_handle.format_image_url(use_https, host, port, file_url, file_content) 312 | logger.info(f"图片地址为:{image_url}") 313 | response = requests.get(image_url) 314 | response.raise_for_status() 315 | message_reply.reply_Image_Url_Message(True, image_url, e_context) 316 | except Exception as e: 317 | replyText = "[😭转换图片失败]" + str(e) + "\n快联系管理员解决问题吧🥰🥰🥰" 318 | message_reply.reply_Text_Message(True, replyText, e_context) 319 | -------------------------------------------------------------------------------- /message/message_type.py: -------------------------------------------------------------------------------- 1 | # 接收到图片消息时的回复格式 2 | def in_image_reply(file_content, request_bot_name, role_options, use_stable_diffusion, 3 | use_music_handle, use_file_handle, is_wecom): 4 | if not is_wecom: 5 | replyText = (f"\n🥰 您的图片编号:\n💖 {file_content}\n\n❗ 请输入指令,以进行图片操作\n" 6 | f"✅ 支持以下指令") 7 | if use_music_handle: 8 | replyText += f"\n\n{request_bot_name}🎧 图生音 {file_content}" 9 | 10 | if use_file_handle: 11 | replyText += f"\n\n{request_bot_name}🖼️ 图像描述 {file_content}" 12 | 13 | if use_stable_diffusion: 14 | replyText += f"\n\n{request_bot_name}🤖 图像修复 {file_content}" 15 | 16 | for role in role_options: 17 | replyText += f"\n\n{request_bot_name} {role['title']} {file_content}" 18 | 19 | replyText += f"\n\n{request_bot_name}🎡 自定义 {file_content} [关键词] 例如 黑色头发 白色短袖 等关键词" 20 | 21 | replyText += f"\n\n{request_bot_name}👀 暂不处理 {file_content}" 22 | 23 | else: 24 | replyText = f"🥰 您的图片编号:\n💖 {file_content}\n\n❗ 请点击指令,以进行图片操作" 25 | if use_music_handle: 26 | replyText += ("\n\n{}". 27 | format(request_bot_name, "🎧 图生音", file_content, "🎧 图生音")) 28 | if use_file_handle: 29 | replyText += ("\n\n{}". 30 | format(request_bot_name, "🖼️ 图像描述", file_content, "🖼️ 图像描述")) 31 | 32 | if use_stable_diffusion: 33 | replyText += ("\n\n{}". 34 | format(request_bot_name, "🤖 图像修复", file_content, "🤖 图像修复")) 35 | for role in role_options: 36 | replyText += ("\n\n{}". 37 | format(request_bot_name, role['title'], file_content, role['title'])) 38 | replyText += f"\n\n{request_bot_name}🎡 自定义 {file_content} [关键词] 例如 黑色头发 白色短袖 等关键词" 39 | 40 | replyText += ("\n\n{}". 41 | format(request_bot_name, "👀 暂不处理", file_content, "👀 暂不处理")) 42 | 43 | if use_stable_diffusion: 44 | replyText += "\n\n🥰 温馨提示\n👑 MODEL_1 : 动漫\n🏆 MODEL_2 : 现实\n🧩 MODEL_3 : Q版" 45 | 46 | replyText += "\n\n🚀 发送指令后,请耐心等待一至两分钟,作品将很快呈现出来!" 47 | return replyText 48 | 49 | 50 | # 图片消息时的回复格式 51 | def on_image_reply(request_bot_name, image_type, all_seeds, modelname, minutes, seconds, is_wecom): 52 | replyText = f"🔥 图片创作成功!\n⏱ 图片处理耗时:{minutes}分钟 {seconds}秒\n🧸点击指令,我将为您进行图片操作!\n\n✅ 支持指令" 53 | composition_1 = 0 54 | composition_2 = 0 55 | if not is_wecom: 56 | for seed in all_seeds: 57 | composition_1 += 1 58 | replyText += ("\n\n{} 🤖 放大 {}.png {}" 59 | .format(request_bot_name, f"{image_type}/{seed}", composition_1)) 60 | for seed in all_seeds: 61 | composition_2 += 1 62 | replyText += ("\n\n{} 🎡 变换 {}.png {} {}" 63 | .format(request_bot_name, f"{image_type}/{seed}", modelname, composition_2)) 64 | else: 65 | for seed in all_seeds: 66 | composition_1 += 1 67 | if composition_1 % 2 == 0: 68 | replyText += "\t\t" 69 | else: 70 | replyText += "\n\n" 71 | replyText += "{}".format( 72 | request_bot_name, f"txt2img-images/{seed}", f"🤖 放大 {composition_1}") 73 | for seed in all_seeds: 74 | composition_2 += 1 75 | if composition_2 % 2 == 0: 76 | replyText += "\t\t" 77 | else: 78 | replyText += "\n\n" 79 | replyText += "{}".format( 80 | request_bot_name, f"txt2img-images/{seed}", modelname, f"🎡 变换 {composition_2}") 81 | replyText += ("\n\n🥰 温馨提示\n✨ 1:左上 2:右上 3:左下 4:右下\n👑 MODEL_1 : 动漫\n🏆 MODEL_2 : 现实\n🧩 MODEL_3 : Q版\n🌈 " 82 | f"图片不满意的话,点击变换\n{request_bot_name}帮你再画一幅吧!\n💖 感谢您的使用!") 83 | return replyText 84 | 85 | 86 | # 用于主函数的帮助回复 87 | def on_help_reply(trigger, rules): 88 | help_text = "💨 结合百度AI 和 Stable-Diffusion 来文生图和图生图,suno音乐,文件和图片识别,群聊聊天模型转换\n\n" 89 | help_text += ( 90 | f"💖 使用方法:\n\n\"{trigger}[关键词1] [关键词2]...:提示语\"的格式作画,如\"{trigger}画高清:男孩,强壮,挺拔,running" 91 | f",黑色耳机,白色短袖(中间有个羊字),黑色头发,黑色短裤\"\n\n") 92 | help_text += "🥰 目前可用关键词:\n\n" 93 | for rule in rules: 94 | keywords = [f"[{keyword}]" for keyword in rule['keywords']] 95 | help_text += f"{','.join(keywords)}" 96 | if "desc" in rule: 97 | help_text += f"-{rule['desc']}\n" 98 | else: 99 | help_text += "\n" 100 | help_text += ( 101 | "\n🥰发送 '一张图片',我将为您进行图片操作\n" 102 | ) 103 | help_text += ("\n🤖 管理员操作\n\n" 104 | "认证管理员 `认证 [管理员密码]`\n\n" 105 | "修改属性 `修改pictureChange参数 [参数名称] [参数数值]`\n\n" 106 | "清空管理员 `清空管理员`\n\n" 107 | "修改管理员密码 `修改密码 [管理员密码]`\n\n" 108 | "修改群聊总功能 `群聊修改 #change [funct] to [state]`\n\n" 109 | "修改已有群聊功能模式 `群聊修改 #change [funct] in [group_name] to [state]`\n\n" 110 | "新增群聊模式 `群聊修改 #add [group_name]` \n\n" 111 | "删除群聊模式 `群聊修改 #del [group_name]`\n\n" 112 | "修改群聊模型模式 `群聊修改 #modify [group_name] to [model]`\n\n" 113 | "[funct]: IMAGE / FILE / MUSIC / MODEL\n\n" 114 | "[state]: true / false\n") 115 | return help_text 116 | -------------------------------------------------------------------------------- /message/music_handle.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | import time 5 | import urllib 6 | 7 | import requests 8 | 9 | from bot import bot_factory 10 | from bridge.bridge import Bridge 11 | from common.log import logger 12 | from plugins import EventAction 13 | from plugins.pictureChange.message import message_reply 14 | 15 | 16 | def music_prompt(bot_prompt, prompt, e_context): 17 | """ 18 | 用于生成音乐提示 19 | :param bot_prompt: 机器人提示 20 | :param prompt: 用户提示 21 | :param e_context: 会话消息 22 | :return: 生成的音乐提示 23 | """ 24 | try: 25 | context = e_context['context'] 26 | session_id = context.content.strip() 27 | bot = bot_factory.create_bot(Bridge().btype['chat']) 28 | session = bot.sessions.build_session(session_id, bot_prompt) 29 | # 不带有之前的提示词 30 | # session.add_query(prompt) 31 | result = bot.reply_text(session) 32 | return str(result.get("content")) 33 | except Exception: 34 | # logger.error("music_handle: music prompt error") 35 | return prompt 36 | 37 | 38 | class music_handle: 39 | def __init__(self): 40 | super().__init__() 41 | 42 | # 用于将文本转换为音乐 43 | @staticmethod 44 | def text_to_music(bot_prompt, is_use_gpt, url, key, prompt, model, is_wecom, e_context): 45 | """ 46 | 用于将文本转换为音乐 47 | :param bot_prompt: 机器人提示 48 | :param is_use_gpt: 是否使用GPT(是:文生音;否:图生音) 49 | :param url: 请求的URL 50 | :param key: 授权Key 51 | :param prompt: 用户提示 52 | :param model: 模型 53 | :param is_wecom: 是否为企业微信 54 | :param e_context: 上下文 55 | :return: None 56 | """ 57 | if is_use_gpt: 58 | prompt = music_prompt(bot_prompt, prompt, e_context) 59 | 60 | # 设置请求头 61 | headers = { 62 | 'Authorization': f'Bearer {key}', 63 | 'Content-Type': 'application/json' 64 | } 65 | # 设置请求体 66 | payload = { 67 | "model": model, 68 | "stream": True, 69 | "messages": [ 70 | { 71 | "content": str(prompt), 72 | "role": "user" 73 | } 74 | ] 75 | } 76 | 77 | # 初始化音乐相关变量,包括歌名、类型、完整歌词、歌曲图片、临时链接、歌曲、视频 78 | song_name = "" 79 | genre = "" 80 | full_lyrics = "" 81 | song_image = "" 82 | music_link1 = "" 83 | music_link2 = "" 84 | song1 = "" 85 | video1 = "" 86 | 87 | # 初始化布尔标识 88 | song_info_printed = False 89 | lyrics_image_printed = False 90 | music_links_printed = False 91 | songs_printed = False 92 | videos_printed = False 93 | 94 | try: 95 | # 发送请求并处理响应 96 | with requests.post(url, headers=headers, json=payload, stream=True) as response: 97 | response.raise_for_status() 98 | for i, line in enumerate(response.iter_lines()): 99 | line = line.decode('utf-8') 100 | if line.startswith("data:") and "[DONE]" not in line: 101 | try: 102 | # 将UTF-8字符串重新编码为GBK并处理可能的编码错误 103 | line_gbk = line.replace("data: ", "") 104 | json_data = json.loads(line_gbk) 105 | if 'choices' in json_data and len(json_data['choices']) > 0: 106 | delta = json_data['choices'][0].get('delta', {}) 107 | message_content = delta.get('content', "") 108 | # 处理生成音乐提示的不同部分 109 | if "违规" in message_content: 110 | replyText = (f"⚠️⚠️ 违规 ⚠️⚠️\n\n🤖 歌曲提示\n\n{str(prompt)}\n\n" 111 | f"🚨 注意\n\n😭您的提示词中存在违规词,歌曲创作失败😭\n\n" 112 | f"🤗 请更换提示词,我会为您重新创作✨") 113 | message_reply.tem_reply_Text_Message(replyText, e_context) 114 | break 115 | if "ID" in message_content: 116 | replyText = (f"🤯 Creating\n\n🤖 歌曲提示\n\n{prompt}\n\n" 117 | "🤩 音乐已经生成,敬请期待!\n\n🤗 温馨提示:用英文表述音乐术语,结果会更准确哟~") 118 | message_reply.tem_reply_Text_Message(replyText, e_context) 119 | if "歌名" in message_content: 120 | song_name_match = re.search(r'歌名\*\*:(.+?)\n', message_content) 121 | if song_name_match: 122 | song_name = song_name_match.group(1).strip() 123 | elif "类型" in message_content: 124 | genre_match = re.search(r'类型\*\*:(.+)', message_content) 125 | if genre_match: 126 | genre = genre_match.group(1).strip() 127 | elif "完整歌词" in message_content: 128 | lyrics_match = re.search(r'```\n(.+?)\n```', message_content, re.DOTALL) 129 | if lyrics_match: 130 | full_lyrics = lyrics_match.group(1).strip() 131 | elif "image_large_url" in message_content: 132 | url_match = re.search(r'https?://.*?\)', message_content) 133 | if url_match: 134 | song_image = url_match.group(0).rstrip(')') 135 | elif "实时音乐" in message_content: 136 | music_links = re.findall(r'https?://\S+', message_content) 137 | if music_link1: 138 | music_link2 = music_links[0] 139 | if len(music_links) > 0 and not music_link1: 140 | music_link1 = music_links[0] 141 | elif "CDN" in message_content and "音乐" in message_content: 142 | url_matches = re.findall(r'https?://\S+\.mp3', message_content) 143 | for idx, match in enumerate(url_matches): 144 | if idx == 0: 145 | song1 = match 146 | elif idx == 1: 147 | song2 = match 148 | elif "视频链接" in message_content: 149 | url_matches = re.findall(r'https?://\S+\.mp4', message_content) 150 | for idx, match in enumerate(url_matches): 151 | if idx == 0: 152 | video1 = match 153 | elif idx == 1: 154 | video2 = match 155 | 156 | if is_wecom: 157 | # 处理企业微信消息 158 | if song_name and genre and full_lyrics and not song_info_printed: 159 | replyText = (f"⭐⭐ 歌曲信息 ⭐⭐\n\n『🤖 歌名』\n" 160 | f"{song_name}\n\n『💄 类型』\n{genre}" 161 | f"\n\n『📖 完整歌词』\n{full_lyrics}" 162 | f"\n\n👀 更多\n\n🚨 歌曲图片和实时音乐链接正在火速生成中🚀🚀🚀") 163 | message_reply.tem_reply_Text_Message(replyText, e_context) 164 | 165 | song_info_printed = True 166 | if song_image and not lyrics_image_printed: 167 | message_reply.tem_reply_Image_Url_Message(song_image, e_context) 168 | lyrics_image_printed = True 169 | if music_link1 and music_link2 and not music_links_printed: 170 | replyText = ( 171 | f"🎵🎵 即刻体验 🎵🎵\n\n『实时音乐1️⃣』\n{music_link1}\n\n『实时音乐2️⃣』\n{music_link2}\n\n" 172 | f"🚀 音乐MP3和视频正在火速生成中,大概需要2-3分钟,请耐心等待!") 173 | message_reply.tem_reply_Text_Message(replyText, e_context) 174 | music_links_printed = True 175 | if song1 and song2 and not songs_printed: 176 | replyText = f"🎧🎧 音乐 🎧🎧\n\n{song1}" 177 | message_reply.tem_reply_Text_Message(replyText, e_context) 178 | songs_printed = True 179 | if video1 and video2 and not videos_printed: 180 | replyText = f"📽📽 视频 📽📽\n\n{video1}" 181 | message_reply.tem_reply_Text_Message(replyText, e_context) 182 | videos_printed = True 183 | else: 184 | # 处理普通消息 185 | # 同时回复歌名、类型和完整歌词 186 | if song_name and genre and full_lyrics and not song_info_printed: 187 | replyText = (f"⭐⭐ 歌曲信息 ⭐⭐\n\n『🤖 歌名』\n" 188 | f"{song_name}\n\n『💄 类型』\n{genre}" 189 | f"\n\n『📖 完整歌词』\n{full_lyrics}" 190 | f"\n\n👀 更多\n\n🚨 歌曲图片和实时音乐链接正在火速生成中🚀🚀🚀") 191 | message_reply.tem_reply_Text_Message(replyText, e_context) 192 | song_info_printed = True 193 | # 回复歌曲图片 194 | if song_image and not lyrics_image_printed: 195 | message_reply.tem_reply_Image_Url_Message(song_image, e_context) 196 | lyrics_image_printed = True 197 | # 回复实时音乐链接 198 | if music_link1 and music_link2 and not music_links_printed: 199 | replyText = ( 200 | f"🎵🎵 即刻体验 🎵🎵\n\n『实时音乐1️⃣』\n{music_link1}\n\n『实时音乐2️⃣』\n{music_link2}\n\n" 201 | f"🚀 音乐MP3和视频正在火速生成中,大概需要2-3分钟,请耐心等待!") 202 | message_reply.tem_reply_Text_Message(replyText, e_context) 203 | music_links_printed = True 204 | # 回复歌曲 205 | if song1 and song2 and not songs_printed: 206 | while requests.get(song1).status_code != 200: 207 | time.sleep(1) 208 | # 生成唯一的文件名 209 | query = "music_" + str(int(time.time())) 210 | file_name1 = f"{query}.mp3" 211 | file_path1 = os.path.join("tmp", file_name1) 212 | # 确保 tmp 目录存在 213 | if not os.path.exists("tmp"): 214 | os.makedirs("tmp") 215 | try: 216 | with urllib.request.urlopen(song1) as response1, open(file_path1, 217 | 'wb') as out_file1: 218 | out_file1.write(response1.read()) 219 | logger.info(f"[singsong]Music {file_path1} 下载成功, {song1}") 220 | message_reply.tem_reply_Video_Message(file_path1, e_context) 221 | songs_printed = True 222 | except Exception as e: 223 | logger.error(f"Error downloading song: {e}") 224 | continue 225 | # 回复视频 226 | if video1 and video2 and not videos_printed: 227 | while requests.get(video1).status_code != 200: 228 | time.sleep(1) 229 | logger.info(f"{video1}") 230 | message_reply.reply_Video_Url_Message(True, video1, e_context) 231 | videos_printed = True 232 | break 233 | except Exception: 234 | continue 235 | 236 | elif "[DONE]" in line: 237 | break 238 | 239 | else: 240 | continue 241 | 242 | except Exception as e: 243 | message_reply.reply_Error_Message(True, f"music_handle: {e}", e_context) 244 | raise Exception(f"An error occurred: {e}") 245 | finally: 246 | e_context.action = EventAction.BREAK_PASS 247 | -------------------------------------------------------------------------------- /message/request_handle.py: -------------------------------------------------------------------------------- 1 | # 识别图片消息的请求体 2 | def recognize_message(prompt: str, image_url: str, model: str): 3 | return { 4 | "messages": [ 5 | { 6 | "role": "user", 7 | "content": [ 8 | { 9 | "type": "text", 10 | "text": prompt 11 | }, 12 | { 13 | "type": "image_url", 14 | "image_url": { 15 | "url": image_url 16 | } 17 | } 18 | ] 19 | } 20 | ], 21 | "stream": False, 22 | "model": model 23 | } 24 | 25 | 26 | # 创造图片的请求体 27 | def image_message(prompt: str, model: str): 28 | return { 29 | "model": model, 30 | "prompt": prompt 31 | } 32 | 33 | 34 | # 创造音乐的请求体 35 | def music_message(prompt: str, model: str): 36 | return { 37 | "model": model, 38 | "stream": False, 39 | "messages": [ 40 | { 41 | "content": prompt, 42 | "role": "user" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /message/run.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/message/run.log -------------------------------------------------------------------------------- /nohup.out: -------------------------------------------------------------------------------- 1 | python3: can't open file 'app.py': [Errno 2] No such file or directory 2 | -------------------------------------------------------------------------------- /pictureChange.py: -------------------------------------------------------------------------------- 1 | import plugins 2 | from bridge.context import ContextType 3 | from bridge.reply import ReplyType 4 | from plugins import * 5 | from plugins.pictureChange.adminService.admin_service import admin_service 6 | from plugins.pictureChange.groupService.change_config import group_config 7 | from plugins.pictureChange.groupService.find_group import find_group 8 | from plugins.pictureChange.message import message_reply as MessageReply, message_type 9 | from plugins.pictureChange.message.message_limit import MessageLimit 10 | from plugins.pictureChange.util import file_handle 11 | from plugins.pictureChange.work.common import Common 12 | 13 | 14 | @plugins.register(name="pictureChange", 15 | desc="百度AI 和 Stable-Diffusion 来文生图和图生图, suno音乐,文件和图片识别,群聊聊天模型转换", 16 | version="2.0.1", 17 | author="yang yang") 18 | class pictureChange(Plugin): 19 | # 定义了模型枚举类型 20 | class Model(Enum): 21 | MODEL_1 = "anything-v5-PrtRE.safetensors [7f96a1a9ca]" 22 | MODEL_2 = "absolutereality_v181.safetensors [463d6a9fe8]" 23 | MODEL_3 = "QteaMix-fp16.safetensors [0c1efcbbd6]" 24 | 25 | def __init__(self): 26 | super().__init__() 27 | cursor = os.path.dirname(__file__) 28 | config_path = os.path.join(cursor, "config.json") 29 | try: 30 | with open(config_path, "r", encoding="utf-8") as f: 31 | config = json.load(f) 32 | 33 | # 用于stable_diffusion参数 34 | self.rules = config["rules"] 35 | self.defaults = config["defaults"] 36 | self.default_params = self.defaults["params"] 37 | self.default_options = self.defaults["options"] 38 | self.role_options = [role for role in config["roles"] if role["enable"]] 39 | self.start_args = config["start"] 40 | self.host = config["start"]["host"] 41 | self.port = config["start"]["port"] 42 | self.use_https = config["start"]["use_https"] 43 | 44 | # 用于变换和放大图片操作 45 | self.file_url = config["file_url"] 46 | 47 | # 用于聊天操作 48 | self.is_group_bot_name = conf().get("group_chat_prefix", [""])[0] 49 | self.single_bot_name = conf().get("single_chat_prefix", [""])[0] 50 | self.other_user_id = config["use_group"] 51 | 52 | # 用于翻译prompt 53 | self.is_use_fanyi = config["is_use_fanyi"] 54 | self.baidu_api_key = config["baidu_api_key"] 55 | self.baidu_secret_key = config["baidu_secret_key"] 56 | 57 | # 用于音乐分析 58 | self.music_model = config["music_model"] 59 | 60 | # 用于图片和文件分析 61 | self.openai_api_key = config["openai_api_key"] 62 | self.openai_api_base = config["openai_api_base"] 63 | self.image_recognize_model = config["image_recognize_model"] 64 | self.file_recognize_model = config["file_recognize_model"] 65 | self.image_recognize_prompt = config["image_recognize_prompt"] 66 | self.file_recognize_prompt = config["file_recognize_prompt"] 67 | 68 | self.negative_prompt = ("(((nsfw))),EasyNegative,badhandv4,ng_deepnegative_v1_75t,(worst quality:2), " 69 | "(low quality:2), (normal quality:2), lowres, ((monochrome)), ((grayscale)), " 70 | "bad anatomy,DeepNegative, skin spots, acnes, skin blemishes,(fat:1.2)," 71 | "facing away, looking away,tilted head, lowres,bad anatomy,bad hands, " 72 | "missing fingers,extra digit, fewer digits,bad feet,poorly drawn hands," 73 | "poorly drawn face,mutation,deformed,extra fingers,extra limbs,extra arms," 74 | "extra legs,malformed limbs,fused fingers,too many fingers,long neck," 75 | "cross-eyed,mutated hands,polar lowres,bad body,bad proportions," 76 | "gross proportions,missing arms,missing legs,extra digit, extra arms, " 77 | "extra leg, extra foot,teethcroppe,signature, watermark, username,blurry," 78 | "cropped,jpeg artifacts,text,error,Lower body exposure") 79 | 80 | self.bot_prompt = '''作为 Stable Diffusion Prompt 提示词专家,您将从关键词中创建提示,通常来自 Danbooru 81 | 等数据库。提示通常描述图像,使用常见词汇,按重要性排列,并用逗号分隔。避免使用"-"或".",但可以接受空格和自然语言。避免词汇重复。为了强调关键词,请将其放在括号中以增加其权重。例如,"( 82 | flowers)"将'flowers'的权重增加1.1倍,而"(((flowers)))"将其增加1.331倍。使用"( 83 | flowers:1.5)"将'flowers'的权重增加1.5倍。只为重要的标签增加权重。提示包括三个部分:前缀(质量标签+风格词+效果器)+ 主题(图像的主要焦点)+ 84 | 场景(背景、环境)。前缀影响图像质量。像"masterpiece"、"best 85 | quality"、"4k"这样的标签可以提高图像的细节。像"illustration"、"lensflare"这样的风格词定义图像的风格。像"bestlighting"、"lensflare 86 | "、"depthoffield"这样的效果器会影响光照和深度。主题是图像的主要焦点,如角色或场景。对主题进行详细描述可以确保图像丰富而详细。增加主题的权重以增强 87 | 其清晰度。对于角色,描述面部、头发、身体、服装、姿势等特征。场景描述环境。没有场景,图像的背景是平淡的,主题显得过大。某些主题本身包含场景(例如建筑物 88 | 、风景)。像"花草草地"、"阳光"、"河流"这样的环境词可以丰富场景。你的任务是设计图像生成的提示。请按照以下步骤进行操作:我会发送给您一个图像场景。生成 详细的图像描述,输出 Positive 89 | Prompt ,并确保用英文回复我。示例:我发送:二战时期的护士。您回复:A WWII-era nurse in a German uniform, holding a wine bottle and 90 | stethoscope, sitting at a table in white attire,with a table in the background, masterpiece, 91 | best quality, 4k, illustration style, best lighting, depth of field, detailed character, 92 | detailed environment.''' 93 | 94 | self.suno_bot_prompt = ("现在,我需要你根据我给你的一段内容,将这段内容按照语义分成 Title(题目),Song Description(歌曲描述),Type of " 95 | "Music(音乐类型)。并根据用户的意图生成对应语言的歌曲提示词.请直接了当地输出Title(题目),Song " 96 | "Description(歌曲描述),Type of Music(音乐类型),不要超过100字。") 97 | self.suno_image_music_prompt = config["image_music_prompt"] 98 | 99 | # 管理员的配置 100 | self.max_number = int(config["max_number"]) 101 | self.max_size = int(config["max_size"]) 102 | self.use_pictureChange = config["use_pictureChange"] 103 | self.music_create_prefix = config["music_create_prefix"] 104 | self.image_create_prefix = config["image_create_prefix"] 105 | self.use_stable_diffusion = config["use_stable_diffusion"] 106 | self.use_music_handle = config["use_music_handle"] 107 | self.use_file_handle = config["use_file_handle"] 108 | self.use_group_handle = config.get("use_group_handle", True) 109 | self.admin = admin_service() 110 | 111 | # 判断回复类型 112 | channel_type = conf().get("channel_type", "wx") 113 | # 根据类型修改 114 | if channel_type == "wx" or channel_type == "wxy": 115 | self.is_wecom = False 116 | else: 117 | self.is_wecom = True 118 | 119 | self.handlers[Event.ON_HANDLE_CONTEXT] = self.on_handle_context 120 | logger.info("[pictureChange] inited") 121 | except Exception as e: 122 | if isinstance(e, FileNotFoundError): 123 | logger.warn(f"[SD] init failed, {config_path} not found.") 124 | else: 125 | logger.warn("[SD] init failed.") 126 | raise e 127 | 128 | @staticmethod 129 | def get_config_path(json_path: str): 130 | curdir = os.path.dirname(__file__) 131 | return os.path.join(curdir, json_path) 132 | 133 | def on_handle_context(self, e_context: EventContext): 134 | channel = e_context['channel'] 135 | if ReplyType.IMAGE in channel.NOT_SUPPORT_REPLYTYPE: 136 | return 137 | 138 | # 初始化消息 139 | context = e_context['context'] 140 | context.get("msg").prepare() 141 | content = context.content.strip() 142 | 143 | # 初始化画图参数 144 | check_exist = False 145 | denoising_strength = 0 146 | cfg_scale = 0 147 | prompt = "" 148 | negative_prompt = self.negative_prompt 149 | roleRule_options = {} 150 | 151 | # 初始化消息类型 152 | is_group = context["msg"].is_group 153 | """ 154 | "IMAGE": "False", 155 | "FILE": "True", 156 | "MUSIC": "False", 157 | "MODEL": "GLM-4", 158 | """ 159 | is_group_enable = True 160 | is_group_image = True 161 | is_group_file = True 162 | is_group_music = True 163 | is_group_model = None 164 | user = e_context["context"]["receiver"] 165 | 166 | if is_group and self.use_group_handle: 167 | group_name = context["msg"].other_user_nickname 168 | data_group_config = find_group.find_group(group_name) 169 | logger.info(data_group_config) 170 | if data_group_config and data_group_config["ENABLE"]: 171 | is_group_image = data_group_config["IMAGE"] 172 | is_group_file = data_group_config["FILE"] 173 | is_group_music = data_group_config["MUSIC"] 174 | is_group_model = data_group_config["MODEL"] 175 | else: 176 | is_group_enable = False 177 | is_group_image = False 178 | is_group_file = False 179 | is_group_music = False 180 | 181 | request_bot_name = self.is_group_bot_name if is_group else self.single_bot_name 182 | if request_bot_name != "": 183 | request_bot_name = request_bot_name + " " 184 | 185 | # 测试 186 | logger.debug(context) 187 | logger.debug(f"收到信息:{content}") 188 | 189 | title = "" 190 | # 是否存在自定义规则 191 | if self.use_stable_diffusion: 192 | for role in self.role_options: 193 | if content.startswith(role['title'] + " "): 194 | title = role['title'] 195 | denoising_strength = role['denoising_strength'] 196 | cfg_scale = role['cfg_scale'] 197 | prompt = role['prompt'] 198 | negative_prompt = role['negative_prompt'] 199 | if "options" in role: 200 | for key in role["options"]: 201 | roleRule_options[key] = role["options"][key] 202 | check_exist = True 203 | break 204 | 205 | # 开启插件,否则不能正常使用(这里可以添加限制) 206 | # if self.use_pictureChange: 207 | # self.handle_image_mode(content, e_context) 208 | 209 | # 判断已经开启插件,没有开启直接跳过 210 | if self.use_pictureChange and is_group_enable: 211 | try: 212 | # 判断消息类型 213 | if (e_context['context'].type == ContextType.IMAGE and is_group_image 214 | and (self.use_music_handle or self.use_stable_diffusion)): 215 | Common.process_init_image(request_bot_name, self.role_options, 216 | self.use_stable_diffusion, self.use_music_handle, True, 217 | self.is_wecom, e_context) 218 | 219 | elif e_context['context'].type == ContextType.FILE and is_group_file and self.use_file_handle: 220 | Common.process_file(self.openai_api_base, self.openai_api_key, self.file_recognize_model, 221 | self.file_recognize_prompt, e_context) 222 | 223 | elif (any(ext in content for ext in ["jpg", "jpeg", "png", "gif", "webp"]) and ( 224 | content.startswith("http://") or content.startswith("https://")) and is_group_image 225 | and (self.use_music_handle or self.use_stable_diffusion)): 226 | Common.process_init_image_url(request_bot_name, self.role_options, self.use_stable_diffusion, 227 | self.use_music_handle, True, self.is_wecom, e_context) 228 | 229 | elif (any(content.startswith(prefix) for prefix in self.image_create_prefix) 230 | and is_group_image and self.use_stable_diffusion): 231 | content = next((content.replace(prefix, "") for prefix in self.image_create_prefix 232 | if content.startswith(prefix)), 233 | content) 234 | e_context['context'].content = content 235 | Common.process_image_create(self.is_use_fanyi, self.bot_prompt, self.rules, self.Model, 236 | request_bot_name, self.start_args, self.default_params, 237 | self.default_options, self.is_wecom, e_context) 238 | 239 | # 以下是对文字消息的操作 240 | elif content.startswith("👀 暂不处理 ") and is_group_image: 241 | file_content = content.split()[2] 242 | logger.info(f"{file_content}") 243 | replyText = file_handle.delete_file(file_content) 244 | MessageReply.reply_Text_Message(True, replyText, e_context) 245 | 246 | elif content.startswith("🤖 图像修复 ") and is_group_image and self.use_stable_diffusion: 247 | Common.process_baidu_image(self.baidu_api_key, self.baidu_secret_key, e_context) 248 | 249 | elif content.startswith("🖼️ 图像描述 ") and is_group_image: 250 | Common.process_image(self.openai_api_base, self.openai_api_key, self.image_recognize_model, 251 | self.image_recognize_prompt, e_context) 252 | 253 | elif content.startswith("🎡 自定义 ") and is_group_image and self.use_stable_diffusion: 254 | message_limit = MessageLimit() 255 | if message_limit.isLimit(self.max_number, e_context): 256 | return 257 | message_limit.using() 258 | Common.process_image_custom(self.is_use_fanyi, self.bot_prompt, self.Model, request_bot_name, 259 | self.start_args, negative_prompt, self.max_size, self.is_wecom, 260 | e_context) 261 | message_limit.success(self.max_number) 262 | 263 | # 判断用户发送的消息是否在config.json预设里面 264 | elif check_exist and is_group_image and self.use_stable_diffusion: 265 | message_limit = MessageLimit() 266 | if message_limit.isLimit(self.max_number, e_context): 267 | return 268 | message_limit.using() 269 | Common.process_image_change(self.Model, request_bot_name, self.start_args, self.default_options, 270 | roleRule_options, denoising_strength, cfg_scale, prompt, 271 | negative_prompt, title, self.max_size, self.is_wecom, e_context) 272 | message_limit.success(self.max_number) 273 | 274 | elif content.startswith("🎡 变换 ") and is_group_image and self.use_stable_diffusion: 275 | Common.process_image_transform(self.Model, request_bot_name, self.start_args, self.use_https, 276 | self.host, self.port, self.file_url, prompt, negative_prompt, 277 | self.max_size, self.is_wecom, e_context) 278 | 279 | elif content.startswith("🤖 放大 ") and is_group_image and self.use_stable_diffusion: 280 | Common.process_image_large(self.use_https, self.host, self.port, self.file_url, e_context) 281 | 282 | elif content.startswith("🎧 图生音 ") and is_group_music and self.use_music_handle: 283 | Common.process_image_music(self.openai_api_base, self.openai_api_key, self.image_recognize_model, 284 | self.music_model, self.suno_image_music_prompt, self.is_wecom, e_context) 285 | 286 | elif any(content.startswith(prefix) for prefix in 287 | self.music_create_prefix) and is_group_music and self.use_music_handle: 288 | prompt = content.replace("文生音 ", "") 289 | Common.process_text_music(self.suno_bot_prompt, self.openai_api_base, self.openai_api_key, 290 | self.music_model, prompt, self.is_wecom, e_context) 291 | 292 | elif not is_group: 293 | self.admin_service(content, user, e_context) 294 | 295 | # 跳过插件,到下一个插件里面 296 | else: 297 | user_data = conf().get_user_data(user) 298 | if is_group_model: 299 | user_data["gpt_model"] = is_group_model 300 | logger.info(f"模型已经更换成为 {is_group_model}") 301 | e_context.action = EventAction.CONTINUE 302 | 303 | except Exception as e: 304 | replyText = "[😭SD画图失败] " + str(e) + "\n🧸快联系管理员解决问题吧!" 305 | logger.error("[SD画图失败] exception: %s" % e) 306 | MessageReply.reply_Error_Message(True, replyText, e_context) 307 | # util.delete_file(file_content) 308 | 309 | elif not self.use_pictureChange and self.admin.is_admin(user): 310 | self.admin_service(content, user, e_context) 311 | 312 | else: 313 | e_context.action = EventAction.CONTINUE 314 | 315 | def get_help_text(self, **kwargs): 316 | if not conf().get('image_create_prefix'): 317 | help_text = "画图功能未启用" 318 | else: 319 | trigger = conf()['image_create_prefix'][0] 320 | help_text = message_type.on_help_reply(trigger, self.rules) 321 | return help_text 322 | 323 | def admin_service(self, content, sender_id, e_context): 324 | # 认证管理员 325 | if content.startswith("认证 "): 326 | # 假设认证管理员的信息应该是:"认证 root" 327 | # 分离参数 328 | content1 = str(content).replace("认证 ", "") 329 | if self.admin.verify_admin(sender_id, content1): 330 | replyText = "🥰认证成功" 331 | else: 332 | replyText = "😭认证失败,请重新认证" 333 | MessageReply.reply_Text_Message(True, replyText, e_context) 334 | return 335 | 336 | if self.admin.is_admin(sender_id): 337 | if content.startswith("修改pictureChange参数"): 338 | content1 = content.split(" ") 339 | if len(content1) != 3: 340 | replyText = f"😭修改成失败,格式错误,应该为‘修改pictureChange参数 参数名称 参数数值’" 341 | MessageReply.reply_Text_Message(True, replyText, e_context) 342 | return 343 | name = str(content1[1]) 344 | change_value = content1[2] 345 | if str(change_value).lower() == "true": 346 | change_value = True 347 | elif str(change_value).lower() == "false": 348 | change_value = False 349 | 350 | if not self.admin.update_json(sender_id, name, value=change_value): 351 | replyText = f"😭修改成失败,没有这个参数名称" 352 | MessageReply.reply_Text_Message(True, replyText, e_context) 353 | else: 354 | replyText = f"🥰修改成功,当前{name}的值为{change_value}" 355 | MessageReply.reply_Text_Message(True, replyText, e_context) 356 | return 357 | 358 | elif content.startswith("修改密码"): 359 | content1 = content.split(" ") 360 | if content1 is None or len(content1) != 2: 361 | return 362 | self.admin.update_password(sender_id, content1[1]) 363 | replyText = f"🥰修改密码成功" 364 | MessageReply.reply_Text_Message(True, replyText, e_context) 365 | return 366 | 367 | elif content.startswith("修改host"): 368 | content1 = content.split(" ") 369 | if len(content1) != 2: 370 | return 371 | self.admin.update_json(sender_id, "start", "host", value=content1[1]) 372 | self.host = content1[1] 373 | replyText = f"🥰修改host成功,当前host为{self.host}" 374 | MessageReply.reply_Text_Message(True, replyText, e_context) 375 | return 376 | 377 | # 清空管理员 378 | elif content.startswith("清空管理员"): 379 | self.admin.clear_admin(sender_id) 380 | replyText = "🥰清空管理员成功" 381 | MessageReply.reply_Text_Message(True, replyText, e_context) 382 | return 383 | 384 | elif content.startswith("群聊修改 "): 385 | content = content.replace("群聊修改 ", "") 386 | replyText = group_config().ins_command(content) 387 | MessageReply.reply_Text_Message(True, replyText, e_context) 388 | return 389 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # sdwebui plugin 2 | webuiapi>=0.6.2 3 | langid 4 | oauthlib -------------------------------------------------------------------------------- /util/__init__.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | import plugins.pictureChange.message.request_handle as MessageHandle 4 | from bridge.bridge import Bridge 5 | from common import const 6 | from common.log import logger 7 | 8 | 9 | # 文字生成图片 10 | def text_to_image(url: str, key: str, prompt: str, model: str, session_id: str = None): 11 | headers = { 12 | 'Authorization': f'Bearer {key}', 13 | 'Content-Type': 'application/json' 14 | } 15 | payload = MessageHandle.image_message(prompt, model) 16 | btype = Bridge().get_bot_type("chat") 17 | if btype not in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI]: 18 | return 19 | bot = Bridge().get_bot("chat") 20 | bot.sessions.session_query(prompt, session_id) 21 | 22 | try: 23 | response = requests.post(url, headers=headers, json=payload) 24 | response.raise_for_status() 25 | reply = str(response.json()["data"][0]["url"]).strip() 26 | # 保存聊天记录(包含群聊和个人) 27 | # bot.sessions.session_reply(reply, session_id) 28 | return reply 29 | except requests.exceptions.RequestException as e: 30 | logger.error(f"An error occurred: {e}") 31 | return f"An error occurred: {e}" 32 | 33 | 34 | # 文字生成音乐 35 | def text_to_music(url: str, key: str, prompt: str, model: str, max_retries: int = 1, 36 | session_id: str = None): 37 | headers = { 38 | 'Authorization': f'Bearer {key}', 39 | 'Content-Type': 'application/json' 40 | } 41 | 42 | payload = MessageHandle.music_message(prompt, model) 43 | btype = Bridge().get_bot_type("chat") 44 | if btype not in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI]: 45 | return 46 | bot = Bridge().get_bot("chat") 47 | bot.sessions.session_query(prompt, session_id) 48 | retries = 0 49 | 50 | while retries < max_retries: 51 | try: 52 | response = requests.post(url, headers=headers, json=payload) 53 | response.raise_for_status() 54 | reply = str(response.json()["choices"][0]["message"]["content"]) 55 | # 1.提取歌曲信息(歌名,风格,歌词,歌曲图片) 56 | # 2.提取有效的url,并下载到相应MP3文件 57 | 58 | # 保存聊天记录(包含群聊和个人) 59 | # bot.sessions.session_reply(reply, session_id) 60 | return reply 61 | 62 | except requests.exceptions.RequestException: 63 | retries += 1 64 | 65 | return f"Failed to retrieve content after {max_retries} attempts due to 503 errors." 66 | 67 | 68 | # 分析文件 69 | def recognize_file(url: str, key: str, prompt: str, file_url: str, model: str, session_id: str = None): 70 | headers = { 71 | 'Authorization': f'Bearer {key}', 72 | 'Content-Type': 'application/json' 73 | } 74 | 75 | payload = MessageHandle.recognize_message(prompt, file_url, model) 76 | btype = Bridge().get_bot_type("chat") 77 | if btype not in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI]: 78 | return 79 | bot = Bridge().get_bot("chat") 80 | bot.sessions.session_query(prompt, session_id) 81 | try: 82 | response = requests.post(url, headers=headers, json=payload) 83 | response.raise_for_status() 84 | reply = str(response.json()["choices"][0]["message"]["content"]) 85 | # 保存聊天记录(包含群聊和个人) 86 | # bot.sessions.session_reply(reply, session_id) 87 | return reply 88 | except requests.exceptions.RequestException as e: 89 | if e.response: 90 | logger.error(f"Response content: {e.response.text}") # Print the response content for debugging 91 | return f"An error occurred: {e}" 92 | 93 | 94 | # 分析图片 95 | def recognize_image(url: str, key: str, prompt: str, image_url: str, model: str, session_id: str = None): 96 | headers = { 97 | 'Authorization': f'Bearer {key}', 98 | 'Content-Type': 'application/json' 99 | } 100 | payload = MessageHandle.recognize_message(prompt, image_url, model) 101 | btype = Bridge().get_bot_type("chat") 102 | if btype not in [const.OPEN_AI, const.CHATGPT, const.CHATGPTONAZURE, const.LINKAI]: 103 | return 104 | bot = Bridge().get_bot("chat") 105 | bot.sessions.session_query(prompt, session_id) 106 | try: 107 | response = requests.post(url, headers=headers, json=payload) 108 | response.raise_for_status() 109 | reply = str(response.json()["choices"][0]["message"]["content"]) 110 | bot.sessions.session_reply(reply, session_id) 111 | return reply 112 | except requests.exceptions.RequestException as e: 113 | logger.error(f"An error occurred: {e}") 114 | return f"An error occurred: {e}" 115 | -------------------------------------------------------------------------------- /util/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /util/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /util/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /util/__pycache__/baidu_image.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/baidu_image.cpython-311.pyc -------------------------------------------------------------------------------- /util/__pycache__/baidu_image.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/baidu_image.cpython-312.pyc -------------------------------------------------------------------------------- /util/__pycache__/baidu_image.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/baidu_image.cpython-38.pyc -------------------------------------------------------------------------------- /util/__pycache__/file_handle.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/file_handle.cpython-311.pyc -------------------------------------------------------------------------------- /util/__pycache__/file_handle.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/file_handle.cpython-312.pyc -------------------------------------------------------------------------------- /util/__pycache__/file_handle.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/file_handle.cpython-38.pyc -------------------------------------------------------------------------------- /util/__pycache__/image_handle.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/image_handle.cpython-311.pyc -------------------------------------------------------------------------------- /util/__pycache__/image_handle.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/image_handle.cpython-312.pyc -------------------------------------------------------------------------------- /util/__pycache__/image_handle.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/image_handle.cpython-38.pyc -------------------------------------------------------------------------------- /util/__pycache__/translate_prompt.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/translate_prompt.cpython-311.pyc -------------------------------------------------------------------------------- /util/__pycache__/translate_prompt.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/translate_prompt.cpython-312.pyc -------------------------------------------------------------------------------- /util/__pycache__/translate_prompt.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/util/__pycache__/translate_prompt.cpython-38.pyc -------------------------------------------------------------------------------- /util/baidu_image.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import io 3 | import urllib.parse 4 | 5 | import requests 6 | from oauthlib.common import urlencoded 7 | 8 | from common.log import logger 9 | from plugins.pictureChange.message import message_reply as MessageReply 10 | from plugins.pictureChange.util import file_handle as FileHandle 11 | 12 | 13 | # 百度图片处理 14 | def read_and_encode_image(file_content): 15 | try: 16 | with open(file_content, 'rb') as file: 17 | image_data = file.read() 18 | encoded_image = base64.b64encode(image_data).decode('utf-8') 19 | if urlencoded: 20 | encoded_image = urllib.parse.quote_plus(encoded_image) 21 | return encoded_image 22 | except Exception as e: 23 | logger.error(f"处理文件数据时出现错误:{e}") 24 | return None 25 | 26 | 27 | # 回复图片 28 | def reply_image(base64_image_data, file_content, e_context): 29 | try: 30 | image_data = base64.b64decode(base64_image_data) 31 | image_storage = io.BytesIO() 32 | image_storage.write(image_data) 33 | image_storage.seek(0) 34 | MessageReply.reply_Image_Message(True, image_storage, e_context) 35 | FileHandle.delete_file(file_content) 36 | except Exception as e: 37 | logger.error(f"回复图片时出现错误:{e}") 38 | 39 | 40 | # 获取访问令牌 41 | def get_access_token(api_key, secret_key): 42 | token_url = "https://aip.baidubce.com/oauth/2.0/token" 43 | token_params = { 44 | "grant_type": "client_credentials", 45 | "client_id": api_key, 46 | "client_secret": secret_key 47 | } 48 | try: 49 | response = requests.post(token_url, params=token_params) 50 | response.raise_for_status() 51 | return response.json().get("access_token") 52 | except requests.RequestException as e: 53 | logger.error(f"获取访问令牌时出现错误:{e}") 54 | return None 55 | 56 | 57 | # 处理图片 58 | def process_image(encoded_image, access_token): 59 | process_url = f"https://aip.baidubce.com/rest/2.0/image-process/v1/image_definition_enhance" 60 | headers = { 61 | 'Content-Type': 'application/x-www-form-urlencoded', 62 | 'Accept': 'application/json' 63 | } 64 | payload = f"image={encoded_image}" 65 | # logger.debug(payload) 66 | try: 67 | response = requests.post(process_url, headers=headers, data=payload, params={"access_token": access_token}) 68 | response.raise_for_status() 69 | logger.debug(response.json()) 70 | return response.json().get('image') 71 | except requests.RequestException as e: 72 | logger.error(f"API请求失败:{e}") 73 | return None 74 | -------------------------------------------------------------------------------- /util/file_handle.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import json 3 | import mimetypes 4 | import os 5 | 6 | from common.log import logger 7 | 8 | 9 | # 读取配置文件 10 | def get_config_path(json_path: str): 11 | cursor = os.path.dirname(__file__) 12 | return os.path.join(cursor, json_path) 13 | 14 | 15 | # 读取配置文件 16 | def update_config(config_path, user_id, append): 17 | with open(config_path, "r", encoding="utf-8") as f: 18 | config = json.load(f) 19 | if append: 20 | config["use_group"].append(user_id) 21 | else: 22 | config["use_group"].remove(user_id) 23 | with open(config_path, "w", encoding="utf-8") as f: 24 | json.dump(config, f, indent=4, ensure_ascii=False) 25 | 26 | 27 | # 用于图片和文件转成base64 28 | def file_toBase64(file_path: str): 29 | if os.path.isfile(file_path): 30 | try: 31 | with open(file_path, 'rb') as file: 32 | image_data = file.read() 33 | base64_image = base64.b64encode(image_data).decode('utf-8') 34 | # 获取文件的MIME类型 35 | mime_type, _ = mimetypes.guess_type(file_path, strict=False) 36 | if mime_type is None: 37 | if file_path.endswith(".docx"): 38 | mime_type = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" 39 | elif file_path.endswith(".xlsx"): 40 | mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" 41 | else: 42 | mime_type = "application/octet-stream" 43 | logger.info(f"文件路径: {file_path}") 44 | logger.info(f"文件MIME类型: {mime_type}") 45 | if mime_type is None: 46 | mime_type = "application/octet-stream" # 默认MIME类型 47 | base64_image = f"data:{mime_type};base64,{base64_image}" 48 | return base64_image 49 | except Exception as e: 50 | logger.error(f"读取文件时出错: {e}") 51 | return None 52 | else: 53 | logger.warning(f"文件不存在: {file_path}") 54 | return None 55 | 56 | 57 | def delete_file(file_content): 58 | try: 59 | if os.path.isfile(file_content): 60 | os.remove(file_content) 61 | return "🥰图片已成功删除\n🧸感谢您的使用!" 62 | except Exception as e: 63 | logger.error(f"{str(e)}") 64 | return "😭文件不存在或已删除" 65 | -------------------------------------------------------------------------------- /util/image_handle.py: -------------------------------------------------------------------------------- 1 | # 用于处理图片大小的工具函数 2 | def adjust_image(width, height, maxsize: int): 3 | # 确保最小尺寸为 768 4 | if width < 768 or height < 768: 5 | scale_factor = max(768 / width, 768 / height) 6 | width = int(width * scale_factor) 7 | height = int(height * scale_factor) 8 | 9 | # 确保最大尺寸不超过 maxsize 10 | if width > maxsize or height > maxsize: 11 | scale_factor = min(maxsize / width, maxsize / height) 12 | width = int(width * scale_factor) 13 | height = int(height * scale_factor) 14 | 15 | return int(width), int(height) 16 | 17 | 18 | # 用于格式化图片 URL 的工具函数 19 | def format_image_url(use_https, host, port, file_url, file_content): 20 | protocol = "https" if use_https else "http" 21 | port_part = f":{port}" if port else "" 22 | image_path = f"{file_url}{file_content}" 23 | image_url = f"{protocol}://{host}{port_part}/{image_path}" 24 | return image_url 25 | -------------------------------------------------------------------------------- /util/translate_prompt.py: -------------------------------------------------------------------------------- 1 | import langid 2 | 3 | from bot import bot_factory 4 | from bridge.bridge import Bridge 5 | from common.log import logger 6 | 7 | 8 | # 翻译提示 9 | def translatePrompt(is_use_fanyi, bot_prompt, prompt, params, session_id): 10 | if prompt: 11 | lang = langid.classify(prompt)[0] 12 | if lang != "en": 13 | logger.info("[SD] translate prompt from {} to en".format(lang)) 14 | try: 15 | if not is_use_fanyi: 16 | bot = bot_factory.create_bot(Bridge().btype['chat']) 17 | session = bot.sessions.build_session(session_id, bot_prompt) 18 | # 不带有之前的提示词 19 | # session.add_query(prompt) 20 | result = bot.reply_text(session) 21 | prompt = result['content'] 22 | else: 23 | prompt = Bridge().fetch_translate(prompt, to_lang="en") 24 | except Exception as e: 25 | logger.info("[SD] translate failed: {}".format(e)) 26 | logger.info("[SD] translated prompt={}".format(prompt)) 27 | params["prompt"] += f", {prompt}" 28 | 29 | 30 | # 返回翻译提示 31 | def simple_translatePrompt(is_use_fanyi, bot_prompt, prompt, session_id): 32 | if prompt: 33 | lang = langid.classify(prompt)[0] 34 | if lang != "en": 35 | logger.info("[SD] translate prompt from {} to en".format(lang)) 36 | try: 37 | if not is_use_fanyi: 38 | bot = bot_factory.create_bot(Bridge().btype['chat']) 39 | session = bot.sessions.build_session(session_id, bot_prompt) 40 | # 不带有之前的提示词 41 | # session.add_query(prompt) 42 | result = bot.reply_text(session) 43 | prompt = result['content'] 44 | else: 45 | prompt = Bridge().fetch_translate(prompt, to_lang="en") 46 | return prompt 47 | except Exception as e: 48 | logger.info("[SD] translate failed: {}".format(e)) 49 | logger.info("[SD] translated prompt={}".format(prompt)) 50 | return None 51 | -------------------------------------------------------------------------------- /work/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/work/__init__.py -------------------------------------------------------------------------------- /work/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/work/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /work/__pycache__/__init__.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/work/__pycache__/__init__.cpython-312.pyc -------------------------------------------------------------------------------- /work/__pycache__/__init__.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/work/__pycache__/__init__.cpython-38.pyc -------------------------------------------------------------------------------- /work/__pycache__/common.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/work/__pycache__/common.cpython-311.pyc -------------------------------------------------------------------------------- /work/__pycache__/common.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/work/__pycache__/common.cpython-312.pyc -------------------------------------------------------------------------------- /work/__pycache__/common.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yanyutin753/pictureChange/9453ae4c87fc1e61dd7fa59db88bde0451fceff8/work/__pycache__/common.cpython-38.pyc -------------------------------------------------------------------------------- /work/common.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import urllib.parse 4 | 5 | import requests 6 | 7 | import plugins.pictureChange.message.message_sd_reply as SDReply 8 | import plugins.pictureChange.util.baidu_image as Baidu_Image 9 | from bridge.context import ContextType 10 | from common.log import logger 11 | from plugins import EventAction 12 | from plugins.pictureChange import util 13 | from plugins.pictureChange.message import message_handle as MessageHandle 14 | from plugins.pictureChange.message import message_reply as MessageReply 15 | from plugins.pictureChange.message import message_type as MessageType 16 | from plugins.pictureChange.message.music_handle import music_handle 17 | from plugins.pictureChange.util import file_handle as FileHandle 18 | 19 | 20 | def process_image_music_content(openai_api_base, openai_api_key, image_recognize_model, prompt, recognize_func, 21 | reply_message_method, e_context): 22 | context = e_context['context'] 23 | session_id = context.kwargs.get('session_id') 24 | file_content = context.content.strip().split()[2] 25 | if os.path.isfile(file_content): 26 | try: 27 | file_url = FileHandle.file_toBase64(file_content) 28 | reply_text = recognize_func(openai_api_base, 29 | openai_api_key, prompt, file_url, 30 | image_recognize_model, session_id) 31 | return reply_text 32 | except Exception as e: 33 | reply_text = str(e) 34 | logger.error("Processing failed: {}".format(str(e))) 35 | reply_message_method(True, reply_text, e_context) 36 | return None 37 | else: 38 | reply_text = "找不到相应的图片文件,请联系管理员!" 39 | reply_message_method(True, reply_text, e_context) 40 | return None 41 | 42 | 43 | # 描述图片信息 44 | def process_image_content(openai_api_base, openai_api_key, image_recognize_model, prompt, recognize_func, 45 | error_message, reply_message_method, e_context): 46 | context = e_context['context'] 47 | session_id = context.kwargs.get('session_id') 48 | file_content = context.content.strip().split()[2] 49 | if os.path.isfile(file_content): 50 | try: 51 | file_url = FileHandle.file_toBase64(file_content) 52 | replyText = recognize_func(openai_api_base + "/chat/completions", 53 | openai_api_key, prompt, file_url, 54 | image_recognize_model, session_id) 55 | except Exception as e: 56 | replyText = error_message 57 | logger.error("Processing failed: {}".format(str(e))) 58 | else: 59 | replyText = error_message 60 | reply_message_method(True, replyText, e_context) 61 | 62 | 63 | # 描述文件信息 64 | def process_file_content(openai_api_base, openai_api_key, file_recognize_model, prompt, recognize_func, 65 | error_message, 66 | reply_message_method, 67 | e_context): 68 | context = e_context['context'] 69 | session_id = context.kwargs.get('session_id') 70 | file_content = context.content.strip() 71 | if os.path.isfile(file_content): 72 | try: 73 | file_url = FileHandle.file_toBase64(file_content) 74 | replyText = recognize_func(openai_api_base + "/chat/completions", 75 | openai_api_key, prompt, file_url, 76 | file_recognize_model, session_id) 77 | except Exception as e: 78 | replyText = error_message 79 | logger.error("Processing failed: {}".format(str(e))) 80 | else: 81 | replyText = error_message 82 | reply_message_method(True, replyText, e_context) 83 | 84 | 85 | class Common: 86 | 87 | @staticmethod 88 | def process_image(openai_api_base, openai_api_key, image_recognize_model, image_recognize_prompt, e_context): 89 | process_image_content(openai_api_base, openai_api_key, image_recognize_model, 90 | image_recognize_prompt, util.recognize_image, 91 | "🥰请先发送图片给我,我将为您进行图像分析", 92 | MessageReply.reply_Text_Message, e_context) 93 | 94 | @staticmethod 95 | def process_file(openai_api_base, openai_api_key, file_recognize_model, file_recognize_prompt, e_context): 96 | process_file_content(openai_api_base, openai_api_key, file_recognize_model, 97 | file_recognize_prompt, util.recognize_file, 98 | "🥰请先发送文件给我,我将为您进行文件分析", 99 | MessageReply.reply_Text_Message, e_context) 100 | 101 | # 图片创作 102 | @staticmethod 103 | def process_image_create(is_use_fanyi, bot_prompt, rules, Model, request_bot_name, start_args, params, options, 104 | is_wecom, e_context): 105 | try: 106 | context = e_context['context'] 107 | content = MessageHandle.init_content(e_context) 108 | session_id = context.kwargs.get('session_id') 109 | 110 | text = "🚀图片生成中~~~\n⏳请您耐心等待1-2分钟\n✨请稍等片刻✨✨\n❤️感谢您的耐心与支持" 111 | MessageReply.tem_reply_Text_Message(text, e_context) 112 | 113 | SDReply.create_Image(content, is_use_fanyi, bot_prompt, rules, Model, request_bot_name, start_args, params, 114 | options, session_id, is_wecom, e_context) 115 | except Exception as e: 116 | raise RuntimeError(f"图片处理发生错误: {e}") from e 117 | 118 | # 图片自定义图生图 119 | @staticmethod 120 | def process_image_custom(is_use_fanyi, bot_prompt, Model, request_bot_name, start_args, 121 | negative_prompt, maxsize, is_wecom, e_context): 122 | try: 123 | context = e_context['context'] 124 | content = MessageHandle.init_content(e_context) 125 | session_id = context.kwargs.get('session_id') 126 | 127 | text = "🚀图片生成中~~~\n⏳请您耐心等待1-2分钟\n✨请稍等片刻✨✨\n❤️感谢您的耐心与支持" 128 | MessageReply.tem_reply_Text_Message(text, e_context) 129 | 130 | SDReply.custom_Image(content, is_use_fanyi, bot_prompt, Model, request_bot_name, start_args, 131 | session_id, negative_prompt, maxsize, is_wecom, e_context) 132 | except Exception as e: 133 | raise RuntimeError(f"图片处理发生错误: {e}") from e 134 | 135 | # 图片按照config配置图生图 136 | @staticmethod 137 | def process_image_change(Model, request_bot_name, start_args, default_options, 138 | roleRule_options, denoising_strength, cfg_scale, 139 | prompt, negative_prompt, title, maxsize: int, is_wecom, e_context): 140 | try: 141 | content = MessageHandle.init_content(e_context) 142 | 143 | text = "🚀图片生成中~~~\n⏳请您耐心等待1-2分钟\n✨请稍等片刻✨✨\n❤️感谢您的耐心与支持" 144 | MessageReply.tem_reply_Text_Message(text, e_context) 145 | 146 | SDReply.change_Image(content, Model, request_bot_name, start_args, default_options, 147 | roleRule_options, denoising_strength, cfg_scale, 148 | prompt, negative_prompt, title, maxsize, is_wecom, e_context) 149 | except Exception as e: 150 | raise RuntimeError(f"图片处理发生错误: {e}") from e 151 | 152 | # 图片变换 153 | @staticmethod 154 | def process_image_transform(Model, request_bot_name, start_args, use_https, host, port, file_url, 155 | prompt, negative_prompt, maxsize: int, is_wecom, e_context): 156 | try: 157 | content = MessageHandle.init_content(e_context) 158 | 159 | text = "🚀图片生成中~~~\n⏳请您耐心等待1-2分钟\n✨请稍等片刻✨✨\n❤️感谢您的耐心与支持" 160 | MessageReply.tem_reply_Text_Message(text, e_context) 161 | 162 | SDReply.transform_Image(content, Model, request_bot_name, start_args, use_https, host, port, file_url, 163 | prompt, negative_prompt, maxsize, is_wecom, e_context) 164 | except Exception as e: 165 | raise RuntimeError(f"图片处理发生错误: {e}") from e 166 | 167 | # 图片放大 168 | @staticmethod 169 | def process_image_large(use_https, host, port, file_url, e_context): 170 | content = MessageHandle.init_content(e_context) 171 | 172 | text = "🚀图片生成中~~~\n⏳请您耐心等待1-2分钟\n✨请稍等片刻✨✨\n❤️感谢您的耐心与支持" 173 | MessageReply.tem_reply_Text_Message(text, e_context) 174 | try: 175 | SDReply.large_Image(content, use_https, host, port, file_url, e_context) 176 | except Exception as e: 177 | raise RuntimeError(f"图片处理发生错误: {e}") from e 178 | 179 | # 接收图片初始化发送信息 180 | @staticmethod 181 | def process_init_image(request_bot_name, role_options, use_stable_diffusion, 182 | use_music_handle, use_file_handle, is_wecom, e_context): 183 | content = MessageHandle.init_content(e_context) 184 | file_content = urllib.parse.quote(content) 185 | if e_context['context'].type == ContextType.IMAGE: 186 | replyText = MessageType.in_image_reply(file_content, request_bot_name, role_options, use_stable_diffusion, 187 | use_music_handle, use_file_handle, is_wecom) 188 | MessageReply.reply_Text_Message(True, replyText, e_context) 189 | 190 | # 接收图片链接初始化发送信息 191 | @staticmethod 192 | def process_init_image_url(request_bot_name, role_options, use_stable_diffusion, 193 | use_music_handle, use_file_handle, is_wecom, e_context): 194 | try: 195 | content = MessageHandle.init_content(e_context) 196 | response = requests.get(content) 197 | file_content = str(int(time.time())) + ".jpg" 198 | if response.status_code == 200: 199 | with open(file_content, 'wb') as file: 200 | file.write(response.content) 201 | replyText = MessageType.in_image_reply(file_content, request_bot_name, role_options, 202 | use_stable_diffusion, use_file_handle, use_music_handle, 203 | is_wecom) 204 | MessageReply.reply_Text_Message(True, replyText, e_context) 205 | else: 206 | logger.error("下载失败") 207 | e_context.action = EventAction.BREAK_PASS 208 | except Exception as e: 209 | raise RuntimeError(f"图片处理发生错误: {e}") from e 210 | 211 | # 处理百度图片(图像修复) 212 | @staticmethod 213 | def process_baidu_image(baidu_api_key, baidu_secret_key, e_context): 214 | try: 215 | content = MessageHandle.init_content(e_context) 216 | file_content = content.split()[2] 217 | 218 | if os.path.isfile(file_content): 219 | encoded_image = Baidu_Image.read_and_encode_image(file_content) 220 | if not encoded_image: 221 | return 222 | MessageReply.tem_reply_Text_Message( 223 | "🚀图片生成中~~~\n⏳请您耐心等待1-2分钟\n✨请稍等片刻✨✨\n❤️感谢您的耐心与支持", 224 | e_context 225 | ) 226 | access_token = Baidu_Image.get_access_token(baidu_api_key, baidu_secret_key) 227 | if not access_token: 228 | MessageReply.reply_Error_Message(True, "无法获取百度AI接口访问令牌", e_context) 229 | return 230 | processed_image_data = Baidu_Image.process_image(encoded_image, access_token) 231 | if processed_image_data: 232 | Baidu_Image.reply_image(processed_image_data, file_content, e_context) 233 | else: 234 | MessageReply.reply_Error_Message(True, "未找到转换后的图像数据", e_context) 235 | else: 236 | MessageReply.reply_Error_Message(True, "🥰请先发送图片给我,我将为您进行图像修复", e_context) 237 | except Exception as e: 238 | raise RuntimeError(f"图片处理发生错误: {e}") from e 239 | 240 | @staticmethod 241 | def process_text_music(bot_prompt, url, key, model, prompt, is_wecom, e_context): 242 | try: 243 | url = url + "/chat/completions" 244 | music_handle.text_to_music(bot_prompt, True, url, key, prompt, model, is_wecom, e_context) 245 | except Exception as e: 246 | raise RuntimeError(f"文生音乐发生错误: {e}") from e 247 | 248 | @staticmethod 249 | def process_image_music(url, key, image_recognize_model, suno_model, bot_prompt, is_wecom, e_context): 250 | try: 251 | url = url + "/chat/completions" 252 | image_text = process_image_music_content(url, key, image_recognize_model, bot_prompt, util.recognize_image, 253 | MessageReply.reply_Text_Message, e_context) 254 | music_handle.text_to_music(bot_prompt, False, url, key, image_text, suno_model, is_wecom, e_context) 255 | except Exception as e: 256 | raise RuntimeError(f"图生音乐发生错误: {e}") from e 257 | --------------------------------------------------------------------------------