diff --git a/generate.py b/generate.py new file mode 100644 index 0000000..e92533e --- /dev/null +++ b/generate.py @@ -0,0 +1,26 @@ +from poe_api_wrapper import PoeApi +import os +client = PoeApi(os.environ['pb']) +import asyncio + +#from revChatGPT.V1 import AsyncChatbot +#chatbot = AsyncChatbot(config={ +# "email": os.environ['email'], +# "password": os.environ['pass'] +#}) + + +#Auth +async def GetText(prompt,bot = "chinchilla"): + prev_text = "" + #code = "" + for chunk in client.send_message(bot, prompt, suggest_replies=True): + await asyncio.sleep(0.05) + prev_text = chunk["text"] + client.delete_chat(bot, del_all=True) + #client.chat_break(bot, chatCode = code) + #async for data in chatbot.ask(prompt): + # message = data["message"][len(prev_text) :] + # #print(message, end="", flush=True) + # prev_text = data["message"] + return prev_text \ No newline at end of file diff --git a/itemname.py b/itemname.py new file mode 100644 index 0000000..7b55567 --- /dev/null +++ b/itemname.py @@ -0,0 +1,509 @@ +import random + +bases = """sword +axe +hammer +amulet +potion +broom +orb +cloak +armor +circlet +boots +bag +shield +shackles +glasses +helmet +book +bow +ring +socks +ointment +deck +fork +cart +boat +paper +arrows +apparatus +slippers +greaves +staff +skull +head +hand +glue +quiver +token +instrument +mirror +flask +keg +javelin +dagger +maul +shuriken +spiked chain +dust +gem +gate +carpet +candle +crystal ball +fortress +figurine +hat +portable ram +trap +box +ioun stone +horn +trinket +machine""" + +locations = """ +fantasy village +magic forest +dragon's lair +enchanted castle +elven city +dwarven mine +wizard's tower +haunted graveyard +orc stronghold +goblin cave +dark swamp +knight's training ground +sorcerer's academy +thieves' guild hideout +mermaid's cove +troll bridge +fairy glen +undead crypt +wizard's library +druidic grove +witch's hut +treasure-filled dungeon +mystical ruins +celestial observatory +elemental plane +astral realm +planar crossroads +forgotten temple +abyssal rift +angelic citadel +shadowy underworld +lycanthrope den +necromancer's sanctum +clockwork workshop +warlock's pact realm +underground city +beastman encampment +mysterious island +underwater cavern +timeless pocket dimension +arcane battleground +demon-infested wasteland +divine garden +floating fortress +jungle temple +mad alchemist's laboratory +phoenix nest +twisted labyrinth +ghost ship +oracle's sanctuary +giant's stronghold +golem foundry +dreamwalker's realm +vampire's castle +plague-ridden village +wandering nomad camp +entangled thicket +celestial court +abandoned celestial city +forgotten astral prison""" + +creatures = """ +dragon +goblin +orc +elf +dwarf +troll +gnome +kobold +centaur +minotaur +harpy +siren +merfolk +sphinx +unicorn +phoenix +werewolf +vampire +zombie +skeleton +ghost +demon +angel +fairy +giant +ogre +cyclops +hydra +chimera +griffin +wyvern +elemental +golem +gargoyle +lich +beholder +mind flayer +nymph +satyr +kraken +manticore +djinn +mummy +wraith +gorgon +kraken +pegasus +treant +lamia +basilisk +rakshasa +salamander +changeling +hobgoblin +tengu +mimic +rust monster +blink dog +displacer beast""" + +spells = """ +enchantment +evocation +illusion +conjuration +abjuration +transmutation +necromancy +divination +charm +hex +curse +blessing +summoning +compulsion +protection +fire +ice +lightning +earth +wind +water +shadow +light +healing +banishment +augmentation +teleportation +mind control +time manipulation +creation +destruction +hexbreaking +illusion +shape-shifting +warding +fortune-telling +invisibility +mind reading +telekinesis +fear +love +truth +memory manipulation +elemental manipulation +fate weaving +spiritual communion +phasing +soulbinding +telepathy +dreamwalking +alchemy +curses +blessings +illusion +prophecy +necromancy +weather manipulation +energy drain +astral projection +illusion +healing +enhancement +demonology +angelic intervention +teleportation +creation +hexbreaking +warding +fey magic +geomancy +songweaving +runecasting +starcalling +chronomancy +geomancy +psionics +planar manipulation +mind melding +polymorphing +molecular disruption +pyromancy +aquamancy +aeromancy +terramancy +cryomancy +celestial magic +transfiguration +curse-breaking +illusion +portal manipulation +spirit calling +divine intervention +time dilation +cosmic manipulation +cataclysmic spells +reanimation +perception alteration +dimensional manipulation +soul manipulation""" + + +enchantments = """flaming +frost +healing +adamantine +death +commanding elementals +flying +talking +awakened +teleportation +unlocking +lucky +unlucky +instant +illusion +illusionary +many things +dwarven +draconic +disguise +feindish +knowledge +toughness +serpentine +folding +theives +holding +devouring +alien +eldritch +fireball +archmage +cubic +crab +stars +wild +natural +lycanthrope (wolf) +ursanthrope (bear) +felinethrope (tiger) +smashing +horripilating +revivification +holy +unholy +gravity +paper +mechanical +electricity +sonic +endless water +cursed (make something up) +jousting +charming +swarming +swarming insects +snake +fuzzy +soft +lifestealing +vorpal +the sphere +ultimate evil +pure good +true neutral +tentacle +enemy detection +secret +wonder +vecna +fish command +sticky +creative +rulership +eyes +fire resistance +telekinesis +wishes +x-ray vision +animal influence +limitless +wild magic +the sewers +todd +love +life trapping +soul trapping +tripping +psychadelic +berserker +dry +elvenkind +displacement +winterlands +northern +levitating +arrow attraction +bat +manta ray +arachnida +drow +glamerous +free action +jumping +warmth +regeneration +annihilation +refridgeration +spherical +monkey +primal +psychic +woodlands +sharpness +smiting +bane of arthropods +fear +web +plane shift +winged +sun +son +spellguard +gaseous form +gaseous +valhalla +horned +golden lion +purple +enlargement +shrinking +slaying +tricky +awakened +unsheathed +prime +mystical +gleaming +enchanted +cursed +ancient +radiant +shadowy +whispering +ornate +runed +ethereal +intricate +glowing +forgotten +dreadful +celestial +fiery +frozen +arcane +serrated +ebon +gilded +luminous +sacrificial +arcane +malevolent +resplendent +vorpal +vengeful +vibrant +timeless +abyssal +otherworldly +necrotic +transcendent +perfected +empyreal +crimson +iridescent +eldritch +corrupted +thunderous +prismatic +harmonious +molten +umbral +blighted +harbinger +fey +pristine +titanic +ethereal +phantom +penumbral +verdant +infernal""" + +#basesList = bases.split("\n") +#print(basesList) +enchantmentsList = enchantments.split("\n") +#print(enchantmentsList) + +magicItemList = [] + +def makeItem(baseString = bases): + base = getBase(baseString.split("\n")) + enchantment = getEnchantment() + magicItem = "" + roll = random.randint(0,20) #rolls to see if it's + if roll < 10: #[ENCHANTMENT] [BASE] (10/21) + magicItem += enchantment+" "+base + elif roll == 20:#[ENCHANTMENT] [BASE] of [ENCHANTMENT 2] (1/21) + enchantment2 = getEnchantment() + magicItem += enchantment+" "+base+" of "+enchantment2 + else: #or [BASE] of [ENCHANTMENT] (10/21) + magicItem += base+" of "+enchantment + return magicItem + +def getEnchantment(): + return enchantmentsList[random.randint(0,len(enchantmentsList)-1)] + +def getBase(ls): + return ls[random.randint(0,len(ls)-1)] diff --git a/keep_alive.py b/keep_alive.py new file mode 100644 index 0000000..f7d5f18 --- /dev/null +++ b/keep_alive.py @@ -0,0 +1,21 @@ +from flask import Flask +from threading import Thread + +app = Flask('') + +header = "None" +body = "The next thing generated will show here!" + +@app.route('/') +def home(): + return f"""

Latest - {header}

+ + {body} + """ + +def run(): + app.run(host='0.0.0.0',port=8080) + +def keep_alive(): + t = Thread(target=run) + t.start() \ No newline at end of file diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..7401b1b --- /dev/null +++ b/license.txt @@ -0,0 +1,22 @@ +For the contents of itemname.py +MIT License + +Copyright (c) 2019 Michaelofthepi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..e79fb77 --- /dev/null +++ b/main.py @@ -0,0 +1,180 @@ +import os + +import discord +import generate +import neural +import itemname +import random +import re +from discord import app_commands +from discord.ext import tasks +import keep_alive +from keep_alive import keep_alive + +client = discord.Client(intents=discord.Intents.all()) +tree = app_commands.CommandTree(client) + +queue = [] + +guildID = 1081397933276155977 +magicForum = 1144040531240964256 +raceForum = 1144075835217825868 +subclassForum = 1144075898870579290 +locationForum = 1144075950749925457 +monsterForum = 1144081512724176947 +npcForum = 1144425240018034878 +otherForum = 1144426051720724602 +botID = 1144041248303366314 +logChannel = 1144064721922834582 + +@client.event +async def on_message(message): + if message.author != client.user: + pass + +@client.event +async def on_thread_create(thread): + # await thread.send("## Sample Concept Art Being Generated") + await neural.Generate(f"art of the '{thread.name}', a form of {thread.parent.name}",thread) + +@tree.command(name = "magicitem", description = "Generate a new magic item!", guild=discord.Object(id=guildID)) +async def magicitem(interaction, name:str, desc: str = ""): + queue.append(["magic item", name,desc + ", and Give detailed rules for item effects and flavor accordingly. Keep non-artifact, non-legendary magic items simple.", magicForum, interaction.user.id]) + embedVar = discord.Embed(title=f'Queue position: {len(queue)}', description=f"It won't be long until we get around to the '{name}'!", color=0xffff00) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "race", description = "Generate a new race!", guild=discord.Object(id=guildID)) +async def race(interaction, name:str, desc: str = ""): + queue.append(["race", name,desc, , interaction.user.id]) + embedVar = discord.Embed(title=f'Queue position: {len(queue)}', description=f"It won't be long until we get around to the '{name}'!", color=0xffff00) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "subclass", description = "Generate a new subclass!", guild=discord.Object(id=guildID)) +async def subclass(interaction, name:str, desc: str = ""): + queue.append(["subclass", name,desc, , interaction.user.id]) + embedVar = discord.Embed(title=f'Queue position: {len(queue)}', description=f"It won't be long until we get around to the '{name}'!", color=0xffff00) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "location", description = "Generate a new location!", guild=discord.Object(id=guildID)) +async def location(interaction, name:str, desc: str = ""): + queue.append(["location", name,desc+", and Describe the location in detail. Provide key locations and any necessary information on NPCs.", locationForum, interaction.user.id]) + embedVar = discord.Embed(title=f'Queue position: {len(queue)}', description=f"It won't be long until we get around to the '{name}'!", color=0xffff00) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "monster", description = "Generate a new monster!", guild=discord.Object(id=guildID)) +async def monster(interaction, name:str, desc: str = ""): + queue.append(["living being", name,desc + ", and Give adequate description to both the flavoring of the monster AND the stat block.", monsterForum, interaction.user.id]) + embedVar = discord.Embed(title=f'Queue position: {len(queue)}', description=f"It won't be long until we get around to the '{name}'!", color=0xffff00) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "npc", description = "Generate a new npc!", guild=discord.Object(id=guildID)) +async def npc(interaction, name:str, desc: str = ""): + queue.append(["NPC", name,desc + ". Provide a stat block for the NPC, but also include a bond, ideal, personality trait, and flaw. Offer a potential quest involving the NPC.", , interaction.user.id]) + embedVar = discord.Embed(title=f'Queue position: {len(queue)}', description=f"It won't be long until we get around to the '{name}'!", color=0xffff00) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "other", description = "Generate a new npc!", guild=discord.Object(id=guildID)) +async def other(interaction, name:str, type:str = "spell", desc: str = ""): + queue.append([type, name,f"Be sure to follow all rules surrounding the {type}.", 1144426051720724602, interaction.user.id]) + embedVar = discord.Embed(title=f'Queue position: {len(queue)}', description=f"It won't be long until we get around to the '{name}'!", color=0xffff00) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "ask", description = "Use in a thread to get more details!", guild=discord.Object(id=guildID)) +async def ask(interaction, question:str): + if interaction.channel.type == discord.ChannelType.public_thread or interaction.channel.type == discord.ChannelType.forum: + embedVar = discord.Embed(title=f'Reply inbound', description=f"Question: {question}", color=0xffffff) + await interaction.response.send_message(embed=embedVar) + #The important code + ctx = "" + async for message in interaction.channel.history(limit=100,oldest_first=True): + if str(message.author.id) == str(botID): + ctx += message.content + await interaction.channel.send(await generate.GetText(f"You are being asked a question about a piece of DND 5E content. The piece of content is: \n{ctx} \n\nThe question is: {question}. Answer the question thoroughly, but keep it less than 200 words. You do not need to restate any of the content's material nor that this is for DND 5E.")) + else : + embedVar = discord.Embed(title=f'Invalid location', description=f"Do this inside of a forum post for context-specific results!", color=0xff0000) + await interaction.response.send_message(embed=embedVar) + +@tree.command(name = "conceptart", description = "Generate concept art!", guild=discord.Object(id=guildID)) +async def conceptart(interaction, prompt:str): + embedVar = discord.Embed(title=f'Art inbound!', description=f"Prompt: '{prompt}'", color=0xffffff) + await interaction.response.send_message(embed=embedVar) + await neural.Generate(f"{prompt}", interaction.channel) + +intervals = 0 +alreadyGenerating = False + +async def AddAutoGen(amount): + if amount > 10: + return + for i in range(amount): + v = random.choice([0,1,2,3 ]) + if v == 0: + queue.append(["magic item", itemname.makeItem().title(),"Give detailed rules for item effects and mechanics.", magicForum, -1]) + elif v == 1: + queue.append(["living being", itemname.makeItem(itemname.creatures).title(),"Give adequate description to both the flavoring of the monster AND the stat block.", monsterForum, -1]) + elif v == 2: + queue.append(["location", itemname.makeItem(itemname.locations).title(),"Describe the location in detail. Provide key locations and any necessary information on NPCs.", locationForum, -1]) + elif v == 3: + queue.append(["spell", itemname.makeItem(itemname.spells).title(),"Be sure to follow all rules surrounding the spell.", otherForum, -1]) + +@tasks.loop(minutes=0.25) +async def FlushQueue(): + global alreadyGenerating + global intervals + try: + if alreadyGenerating: + return + alreadyGenerating = True + + if len(queue) == 0: + intervals = (intervals+1)%10 + if intervals == 0: + await AddAutoGen(1) + + if len(queue) > 0: + obj = queue.pop(0) + keep_alive.header = obj[1] + obj[1] = re.sub('[^A-Za-z0-9 ]+', '', obj[1]) + log = client.get_channel(logChannel) + await log.send(f"**Now generating the {obj[0]} '{obj[1]}'**") + channel = client.get_channel(obj[3]) + st = f"Create a DND 5e {obj[0]} named the '{obj[1]}'. {obj[2]} Keep the final text under 300 words. Use markdown text formatting." + c = await generate.GetText(st) + keep_alive.body = c + if len(c) > 1900: + c2 = c[1899:(len(c)-1)] + c = c[0:1899] + thread = await channel.create_thread(name = obj[1], content = c + " (Truncated)") + await thread.thread.send(c2) + if (obj[4]!=-1): + await thread.thread.send("<@" + str(obj[4]) + ">") + else: + await thread.thread.send("Autogenerated by the bot") + else: + thread = await channel.create_thread(name = obj[1], content = c) + if (obj[4]!=-1): + await thread.thread.send("<@" + str(obj[4]) + ">") + else: + await thread.thread.send("Autogenerated by the bot") + + alreadyGenerating = False + except Exception as e: + + log = client.get_channel(logChannel) + await log.send("I'm just as confused as you are, there was an error somehow! The next time it should work though.") + await log.send("Error message: " + str(e)) + alreadyGenerating = False + +@client.event +async def on_ready(): + print("I'm in") + print(client.user) + await tree.sync(guild=discord.Object(id=guildID)) + log = client.get_channel(logChannel) + await log.send("Bot now online") + FlushQueue.start() + +keep_alive() + +my_secret = os.environ['discordbot'] +client.run(my_secret) diff --git a/neural.py b/neural.py new file mode 100644 index 0000000..366c0f3 --- /dev/null +++ b/neural.py @@ -0,0 +1,86 @@ +import asyncio +import os +import requests +from io import BytesIO +from PIL import Image +import discord + +def create_image(links): + + images=[] + for index in links: + response = requests.get(index) + images.append(Image.open(BytesIO(response.content))) + image = Image.new("RGB", (images[0].width*len(images), images[0].height)) + i=0 + for img in images: + image.paste(images[i], (images[i].width*i, 0)) + i+=1 + return image + +async def Generate(prompt, channel, count=2, negativePrompt = "",size="square"): + url = "https://api.neural.love/v1/ai-art/generate" + payload = { + "amount": count, + "isPublic": True, + "isPriority": False, + "isHd": False, + "steps": 25, + "cfgScale": 7.5, + "prompt": prompt, + "style": "anything", + "layout": size, + "negativePrompt": negativePrompt + } + headers = { + "accept": + "application/json", + "content-type": + "application/json", + "authorization": + os.environ['neural'] + } + response = requests.post(url, json=payload, headers=headers) + data = response.json() + + if not "orderId" in data.keys(): + await channel.send("**Error while generating, try again?**") + return + orderId = data["orderId"] + print(orderId) + url2 = "https://api.neural.love/v1/ai-art/orders/" + orderId + print(url2) + headers2 = { + "accept": + "application/json", + "authorization": + os.environ['neural'] + } + count = 0 + while True: + await asyncio.sleep(3) + response2 = requests.get(url2, headers=headers2) + + count += 1 + + data2 = response2.json() + if data2["status"]["isReady"]: + links = [] + for i in range(data2["input"]["amount"]): + data3 = data2["output"][i] + links.append(data3["full"]) + with BytesIO() as image_binary: + create_image(links).save(image_binary, 'PNG') + image_binary.seek(0) + await channel.send(file=discord.File(fp=image_binary, filename='image.png')) + break + elif data2["status"]["code"] == 998: + await channel.send("All results were NSFW, aborting!") + break + elif count > 10: + await channel.send("Taking too long, aborting!") + break + else: + #await message.channel.send("Not done, waiting 10 seconds to retry..." + + await asyncio.sleep(7) \ No newline at end of file