# `7MMM. ,MMF'`7MMM. ,MMF' `7MMM. ,MMF' `7MM
# MMMb dPMM MMMb dPMM MMMb dPMM MM
# M YM ,M MM M YM ,M MM M YM ,M MM ,pW"Wq. ,M""bMM ,pP"Ybd
# M Mb M' MM M Mb M' MM M Mb M' MM 6W' `Wb ,AP MM 8I `"
# M YM.P' MM M YM.P' MM mmmmm M YM.P' MM 8M M8 8MI MM `YMMMa.
# M `YM' MM M `YM' MM M `YM' MM YA. ,A9 `Mb MM L. I8
# .JML. `' .JMML..JML. `' .JMML. .JML. `' .JMML.`Ybmd9' `Wbmd"MML.M9mmmP'
#
# (c) 2023 — licensed under Apache 2.0 — https://www.apache.org/licenses/LICENSE-2.0
# meta pic: https://img.icons8.com/?size=512&id=17387&format=png
# meta developer: @mm_mods
__version__ = "1.0"
import asyncio
import aiohttp
from hikka import loader, utils
from telethon.tl.patched import Message
import logging
import random
logger = logging.getLogger(__name__)
name_models = {
"Analog Diffusion V1": "analog-diffusion-1.0.ckpt [9ca13f02]",
"Anything V3": "anythingv3_0-pruned.ckpt [2700c435]",
"Anything V4": "anything-v4.5-pruned.ckpt [65745d25]",
"Anything V5": "anythingV5_PrtRE.safetensors [893e49b9]",
"Orangemix": "AOM3A3_orangemixs.safetensors [9600da17]",
"Deliberate V2": "deliberate_v2.safetensors [10ec4b29]",
"Dreamlike Diffusion V1": "dreamlike-diffusion-1.0.safetensors [5c9fd6e0]",
"Dreamlike Diffusion V2": "dreamlike-diffusion-2.0.safetensors [fdcf65e7]",
"Dreamshaper V5": "dreamshaper_5BakedVae.safetensors [a3fbf318]",
"Dreamshaper V6": "dreamshaper_6BakedVae.safetensors [114c8abb]",
"Elldreth's Vivid Mix": "elldreths-vivid-mix.safetensors [342d9d26]",
"Lyriel V1.5": "lyriel_v15.safetensors [65d547c5]",
"Meina V9": "meinamix_meinaV9.safetensors [2ec66ab0]",
"OpenJourney V4": "openjourney_V4.ckpt [ca2f377f]",
"Portrait V1": "portrait+1.0.safetensors [1400e684]",
"Realistic Vision V2": "Realistic_Vision_V2.0.safetensors [79587710]",
"Rev Animated V1.22": "revAnimated_v122.safetensors [3f4fefd9]",
"Riffusion V1": "riffusion-model-v1.ckpt [3aafa6fe]",
"StableDiffusion V1.4": "sdv1_4.ckpt [7460a6fa]",
"StableDiffusion V1.5": "v1-5-pruned-emaonly.ckpt [81761151]",
"Shonin's Beautiful People V1": "shoninsBeautiful_v10.safetensors [25d8c546]",
"The Ally's Mix II": "theallys-mix-ii-churned.safetensors [5d9225a4]",
"Timeless V1": "timeless-1.0.ckpt [7c4971d4]",
}
samplers = ["Euler", "Euler a", "Heun", "DPM++ 2M Karras", "DDIM"]
# noinspection PyCallingNonCallable
@loader.tds
class ProdiaMod(loader.Module):
"""Image generator based on Prodia API. No API key required."""
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"model",
"StableDiffusion V1.5",
lambda: self.strings("model-h"),
validator=loader.validators.Choice(list(name_models.keys())),
),
loader.ConfigValue(
"cfg",
8,
lambda: self.strings("cfg-h"),
validator=loader.validators.Integer(minimum=0, maximum=20),
),
loader.ConfigValue(
"sampler",
"Euler",
lambda: self.strings("sampler-h"),
validator=loader.validators.Choice(samplers),
),
loader.ConfigValue(
"steps",
30,
lambda: self.strings("steps-h"),
validator=loader.validators.Integer(minimum=1, maximum=30),
),
loader.ConfigValue(
"neg_def",
"",
lambda: self.strings("ndef-h"),
validator=loader.validators.String(),
),
)
strings = {
"name": "Prodia IG",
"model-h": "An initial image set on which the model is based.",
"cfg-h": "The higher the value, the closer to prompt the image will be.",
"sampler-h": "The algorithm by which the image will be generated.",
"steps-h": "The number of steps to generate the image, the higher the value, "
"the more detailed the image will be.",
"ndef-h": "Default negative prompt. Negative prompt makes AI to NOT generate described in it thing.",
"args?": "🟡 You provided no args…",
"working": "🎨 Working on your image…\n"
"Prompt: {}{}\n\n"
"Provided parameters:\n"
"Model: {} — ID {}\n"
"CFG: {}\n"
"Sampler: {}\n"
"Steps: {}",
"done": "🎉 Your image is done!\n"
"Prompt: {}{}\n\n"
"Model: {} — ID {}\n"
"CFG: {}\n"
"Sampler: {}\n"
"Steps: {}\n",
"neg_prompt": "\nNegative prompt: {}",
}
strings_ru = {
"name": "Prodia IG",
"model-h": "Начальный набор изображений, на основе которого построена модель.",
"cfg-h": "Чем выше значение, тем более близким к запросу будет изображение.",
"sampler-h": "Алгоритм, по которому будет сгенерировано изображение.",
"steps-h": "Количество шагов для генерации изображения, чем выше значение, "
"тем более детализированным будет изображение.",
"ndef-h": "Стандартный негативный запрос. Негативный промпт заставляет ИИ НЕ генерировать "
"описанную в нём вещь.",
"args?": "🟡 Вы не указали аргументы…",
"working": "🎨 Работаю над изображением…\n"
"Запрос: {}{}\n\n"
"Указанные параметры:\n"
"Модель: {} — ID {}\n"
"CFG: {}\n"
"Сэмплер: {}\n"
"Шаги: {}",
"done": "🎉 Ваше изображение готово!\n"
"Запрос: {}{}\n\n"
"Модель: {} — ID {}\n"
"CFG: {}\n"
"Сэмплер: {}\n"
"Шаги: {}\n",
"neg_prompt": "\nОтрицательный запрос: {}",
"_cls_doc": "Генератор изображений на основе Prodia API. Не требует API ключа.",
"_cmd_doc_prodia": "Сгенерировать изображение с помощью Prodia API.",
}
strings_de = {
"name": "Prodia IG",
"model-h": "Ein Initialbildsatz, auf dem das Modell basiert.",
"cfg-h": "Je höher der Wert, desto näher am Prompt wird das Bild sein.",
"sampler-h": "Der Algorithmus, nach dem das Bild generiert wird.",
"steps-h": "Die Anzahl der Schritte zur Generierung des Bildes, je höher der Wert, "
"desto detaillierter wird das Bild sein.",
"ndef-h": "Standardmäßiger negativer Prompt. Negativer Prompt bewirkt, dass die KI NICHT das in ihm "
"beschriebene Ding generiert.",
"args?": "🟡 Du hast keine Argumente angegeben…",
"working": "🎨 Arbeite an deinem Bild…\n"
"Prompt: {}{}\n\n"
"Angegebene Parameter:\n"
"Modell: {} — ID {}\n"
"CFG: {}\n"
"Sampler: {}\n"
"Schritte: {}",
"done": "🎉 Dein Bild ist fertig!\n"
"Prompt: {}{}\n\n"
"Modell: {} — ID {}\n"
"CFG: {}\n"
"Sampler: {}\n"
"Schritte: {}\n",
"neg_prompt": "\nNegativer Prompt: {}",
"_cls_doc": "Ein Bildgenerator auf Basis der Prodia-API. Benötigt keinen API-Schlüssel.",
"_cmd_doc_prodia": "Generieren Sie ein Bild mit der Prodia-API.",
}
@loader.command("prodia")
async def prodiacmd(self, m: Message):
"""Generate an image using Prodia API."""
prompt = utils.get_args_raw(m)
neg_prompt = ""
if not prompt:
return await utils.answer(m, self.strings("args?"))
if "\n" in prompt:
prompt, neg_prompt = prompt.split("\n", 1)
if neg_prompt == "[]":
neg_prompt = self.config["neg_def"]
mid = name_models[self.config["model"]]
model = self.config["model"]
cfg = self.config["cfg"]
sampler = self.config["sampler"]
steps = self.config["steps"]
if neg_prompt:
nm = await utils.answer(
m,
self.strings("working").format(
prompt,
self.strings("neg_prompt").format(neg_prompt),
model,
mid,
cfg,
sampler,
steps,
),
)
else:
nm = await utils.answer(
m,
self.strings("working").format(
prompt, "", model, mid, cfg, sampler, steps
),
)
url = "https://api.prodia.com"
pars = {
"new": "true",
"prompt": prompt,
"model": mid,
"negative_prompt": neg_prompt,
"steps": steps,
"cfg": cfg,
"seed": random.randint(0, 1000000000),
"sampler": sampler,
"aspect_ratio": "square",
}
async with aiohttp.ClientSession() as s:
async with s.get(f"{url}/generate", params=pars) as r:
resp = await r.json()
job_id = resp["job"]
while True:
async with s.get(f"{url}/job/{job_id}") as r:
resp = await r.json()
if resp["status"] == "succeeded":
break
await asyncio.sleep(0.15)
if neg_prompt:
await utils.answer_file(
nm,
f"https://images.prodia.xyz/{job_id}.png",
caption=self.strings("done").format(
prompt,
self.strings("neg_prompt").format(neg_prompt),
model,
mid,
cfg,
sampler,
steps,
),
)
else:
await utils.answer_file(
nm,
f"https://images.prodia.xyz/{job_id}.png",
caption=self.strings("done").format(
prompt, "", model, mid, cfg, sampler, steps
),
)