Skip to content

Commit

Permalink
0.5.1
Browse files Browse the repository at this point in the history
  • Loading branch information
sena-nana committed Nov 21, 2022
1 parent 7ce7243 commit 0853bd3
Show file tree
Hide file tree
Showing 20 changed files with 367 additions and 194 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
说明书:https://sena-nana.github.io/MutsukiDocs/

环境需求:
- Python>=3.7
- Python>=3.8
- nonebot2>=b4
## 依赖
aiohttp,aiofiles
Expand All @@ -17,11 +17,11 @@ aiohttp,aiofiles
1. NOVELAI_TAG="str" 所有生成都会事先加上这些tag,用来塞私货或者精简指令
2. NOVELAI_CD=int 单个用户的cd,默认为60s
3. NOVELAI_LIMIT=bool 是否启用并行限制,启用的话,bot会将请求加入队列,在服务器返回之前的结果后再申请。可以防止请求过快,在不知道官方会不会封号的情况下有心理安慰作用。默认开启
4. NOVELAI_API_DOMAIN="str" 白嫖服务器时修改,不设置默认官方服务器(未完成)
5. NOVELAI_SITE_DOMAIN="str" 白嫖服务器时修改,不设置默认官方服务器(未完成)
6. NOVELAI_SAVE_PIC=bool 是否自动保存到本地,默认开启
5. NOVELAI_SITE="str" 自定义后端时设置
6. NOVELAI_SAVE=int 是否自动保存到本地,默认开启(1)
7. NOVELAI_MODE="str" 设置插件运行模式,默认"novelai",详细查看说明书(还没写)
8. NOVELAI_PAID=int 是否启用已付费模式,默认为0(禁用),1为点数模式,详细查看说明书,2为无限制模式
8. NOVELAI_PAID=int 是否启用已付费模式,默认为0(禁用),1为点数模式,2为严格点数模式,3为无限制模式
9. NOVELAI_DAYLIMIT=int 是否启用每日次数限制,默认为0(禁用),值为次数
9. NOVELAI_BAN=list[int] 设置在哪些群禁用,默认为空,运行时可通过指令修改
10. NOVELAI_H=bool 是否启用r18模式,默认关闭(开启后被风控或者封号不要发issue)
10. NOVELAI_ONCEMAX=int 单次允许生成的最大数量
Expand Down Expand Up @@ -59,8 +59,8 @@ novelai模式需要token才能运行,所以你需要首先购买novelai的25

## FEATURE
- NAIFU
- [ ] 支持文本生图
- [ ] 支持以图生图
- [x] 支持文本生图
- [x] 支持以图生图
- WEBUI
- [ ] 支持文本生图
- [ ] 支持以图生图
Expand All @@ -73,7 +73,8 @@ novelai模式需要token才能运行,所以你需要首先购买novelai的25
- 速度限制
- [x] 支持内置CD和并行限制
- [x] 付费点数制
- [ ] 每日上限制
- [x] 严格点数制
- [x] 每日上限制
- 娱乐功能
- [x] 支持查询图片词条
- [ ] 随机少女
Expand All @@ -89,7 +90,6 @@ novelai模式需要token才能运行,所以你需要首先购买novelai的25
- [x] 支持机翻词条为英文
- [x] 生成图片自动保存至data/novelai文件夹
- [x] 支持开关禁止nsfw
- [ ] 支持私聊
- [x] 更新提醒
- [ ] 支持多台后端负载均衡
- [ ] 说明书
60 changes: 37 additions & 23 deletions nonebot-plugin-novelai/aidraw.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
import time
import random
from collections import deque
import aiohttp
from aiohttp.client_exceptions import ClientConnectorError
from argparse import Namespace
from asyncio import get_running_loop
from nonebot import get_bot, on_shell_command

from nonebot.adapters.onebot.v11 import GroupMessageEvent, MessageSegment, Bot
from nonebot.rule import ArgumentParser
from nonebot.permission import SUPERUSER
from nonebot.log import logger
from nonebot.params import ShellCommandArgs

from .config import config, nickname
from .utils.data import lowQuality, basetag
from .novelai import post, FIFO
from .mode import post, FIFO
from .extension.anlas import anlas_check, anlas_set
from .extension.daylimit import DayLimit
from .utils.save import save_img
from .utils.prepocess import prepocess_tags
from .version import version
from .outofdate.explicit_api import check_safe_method

from .utils import sendtosuperuser
cd = {}
gennerating = False
limit_list = deque([])

aidraw_parser = ArgumentParser()
aidraw_parser.add_argument("tags", default=" ", nargs="*", help="标签")
aidraw_parser.add_argument("-p", "--shape", "-形状",
default="p", help="画布形状", dest="shape")
aidraw_parser.add_argument("tags", nargs="*", help="标签")
aidraw_parser.add_argument("-p", "--shape", "-形状", help="画布形状", dest="shape")
aidraw_parser.add_argument("-w", "--width", "-宽", help="画布宽", dest="width")
aidraw_parser.add_argument("-h", "--height", "-高", help="画布高", dest="height")
aidraw_parser.add_argument("-c", "--scale", "-规模",
type=float, help="规模", dest="scale")
aidraw_parser.add_argument(
"-s", "--seed", "-种子", default=random.randint(0,4294967295), type=int, help="种子", dest="seed")
"-s", "--seed", "-种子", type=int, help="种子", dest="seed")
aidraw_parser.add_argument("-u", "--count", "-数量",
type=int, default=1, help="生成数量", dest="count")
aidraw_parser.add_argument("-t", "--steps", "-步数",
Expand All @@ -47,7 +50,7 @@

aidraw = on_shell_command(
".aidraw",
aliases={"绘画", "咏唱", "召唤"},
aliases={"绘画", "咏唱", "召唤", "aidraw"},
parser=aidraw_parser,
priority=5
)
Expand All @@ -62,10 +65,18 @@ async def aidraw_get(bot: Bot,
group_id = str(event.group_id)
# 判断是否禁用,若没禁用,进入处理流程
if await config.get_value(group_id, "on"):
message = ""
# 判断最大生成数量
if args.count>config.novelai_max:
await aidraw.send(f"批量生成数量过多,自动修改为{config.novelai_max}")
args.count=config.novelai_max
if args.count > config.novelai_max:
message = message+f",批量生成数量过多,自动修改为{config.novelai_max}"
args.count = config.novelai_max
# 判断次数限制
if config.novelai_daylimit and not await SUPERUSER(bot,event):
left = DayLimit.count(user_id, args.count)
if left == -1:
aidraw.finish(f"今天你的次数不够了哦")
else:
message = message + f",今天你还能够生成{left}张"
# 判断cd
nowtime = time.time()
deltatime = nowtime - cd.get(user_id, 0)
Expand All @@ -76,7 +87,7 @@ async def aidraw_get(bot: Bot,
cd[user_id] = nowtime

# 初始化参数
fifo = FIFO(user_id=user_id, group_id=group_id, args=args)
fifo = FIFO(user_id=user_id, group_id=group_id, **vars(args))
error = await prepocess_tags(fifo.tags) or await prepocess_tags(fifo.ntags)
if error:
await aidraw.finish(error)
Expand All @@ -98,6 +109,7 @@ async def aidraw_get(bot: Bot,
logger.info(f"检测到图片,自动切换到以图生图,正在获取图片")
async with session.get(img_url) as resp:
fifo.add_image(await resp.read())
message = f",识别到图片,自动切换至以图生图"+message
else:
await aidraw.finish(f"以图生图功能已禁用")
logger.debug(fifo)
Expand All @@ -106,18 +118,18 @@ async def aidraw_get(bot: Bot,
anlascost = fifo.cost
hasanlas = await anlas_check(fifo.user_id)
if hasanlas >= anlascost:
await wait_fifo(fifo, anlascost, hasanlas - anlascost)
await wait_fifo(fifo, anlascost, hasanlas - anlascost, message=message)
else:
await aidraw.finish(f"你的点数不足,你的剩余点数为{hasanlas}")
else:
await wait_fifo(fifo)
await wait_fifo(fifo, message=message)


async def wait_fifo(fifo, anlascost=None, anlas=None):
async def wait_fifo(fifo, anlascost=None, anlas=None, message=""):
# 创建队列
list_len = get_wait_num()
has_wait = f"排队中,你的前面还有{list_len}人"
no_wait = "请稍等,图片生成中"
has_wait = f"排队中,你的前面还有{list_len}人"+message
no_wait = "请稍等,图片生成中"+message
if anlas:
has_wait += f"\n本次生成消耗点数{anlascost},你的剩余点数为{anlas}"
no_wait += f"\n本次生成消耗点数{anlascost},你的剩余点数为{anlas}"
Expand Down Expand Up @@ -153,7 +165,7 @@ async def generate(fifo: FIFO):
logger.exception("生成失败")
message = f"生成失败,"
for i in e.args:
message += i
message += str(i)
await bot.send_group_msg(
message=message,
group_id=fifo.group_id
Expand Down Expand Up @@ -208,17 +220,19 @@ async def generate(fifo: FIFO):

async def _run_gennerate(fifo: FIFO):
# 处理单个请求
img_bytes = await post(fifo)
if isinstance(img_bytes, str):
raise RuntimeError(img_bytes)
message = MessageSegment.text(fifo.format())

try:
img_bytes = await post(fifo)
except ClientConnectorError:
await sendtosuperuser(f"远程服务器拒绝连接,请检查配置是否正确,服务器是否已经启动")
raise RuntimeError(f"远程服务器拒绝连接,请检查配置是否正确,服务器是否已经启动")
# 若启用ai检定,取消注释下行代码,并将构造消息体部分注释
# message = await check_safe_method(fifo, img_bytes, message)
# 构造消息体并保存图片
for i in img_bytes:
await save_img(fifo, i, fifo.group_id)
message += MessageSegment.image(i)
for i in fifo.format():
message = MessageSegment.text(i)
# 扣除点数
if fifo.cost > 0:
await anlas_set(fifo.user_id, -fifo.cost)
Expand Down
122 changes: 122 additions & 0 deletions nonebot-plugin-novelai/base/fifo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import base64
from io import BytesIO
import time
from PIL import Image
from nonebot import get_driver
from ..utils.data import shapemap
from ..config import config
import random


class FIFO_BASE():
model: str = ""
sampler: str = ""

def __init__(self,
user_id: str,
group_id: str,
tags: list[str] = [],
seed: int = None,
scale: int = None,
strength: float = None,
steps: int = None,
count: int = None,
noise: float = None,
ntags: list[str] = [],
shape: str = "p",
width: int = None,
height: int = None,
**kwargs):
self.time = time.strftime("%Y-%m-%d %H:%M:%S")
self.user_id: str = user_id
self.tags: str = "".join([i+" " for i in tags])
self.seed: list[int] = [seed or random.randint(0, 4294967295)]
self.group_id: str = group_id
self.scale: int = int(scale or 11)
self.strength: float = strength or 0.7
self.count: int = count or 1
self.steps: int = steps or 28
self.noise: float = noise or 0.2
self.ntags: str = "".join([i+" " for i in ntags])
self.img2img: bool = False
self.image: str = None
if width and height:
self.shape_set(width,height)
else:
self.width, self.height = shapemap.get(shape or "p")
# 数值合法检查
if self.steps <= 0 or self.steps > 50:
self.steps = 28
# 多图时随机填充剩余seed
for i in range(self.count-1):
self.seed.append(random.randint(0, 4294967295))
# 计算cost
self.update_cost()

def update_cost(self):
if config.novelai_paid == 1:
anlas = 0
if (self.width * self.height > 409600) or self.image or self.count > 1:
anlas += round(self.width * self.height *
self.strength * self.count * self.steps / 2293750)
if self.user_id in get_driver().config.superusers:
self.cost = 0
else:
self.cost = anlas
elif config.novelai_paid == 2:
anlas += round(self.width * self.height *
self.strength * self.count * self.steps / 2293750)
if self.user_id in get_driver().config.superusers:
self.cost = 0
else:
self.cost = anlas
else:
self.cost = 0

def add_image(self, image):
# 根据图片重写长宽
tmpfile = BytesIO(image)
image = Image.open(tmpfile)
width, height = image.size
self.shape_set(width, height)
self.image = str(base64.b64encode(self.image), "utf-8")
self.steps = 50
self.img2img = True
self.update_cost()

def shape_set(self, width, height):
base = round(min(width,height)/64)
if base>16:
base=16
if width >= height:
self.width = round(width / height * base) * 64
self.height = 64*base
else:
self.height = round(height / width * base) * 64
self.width = 64*base

def body(self):
pass

def keys(self):
return (
"seed","scale", "strength", "noise", "sampler", "model", "steps", "width", "height", "img2img")

def __getitem__(self, item):
return getattr(self, item)

def format(self):
dict_self = dict(self)
list=[]
str = ""
for i, v in dict_self.items():
str += f"{i}={v}\n"
list.append(str)
list.append(f"tags={dict_self['tags']}\n")
list.append(f"ntags={dict_self['ntags']}")
return list
def __repr__(self):
return f"time={self.time}\nuser_id={self.user_id}\ngroup_id={self.group_id}\ncost={self.cost}\ncount={self.count}\n"+"".join(self.format())

def __str__(self):
return self.__repr__().replace("\n", ";")
26 changes: 26 additions & 0 deletions nonebot-plugin-novelai/base/post.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import base64
from io import BytesIO

import aiohttp
from nonebot.log import logger
from .fifo import FIFO_BASE
from ..utils import png2jpg

async def post_base(fifo: FIFO_BASE, header, post_api):
# 请求交互
img_bytes = []
async with aiohttp.ClientSession(headers=header) as session:
for i in range(fifo.count):
# 向服务器发送请求
async with session.post(post_api, json=fifo.body(i)) as resp:
if resp.status not in [200, 201]:
raise RuntimeError("与服务器沟通时发生{resp.status}错误")
img = await resp.text()
img = img.split("data:")[1]
logger.debug(f"获取到返回图片,正在处理")

# 将图片转化为jpg(BytesIO)
image = BytesIO(base64.b64decode(img))
image_new = await png2jpg(image)
img_bytes.append(image_new)
return img_bytes
Loading

0 comments on commit 0853bd3

Please sign in to comment.