diff --git a/cogs/gem.py b/cogs/gem.py
index 2d7e9491e25e104ec5184bd8a3b30ddbe9030006..3ee2078df8e87589b347fbd985379827bfa588fa 100644
--- a/cogs/gem.py
+++ b/cogs/gem.py
@@ -1,46 +1,43 @@
 import discord
 from discord.ext import commands
-import asyncio
-import configparser
+from dotenv import load_dotenv
 import requests
 import logging
 
-config = configparser.ConfigParser()
-config.read('config.ini')
-GEMINI_API_KEY = config['GOOGLE']['GEMINI_API_KEY']
+GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
 
 
 class GemCog(commands.Cog):
     def __init__(self, bot):
         self.bot = bot
 
-        @commands.hybrid_command(name="gem", description="Ask a question to Gemini Pro")
-        async def ask_gemini_slash(self, ctx, question: str):
-            try:
-                headers = {'Authorization': f'Bearer {GEMINI_API_KEY}'}
-                response = requests.post(
-                    'https://api.gemini.com/v1/completions',
-                    headers=headers,
-                    json={'prompt': question}
-                )
-                response.raise_for_status()
-                data = response.json()
+    @commands.hybrid_command(name="gem", description="Ask a question to Gemini Pro")
+    async def ask_gemini_slash(self, ctx, question: str):
+        try:
+            headers = {'Authorization': f'Bearer {GEMINI_API_KEY}'}
+            response = requests.post(
+                'https://api.gemini.com/v1/completions',
+                headers=headers,
+                json={'prompt': question}
+            )
+            response.raise_for_status()
+            data = response.json()
 
-                if 'choices' in data and len(data['choices']) > 0:
-                    answer = data['choices'][0]['text']
-                    embed = discord.Embed(
-                        title="Gem says:", description=answer)
-                    await ctx.send(embed=embed)
-                else:
-                    logging.error(
-                        "The API response format for Gemini Pro has changed, Unable to extract response.")
-                    await ctx.send("Unexpected response from gem <:SatisfiedCheems:1230175084912840734>")
-                except requests.exceptions.RequestException as e:
-                    logging.error(f"404 from gem: {e}")
-                    await ctx.send("An error returned while trying to communicate with Gemini Pro, Try again.")
-                except Exception as e:
-                    logging.error(f"Random error in 'gem' command: {e}")
-                    await ctx.send("Gem is not working at the moment.")
+            if 'choices' in data and len(data['choices']) > 0:
+                answer = data['choices'][0]['text']
+                embed = discord.Embed(title="Gem says:", description=answer)
+                await ctx.send(embed=embed)
+            else:
+                logging.error(
+                    "The API response format for Gemini Pro has changed, Unable to extract response.")
+                await ctx.send("Unexpected response from gem <:SatisfiedCheems:1230175084912840734>")
+        except requests.exceptions.RequestException as e:  # Moved except block out of the if/else
+            logging.error(f"Error communicating with Gemini API: {e}")
+            await ctx.send("An error occurred while communicating with Gemini Pro. Try again.")
+        except Exception as e:  # Moved except block out of the if/else
+            logging.error(f"Random error in 'gem' command: {e}")
+            await ctx.send("Gem is not working at the moment.")
 
-    async def setup(bot):
-        await bot.add_cog(GemCog(bot))
+
+async def setup(bot):
+    await bot.add_cog(GemCog(bot))
diff --git a/cogs/imagesearch.py b/cogs/imagesearch.py
index 6854563e44adcb01fb6af55bdb939f377999c259..38b0ef7997c75d8cbf5fd76148afbbeee22d8b0f 100644
--- a/cogs/imagesearch.py
+++ b/cogs/imagesearch.py
@@ -1,13 +1,12 @@
+import discord
 from discord.ext import commands
+from dotenv import load_dotenv
 from google_images_search import GoogleImagesSearch
 import logging
 import asyncio
 
-
-config = configparser.ConfigParser()
-config.read('config.ini')
-GCS_DEVELOPER_KEY = config['GOOGLE']['GCS_DEVELOPER_KEY']
-GCS_CX = config['GOOGLE']['GCS_CX']
+GCS_DEVELOPER_KEY = os.getenv("GCS_DEVELOPER_KEY")
+GCS_CX = os.getenv("GCS_CX")
 
 
 class ImageCog(commands.Cog):
@@ -23,7 +22,7 @@ class ImageCog(commands.Cog):
                 'num': 5,
                 'safe': 'off',
             }
-            self.gis.async_search(search_params=_search_params)
+            await self.gis.async_search(search_params=_search_params)
 
             if not self.gis.results():
                 await ctx.send("No images found for your search.")
@@ -38,11 +37,11 @@ class ImageCog(commands.Cog):
                 embed.set_image(url=self.gis.results()[current_page].url)
                 return embed
 
-        message = await ctx.send(embed=embed)
-        await message.add_reaction('⬅️')
-          await message.add_reaction('➡️')
+            message = await ctx.send(embed=create_embed())
+            await message.add_reaction('⬅️')
+            await message.add_reaction('➡️')
 
-           def check(reaction, user):
+            def check(reaction, user):
                 return user == ctx.author and str(reaction.emoji) in ['⬅️', '➡️'] and reaction.message == message
 
             while True:
@@ -53,77 +52,6 @@ class ImageCog(commands.Cog):
                         current_page -= 1
                     elif str(reaction.emoji) == '➡️' and current_page < total_pages:
                         current_page += 1
-                        embed.set_image(url=self.gis.results()
-                                        [current_page * 5].url)
-                        embed.title = f"Results for '{
-                            query}' ({current_page+1}/{total_pages+1})"
-                        await message.edit(embed=embed)
-                        await message.remove_reaction(reaction, ctx.author)
-
-                    except asyncio.TimeoutError:
-                        await message.clear_reactions()
-                        break
-            except Exception as e:
-                logging.error(f"Error during image search: {e}")
-                await ctx.send("An error occured while searching for '{query}'.")
-
-    async def setup(bot):
-        await bot.add_cog(ImageCog(bot))
-import discord
-from discord.ext import commands
-from google_images_search import GoogleImagesSearch
-import logging
-import asyncio
-
-
-config = configparser.ConfigParser()
-config.read('config.ini')
-GCS_DEVELOPER_KEY = config['GOOGLE']['GCS_DEVELOPER_KEY']
-GCS_CX = config['GOOGLE']['GCS_CX']
-
-
-class ImageCog(commands.Cog):
-    def __init__(self, bot):
-        self.bot = bot
-        self.gis = GoogleImagesSearch(GCS_DEVELOPER_KEY, GCS_CX)
-
-    @commands.hybrid_command(name='image', description="Search for images from Google.")
-    async def search_image(self, ctx, *, query):
-        try:
-            _search_params = {
-                'q': query,
-                'num': 5,
-                'safe': 'off',
-            }
-            self.gis.async_search(search_params=_search_params)
-
-            if not self.gis.results():
-                await ctx.send("No images found for your search.")
-                return
-
-            current_page = 0
-            total_pages = (len(self.gis.results()) - 1) // 5
-
-            def create_embed():
-                embed = discord.Embed(title=f"Image search Results for '{query}' ({current_page+1}/{total_pages+1})")
-                embed.set_image(url=self.gis.results()[current_page].url)
-                return embed
-
-        message = await ctx.send(embed=embed)
-        await message.add_reaction('⬅️')
-        await message.add_reaction('➡️')
-
-        def check(reaction, user):
-            return user == ctx.author and str(reaction.emoji) in ['⬅️', '➡️'] and
-
-        while True:
-            try:
-                reaction, _ = await self.bot.wait_for('reaction_add', timeout=60.0, check=check)
-
-                if str(reaction.emoji) == '⬅️' and current_page > 0:
-                    current_page -= 1
-                elif str(reaction.emoji) == '➡️' and current_page < total_pages:
-                    current_page += 1
 
                     await message.edit(embed=create_embed())
                     await message.remove_reaction(reaction, ctx.author)
@@ -131,9 +59,11 @@ class ImageCog(commands.Cog):
                 except asyncio.TimeoutError:
                     await message.clear_reactions()
                     break
-            except Exception as e:
-                logging.error(f"Error during image search: {e}")
-                await ctx.send("An error occured while searching for '{query}'.")
 
-    async def setup(bot):
-        await bot.add_cog(ImageCog(bot))
+        except Exception as e:
+            logging.error(f"Error during image search: {e}")
+            await ctx.send(f"An error occurred while searching for '{query}'.")
+
+
+async def setup(bot):
+    await bot.add_cog(ImageCog(bot))
diff --git a/cogs/todo.md b/cogs/todo.md
index 381556843cbd313bf473e6fb9c1ff1b854386321..0b8cc699111dc341f70af72d56d3c94ec46565e4 100644
--- a/cogs/todo.md
+++ b/cogs/todo.md
@@ -4,7 +4,7 @@
 
 - [x] Gem
 
-- [ ]  websearch
-- [ ] imagesearch
-- [x] videosearch
-- [ ] embedcreat
+- [X]  websearch
+- [X] imagesearch
+- [X] videosearch
+- [ ] embedcreate
diff --git a/cogs/video.py b/cogs/video.py
index 5a8e898b7a8136e96f493e996ef37e5438bc8f8e..ab6292afb7dc54be970d8d68c5bda5edcfe2330d 100644
--- a/cogs/video.py
+++ b/cogs/video.py
@@ -1,5 +1,75 @@
+import discord
 from discord.ext import commands
-import requests
+from dotenv import load_dotenv
+from googleapiclient.discovery import build
 import asyncio
 import logging
 
+YOUTUBE_API_KEY = os.getenv("YOUTUBE_API_KEY")
+
+
+class VideoCog(commands.Cog):
+    def __init__(self, bot):
+        self.bot = bot
+        self.youtube = build('youtube', 'v3', developerKey=YOUTUBE_API_KEY)
+
+    @commands.hybrid_command(name='video', description="search youtube videos.")
+    async def search_video(self, ctx, *, query):
+        try:
+            request = self.youtube.search().list(
+                part="snippet",
+                maxResults=10,
+                q=query,
+                type='video'
+            )
+            response = request.execute()
+
+            if not response['items']:
+                await ctx.send("No videos found for your query.")
+                return
+
+            current_page = 0
+            total_pages = len(response['items']) - 1
+
+            def create_embed():
+                item = response['items'][current_page]
+                video_id = item['id']['videoId']
+                video_url = f"https://www.youtube.com/watch?v={video_id}"
+                embed = discord.Embed(
+                    title=item['snippet']['title'], url=video_url)
+                embed.set_image(url=item['snippet']
+                                ['thumbnails']['high']['url'])
+                embed.set_footer(
+                    text=f"Page {current_page+1} of {total_pages+1}")
+                return embed
+
+            message = await ctx.send(embed=create_embed())
+            await message.add_reaction('⬅️')
+            await message.add_reaction('➡️')
+
+            def check(reaction, user):
+                return user == ctx.author and str(reaction.emoji) in ['⬅️', '➡️'] and reaction.message == message
+
+            while True:
+                try:
+                    reaction, _ = await self.bot.wait_for('reaction_add', timeout=60.0, check=check)
+
+                    if str(reaction.emoji) == '⬅️' and current_page > 0:
+                        current_page -= 1
+                    elif str(reaction.emoji) == '➡️' and current_page < total_pages:
+                        current_page += 1
+
+                    await message.edit(embed=create_embed())
+                    await message.remove_reaction(reaction, ctx.author)
+
+                except asyncio.TimeoutError:
+                    await message.clear_reactions()
+                    break
+
+        except Exception as e:
+            logging.error(f"Error during video search: {e}")
+            await ctx.send("An error occurred while searching for videos.")
+
+
+async def setup(bot):
+    await bot.add_cog(VideoCog(bot))
diff --git a/cogs/web.py b/cogs/web.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..11917babe712d78e77b65459c810f49a823dc041 100644
--- a/cogs/web.py
+++ b/cogs/web.py
@@ -0,0 +1,78 @@
+import discord
+from discord.ext import commands
+from dotenv import load_dotenv
+import requests
+import logging
+import asyncio
+
+
+GCS_DEVELOPER_KEY = os.getenv("GCS_DEVELOPER_KEY")
+GCS_CX = os.getenv("GCS_CX")
+
+
+class WebSearchCog(commands.Cog):
+    def __init__(self, bot):
+        self.bot = bot
+
+    @commands.hybrid_command(name='search', description="Search google")
+    async def search_web(self, ctx, *, query):
+        try:
+            search_params = {
+                'q': query,
+                'cx': GCS_CX,
+                'key': GCS_DEVELOPER_KEY,
+                'num': 10,
+            }
+            response = requests.get(
+                "https://www.googleapis.com/customsearch/v1", params=search_params)
+            response.raise_for_status()
+            data = response.json()
+
+            if 'items' not in data:
+                await ctx.send("No results found for your search")
+                return
+
+            current_page = 0
+            total_pages = len(data['items']) - 1
+
+            def create_embed():
+                item = data['items'][current_page]
+                embed = discord.Embed(
+                    title=item['title'], url=item['link'], description=item['snippet'])
+                embed.set_footer(
+                    text=f"Page {current_page+1} of {total_pages+1}")
+                return embed
+
+            message = await ctx.send(embed=create_embed())
+            await message.add_reaction('⬅️')
+            await message.add_reaction('➡️')
+
+            def check(reaction, user):
+                return user == ctx.author and str(reaction.emoji) in ['⬅️', '➡️'] and reaction.message == message
+
+            while True:
+                try:
+                    reaction, _ = await self.bot.wait_for('reaction_add', timeout=60.0, check=check)
+
+                    if str(reaction.emoji) == '⬅️' and current_page > 0:
+                        current_page -= 1
+                    elif str(reaction.emoji) == '➡️' and current_page < total_pages:
+                        current_page += 1
+
+                    await message.edit(embed=create_embed())
+                    await message.remove_reaction(reaction, ctx.author)
+
+                except asyncio.TimeoutError:
+                    await message.clear_reactions()
+                    break
+
+        except requests.exceptions.RequestException as e:
+            logging.error(f"Error during web search: {e}")
+            await ctx.send("An error occurred while searching the web.")
+        except Exception as e:
+            logging.error(f"Unexpected error in web search: {e}")
+            await ctx.send("An unexpected error occurred.")
+
+
+async def setup(bot):
+    await bot.add_cog(WebSearchCog(bot))
diff --git a/main.py b/main.py
index ccf7fc735f3b05594900dcd9e9f8ffae54d7ee9a..5d3461369c4298361d876dc075ce38ed035bb5f8 100644
--- a/main.py
+++ b/main.py
@@ -1,191 +1,70 @@
 import discord
 from discord.ext import commands
+import os
 import asyncio
-import configparser
 import logging
-import requests
-from google_images_search import GoogleImagesSearch
-from googleapiclient.discovery import build
-from googleapiclient import errors
-# Logging configuration
+from logging.handlers import RotatingFileHandler
+from discord.ext.commands import errors
+from dotenv import load_dotenv
+
+load_dotenv()
+
 logging.basicConfig(level=logging.INFO,
                     format='%(asctime)s - %(levelname)s - %(message)s')
 
-# Configuration
-config = configparser.ConfigParser()
-config.read('config.ini')
 
-# Discord config
-TOKEN = config['DISCORD']['TOKEN']
-ROLE_ID = int(config['DISCORD']['ROLE_ID'])
-LOG_CHANNEL_ID = int(config['DISCORD']['LOG_CHANNEL_ID'])
-WELCOME_CHANNEL_ID = int(config['DISCORD']['WELCOME_CHANNEL_ID'])
-ROLE_DURATION_SECONDS = 604800
 
-# Google Images Search config
-GCS_DEVELOPER_KEY = config['GOOGLE']['GCS_DEVELOPER_KEY']
-GCS_CX = config['GOOGLE']['GCS_CX']
-YOUTUBE_API_KEY = config['GOOGLE']['YOUTUBE_API_KEY']
-GEMINI_API_KEY = config['GOOGLE']['GEMINI_API_KEY']
-# Discord bot setup
+
 intents = discord.Intents.default()
 intents.members = True
 intents.message_content = True
 bot = commands.Bot(command_prefix='!', intents=intents)
 
-gis = GoogleImagesSearch(GCS_DEVELOPER_KEY, GCS_CX)
-youtube = build('youtube', 'v3', developerKey=YOUTUBE_API_KEY)
-
-# New member join event
-
-
-@bot.event
-async def on_member_join(member):
-    try:
-        logging.info(f"New member joined: {member.name} (ID: {member.id})")
-
-        role = discord.utils.get(member.guild.roles, id=ROLE_ID)
-        if role is None:
-            raise ValueError(f"Role with ID {ROLE_ID} not found.")
-
-        logging.info(f"Assigning role '{role.name}' to {member.name}")
-        await member.add_roles(role)
-
-        welcome_channel = bot.get_channel(WELCOME_CHANNEL_ID)
-        if welcome_channel:
-            logging.info(f"Sending welcome message to #{welcome_channel.name}")
-            await welcome_channel.send(
-                f"aha ! <:rizzhotsexy:1195895549199663275> , {
-                    member.mention} Looks like you been given the <@&{ROLE_ID}> Role, for 7 days !"
-            )
-        else:
-            logging.error(
-                f"Welcome channel (ID: {WELCOME_CHANNEL_ID}) not found.")
-
-        log_channel = bot.get_channel(LOG_CHANNEL_ID)
-        if log_channel:
-            logging.info(f"Sending log message to #{log_channel.name}")
-            await log_channel.send(
-                f"User {member.mention} (ID: {
-                                         member.id}) has been assigned the \"New Victim\" role for 7 days."
-            )
-        else:
-            logging.error(f"Log channel (ID: {LOG_CHANNEL_ID}) not found.")
-
-        logging.info(f"Waiting for {ROLE_DURATION_SECONDS} seconds...")
-        await asyncio.sleep(ROLE_DURATION_SECONDS)
-
-        logging.info(f"Removing role '{role.name}' from {member.name}")
-        await member.remove_roles(role)
-
-    except discord.Forbidden as e:
-        logging.error(f"Forbidden error: {e} (Missing permissions?)")
-    except ValueError as ve:
-        logging.error(ve)
-    except Exception as e:
-        logging.error(f"Unexpected error: {e}")
-
-
-@bot.command(name='search')
-async def search_web(ctx, *, query):
-    try:
-        # Perform the search
-        search_params = {
-            'q': query,
-            'cx': GCS_CX,  # Use your Custom Search Engine ID
-            'key': GCS_DEVELOPER_KEY,
-            'num': 1,  # Get top 3 results
-        }
-        response = requests.get("https://www.googleapis.com/customsearch/v1", params=search_params)
-        response.raise_for_status()  # Raise an exception for bad responses
-        data = response.json()
-
-        # Check if results were found
-        if 'items' not in data:
-            raise ValueError("No results found for your query.")
-
-        # Format and send results
-        for item in data['items']:
-            await ctx.send(f"**{item['title']}**\n{item['link']}\n{item['snippet']}\n")
-
-    except requests.exceptions.RequestException as e:
-        logging.error(f"Error during web search: {e}")
-        await ctx.send("An error occurred while searching the web.")
-    except ValueError as ve:
-        logging.error(f"Error during web search: {ve}")
-        await ctx.send(str(ve))
-    except Exception as e:
-        logging.error(f"Error during web search: {e}")
-        await ctx.send("An error occurred while searching the web.")
-
-@bot.command(name='video')
-async def search_video(ctx, *, query):
-    try:
-        # Search for videos
-        request = youtube.search().list(
-            part="snippet",
-            maxResults=1, 
-            q=query,
-            type='video'
-        )
-        response = request.execute()
-
-        # Check if results were found
-        if not response['items']:
-            raise ValueError("No videos found for your query.")
-
-        # Extract and send video URL
-        video_id = response['items'][0]['id']['videoId']
-        video_url = f"https://www.youtube.com/watch?v={video_id}"
-        await ctx.send(video_url)
-
-    except ValueError as ve:
-        logging.error(f"Error during video search: {ve}")
-        await ctx.send(str(ve))
-    except Exception as e:
-        logging.error(f"Error during video search: {e}")
-        await ctx.send("An error occurred while searching for videos.")
-
-
-
-class EmbedModal(discord.ui.Modal, title='Create Embed'):
-    title = discord.ui.TextInput(label='Embed Title', style=discord.TextStyle.short, required=True)
-    description = discord.ui.TextInput(label='Description', style=discord.TextStyle.paragraph, required=True)
-    color = discord.ui.TextInput(label='Color (hex or name)', style=discord.TextStyle.short, required=False)
-    thumbnail = discord.ui.TextInput(label='Thumbnail URL', style=discord.TextStyle.short, required=False)
-    image = discord.ui.TextInput(label='Image URL', style=discord.TextStyle.short, required=False)
-    author_name = discord.ui.TextInput(label='Author Name', style=discord.TextStyle.short, required=False)
-    author_icon = discord.ui.TextInput(label='Author Icon URL', style=discord.TextStyle.short, required=False)
-    footer_text = discord.ui.TextInput(label='Footer Text', style=discord.TextStyle.short, required=False)
-    footer_icon = discord.ui.TextInput(label='Footer Icon URL', style=discord.TextStyle.short, required=False)
-
-    async def on_submit(self, interaction: discord.Interaction):
-        embed = discord.Embed(title=self.title.value, description=self.description.value)
-        if self.color.value:
+logger = logging.getLogger('discord')
+logger.setLevel(logging.INFO)
+handler = RotatingFileHandler(
+    filename='bot.log',
+    encoding='utf-8',
+    maxBytes=32 * 1024 * 1024,
+    backupCount=5
+)
+dt_fmt = '%Y-%m-%d %H:%M:%S'
+formatter = logging.Formatter(
+    '[{asctime}] [{levelname:<8}] {name}: {message}', dt_fmt, style='{')
+handler.setFormatter(formatter)
+logger.addHandler(handler)
+
+TOKEN = os.getenv("DISCORD_BOT_TOKEN")
+MESSAGE_CHANNEL_ID = int(os.getenv("MESSAGE_CHANNEL_ID"))
+
+async def load_cogs():
+    for filename in os.listdir("./cogs"):
+        if filename.endswith(".py"):
             try:
-                embed.color = discord.Color.from_str(self.color.value)
-            except ValueError:
-                pass  # Use default color
-        if self.thumbnail.value:
-            embed.set_thumbnail(url=self.thumbnail.value)
-        if self.image.value:
-            embed.set_image(url=self.image.value)
-        if self.author_name.value or self.author_icon.value:
-            embed.set_author(name=self.author_name.value, icon_url=self.author_icon.value)
-        if self.footer_text.value or self.footer_icon.value:
-            embed.set_footer(text=self.footer_text.value, icon_url=self.footer_icon.value)
+                await bot.load_extension(f"cogs.{filename[:-3]}")
+                logging.info(f"Loaded cog: {filename[:-3]}")
+            except (errors.ExtensionNotFound, errors.ExtensionFailed) as e:
+                logging.error(f"Failed to load cog '{filename[:-3]}': {e}")
 
-        await interaction.response.send_message(embed=embed, ephemeral=True)
 
+@bot.event
+async def on_ready():
+    await load_cogs()
+    await bot.tree.sync()
+    logging.info(f'Logged in as {bot.user.name} (ID: {bot.user.id})')
+    channel = bot.get_channel(MESSAGE_CHANNEL_ID)
+    if channel:
+        await channel.send("mamahuevooo")
+    else:
+        logging.error(f"Channel is not found (ID: {MESSAGE_CHANNEL_ID})")
 
-@bot.slash_command(description="Create a custom embed")
-async def create_embed(ctx):
-    """Slash command to trigger the embed creation modal."""
-    if not ctx.author.guild_permissions.manage_messages:  # Permissions check
-        await ctx.respond("You don't have permission to use this command.", ephemeral=True)
-        return
-    await ctx.send_modal(EmbedModal())
 
+@bot.event
+async def on_command_error(ctx, error):
+    if isinstance(error, commands.MissingPermissions):
+        await ctx.send("You have no perms to use this command.")
+    else:
+        logging.error(f"An error occurred: {error}")
+        await ctx.send("An error occurred while processing this command...")
 
 bot.run(TOKEN)
-
diff --git a/pyhub/bin/Activate.ps1 b/pyhub/bin/Activate.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..b49d77ba44b24fe6d69f6bbe75139b3b5dc23075
--- /dev/null
+++ b/pyhub/bin/Activate.ps1
@@ -0,0 +1,247 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies: 
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+    [Parameter(Mandatory = $false)]
+    [String]
+    $VenvDir,
+    [Parameter(Mandatory = $false)]
+    [String]
+    $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+    # Revert to original values
+
+    # The prior prompt:
+    if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+        Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+        Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+    }
+
+    # The prior PYTHONHOME:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+        Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+    }
+
+    # The prior PATH:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+        Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+    }
+
+    # Just remove the VIRTUAL_ENV altogether:
+    if (Test-Path -Path Env:VIRTUAL_ENV) {
+        Remove-Item -Path env:VIRTUAL_ENV
+    }
+
+    # Just remove VIRTUAL_ENV_PROMPT altogether.
+    if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
+        Remove-Item -Path env:VIRTUAL_ENV_PROMPT
+    }
+
+    # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+    if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+        Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+    }
+
+    # Leave deactivate function in the global namespace if requested:
+    if (-not $NonDestructive) {
+        Remove-Item -Path function:deactivate
+    }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+    [String]
+    $ConfigDir
+) {
+    Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+    # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+    $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+    # An empty map will be returned if no config file is found.
+    $pyvenvConfig = @{ }
+
+    if ($pyvenvConfigPath) {
+
+        Write-Verbose "File exists, parse `key = value` lines"
+        $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+        $pyvenvConfigContent | ForEach-Object {
+            $keyval = $PSItem -split "\s*=\s*", 2
+            if ($keyval[0] -and $keyval[1]) {
+                $val = $keyval[1]
+
+                # Remove extraneous quotations around a string value.
+                if ("'""".Contains($val.Substring(0, 1))) {
+                    $val = $val.Substring(1, $val.Length - 2)
+                }
+
+                $pyvenvConfig[$keyval[0]] = $val
+                Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+            }
+        }
+    }
+    return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+    Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+    Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+    $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+    Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+    Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+    Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+    if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+        Write-Verbose "  Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+        $Prompt = $pyvenvCfg['prompt'];
+    }
+    else {
+        Write-Verbose "  Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
+        Write-Verbose "  Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+        $Prompt = Split-Path -Path $venvDir -Leaf
+    }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+    Write-Verbose "Setting prompt to '$Prompt'"
+
+    # Set the prompt to include the env name
+    # Make sure _OLD_VIRTUAL_PROMPT is global
+    function global:_OLD_VIRTUAL_PROMPT { "" }
+    Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+    New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+    function global:prompt {
+        Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+        _OLD_VIRTUAL_PROMPT
+    }
+    $env:VIRTUAL_ENV_PROMPT = $Prompt
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+    Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+    Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
diff --git a/pyhub/bin/activate b/pyhub/bin/activate
new file mode 100644
index 0000000000000000000000000000000000000000..af038af7595247763d3057fdc2a1a51fd9ed9426
--- /dev/null
+++ b/pyhub/bin/activate
@@ -0,0 +1,70 @@
+# This file must be used with "source bin/activate" *from bash*
+# You cannot run it directly
+
+deactivate () {
+    # reset old environment variables
+    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+        PATH="${_OLD_VIRTUAL_PATH:-}"
+        export PATH
+        unset _OLD_VIRTUAL_PATH
+    fi
+    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+        export PYTHONHOME
+        unset _OLD_VIRTUAL_PYTHONHOME
+    fi
+
+    # Call hash to forget past commands. Without forgetting
+    # past commands the $PATH changes we made may not be respected
+    hash -r 2> /dev/null
+
+    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+        PS1="${_OLD_VIRTUAL_PS1:-}"
+        export PS1
+        unset _OLD_VIRTUAL_PS1
+    fi
+
+    unset VIRTUAL_ENV
+    unset VIRTUAL_ENV_PROMPT
+    if [ ! "${1:-}" = "nondestructive" ] ; then
+    # Self destruct!
+        unset -f deactivate
+    fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+# on Windows, a path can contain colons and backslashes and has to be converted:
+if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
+    # transform D:\path\to\venv to /d/path/to/venv on MSYS
+    # and to /cygdrive/d/path/to/venv on Cygwin
+    export VIRTUAL_ENV=$(cygpath "/home/tay/Git/pyhub/pyhub")
+else
+    # use the path as-is
+    export VIRTUAL_ENV="/home/tay/Git/pyhub/pyhub"
+fi
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/bin:$PATH"
+export PATH
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+    unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+    _OLD_VIRTUAL_PS1="${PS1:-}"
+    PS1="(pyhub) ${PS1:-}"
+    export PS1
+    VIRTUAL_ENV_PROMPT="(pyhub) "
+    export VIRTUAL_ENV_PROMPT
+fi
+
+# Call hash to forget past commands. Without forgetting
+# past commands the $PATH changes we made may not be respected
+hash -r 2> /dev/null
diff --git a/pyhub/bin/activate.csh b/pyhub/bin/activate.csh
new file mode 100644
index 0000000000000000000000000000000000000000..a1fb09205189a62c75098df9ba3b83ae7d9469ef
--- /dev/null
+++ b/pyhub/bin/activate.csh
@@ -0,0 +1,27 @@
+# This file must be used with "source bin/activate.csh" *from csh*.
+# You cannot run it directly.
+
+# Created by Davide Di Blasi <davidedb@gmail.com>.
+# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
+
+alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+setenv VIRTUAL_ENV "/home/tay/Git/pyhub/pyhub"
+
+set _OLD_VIRTUAL_PATH="$PATH"
+setenv PATH "$VIRTUAL_ENV/bin:$PATH"
+
+
+set _OLD_VIRTUAL_PROMPT="$prompt"
+
+if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
+    set prompt = "(pyhub) $prompt"
+    setenv VIRTUAL_ENV_PROMPT "(pyhub) "
+endif
+
+alias pydoc python -m pydoc
+
+rehash
diff --git a/pyhub/bin/activate.fish b/pyhub/bin/activate.fish
new file mode 100644
index 0000000000000000000000000000000000000000..beb58daf248b7696513d761c8472d0d05becea1c
--- /dev/null
+++ b/pyhub/bin/activate.fish
@@ -0,0 +1,69 @@
+# This file must be used with "source <venv>/bin/activate.fish" *from fish*
+# (https://fishshell.com/). You cannot run it directly.
+
+function deactivate  -d "Exit virtual environment and return to normal shell environment"
+    # reset old environment variables
+    if test -n "$_OLD_VIRTUAL_PATH"
+        set -gx PATH $_OLD_VIRTUAL_PATH
+        set -e _OLD_VIRTUAL_PATH
+    end
+    if test -n "$_OLD_VIRTUAL_PYTHONHOME"
+        set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
+        set -e _OLD_VIRTUAL_PYTHONHOME
+    end
+
+    if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
+        set -e _OLD_FISH_PROMPT_OVERRIDE
+        # prevents error when using nested fish instances (Issue #93858)
+        if functions -q _old_fish_prompt
+            functions -e fish_prompt
+            functions -c _old_fish_prompt fish_prompt
+            functions -e _old_fish_prompt
+        end
+    end
+
+    set -e VIRTUAL_ENV
+    set -e VIRTUAL_ENV_PROMPT
+    if test "$argv[1]" != "nondestructive"
+        # Self-destruct!
+        functions -e deactivate
+    end
+end
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+set -gx VIRTUAL_ENV "/home/tay/Git/pyhub/pyhub"
+
+set -gx _OLD_VIRTUAL_PATH $PATH
+set -gx PATH "$VIRTUAL_ENV/bin" $PATH
+
+# Unset PYTHONHOME if set.
+if set -q PYTHONHOME
+    set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
+    set -e PYTHONHOME
+end
+
+if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
+    # fish uses a function instead of an env var to generate the prompt.
+
+    # Save the current fish_prompt function as the function _old_fish_prompt.
+    functions -c fish_prompt _old_fish_prompt
+
+    # With the original prompt function renamed, we can override with our own.
+    function fish_prompt
+        # Save the return status of the last command.
+        set -l old_status $status
+
+        # Output the venv prompt; color taken from the blue of the Python logo.
+        printf "%s%s%s" (set_color 4B8BBE) "(pyhub) " (set_color normal)
+
+        # Restore the return status of the previous command.
+        echo "exit $old_status" | .
+        # Output the original/"old" prompt.
+        _old_fish_prompt
+    end
+
+    set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
+    set -gx VIRTUAL_ENV_PROMPT "(pyhub) "
+end
diff --git a/pyhub/bin/dotenv b/pyhub/bin/dotenv
new file mode 100755
index 0000000000000000000000000000000000000000..ae9d01e70ab517c2d56906eb6800c45110003ada
--- /dev/null
+++ b/pyhub/bin/dotenv
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from dotenv.__main__ import cli
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(cli())
diff --git a/pyhub/bin/get_gprof b/pyhub/bin/get_gprof
new file mode 100755
index 0000000000000000000000000000000000000000..4debe2124aa13eb4406bcd7a93ca03cf41c81c49
--- /dev/null
+++ b/pyhub/bin/get_gprof
@@ -0,0 +1,75 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+#
+# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
+# Copyright (c) 2008-2016 California Institute of Technology.
+# Copyright (c) 2016-2024 The Uncertainty Quantification Foundation.
+# License: 3-clause BSD.  The full license text is available at:
+#  - https://github.com/uqfoundation/dill/blob/master/LICENSE
+'''
+build profile graph for the given instance
+
+running:
+  $ get_gprof <args> <instance>
+
+executes:
+  gprof2dot -f pstats <args> <type>.prof | dot -Tpng -o <type>.call.png
+
+where:
+  <args> are arguments for gprof2dot, such as "-n 5 -e 5"
+  <instance> is code to create the instance to profile
+  <type> is the class of the instance (i.e. type(instance))
+
+For example:
+  $ get_gprof -n 5 -e 1 "import numpy; numpy.array([1,2])"
+
+will create 'ndarray.call.png' with the profile graph for numpy.array([1,2]),
+where '-n 5' eliminates nodes below 5% threshold, similarly '-e 1' eliminates
+edges below 1% threshold
+'''
+
+if __name__ == "__main__":
+    import sys
+    if len(sys.argv) < 2:
+        print ("Please provide an object instance (e.g. 'import math; math.pi')")
+        sys.exit()
+    # grab args for gprof2dot
+    args = sys.argv[1:-1]
+    args = ' '.join(args)
+    # last arg builds the object
+    obj = sys.argv[-1]
+    obj = obj.split(';')
+    # multi-line prep for generating an instance
+    for line in obj[:-1]:
+        exec(line)
+    # one-line generation of an instance
+    try:
+        obj = eval(obj[-1])
+    except Exception:
+        print ("Error processing object instance")
+        sys.exit()
+
+    # get object 'name'
+    objtype = type(obj)
+    name = getattr(objtype, '__name__', getattr(objtype, '__class__', objtype))
+
+    # profile dumping an object
+    import dill
+    import os
+    import cProfile
+    #name = os.path.splitext(os.path.basename(__file__))[0]
+    cProfile.run("dill.dumps(obj)", filename="%s.prof" % name)
+    msg = "gprof2dot -f pstats %s %s.prof | dot -Tpng -o %s.call.png" % (args, name, name)
+    try:
+        res = os.system(msg)
+    except Exception:
+        print ("Please verify install of 'gprof2dot' to view profile graphs")
+    if res:
+        print ("Please verify install of 'gprof2dot' to view profile graphs")
+
+    # get stats
+    f_prof = "%s.prof" % name
+    import pstats
+    stats = pstats.Stats(f_prof, stream=sys.stdout)
+    stats.strip_dirs().sort_stats('cumtime')
+    stats.print_stats(20) #XXX: save to file instead of print top 20?
+    os.remove(f_prof)
diff --git a/pyhub/bin/get_objgraph b/pyhub/bin/get_objgraph
new file mode 100755
index 0000000000000000000000000000000000000000..f01f9655acac61cfd4972150f08c5e600805e565
--- /dev/null
+++ b/pyhub/bin/get_objgraph
@@ -0,0 +1,54 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+#
+# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
+# Copyright (c) 2008-2016 California Institute of Technology.
+# Copyright (c) 2016-2024 The Uncertainty Quantification Foundation.
+# License: 3-clause BSD.  The full license text is available at:
+#  - https://github.com/uqfoundation/dill/blob/master/LICENSE
+"""
+display the reference paths for objects in ``dill.types`` or a .pkl file
+
+Notes:
+    the generated image is useful in showing the pointer references in
+    objects that are or can be pickled.  Any object in ``dill.objects``
+    listed in ``dill.load_types(picklable=True, unpicklable=True)`` works.
+
+Examples::
+
+    $ get_objgraph ArrayType
+    Image generated as ArrayType.png
+"""
+
+import dill as pickle
+#pickle.debug.trace(True)
+#import pickle
+
+# get all objects for testing
+from dill import load_types
+load_types(pickleable=True,unpickleable=True)
+from dill import objects
+
+if __name__ == "__main__":
+    import sys
+    if len(sys.argv) != 2:
+        print ("Please provide exactly one file or type name (e.g. 'IntType')")
+        msg = "\n"
+        for objtype in list(objects.keys())[:40]:
+            msg += objtype + ', '
+        print (msg + "...")
+    else:
+        objtype = str(sys.argv[-1])
+        try:
+            obj = objects[objtype]
+        except KeyError:
+            obj = pickle.load(open(objtype,'rb'))
+            import os
+            objtype = os.path.splitext(objtype)[0]
+        try:
+            import objgraph
+            objgraph.show_refs(obj, filename=objtype+'.png')
+        except ImportError:
+            print ("Please install 'objgraph' to view object graphs")
+
+
+# EOF
diff --git a/pyhub/bin/gimages b/pyhub/bin/gimages
new file mode 100755
index 0000000000000000000000000000000000000000..1f7fcbdacdc79cfcc0c10c3ce97e9cd42bee9d14
--- /dev/null
+++ b/pyhub/bin/gimages
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from google_images_search.cli import cli
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(cli())
diff --git a/pyhub/bin/isort b/pyhub/bin/isort
new file mode 100755
index 0000000000000000000000000000000000000000..1636719dc33a389a6a9007b3d3f622c7eb07823b
--- /dev/null
+++ b/pyhub/bin/isort
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from isort.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/pyhub/bin/isort-identify-imports b/pyhub/bin/isort-identify-imports
new file mode 100755
index 0000000000000000000000000000000000000000..e047fdcd7ea73afd2eb7eaf7f402fea23c658ff1
--- /dev/null
+++ b/pyhub/bin/isort-identify-imports
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from isort.main import identify_imports_main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(identify_imports_main())
diff --git a/pyhub/bin/normalizer b/pyhub/bin/normalizer
new file mode 100755
index 0000000000000000000000000000000000000000..ff7a3ddbd9a8a182231b22d407baea7a65c1ce1b
--- /dev/null
+++ b/pyhub/bin/normalizer
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from charset_normalizer.cli import cli_detect
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(cli_detect())
diff --git a/pyhub/bin/pip b/pyhub/bin/pip
new file mode 100755
index 0000000000000000000000000000000000000000..69b5072c011639c113985199ebbe75c69a36d158
--- /dev/null
+++ b/pyhub/bin/pip
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/pyhub/bin/pip3 b/pyhub/bin/pip3
new file mode 100755
index 0000000000000000000000000000000000000000..69b5072c011639c113985199ebbe75c69a36d158
--- /dev/null
+++ b/pyhub/bin/pip3
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/pyhub/bin/pip3.12 b/pyhub/bin/pip3.12
new file mode 100755
index 0000000000000000000000000000000000000000..69b5072c011639c113985199ebbe75c69a36d158
--- /dev/null
+++ b/pyhub/bin/pip3.12
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/pyhub/bin/pyfiglet b/pyhub/bin/pyfiglet
new file mode 100755
index 0000000000000000000000000000000000000000..cb55496e98a8d59eef20f75cbe8ece18fb9106ba
--- /dev/null
+++ b/pyhub/bin/pyfiglet
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pyfiglet import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/pyhub/bin/pylint b/pyhub/bin/pylint
new file mode 100755
index 0000000000000000000000000000000000000000..84010f0357284120e61535ded4d547170a9689e4
--- /dev/null
+++ b/pyhub/bin/pylint
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import run_pylint
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(run_pylint())
diff --git a/pyhub/bin/pylint-config b/pyhub/bin/pylint-config
new file mode 100755
index 0000000000000000000000000000000000000000..f5923d6dd17125097b8d6e818d61b6b39f6a4348
--- /dev/null
+++ b/pyhub/bin/pylint-config
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import _run_pylint_config
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(_run_pylint_config())
diff --git a/pyhub/bin/pyreverse b/pyhub/bin/pyreverse
new file mode 100755
index 0000000000000000000000000000000000000000..4f4bcb530ad055ad5513e8014440f0f2a4689ff7
--- /dev/null
+++ b/pyhub/bin/pyreverse
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import run_pyreverse
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(run_pyreverse())
diff --git a/pyhub/bin/pyrsa-decrypt b/pyhub/bin/pyrsa-decrypt
new file mode 100755
index 0000000000000000000000000000000000000000..cfe19b043fc8690b6056cde4ba45436ab250ffa3
--- /dev/null
+++ b/pyhub/bin/pyrsa-decrypt
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from rsa.cli import decrypt
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(decrypt())
diff --git a/pyhub/bin/pyrsa-encrypt b/pyhub/bin/pyrsa-encrypt
new file mode 100755
index 0000000000000000000000000000000000000000..1fc41af570a7ebc64b6716b381db23754b11b6b2
--- /dev/null
+++ b/pyhub/bin/pyrsa-encrypt
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from rsa.cli import encrypt
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(encrypt())
diff --git a/pyhub/bin/pyrsa-keygen b/pyhub/bin/pyrsa-keygen
new file mode 100755
index 0000000000000000000000000000000000000000..a46e381b8561374958293daabc61bc9ed7807281
--- /dev/null
+++ b/pyhub/bin/pyrsa-keygen
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from rsa.cli import keygen
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(keygen())
diff --git a/pyhub/bin/pyrsa-priv2pub b/pyhub/bin/pyrsa-priv2pub
new file mode 100755
index 0000000000000000000000000000000000000000..399f87ffda082d9fb8433f82ff8d63cbe3b96bd6
--- /dev/null
+++ b/pyhub/bin/pyrsa-priv2pub
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from rsa.util import private_to_public
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(private_to_public())
diff --git a/pyhub/bin/pyrsa-sign b/pyhub/bin/pyrsa-sign
new file mode 100755
index 0000000000000000000000000000000000000000..80494c93b58b3d2a91da1d6743b0eace153b4967
--- /dev/null
+++ b/pyhub/bin/pyrsa-sign
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from rsa.cli import sign
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(sign())
diff --git a/pyhub/bin/pyrsa-verify b/pyhub/bin/pyrsa-verify
new file mode 100755
index 0000000000000000000000000000000000000000..2595b6173a1e69ef4c5a0037d9979f5f7eb2d775
--- /dev/null
+++ b/pyhub/bin/pyrsa-verify
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from rsa.cli import verify
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(verify())
diff --git a/pyhub/bin/python b/pyhub/bin/python
new file mode 120000
index 0000000000000000000000000000000000000000..b8a0adbbb97ea11f36eb0c6b2a3c2881e96f8e26
--- /dev/null
+++ b/pyhub/bin/python
@@ -0,0 +1 @@
+python3
\ No newline at end of file
diff --git a/pyhub/bin/python3 b/pyhub/bin/python3
new file mode 120000
index 0000000000000000000000000000000000000000..ae65fdaa12936b0d7525b090d198249fa7623e66
--- /dev/null
+++ b/pyhub/bin/python3
@@ -0,0 +1 @@
+/usr/bin/python3
\ No newline at end of file
diff --git a/pyhub/bin/python3.12 b/pyhub/bin/python3.12
new file mode 120000
index 0000000000000000000000000000000000000000..b8a0adbbb97ea11f36eb0c6b2a3c2881e96f8e26
--- /dev/null
+++ b/pyhub/bin/python3.12
@@ -0,0 +1 @@
+python3
\ No newline at end of file
diff --git a/pyhub/bin/symilar b/pyhub/bin/symilar
new file mode 100755
index 0000000000000000000000000000000000000000..64092be67fd2894813f215691f7c621477166a4c
--- /dev/null
+++ b/pyhub/bin/symilar
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pylint import run_symilar
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(run_symilar())
diff --git a/pyhub/bin/undill b/pyhub/bin/undill
new file mode 100755
index 0000000000000000000000000000000000000000..3ae46440a15efcefee64fbcb386e5786f88962f8
--- /dev/null
+++ b/pyhub/bin/undill
@@ -0,0 +1,22 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+#
+# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
+# Copyright (c) 2008-2016 California Institute of Technology.
+# Copyright (c) 2016-2024 The Uncertainty Quantification Foundation.
+# License: 3-clause BSD.  The full license text is available at:
+#  - https://github.com/uqfoundation/dill/blob/master/LICENSE
+"""
+unpickle the contents of a pickled object file
+
+Examples::
+
+    $ undill hello.pkl
+    ['hello', 'world']
+"""
+
+if __name__ == '__main__':
+    import sys
+    import dill
+    for file in sys.argv[1:]:
+        print (dill.load(open(file,'rb')))
+
diff --git a/pyhub/bin/wsdump b/pyhub/bin/wsdump
new file mode 100755
index 0000000000000000000000000000000000000000..cac93d955f0041ffa8f85fd27bb9dab31841d962
--- /dev/null
+++ b/pyhub/bin/wsdump
@@ -0,0 +1,8 @@
+#!/home/tay/Git/pyhub/pyhub/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from websocket._wsdump import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())
diff --git a/pyhub/lib64 b/pyhub/lib64
new file mode 120000
index 0000000000000000000000000000000000000000..7951405f85a569efbacc12fccfee529ef1866602
--- /dev/null
+++ b/pyhub/lib64
@@ -0,0 +1 @@
+lib
\ No newline at end of file
diff --git a/pyhub/pyvenv.cfg b/pyhub/pyvenv.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..80220d6a11100c91d24f4e19ee16ecea5c127ee1
--- /dev/null
+++ b/pyhub/pyvenv.cfg
@@ -0,0 +1,5 @@
+home = /usr/bin
+include-system-site-packages = false
+version = 3.12.3
+executable = /usr/bin/python3.12
+command = /usr/bin/python3 -m venv /home/tay/Git/pyhub/pyhub
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dd518731b5f0fee5bafa84633c78c2d2a3428577
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,50 @@
+aiohttp==3.9.5
+aiosignal==1.3.1
+astroid==3.2.2
+attrs==23.2.0
+cachetools==5.3.3
+certifi==2024.2.2
+charset-normalizer==3.3.2
+click==8.1.7
+colorama==0.4.6
+configparser==7.0.0
+dill==0.3.8
+discord.py==2.3.2
+frozenlist==1.4.1
+google-api-core==2.19.0
+google-api-python-client==2.48.0
+google-auth==2.29.0
+google-auth-httplib2==0.2.0
+Google-Images-Search==1.4.6
+googleapis-common-protos==1.63.0
+httplib2==0.22.0
+idna==3.7
+isort==5.13.2
+mccabe==0.7.0
+multidict==6.0.5
+oauthlib==3.2.2
+pillow==10.3.0
+platformdirs==4.2.2
+praw==7.7.1
+prawcore==2.4.0
+proto-plus==1.23.0
+protobuf==4.25.3
+pyasn1==0.6.0
+pyasn1_modules==0.4.0
+pyfiglet==0.8.post1
+pylint==3.2.2
+pyparsing==3.1.2
+python-dotenv==1.0.1
+python-resize-image==1.1.20
+requests==2.32.3
+requests-oauthlib==1.3.1
+rsa==4.9
+six==1.16.0
+termcolor==1.1.0
+tomlkit==0.12.5
+tweepy==4.14.0
+update-checker==0.18.0
+uritemplate==4.1.1
+urllib3==2.2.1
+websocket-client==1.8.0
+yarl==1.9.4