From 3f6fa3c98a1a8326d64e82a885625a0fce7732f4 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 19 May 2025 13:47:37 -0400 Subject: [PATCH 01/77] WIP --- DATAPOLICY.md | 5 +--- DexScript/github/installer.py | 54 ++++------------------------------- DexScript/package/cog.py | 20 ++++++++----- DexScript/package/parser.py | 4 ++- DexScript/package/utils.py | 13 ++++++--- README.md | 4 +-- pyproject.toml | 2 +- 7 files changed, 34 insertions(+), 68 deletions(-) diff --git a/DATAPOLICY.md b/DATAPOLICY.md index 1ff136e..72dbe3f 100644 --- a/DATAPOLICY.md +++ b/DATAPOLICY.md @@ -8,10 +8,7 @@ We do not collect, store, or access any data from your application. DexScript on ## File Modifications -During the DexScript installation process, DexScript will modify the following based on the Ballsdex version: - -- **2.22.0-** - `ballsdex/core/bot.py`. -- **2.22.0+** - `config.yml` +During the DexScript installation process, DexScript will modify your application's `config.yml` file. DexScript will add a single line of code to allow the DexScript extension to load when your application starts. This modification is solely for the purpose of running DexScript. You can view the code added by checking the `DexScript/github/installer.py` file in the official DexScript GitHub repository. diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index 6713240..fd9b7c8 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -6,6 +6,7 @@ # An explanation of the code will be provided below. # # # # THIS CODE IS RAN VIA THE `EVAL` COMMAND. # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # import os @@ -25,11 +26,7 @@ from discord.ext import commands UPDATING = os.path.isdir("ballsdex/packages/dexscript") - - -class MigrationType(Enum): - APPEND = 1 - REPLACE = 2 +ASSET_PATH = "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/main/assets" @dataclass @@ -41,22 +38,10 @@ class InstallerConfig: github = ["Dotsian/DexScript", "dev"] files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py"] appearance = { - "logo": "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/dev/assets/DexScriptLogo.png", - "logo_error": "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/dev/assets/DexScriptLogoError.png", - "banner": "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/dev/assets/DexScriptPromo.png", + "logo": f"{ASSET_PATH}/DexScriptLogo.png", + "logo_error": f"{ASSET_PATH}/DexScriptLogoError.png", + "banner": f"{ASSET_PATH}/DexScriptPromo.png", } - install_migrations = [ - ( - "||await self.add_cog(Core(self))", - '||await self.load_extension("ballsdex.packages.dexscript")\n', - MigrationType.APPEND, - ), - ( - '||await self.load_extension("ballsdex.core.dexscript")\n', - '||await self.load_extension("ballsdex.packages.dexscript")\n', - MigrationType.REPLACE, - ), - ] uninstall_migrations = [ '||await self.load_extension("ballsdex.core.dexscript")\n', '||await self.load_extension("ballsdex.packages.dexscript")\n', @@ -270,30 +255,6 @@ def uninstall_migrate(self): with open("ballsdex/core/bot.py", "w") as write_file: write_file.writelines(lines) - def install_migrate(self): - with open("ballsdex/core/bot.py", "r") as read_file: - lines = read_file.readlines() - - for index, line in enumerate(lines): - for migration in config.install_migrations: - original = self.format_migration(migration[0]) - new = self.format_migration(migration[1]) - - match migration[2]: - case MigrationType.REPLACE: - if line.rstrip() != original: - continue - - lines[index] = new - case MigrationType.APPEND: - if line.rstrip() != original or new in lines: - continue - - lines.insert(index + 1, new) - - with open("ballsdex/core/bot.py", "w") as write_file: - write_file.writelines(lines) - async def install(self): if os.path.isfile("ballsdex/core/dexscript.py"): os.remove("ballsdex/core/dexscript.py") @@ -325,10 +286,7 @@ async def install(self): logger.log("Applying bot.py migrations", "INFO") - if self.add_package(config.path.replace("/", ".")): - self.uninstall_migrate() - else: - self.install_migrate() + self.add_package(config.path.replace("/", ".")) logger.log("Loading DexScript extension", "INFO") diff --git a/DexScript/package/cog.py b/DexScript/package/cog.py index 044a265..b41a773 100644 --- a/DexScript/package/cog.py +++ b/DexScript/package/cog.py @@ -12,10 +12,12 @@ __version__ = "0.5" +ASSET_PATH = "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/main/assets" + class DexScript(commands.Cog): """ - DexScript commands + DexScript commands. """ def __init__(self, bot): @@ -41,7 +43,7 @@ def check_version(): return ( f"Your DexScript version ({__version__}) is outdated. " f"Please update to version ({new_version}) " - f"by running `{settings.prefix}upgrade`" + f"by running `{settings.prefix}installer`" ) return None @@ -55,7 +57,7 @@ async def run(self, ctx: commands.Context, *, code: str): Parameters ---------- code: str - The code you want to execute. + The code you want to execute. """ body = Utils.remove_code_markdown(code) @@ -102,7 +104,7 @@ async def about(self, ctx: commands.Context): ) embed = discord.Embed( - title="DexScript - BETA", + title="DexScript", description=description, color=discord.Color.from_str("#03BAFC"), ) @@ -110,7 +112,7 @@ async def about(self, ctx: commands.Context): version_check = "OUTDATED" if self.check_version() is not None else "LATEST" embed.set_thumbnail( - url="https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/dev/assets/DexScriptLogo.png" + url=f"{ASSET_PATH}/DexScriptLogo.png" ) embed.set_footer(text=f"DexScript {__version__} ({version_check})") @@ -119,9 +121,13 @@ async def about(self, ctx: commands.Context): @commands.command() @commands.is_owner() async def installer(self, ctx: commands.Context): + """ + Displays the DexScript installer. + """ link = ( "https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py" ) + request = requests.get(link, {"ref": config.reference}) match request.status_code: @@ -147,9 +153,9 @@ async def setting(self, ctx: commands.Context, setting: str, value: str | None = Parameters ---------- setting: str - The setting you want to toggle. + The setting you want to toggle. value: str | None - The value you want to set the setting to. + The value you want to set the setting to. """ setting = setting.lower() diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index b5798dc..ad93cc2 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -10,6 +10,8 @@ from . import commands from .utils import Types, Utils, config +PARSER_RE = re.compile(r"[^>]+") + @dataclass class Value: @@ -97,7 +99,7 @@ async def execute(self, code: str, run_commands=True): split_code = [x for x in code.split("\n") if x.strip() != ""] parsed_code = [ - [self.create_value(s.strip()) for s in re.findall(r"[^>]+", line)] + [self.create_value(s.strip()) for s in PARSER_RE.findall(line)] for line in split_code if not line.strip().startswith("--") ] diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index b352ef8..4e342c3 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -11,17 +11,19 @@ from typing import Any, Callable import discord -from ballsdex.core.models import Ball, Economy, Regime, Special # noqa: F401, I001 +from ballsdex.core.models import Ball, BallInstance, Economy, Regime, Special # noqa: F401, I001 from dateutil.parser import parse as parse_date START_CODE_BLOCK_RE = re.compile(r"^((```sql?)(?=\s)|(```))") FILENAME_RE = re.compile(r"^(.+)(\.\S+)$") +STR_RE = re.compile(r"return\s+self\.(\w+)") # TODO: Add `return str()` STATIC = os.path.isdir("static") MEDIA_PATH = "./static/uploads" if STATIC else "./admin_panel/media" MODELS = [ "Ball", + "BallInstance", "Regime", "Economy", "Special", @@ -386,7 +388,7 @@ def autocorrect(string: str, correction_list: list[str], error="does not exist." return autocorrection[0] @staticmethod - def extract_str_attr(object: Any): + def extract_str_attr(object: Any) -> str: """ Extracts the attribute used in the `__str__` method of a class. @@ -395,9 +397,12 @@ def extract_str_attr(object: Any): object: Any The class you want to fetch the `__str__` attribute from. """ - expression = r"return\s+self\.(\w+)" # TODO: Add `return str()` + extracted = STR_RE.search(inspect.getsource(object.__str__)).group(1) - return re.search(expression, inspect.getsource(object.__str__)).group(1) + if extracted == "to_string": + return "pk" + + return extracted @staticmethod def remove_code_markdown(content: str) -> str: diff --git a/README.md b/README.md index e0adb9f..e9db415 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# DexScript - BETA +# DexScript ![DexScript Banner](assets/DexScriptPromo.png) @@ -20,8 +20,6 @@ DexScript has a ton more features too! All of them can be found within our exten * **Mass updating and deleting Balls, Regimes, Specials, etc.** * **Saving evals and loading them**. -DexScript is currently in beta. However, the latest version is a release candidate for the full release. - ## DexScript Requirements To install DexScript, you must have the following: diff --git a/pyproject.toml b/pyproject.toml index fed695d..85f5d35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dexscript" -version = "0.5" +version = "0.6" description = "" authors = ["dotzz "] license = "MIT" From d6bd29e6765e0c0320aadffe6f6f4ba7960f3141 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 09:00:23 -0400 Subject: [PATCH 02/77] Attempt link support --- DexScript/package/commands.py | 40 ++++++++++++++++++++++--------- DexScript/package/utils.py | 45 ++++++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 99a492c..4a827c2 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -73,7 +73,7 @@ async def delete(self, ctx, model, identifier): async def update(self, ctx, model, identifier, attribute, value=None): """ - Updates a model instance's attribute. If value is None, it will check + Updates a model instance's attribute. If value is None, it will check for any attachments. Documentation @@ -93,13 +93,22 @@ async def update(self, ctx, model, identifier, attribute, value=None): ), ) - if value is None and self.shared.attachments and attribute_name in image_fields: - image_path = await Utils.save_file(self.shared.attachments.pop(0)) - new_value = f"/static/uploads/{image_path}" if STATIC else f"/{image_path}" + if attribute_name in image_fields: + file = None + + if Value is not None and new_value.startswith("https://"): + file = Utils.from_link(new_value) + else: + file = self.shared.attachments.pop(0) + + image_path = await Utils.save_file(file) + + new_value = f"/static/uploads/{image_path}" if STATIC else image_path if attribute.type == Types.MODEL: attribute_name = f"{attribute.name.lower()}_id" attribute_model = await Utils.get_model(attribute, value) + new_value = attribute_model.pk setattr(returned_model, attribute_name, new_value) @@ -111,7 +120,7 @@ async def update(self, ctx, model, identifier, attribute, value=None): async def view(self, ctx, model, identifier, attribute=None): """ - Displays an attribute of a model instance. If `ATTRIBUTE` is left blank, + Displays an attribute of a model instance. If `ATTRIBUTE` is left blank, it will display every attribute of that model instance. Documentation @@ -188,8 +197,8 @@ class Filter(DexCommand): async def update(self, ctx, model, attribute, old_value, new_value, tortoise_operator=None): """ - Updates all instances of a model to the specified value where the specified attribute - meets the condition defined by the optional `TORTOISE_OPERATOR` argument + Updates all instances of a model to the specified value where the specified attribute + meets the condition defined by the optional `TORTOISE_OPERATOR` argument (e.g., greater than, equal to, etc.). Documentation @@ -217,8 +226,8 @@ async def update(self, ctx, model, attribute, old_value, new_value, tortoise_ope async def delete(self, ctx, model, attribute, value, tortoise_operator=None): """ - Deletes all instances of a model where the specified attribute meets the condition - defined by the optional `TORTOISE_OPERATOR` argument + Deletes all instances of a model where the specified attribute meets the condition + defined by the optional `TORTOISE_OPERATOR` argument (e.g., greater than, equal to, etc.). Documentation @@ -244,8 +253,8 @@ async def delete(self, ctx, model, attribute, value, tortoise_operator=None): async def view(self, ctx, model, attribute, value, tortoise_operator=None): """ - Displays all instances of a model where the specified attribute meets the condition - defined by the optional `TORTOISE_OPERATOR` argument + Displays all instances of a model where the specified attribute meets the condition + defined by the optional `TORTOISE_OPERATOR` argument (e.g., greater than, equal to, etc.). Documentation @@ -335,6 +344,13 @@ async def remove(self, ctx, name): await ctx.send(f"Removed `{name}` preset.") async def list(self, ctx): + """ + Lists all eval presets. + + Documentation + ------------- + EVAL > LIST + """ if os.listdir("eval_presets") == []: await ctx.send("You have no eval presets saved.") return @@ -466,6 +482,8 @@ async def create(self, ctx, model, argument="..."): f"UPDATE > BALL > {argument} > CREDITS > ...", f"UPDATE > BALL > {argument} > CAPACITY_NAME > ...", f"UPDATE > BALL > {argument} > CAPACITY_DESCRIPTION > ...", + f"UPDATE > BALL > {argument} > WILD_CARD > ...", + f"UPDATE > BALL > {argument} > COLLECTION_CARD > ..." ] await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 4e342c3..e7ea1d8 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -3,6 +3,7 @@ import inspect import os import re +import requests from dataclasses import dataclass from difflib import get_close_matches from enum import Enum @@ -16,6 +17,7 @@ START_CODE_BLOCK_RE = re.compile(r"^((```sql?)(?=\s)|(```))") FILENAME_RE = re.compile(r"^(.+)(\.\S+)$") +PASCAL_RE = re.compile(r"(_[a-z])") STR_RE = re.compile(r"return\s+self\.(\w+)") # TODO: Add `return str()` STATIC = os.path.isdir("static") @@ -59,6 +61,25 @@ class Utils: Utility functions for DexScript. """ + @staticmethod + def from_link(link: str) -> (str, bytes): + """ + Returns a image link's content and filename. + + Parameters + ---------- + link: str + The image link you want to load the data from. + """ + data = requests.get(link, stream=True) + + if not data.ok: + raise Exception(f"Failed to request '{link}'") + + filename = os.path.basename(link).split("?")[0] + + return (filename, data.content) + @staticmethod def image_path(path: str) -> bool: """ @@ -115,8 +136,9 @@ def pascal_case(string: str) -> str: The string you want to convert. """ string = string.lower() - return re.sub( - r"(_[a-z])", lambda m: m.group(1)[1].upper(), string[:1].upper() + string[1:] + + return PASCAL_RE.sub( + lambda m: m.group(1)[1].upper(), string[:1].upper() + string[1:] ) @staticmethod @@ -187,17 +209,20 @@ def check(message): break @staticmethod - async def save_file(attachment: discord.Attachment) -> Path: + async def save_file(attachment: discord.Attachment | tuple[str, bytes]) -> Path: """ - Saves a `discord.Attachment` object into a directory. + Saves a `discord.Attachment` or a `tuple` object into a directory. Parameters ---------- - attachment: discord.Attachment + attachment: discord.Attachment | tuple[str, bytes] The attachment you want to save. """ - path = Path(f"{MEDIA_PATH}/{attachment.filename}") - match = FILENAME_RE.match(attachment.filename) + is_attachment = isinstance(attachment, discord.Attachment) + filename = attachment.filename if is_attachment else attachment[0] + + path = Path(f"{MEDIA_PATH}/{filename}") + match = FILENAME_RE.match(filename) if not match: raise TypeError("The file you uploaded lacks an extension.") @@ -208,7 +233,11 @@ async def save_file(attachment: discord.Attachment) -> Path: path = Path(f"{MEDIA_PATH}/{match.group(1)}-{i}{match.group(2)}") i = i + 1 - await attachment.save(path) + if isinstance(attachment, discord.Attachment): + await attachment.save(path) + else: + with open(path, "wb") as write_file: + write_file.write(attachment[1]) return path.relative_to(MEDIA_PATH) From 6c56e3133ec06c2b124a20423e677f6a824e2f64 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 09:20:03 -0400 Subject: [PATCH 03/77] Add `player` model --- DexScript/github/installer.py | 1 - DexScript/package/utils.py | 14 +++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index fd9b7c8..c292343 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -16,7 +16,6 @@ from dataclasses import dataclass from dataclasses import field as datafield from datetime import datetime -from enum import Enum from io import StringIO from traceback import format_exc diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index e7ea1d8..fa086ea 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -2,8 +2,8 @@ import contextlib import inspect import os -import re import requests +import re from dataclasses import dataclass from difflib import get_close_matches from enum import Enum @@ -12,13 +12,15 @@ from typing import Any, Callable import discord -from ballsdex.core.models import Ball, BallInstance, Economy, Regime, Special # noqa: F401, I001 +from ballsdex.core.models import ( + Ball, BallInstance, Economy, Player, Regime, Special # noqa: F401, I001 +) from dateutil.parser import parse as parse_date START_CODE_BLOCK_RE = re.compile(r"^((```sql?)(?=\s)|(```))") FILENAME_RE = re.compile(r"^(.+)(\.\S+)$") PASCAL_RE = re.compile(r"(_[a-z])") -STR_RE = re.compile(r"return\s+self\.(\w+)") # TODO: Add `return str()` +STR_RE = re.compile(r"return\s+self\.(\w+)") STATIC = os.path.isdir("static") MEDIA_PATH = "./static/uploads" if STATIC else "./admin_panel/media" @@ -26,8 +28,9 @@ MODELS = [ "Ball", "BallInstance", - "Regime", "Economy", + "Player", + "Regime", "Special", ] @@ -426,7 +429,8 @@ def extract_str_attr(object: Any) -> str: object: Any The class you want to fetch the `__str__` attribute from. """ - extracted = STR_RE.search(inspect.getsource(object.__str__)).group(1) + source = inspect.getsource(object.__str__).replace("str(", "") + extracted = STR_RE.search(source).group(1) if extracted == "to_string": return "pk" From 892eb207bc7890caaa092b85a36cab74f99c150a Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 09:36:00 -0400 Subject: [PATCH 04/77] Force `list[str]` during autocorrect --- DexScript/package/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index fa086ea..f07add0 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -350,6 +350,9 @@ async def get_model(model, identifier: str): """ correction_list = await model.value.all().values_list(model.extra_data[0], flat=True) + if not all([isinstance(x, str) for x in correction_list]): + correction_list = [str(x) for x in correction_list] + try: returned_model = await model.value.filter( **{model.extra_data[0]: Utils.autocorrect(str(identifier), correction_list)} From fc803fd3a07a0723c05da830ce1bffdb54ef5c42 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 09:50:41 -0400 Subject: [PATCH 05/77] Fix pascal casing detection --- DexScript/package/parser.py | 7 ++++--- DexScript/package/utils.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index ad93cc2..6981180 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -8,7 +8,7 @@ from dateutil.parser import parse as parse_date from . import commands -from .utils import Types, Utils, config +from .utils import Types, Utils, config, pascal_case PARSER_RE = re.compile(r"[^>]+") @@ -52,11 +52,12 @@ def create_value(self, line): value.value = line lower = line.lower() + pascal = pascal_case(line) type_dict = { Types.METHOD: lower in self.global_methods, Types.CLASS: lower in [x[0].lower() for x in self.command_classes], - Types.MODEL: lower in Utils.models(True, key=str.lower), + Types.MODEL: pascal in Utils.models(True), Types.DATETIME: Utils.is_date(lower) and lower.count("-") >= 2, Types.BOOLEAN: lower in ["true", "false"], } @@ -70,7 +71,7 @@ def create_value(self, line): match value.type: case Types.MODEL: - model = Utils.fetch_model(line) + model = Utils.fetch_model(pascal) if model is None: raise Exception(f"'{line}' is not a valid model") diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index f07add0..7bed0dd 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -19,7 +19,7 @@ START_CODE_BLOCK_RE = re.compile(r"^((```sql?)(?=\s)|(```))") FILENAME_RE = re.compile(r"^(.+)(\.\S+)$") -PASCAL_RE = re.compile(r"(_[a-z])") +PASCAL_RE = re.compile(r"((-|_)[a-z])") STR_RE = re.compile(r"return\s+self\.(\w+)") STATIC = os.path.isdir("static") From 593d72b799ecafd2bbfc710286b448216b51de32 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 09:52:59 -0400 Subject: [PATCH 06/77] Fix import mistake --- DexScript/package/parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 6981180..c3986c5 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -8,7 +8,7 @@ from dateutil.parser import parse as parse_date from . import commands -from .utils import Types, Utils, config, pascal_case +from .utils import Types, Utils, config PARSER_RE = re.compile(r"[^>]+") @@ -52,7 +52,7 @@ def create_value(self, line): value.value = line lower = line.lower() - pascal = pascal_case(line) + pascal = Utils.pascal_case(line) type_dict = { Types.METHOD: lower in self.global_methods, From 03cc0c10ed1412376c1937b368191af80c42f3b8 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 10:03:55 -0400 Subject: [PATCH 07/77] Force pascal casing --- DexScript/package/parser.py | 4 ++-- DexScript/package/utils.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index c3986c5..2fc0702 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -71,10 +71,10 @@ def create_value(self, line): match value.type: case Types.MODEL: - model = Utils.fetch_model(pascal) + model = Utils.fetch_model(pascal, False) if model is None: - raise Exception(f"'{line}' is not a valid model") + raise Exception(f"'{pascal}' is not a valid model") string_key = Utils.extract_str_attr(model) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 7bed0dd..fa444f8 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -245,7 +245,7 @@ async def save_file(attachment: discord.Attachment | tuple[str, bytes]) -> Path: return path.relative_to(MEDIA_PATH) @staticmethod - def fetch_model(model: str): + def fetch_model(model: str, force_pascal: bool = True): """ Fetches a model's class based on the model name provided. @@ -253,8 +253,10 @@ def fetch_model(model: str): ---------- model: str The name of the model you want to fetch. + force_pascal: bool + Whether you want to force pascal casing on the model name. """ - return globals().get(Utils.pascal_case(model)) + return globals().get(Utils.pascal_case(model) if force_pascal else model) @staticmethod def models(names=False, key: Callable | None = None): From 08d92f95d6bd8ad2996542a459b06885454e8c81 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 10:13:43 -0400 Subject: [PATCH 08/77] Assume ID --- DexScript/package/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index fa444f8..6bfcc16 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -438,7 +438,7 @@ def extract_str_attr(object: Any) -> str: extracted = STR_RE.search(source).group(1) if extracted == "to_string": - return "pk" + return "id" return extracted From 10df93f718e756ee8f089668abc576c37e15925f Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 11:51:16 -0400 Subject: [PATCH 09/77] Add hex type --- DexScript/package/parser.py | 4 ++++ DexScript/package/utils.py | 1 + 2 files changed, 5 insertions(+) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 2fc0702..4009011 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -60,6 +60,7 @@ def create_value(self, line): Types.MODEL: pascal in Utils.models(True), Types.DATETIME: Utils.is_date(lower) and lower.count("-") >= 2, Types.BOOLEAN: lower in ["true", "false"], + Types.HEX: lower.startswith("#"), } for key, operation in type_dict.items(): @@ -89,6 +90,9 @@ def create_value(self, line): case Types.DATETIME: value.value = parse_date(line) + case Types.HEX: + value.value = int(line[1:], 16) + return value def error(self, message, log): diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 6bfcc16..ce69875 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -42,6 +42,7 @@ class Types(Enum): BOOLEAN = 3 MODEL = 4 DATETIME = 5 + HEX = 6 @dataclass From 025b65da8ec4719f325393dac8a53a8556de98c1 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 11:55:06 -0400 Subject: [PATCH 10/77] Fix --- DexScript/package/parser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 4009011..141ee1a 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -91,7 +91,10 @@ def create_value(self, line): value.value = parse_date(line) case Types.HEX: - value.value = int(line[1:], 16) + hex_int = int(line[1:], 16) + + value.name = hex_int + value.value = hex_int return value From f1fa6845bd29cec27f1bdf4ff55be85d1ddc7c74 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 11:56:48 -0400 Subject: [PATCH 11/77] Use reference in installer command --- DexScript/package/cog.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/DexScript/package/cog.py b/DexScript/package/cog.py index b41a773..86810b0 100644 --- a/DexScript/package/cog.py +++ b/DexScript/package/cog.py @@ -120,22 +120,27 @@ async def about(self, ctx: commands.Context): @commands.command() @commands.is_owner() - async def installer(self, ctx: commands.Context): + async def installer(self, ctx: commands.Context, reference: str = "main"): """ Displays the DexScript installer. + + Parameters + ---------- + reference: str + The DexScript branch you want to run the installer on. """ link = ( "https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py" ) - request = requests.get(link, {"ref": config.reference}) + request = requests.get(link, {"ref": reference}) match request.status_code: case requests.codes.not_found: - await ctx.send(f"Could not find installer for the {config.reference} branch.") + await ctx.send(f"Could not find installer for the {reference} branch.") case requests.codes.ok: - content = requests.get(link, {"ref": config.reference}).json()["content"] + content = requests.get(link, {"ref": reference}).json()["content"] await ctx.invoke( self.bot.get_command("eval"), body=base64.b64decode(content).decode() From a61a80a3c9c4fc12e693b14874f655ae5c6645a7 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 11:59:11 -0400 Subject: [PATCH 12/77] Fix #2 --- DexScript/package/parser.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 141ee1a..90c8bca 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -91,10 +91,10 @@ def create_value(self, line): value.value = parse_date(line) case Types.HEX: - hex_int = int(line[1:], 16) + hex_str = str(int(line[1:], 16)) - value.name = hex_int - value.value = hex_int + value.name = hex_str + value.value = hex_str return value From 361dd6e5df0e143974379df902804d1f5ad9c4d6 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 13:24:45 -0400 Subject: [PATCH 13/77] Add array type and some other changes --- DexScript/package/api.py | 53 +++++++++++++++++++++++++++++ DexScript/package/commands.py | 63 ++++++++++++++++------------------- DexScript/package/parser.py | 7 +++- DexScript/package/utils.py | 1 + README.md | 12 +++---- 5 files changed, 95 insertions(+), 41 deletions(-) create mode 100644 DexScript/package/api.py diff --git a/DexScript/package/api.py b/DexScript/package/api.py new file mode 100644 index 0000000..35eaf62 --- /dev/null +++ b/DexScript/package/api.py @@ -0,0 +1,53 @@ +from .utils import Types + + +class DexCommand: + """ + Default class for all dex commands. + """ + + def __init__(self, bot, shared): + self.bot = bot + self.shared = shared + + def __loaded__(self): + """ + Calls whenever the command is loaded for the first time. + """ + pass + + def attribute_error(self, model, attribute): + """ + Raises an error if an attribute doesn't exist in a model. + + Parameters + ---------- + model: Value + The model you want to check in. + attribute: Value + The attribute you want to check. + """ + if model.value is None or hasattr(model.value(), attribute): + return + + raise Exception( + f"'{attribute}' is not a valid {model.name} attribute\n" + f"Run `ATTRIBUTES > {model.name}` to see a list of " + "all attributes for that model" + ) + + def type_error(self, value, allowed_types: list[Types]): + """ + Raises an error if the type of a `Value` is not allowed. + + Parameters + ---------- + value: Value + The value that has the original type. + allowed_types: list[Types] + A list of types that are allowed. + """ + if value.type in allowed_types: + return + + raise Exception(f"'{value.type}' is an invalid type.") diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 4a827c2..ab271c8 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -6,6 +6,7 @@ import discord +from .api import DexCommand from .utils import STATIC, Types, Utils @@ -18,29 +19,6 @@ class Shared: attachments: list = datafield(default_factory=list) -class DexCommand: - """ - Default class for all dex commands. - """ - - def __init__(self, bot, shared): - self.bot = bot - self.shared = shared - - def __loaded__(self): - pass - - def attribute_error(self, model, attribute): - if model.value is None or hasattr(model.value(), attribute): - return - - raise Exception( - f"'{attribute}' is not a valid {model.name} attribute\n" - f"Run `ATTRIBUTES > {model.name}` to see a list of " - "all attributes for that model" - ) - - class Global(DexCommand): """ Main methods for DexScript. @@ -57,19 +35,27 @@ async def create(self, ctx, model, identifier): await Utils.create_model(model.value, identifier) await ctx.send(f"Created `{identifier}` {model.name.lower()}") - async def delete(self, ctx, model, identifier): + async def delete(self, ctx, model, identifiers): """ - Deletes a model instance. + Deletes one or multiple model instances. Documentation ------------- - DELETE > MODEL > IDENTIFIER + DELETE > MODEL > IDENTIFIER(S) """ - fetched_model = await Utils.get_model(model, identifier) - - await fetched_model.delete() + async def delete_model(identifier): + fetched_model = await Utils.get_model(model, identifier) + + await fetched_model.delete() + + await ctx.send(f"Deleted `{identifier}` {model.name.lower()}") + + if identifiers.type != Types.ARRAY: + await delete_model(identifiers) + return - await ctx.send(f"Deleted `{identifier}` {model.name.lower()}") + for identifier in identifiers.value: + await delete_model(identifier) async def update(self, ctx, model, identifier, attribute, value=None): """ @@ -382,15 +368,24 @@ class File(DexCommand): Commands for managing and modifying the bot's internal filesystem. """ - async def read(self, ctx, file_path): + async def read(self, ctx, file_paths): """ - Sends a file based on the specified file path. + Sends one or more files based on the specified file paths. Documentation ------------- - FILE > READ > FILE_PATH + FILE > READ > FILE_PATH(S) """ - await ctx.send(file=discord.File(file_path.name)) + if file_paths.type != Types.ARRAY: + await ctx.send(file=discord.File(file_paths.name)) + return + + files = [] + + for path in file_paths.values: + files.append(discord.File(path.name)) + + await ctx.send(files=files) async def write(self, ctx, file_path): """ diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 90c8bca..975020d 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -8,6 +8,7 @@ from dateutil.parser import parse as parse_date from . import commands +from .api import DexCommand from .utils import Types, Utils, config PARSER_RE = re.compile(r"[^>]+") @@ -39,7 +40,7 @@ def __init__(self, ctx, bot): commands, lambda o: ( inspect.isclass(o) - and issubclass(o, commands.DexCommand) + and issubclass(o, DexCommand) and not issubclass(o, commands.Global) and o.__name__ != "DexCommand" ), @@ -61,6 +62,7 @@ def create_value(self, line): Types.DATETIME: Utils.is_date(lower) and lower.count("-") >= 2, Types.BOOLEAN: lower in ["true", "false"], Types.HEX: lower.startswith("#"), + Types.ARRAY: lower.startswith("[") and lower.endswith("]") } for key, operation in type_dict.items(): @@ -96,6 +98,9 @@ def create_value(self, line): value.name = hex_str value.value = hex_str + case Types.ARRAY: + value.value = [x.strip() for x in line[1:-1].split("|")] + return value def error(self, message, log): diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index ce69875..a42c66e 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -43,6 +43,7 @@ class Types(Enum): MODEL = 4 DATETIME = 5 HEX = 6 + ARRAY = 7 @dataclass diff --git a/README.md b/README.md index e9db415..4d97153 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,15 @@ To install DexScript, run the following eval command: #### Release Version -```py -import base64, requests; await ctx.invoke(bot.get_command("eval"), body=base64.b64decode(requests.get("https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py").json()["content"]).decode()) -``` +> ```py +> import base64, requests; await ctx.invoke(bot.get_command("eval"), body=base64.b64decode(requests.get("https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py").json()["content"]).decode()) +> ``` #### Development Version -```py -import base64, requests; await ctx.invoke(bot.get_command("eval"), body=base64.b64decode(requests.get("https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py", {"ref": "dev"}).json()["content"]).decode()) -``` +> ```py +> import base64, requests; await ctx.invoke(bot.get_command("eval"), body=base64.b64decode(requests.get("https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py", {"ref": "dev"}).json()["content"]).decode()) +> ``` ### DexScript Installer From b9b7c6c033b81d27ebe845f878b4dce58d4838c2 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 13:29:10 -0400 Subject: [PATCH 14/77] Fix missing file --- DexScript/github/installer.py | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index c292343..d722123 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -35,7 +35,7 @@ class InstallerConfig: """ github = ["Dotsian/DexScript", "dev"] - files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py"] + files = ["__init__.py", "api.py", "cog.py", "commands.py", "parser.py", "utils.py"] appearance = { "logo": f"{ASSET_PATH}/DexScriptLogo.png", "logo_error": f"{ASSET_PATH}/DexScriptLogoError.png", diff --git a/README.md b/README.md index 4d97153..1232b01 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ To install DexScript, run the following eval command: ### DexScript Installer > [!NOTE] -> If DexScript is already installed, you can run `b.installer` to show the DexScript installer, replacing `b.` with your application's prefix.. +> If DexScript is already installed, you can run `b.installer` to show the DexScript installer, replacing `b.` with your application's prefix. Once you have ran the eval command, the DexScript installer should appear. There will be three buttons: From 518b522a145a215790b71809c41a012cf6f789a3 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 13:32:44 -0400 Subject: [PATCH 15/77] whoopsie --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index ab271c8..3f9f354 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -382,7 +382,7 @@ async def read(self, ctx, file_paths): files = [] - for path in file_paths.values: + for path in file_paths.value: files.append(discord.File(path.name)) await ctx.send(files=files) From 9431be28c866f045ce6aa97050c7f53ab3c305ae Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 20 May 2025 13:37:52 -0400 Subject: [PATCH 16/77] Create values --- DexScript/package/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 975020d..f2147b8 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -99,7 +99,7 @@ def create_value(self, line): value.value = hex_str case Types.ARRAY: - value.value = [x.strip() for x in line[1:-1].split("|")] + value.value = [self.create_value(x.strip()) for x in line[1:-1].split("|")] return value From d7aae72f8092d56b30319c8935faa400acd18159 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Wed, 21 May 2025 14:21:51 -0400 Subject: [PATCH 17/77] Bump dev version and some other changes --- DexScript/github/installer.py | 16 +++++++++- DexScript/github/migration.py | 31 ------------------ DexScript/package/api.py | 53 ------------------------------- DexScript/package/cog.py | 2 +- DexScript/package/commands.py | 60 ++++++++++++++++++++++++++++++++++- DexScript/package/parser.py | 4 +-- pyproject.toml | 2 +- 7 files changed, 77 insertions(+), 91 deletions(-) delete mode 100644 DexScript/github/migration.py delete mode 100644 DexScript/package/api.py diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index d722123..d7fc21f 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -109,7 +109,12 @@ def error(self): self.timestamp = datetime.now() if logger.output != []: - self.description += f"\n```{logger.output[-1]}```" + output = logger.output[-1] + + if len(output) >= 750: + output = logger.output[-1][:750] + "..." + + self.description += f"\n```{output}```" self.installer.interface.attachments = [logger.file("DexScript.log")] @@ -216,6 +221,12 @@ class Installer: def __init__(self): self.interface = InstallerGUI(self) + def has_package_config(self) -> bool: + with open("config.yml", "r") as file: + lines = file.readlines() + + return "packages:\n" not in lines + def add_package(self, package: str) -> bool: with open("config.yml", "r") as file: lines = file.readlines() @@ -255,6 +266,9 @@ def uninstall_migrate(self): write_file.writelines(lines) async def install(self): + if not has_package_config(): + raise Exception("Your Ballsdex version is no longer compatible with DexScript") + if os.path.isfile("ballsdex/core/dexscript.py"): os.remove("ballsdex/core/dexscript.py") diff --git a/DexScript/github/migration.py b/DexScript/github/migration.py deleted file mode 100644 index 10ad6a9..0000000 --- a/DexScript/github/migration.py +++ /dev/null @@ -1,31 +0,0 @@ -def repair_bot_file(): - """ - Repairs the `bot.py` file and removes extra newlines caused by an old DexScript installer. - """ - new_lines = [] - - with open("ballsdex/core/bot.py", "r") as file: - if "import asyncio\n\n" not in file.read(): - return - - with open("ballsdex/core/bot.py", "r") as file: - last_was_newline = False - - for line in file.readlines(): - if last_was_newline is True: - last_was_newline = False - continue - - if line.endswith("\n") and line != "\n" or line == "\n": - last_was_newline = True - - new_lines.append(line) - - with open("ballsdex/core/bot.py", "w") as file: - file.writelines(new_lines) - - -# |-----------------------------------------------------------------------------------------|# - - -repair_bot_file() diff --git a/DexScript/package/api.py b/DexScript/package/api.py deleted file mode 100644 index 35eaf62..0000000 --- a/DexScript/package/api.py +++ /dev/null @@ -1,53 +0,0 @@ -from .utils import Types - - -class DexCommand: - """ - Default class for all dex commands. - """ - - def __init__(self, bot, shared): - self.bot = bot - self.shared = shared - - def __loaded__(self): - """ - Calls whenever the command is loaded for the first time. - """ - pass - - def attribute_error(self, model, attribute): - """ - Raises an error if an attribute doesn't exist in a model. - - Parameters - ---------- - model: Value - The model you want to check in. - attribute: Value - The attribute you want to check. - """ - if model.value is None or hasattr(model.value(), attribute): - return - - raise Exception( - f"'{attribute}' is not a valid {model.name} attribute\n" - f"Run `ATTRIBUTES > {model.name}` to see a list of " - "all attributes for that model" - ) - - def type_error(self, value, allowed_types: list[Types]): - """ - Raises an error if the type of a `Value` is not allowed. - - Parameters - ---------- - value: Value - The value that has the original type. - allowed_types: list[Types] - A list of types that are allowed. - """ - if value.type in allowed_types: - return - - raise Exception(f"'{value.type}' is an invalid type.") diff --git a/DexScript/package/cog.py b/DexScript/package/cog.py index 86810b0..ecd9e5d 100644 --- a/DexScript/package/cog.py +++ b/DexScript/package/cog.py @@ -10,7 +10,7 @@ from .parser import DexScriptParser from .utils import Utils, config -__version__ = "0.5" +__version__ = "1.0" ASSET_PATH = "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/main/assets" diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 3f9f354..91e00a6 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -6,7 +6,6 @@ import discord -from .api import DexCommand from .utils import STATIC, Types, Utils @@ -19,6 +18,58 @@ class Shared: attachments: list = datafield(default_factory=list) +class DexCommand: + """ + Default class for all dex commands. + """ + + def __init__(self, bot, shared): + self.bot = bot + self.shared = shared + + def __loaded__(self): + """ + Calls whenever the command is loaded for the first time. + """ + pass + + def attribute_error(self, model, attribute): + """ + Raises an error if an attribute doesn't exist in a model. + + Parameters + ---------- + model: Value + The model you want to check in. + attribute: Value + The attribute you want to check. + """ + if model.value is None or hasattr(model.value(), attribute): + return + + raise Exception( + f"'{attribute}' is not a valid {model.name} attribute\n" + f"Run `ATTRIBUTES > {model.name}` to see a list of " + "all attributes for that model" + ) + + def type_error(self, value, allowed_types: list[Types]): + """ + Raises an error if the type of a `Value` is not allowed. + + Parameters + ---------- + value: Value + The value that has the original type. + allowed_types: list[Types] + A list of types that are allowed. + """ + if value.type in allowed_types: + return + + raise Exception(f"'{value.type}' is an invalid type.") + + class Global(DexCommand): """ Main methods for DexScript. @@ -482,3 +533,10 @@ async def create(self, ctx, model, argument="..."): ] await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") + +class Utils(DexCommand): + """ + Utility commands for DexScript + """ + + async def blacklist \ No newline at end of file diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index f2147b8..0d8b4cc 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -8,7 +8,6 @@ from dateutil.parser import parse as parse_date from . import commands -from .api import DexCommand from .utils import Types, Utils, config PARSER_RE = re.compile(r"[^>]+") @@ -34,13 +33,12 @@ class DexScriptParser: def __init__(self, ctx, bot): self.ctx = ctx self.bot = bot - # self.attachments = ctx.message.attachments self.command_classes = inspect.getmembers( commands, lambda o: ( inspect.isclass(o) - and issubclass(o, DexCommand) + and issubclass(o, commands.DexCommand) and not issubclass(o, commands.Global) and o.__name__ != "DexCommand" ), diff --git a/pyproject.toml b/pyproject.toml index 85f5d35..1696b4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dexscript" -version = "0.6" +version = "1.0" description = "" authors = ["dotzz "] license = "MIT" From dd707459cb45637d15fd154755a5519391120ab4 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 13:12:54 -0400 Subject: [PATCH 18/77] Add `self` --- DexScript/github/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index d7fc21f..bc27da7 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -266,7 +266,7 @@ def uninstall_migrate(self): write_file.writelines(lines) async def install(self): - if not has_package_config(): + if not self.has_package_config(): raise Exception("Your Ballsdex version is no longer compatible with DexScript") if os.path.isfile("ballsdex/core/dexscript.py"): From 454cac280fb99ef34388ff0b7375290e31a87f8c Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 13:14:03 -0400 Subject: [PATCH 19/77] Whoops --- DexScript/github/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index bc27da7..5d37486 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -225,7 +225,7 @@ def has_package_config(self) -> bool: with open("config.yml", "r") as file: lines = file.readlines() - return "packages:\n" not in lines + return "packages:\n" in lines def add_package(self, package: str) -> bool: with open("config.yml", "r") as file: From 3ca2be4b5100b45a636225ea420724323d791bba Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 13:14:38 -0400 Subject: [PATCH 20/77] Remove `api.py` --- DexScript/github/installer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index 5d37486..ab50579 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -35,7 +35,7 @@ class InstallerConfig: """ github = ["Dotsian/DexScript", "dev"] - files = ["__init__.py", "api.py", "cog.py", "commands.py", "parser.py", "utils.py"] + files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py"] appearance = { "logo": f"{ASSET_PATH}/DexScriptLogo.png", "logo_error": f"{ASSET_PATH}/DexScriptLogoError.png", From 1b56c9206989c2e640241c6954a994a3c7addd11 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 13:15:53 -0400 Subject: [PATCH 21/77] Hide utils --- DexScript/package/commands.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 91e00a6..0a9a0dd 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -534,9 +534,9 @@ async def create(self, ctx, model, argument="..."): await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") -class Utils(DexCommand): - """ - Utility commands for DexScript - """ - - async def blacklist \ No newline at end of file +# class Utils(DexCommand): +# """ +# Utility commands for DexScript +# """ +# +# async def blacklist \ No newline at end of file From 027ab3b69293154c22785a4ef98ac4dbca68e01c Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 22:53:09 -0400 Subject: [PATCH 22/77] Fix smol mistake --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 0a9a0dd..0373285 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -133,7 +133,7 @@ async def update(self, ctx, model, identifier, attribute, value=None): if attribute_name in image_fields: file = None - if Value is not None and new_value.startswith("https://"): + if value is not None and new_value.startswith("https://"): file = Utils.from_link(new_value) else: file = self.shared.attachments.pop(0) From 874731c536e947dac9fcd71ad0c9edee0baf94fe Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 23:23:14 -0400 Subject: [PATCH 23/77] Add utils --- DexScript/package/commands.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 0373285..42de988 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -534,9 +534,23 @@ async def create(self, ctx, model, argument="..."): await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") -# class Utils(DexCommand): -# """ -# Utility commands for DexScript -# """ -# -# async def blacklist \ No newline at end of file +class Utils(DexCommand): + """ + Utility commands for DexScript. + """ + + async def emoji(self, ctx, name): + """ + Creates an application emoji based on the provided image and name. + + Documentation + ------------- + UTILS > EMOJI > NAME + """ + image = await ctx.message.attachments[0].read() + + emoji = await self.bot.create_application_emoji( + name=name.replace(" ", "").replace('"', ""), image=image + ) + + await ctx.send(f"Created emoji: {emoji} `({emoji.id})`") From 5392493230f42e190cf1af7fc87fbb2edfacd8eb Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 23:25:58 -0400 Subject: [PATCH 24/77] Rename class --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 42de988..6bbeaa7 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -534,7 +534,7 @@ async def create(self, ctx, model, argument="..."): await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") -class Utils(DexCommand): +class DexUtils(DexCommand): """ Utility commands for DexScript. """ From 449095c45d5b933dd37082ca8d5c22ace8e57d88 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 23:30:39 -0400 Subject: [PATCH 25/77] Fix casing --- DexScript/package/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 6bbeaa7..e3cba3b 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -534,7 +534,7 @@ async def create(self, ctx, model, argument="..."): await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") -class DexUtils(DexCommand): +class Dexutils(DexCommand): """ Utility commands for DexScript. """ @@ -545,7 +545,7 @@ async def emoji(self, ctx, name): Documentation ------------- - UTILS > EMOJI > NAME + DEXUTILS > EMOJI > NAME """ image = await ctx.message.attachments[0].read() From ed00434389b0c78fd786edd7b0c341be0d867ea3 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 22 May 2025 23:31:11 -0400 Subject: [PATCH 26/77] Cryign rn --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index e3cba3b..b4c80b9 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -550,7 +550,7 @@ async def emoji(self, ctx, name): image = await ctx.message.attachments[0].read() emoji = await self.bot.create_application_emoji( - name=name.replace(" ", "").replace('"', ""), image=image + name=name.value.replace(" ", "").replace('"', ""), image=image ) await ctx.send(f"Created emoji: {emoji} `({emoji.id})`") From 5b60540b3f71b84853303ef3b9b6e43403c8e323 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 09:13:14 -0400 Subject: [PATCH 27/77] Softcode template --- DexScript/package/commands.py | 58 ++++++++++++++++++++--------------- DexScript/package/parser.py | 2 +- DexScript/package/utils.py | 2 +- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index b4c80b9..50e595c 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -53,7 +53,7 @@ def attribute_error(self, model, attribute): "all attributes for that model" ) - def type_error(self, value, allowed_types: list[Types]): + def type_error(self, value, name: str, allowed_types: list[Types]): """ Raises an error if the type of a `Value` is not allowed. @@ -61,13 +61,15 @@ def type_error(self, value, allowed_types: list[Types]): ---------- value: Value The value that has the original type. + name: str + The name of the value. allowed_types: list[Types] A list of types that are allowed. """ if value.type in allowed_types: return - raise Exception(f"'{value.type}' is an invalid type.") + raise Exception(f"'{value.type}' is an invalid type for '{name}'.") class Global(DexCommand): @@ -207,7 +209,7 @@ async def attributes(self, ctx, model, filter=None): ATTRIBUTES > MODEL > FILTER(?) """ def filter_function(_, field_type): - if field_type == "BackwardFKRelation": + if field_type.__class__.__name__ == "BackwardFKRelation": return False if filter is None: @@ -507,38 +509,44 @@ class Template(DexCommand): Template commands used to assist with DexScript commands. """ - # TODO: Softcode model creation template. - async def create(self, ctx, model, argument="..."): + async def create(self, ctx, model, argument="...", include_args=None): """ Sends the `create` template for a model. Documentation ------------- - TEMPLATE > CREATE > MODEL > ARGUMENT(?) - """ - match model.name.lower(): - case "ball": - template_commands = [ - f"CREATE > BALL > {argument}", - f"UPDATE > BALL > {argument} > REGIME > ...", - f"UPDATE > BALL > {argument} > HEALTH > ...", - f"UPDATE > BALL > {argument} > ATTACK > ...", - f"UPDATE > BALL > {argument} > RARITY > ...", - f"UPDATE > BALL > {argument} > EMOJI_ID > ...", - f"UPDATE > BALL > {argument} > CREDITS > ...", - f"UPDATE > BALL > {argument} > CAPACITY_NAME > ...", - f"UPDATE > BALL > {argument} > CAPACITY_DESCRIPTION > ...", - f"UPDATE > BALL > {argument} > WILD_CARD > ...", - f"UPDATE > BALL > {argument} > COLLECTION_CARD > ..." - ] - - await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") + TEMPLATE > CREATE > MODEL > ARGUMENT(?) > INCLUDE_ARGS(?) + """ + def filter_function(field, field_type): + if field_type.__class__.__name__ in ["BackwardFKRelation", "JSONField"]: + return False + + if field_type.null or field_type.default or field.endswith("_id"): + return False + + if field in [model.extra_data[0], "id"]: + return False + + return True + + args = [x.upper() for x in Utils.fetch_fields(model.value, filter_function)] + + structure = f"UPDATE > {model.name.upper()} > {argument} > $value > ..." + template_commands = [f"CREATE > {model.name.upper()} > {argument}"] + + if include_args is not None: + args += include_args + + for arg in args: + template_commands.append(structure.replace("$value", arg)) + + await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") class Dexutils(DexCommand): """ Utility commands for DexScript. """ - + async def emoji(self, ctx, name): """ Creates an application emoji based on the provided image and name. diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 0d8b4cc..6ea5856 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -16,7 +16,7 @@ @dataclass class Value: name: str - type: Types = Types.DEFAULT + type: Types = Types.STRING value: Any = None extra_data: list = datafield(default_factory=list) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index a42c66e..6f82c84 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -36,7 +36,7 @@ class Types(Enum): - DEFAULT = 0 + STRING = 0 METHOD = 1 CLASS = 2 BOOLEAN = 3 From 6efc16c2772a9869090a87b01411713c70339e66 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 09:18:46 -0400 Subject: [PATCH 28/77] Small fix --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 50e595c..c220965 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -535,7 +535,7 @@ def filter_function(field, field_type): template_commands = [f"CREATE > {model.name.upper()} > {argument}"] if include_args is not None: - args += include_args + args += [x.name.upper() for x in include_args.value] for arg in args: template_commands.append(structure.replace("$value", arg)) From 3efa30f1e02fd4fe20111f6a91b3f3faf4991083 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 10:26:03 -0400 Subject: [PATCH 29/77] Add dict type and improve parser --- DexScript/package/commands.py | 10 +++--- DexScript/package/parser.py | 59 +++++++++++++++++++++++++++++++---- DexScript/package/utils.py | 8 ++++- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index c220965..895d17c 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -77,15 +77,17 @@ class Global(DexCommand): Main methods for DexScript. """ - async def create(self, ctx, model, identifier): + async def create(self, ctx, model, identifier, values=None): """ Creates a model instance. Documentation ------------- - CREATE > MODEL > IDENTIFIER + CREATE > MODEL > IDENTIFIER > VALUES(?) """ - await Utils.create_model(model.value, identifier) + type_error(values, "values", [Types.DICT]) + + await Utils.create_model(model.value, identifier, values) await ctx.send(f"Created `{identifier}` {model.name.lower()}") async def delete(self, ctx, model, identifiers): @@ -396,7 +398,7 @@ async def list(self, ctx): await Utils.message_list(ctx, os.listdir("eval_presets")) - async def run(self, ctx, name): # TODO: Allow args to be passed through `run`. + async def run(self, ctx, name): """ Runs an eval preset. diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 6ea5856..fbdcc30 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -60,7 +60,8 @@ def create_value(self, line): Types.DATETIME: Utils.is_date(lower) and lower.count("-") >= 2, Types.BOOLEAN: lower in ["true", "false"], Types.HEX: lower.startswith("#"), - Types.ARRAY: lower.startswith("[") and lower.endswith("]") + Types.ARRAY: lower.startswith("[") and lower.endswith("]"), + Types.DICT: lower.startsiwth("{") and lower.endswith("}") } for key, operation in type_dict.items(): @@ -98,9 +99,55 @@ def create_value(self, line): case Types.ARRAY: value.value = [self.create_value(x.strip()) for x in line[1:-1].split("|")] + + case Types.DICT: + value_dict = {} + new_line = line[1:-1] + + for item in new_line.split("|"): + keyitems = item.strip().split(">") + + value = self.create_value(keyitems[1].strip()) + + value_dict[keyitems[0].strip()] = value + + value.value = value_dict return value + def parse_split(self, input_string): + result = [] + buffer = "" + + persist = False + + for character in input_string: + is_string = character in ['"', "'"] + + if character == "{" or is_string: + persist = True + buffer += "" if is_string else character + continue + + if character == "}" or is_string and persist: + persist = False + buffer += "" if is_string else character + continue + + if character == ">" and not persist: + if buffer.strip(): + result.append(buffer.strip()) + + buffer = "" + continue + + buffer += character + + if buffer.strip(): + result.append(buffer.strip()) + + return result + def error(self, message, log): return (message, log)[config.debug] @@ -109,11 +156,11 @@ async def execute(self, code: str, run_commands=True): split_code = [x for x in code.split("\n") if x.strip() != ""] - parsed_code = [ - [self.create_value(s.strip()) for s in PARSER_RE.findall(line)] - for line in split_code - if not line.strip().startswith("--") - ] + for line in split_code: + if line.strip().startswith("--"): + continue + + parsed_code.append([self.create_value(x.strip()) for x in parse_split(line)]) if not run_commands: return parsed_code diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 6f82c84..b84b91a 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -44,6 +44,7 @@ class Types(Enum): DATETIME = 5 HEX = 6 ARRAY = 7 + DICT = 8 @dataclass @@ -285,7 +286,7 @@ def models(names=False, key: Callable | None = None): return model_list @staticmethod - async def create_model(model, identifier, fields_only=False): + async def create_model(model, identifier, values=None, fields_only=False): """ Creates a model instance while providing default values for all. @@ -295,6 +296,8 @@ async def create_model(model, identifier, fields_only=False): The tortoise model you want to use. identifier: str The name of the model instance. + values: dict | None + Default values that will be added to the model. fields_only: bool Whether you want to return the fields created only or not (debugging). """ @@ -335,6 +338,9 @@ async def create_model(model, identifier, fields_only=False): case _: fields[field] = 1 + for key, item in values.items(): + fields[key] = item + if fields_only: return fields From 3647c70f514b7ea4edc62ee941e8e45491fa70b9 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 10:28:53 -0400 Subject: [PATCH 30/77] Remove string stuff --- DexScript/package/parser.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index fbdcc30..5191d06 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -122,16 +122,14 @@ def parse_split(self, input_string): persist = False for character in input_string: - is_string = character in ['"', "'"] - - if character == "{" or is_string: + if character == "{": persist = True - buffer += "" if is_string else character + buffer += character continue - if character == "}" or is_string and persist: + if character == "}" and persist: persist = False - buffer += "" if is_string else character + buffer += character continue if character == ">" and not persist: From 6647c40b901c90948336c59b49d86a7e12ac9592 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 10:30:12 -0400 Subject: [PATCH 31/77] Whoooopss --- DexScript/package/parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 5191d06..2b0d5e9 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -153,6 +153,7 @@ async def execute(self, code: str, run_commands=True): shared_instance = commands.Shared(self.ctx.message.attachments) split_code = [x for x in code.split("\n") if x.strip() != ""] + parsed_code = [] for line in split_code: if line.strip().startswith("--"): From 36fc0e162dcfcb44e08944e3d295fd92ec6e2d9f Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 10:31:26 -0400 Subject: [PATCH 32/77] Bruh --- DexScript/package/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 2b0d5e9..ff0b6e6 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -159,7 +159,7 @@ async def execute(self, code: str, run_commands=True): if line.strip().startswith("--"): continue - parsed_code.append([self.create_value(x.strip()) for x in parse_split(line)]) + parsed_code.append([self.create_value(x.strip()) for x in self.parse_split(line)]) if not run_commands: return parsed_code From 00169af5b3e2e812aaee478af38da2c91e18c0a5 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 10:32:24 -0400 Subject: [PATCH 33/77] Fix typo --- DexScript/package/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index ff0b6e6..dcd50f7 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -61,7 +61,7 @@ def create_value(self, line): Types.BOOLEAN: lower in ["true", "false"], Types.HEX: lower.startswith("#"), Types.ARRAY: lower.startswith("[") and lower.endswith("]"), - Types.DICT: lower.startsiwth("{") and lower.endswith("}") + Types.DICT: lower.startswith("{") and lower.endswith("}") } for key, operation in type_dict.items(): From 17310b1e082c550c072889f5cb3aff76772fd508 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 10:39:49 -0400 Subject: [PATCH 34/77] Remove `re` from parser --- DexScript/package/parser.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index dcd50f7..f5fb977 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -1,5 +1,4 @@ import inspect -import re import traceback from dataclasses import dataclass from dataclasses import field as datafield @@ -10,8 +9,6 @@ from . import commands from .utils import Types, Utils, config -PARSER_RE = re.compile(r"[^>]+") - @dataclass class Value: From 8d8b6b33981342aed9ff3331238c66c395f1a651 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 22:04:13 -0400 Subject: [PATCH 35/77] Add `self` --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 895d17c..ecddbfa 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -85,7 +85,7 @@ async def create(self, ctx, model, identifier, values=None): ------------- CREATE > MODEL > IDENTIFIER > VALUES(?) """ - type_error(values, "values", [Types.DICT]) + self.type_error(values, "values", [Types.DICT]) await Utils.create_model(model.value, identifier, values) await ctx.send(f"Created `{identifier}` {model.name.lower()}") From 7363cdf4b8fff1f77f8f09042e9a51d06415a7be Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 22:04:59 -0400 Subject: [PATCH 36/77] Add none check --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index ecddbfa..7e01ca9 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -66,7 +66,7 @@ def type_error(self, value, name: str, allowed_types: list[Types]): allowed_types: list[Types] A list of types that are allowed. """ - if value.type in allowed_types: + if value is None or value.type in allowed_types: return raise Exception(f"'{value.type}' is an invalid type for '{name}'.") From 3ddd333e87d9288e69b11558e615ecaf4be8723e Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 23 May 2025 22:05:49 -0400 Subject: [PATCH 37/77] Add none check 2 --- DexScript/package/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index b84b91a..2f894d6 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -338,8 +338,9 @@ async def create_model(model, identifier, values=None, fields_only=False): case _: fields[field] = 1 - for key, item in values.items(): - fields[key] = item + if values is not None: + for key, item in values.items(): + fields[key] = item if fields_only: return fields From bf32c86074fbf0678e30973969753ced46f86dcc Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 2 Jun 2025 08:28:51 -0400 Subject: [PATCH 38/77] Create emojis with link --- DexScript/package/commands.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 7e01ca9..66221a4 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -549,18 +549,21 @@ class Dexutils(DexCommand): Utility commands for DexScript. """ - async def emoji(self, ctx, name): + async def emoji(self, ctx, name, image=None): """ Creates an application emoji based on the provided image and name. Documentation ------------- - DEXUTILS > EMOJI > NAME + DEXUTILS > EMOJI > NAME > IMAGE(?) """ - image = await ctx.message.attachments[0].read() + image_content = await ctx.message.attachments[0].read() + + if image is not None: + image_content = Utils.from_link(image.value)[1] emoji = await self.bot.create_application_emoji( - name=name.value.replace(" ", "").replace('"', ""), image=image + name=name.value.replace(" ", "").replace('"', ""), image=image_content ) - await ctx.send(f"Created emoji: {emoji} `({emoji.id})`") + await ctx.send(f"Created {emoji} `({emoji.id})`") From 55831c6d690bcb2e6f56b9537bda0daa44182f98 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 2 Jun 2025 08:33:25 -0400 Subject: [PATCH 39/77] IndexError fix --- DexScript/package/commands.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 66221a4..d8ed39c 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -557,9 +557,11 @@ async def emoji(self, ctx, name, image=None): ------------- DEXUTILS > EMOJI > NAME > IMAGE(?) """ - image_content = await ctx.message.attachments[0].read() - - if image is not None: + image_content = None + + if Image is None: + image_content = await ctx.message.attachments[0].read() + else: image_content = Utils.from_link(image.value)[1] emoji = await self.bot.create_application_emoji( From 3e94783334e6d0115cd1cf0d3ef75c9c71529790 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 2 Jun 2025 08:35:33 -0400 Subject: [PATCH 40/77] Whoops --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index d8ed39c..973613d 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -559,7 +559,7 @@ async def emoji(self, ctx, name, image=None): """ image_content = None - if Image is None: + if image is None: image_content = await ctx.message.attachments[0].read() else: image_content = Utils.from_link(image.value)[1] From 713215f12ba9c919254ef9dc0845465ef0e95dd9 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 2 Jun 2025 08:44:48 -0400 Subject: [PATCH 41/77] More changes --- DexScript/package/commands.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 973613d..9555479 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -349,9 +349,6 @@ async def save(self, ctx, name): f"`{name}` exceeds the {NAME_LIMIT}-character limit ({len(name)} > {NAME_LIMIT})" ) - if os.path.isfile(f"eval_presets/{name}.py"): - raise Exception(f"`{name}` already exists.") - await ctx.send("Please send the eval command below...") try: @@ -564,8 +561,8 @@ async def emoji(self, ctx, name, image=None): else: image_content = Utils.from_link(image.value)[1] - emoji = await self.bot.create_application_emoji( - name=name.value.replace(" ", "").replace('"', ""), image=image_content - ) + new_name = name.value.replace(" ", "").replace('"', "") + + emoji = await self.bot.create_application_emoji(name=new_name, image=image_content) - await ctx.send(f"Created {emoji} `({emoji.id})`") + await ctx.send(f"Created {new_name} {emoji} `({emoji.id})`") From 7da5149512331d4c9c0ebbdd8b8d2826ce96c772 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Wed, 4 Jun 2025 14:57:14 -0400 Subject: [PATCH 42/77] New emoji class --- DexScript/package/commands.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 9555479..4593786 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -8,6 +8,8 @@ from .utils import STATIC, Types, Utils +EMOJI_RE = re.compile(r"^[a-zA-Z0-9_]{2,32}$") + @dataclass class Shared: @@ -541,18 +543,18 @@ def filter_function(field, field_type): await ctx.send(f"```sql\n{'\n'.join(template_commands)}\n```") -class Dexutils(DexCommand): +class Emoji(DexCommand): """ - Utility commands for DexScript. + Commands used for modifying application emojis. """ - - async def emoji(self, ctx, name, image=None): + + async def new(self, ctx, name, image=None): """ Creates an application emoji based on the provided image and name. Documentation ------------- - DEXUTILS > EMOJI > NAME > IMAGE(?) + EMOJI > NEW > NAME > IMAGE(?) """ image_content = None @@ -563,6 +565,9 @@ async def emoji(self, ctx, name, image=None): new_name = name.value.replace(" ", "").replace('"', "") + if not bool(EMOJI_RE.match(new_name)): + raise Exception(f"Emoji name `{new_name}` is invalid.") + emoji = await self.bot.create_application_emoji(name=new_name, image=image_content) - await ctx.send(f"Created {new_name} {emoji} `({emoji.id})`") + await ctx.send(f"Created {emoji} **{new_name}** `({emoji.id})`") From c0bd49d9fdee00dde87135ae22e70a84a1e6a474 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 5 Jun 2025 10:10:04 -0400 Subject: [PATCH 43/77] Add delete method to Emoji --- DexScript/package/commands.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 4593786..5fb2c9e 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -571,3 +571,21 @@ async def new(self, ctx, name, image=None): emoji = await self.bot.create_application_emoji(name=new_name, image=image_content) await ctx.send(f"Created {emoji} **{new_name}** `({emoji.id})`") + + async def delete(self, ctx, name): + """ + Deletes an application emoji. + + Documentation + ------------- + EMOJI > DELETE > NAME + """ + emojis = await self.bot.fetch_application_emojis() + new_name = name.value.replace(" ", "").replace('"', "") + + emoji = discord.utils.get(emojis, name=new_name) + + if not emoji.is_application_owned(): + raise Exception("This emoji is not owned by the application.") + + await emoji.delete() From 5a6d58125ec364ceab8061b991c776dbd079ddaf Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 5 Jun 2025 10:11:44 -0400 Subject: [PATCH 44/77] Import RegEx --- DexScript/package/commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 5fb2c9e..166f495 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -1,5 +1,6 @@ import asyncio import os +import re import shutil from dataclasses import dataclass from dataclasses import field as datafield From f5ea14718667f332aa95e00ae93e8a6a863fc640 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 7 Jun 2025 16:52:18 -0400 Subject: [PATCH 45/77] Add eval file and emoji info commands. --- DexScript/package/commands.py | 81 ++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 166f495..8fd91cc 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -84,6 +84,15 @@ async def create(self, ctx, model, identifier, values=None): """ Creates a model instance. + Parameters + ------------- + model: Model + The model you want to create an instance for. + identifier: Any + The identifier (name) of the model instance. + values: dict? + The values you want to provide to the newly created model instance. + Documentation ------------- CREATE > MODEL > IDENTIFIER > VALUES(?) @@ -97,6 +106,13 @@ async def delete(self, ctx, model, identifiers): """ Deletes one or multiple model instances. + Parameters + ------------- + model: Model + The model you want to delete the instance from. + identifiers: Any | Array[Any] + The identifier(s) of the instance(s) you want to delete. + Documentation ------------- DELETE > MODEL > IDENTIFIER(S) @@ -117,8 +133,18 @@ async def delete_model(identifier): async def update(self, ctx, model, identifier, attribute, value=None): """ - Updates a model instance's attribute. If value is None, it will check - for any attachments. + Updates a model instance's attribute. + + Parameters + ------------- + model: Model + The model you want to update the instance from. + identifier: Any + The identifier of the model instance. + attribute: Any + The attribute you want to update. + value: Any + The new value of the specified attribute. If blank, it will search for attachments. Documentation ------------- @@ -369,6 +395,20 @@ async def save(self, ctx, name): await ctx.send(f"`{name}` eval preset has been saved!") + async def file(self, ctx): + """ + Runs an eval command from a file. + + Documentation + ------------- + EVAL > FILE + """ + content = await ctx.message.attachments[0].read() + + ctx.message.attachments.pop(0) + + await ctx.invoke(self.bot.get_command("eval"), body=content.decode()) + async def remove(self, ctx, name): """ Removes an eval preset. @@ -553,6 +593,13 @@ async def new(self, ctx, name, image=None): """ Creates an application emoji based on the provided image and name. + Parameters + ------------- + name: Any + The name of the emoji you want to create. + image: Any? + The image link you want to use if there are no attachments. + Documentation ------------- EMOJI > NEW > NAME > IMAGE(?) @@ -577,6 +624,11 @@ async def delete(self, ctx, name): """ Deletes an application emoji. + Parameters + ------------- + name: Any + The name of the emoji you want to delete. + Documentation ------------- EMOJI > DELETE > NAME @@ -589,4 +641,29 @@ async def delete(self, ctx, name): if not emoji.is_application_owned(): raise Exception("This emoji is not owned by the application.") + await ctx.send(f"Deleted {emoji} **{new_name}**") + await emoji.delete() + + async def info(self, ctx, name): + """ + Displays information about an application emoji. + + Parameters + ------------- + name: Any + The name of the emoji you want to view. + + Documentation + ------------- + EMOJI > INFO > NAME + """ + emojis = await self.bot.fetch_application_emojis() + new_name = name.value.replace(" ", "").replace('"', "") + + emoji = discord.utils.get(emojis, name=new_name) + + if not emoji.is_application_owned(): + raise Exception("This emoji is not owned by the application.") + + await ctx.send(f"{emoji} **{new_name}**\nID: `{emoji.id}`") From 36bd5dd5d65c4db11d410ae206980b3f690d9764 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 7 Jun 2025 17:03:05 -0400 Subject: [PATCH 46/77] Fix checks and update dep --- DexScript/package/commands.py | 10 +++++---- DexScript/package/utils.py | 30 ++++++++++++++++++++------ poetry.lock | 40 +++++++++++++++++------------------ pyproject.toml | 2 +- 4 files changed, 50 insertions(+), 32 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 8fd91cc..dd4cede 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -166,7 +166,7 @@ async def update(self, ctx, model, identifier, attribute, value=None): if attribute_name in image_fields: file = None - if value is not None and new_value.startswith("https://"): + if new_value is not None and new_value.startswith("https://"): file = Utils.from_link(new_value) else: file = self.shared.attachments.pop(0) @@ -175,7 +175,7 @@ async def update(self, ctx, model, identifier, attribute, value=None): new_value = f"/static/uploads/{image_path}" if STATIC else image_path - if attribute.type == Types.MODEL: + if attribute.type == Types.MODEL and value is not None: attribute_name = f"{attribute.name.lower()}_id" attribute_model = await Utils.get_model(attribute, value) @@ -209,7 +209,9 @@ async def view(self, ctx, model, identifier, attribute=None): fields["content"] += f"{key}: {value}\n" if isinstance(value, str) and Utils.is_image(value): - fields.setdefault("files", []).append(discord.File(Utils.image_path(value))) + fields.setdefault( + "files", []).append(discord.File(Utils.image_path(value)) # type: ignore + ) fields["content"] += "```" await ctx.send(**fields) @@ -226,7 +228,7 @@ async def view(self, ctx, model, identifier, attribute=None): ) return - if attribute.type == Types.MODEL: + if attribute.type == Types.MODEL and not isinstance(new_attribute, str): new_attribute = await new_attribute.values_list(attribute.extra_data[0], flat=True) await ctx.send(f"```{new_attribute}```") diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 2f894d6..a43cd1b 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -2,7 +2,6 @@ import contextlib import inspect import os -import requests import re from dataclasses import dataclass from difflib import get_close_matches @@ -12,8 +11,14 @@ from typing import Any, Callable import discord +import requests from ballsdex.core.models import ( - Ball, BallInstance, Economy, Player, Regime, Special # noqa: F401, I001 + Ball, # noqa: F401 + BallInstance, # noqa: F401 + Economy, # noqa: F401 + Player, # noqa: F401 + Regime, # noqa: F401 + Special, # noqa: F401 ) from dateutil.parser import parse as parse_date @@ -68,7 +73,7 @@ class Utils: """ @staticmethod - def from_link(link: str) -> (str, bytes): + def from_link(link: str) -> tuple[str, bytes]: """ Returns a image link's content and filename. @@ -87,7 +92,7 @@ def from_link(link: str) -> (str, bytes): return (filename, data.content) @staticmethod - def image_path(path: str) -> bool: + def image_path(path: str) -> str: """ Formats an image path correctly. @@ -210,7 +215,11 @@ def check(message): if response.content.lower() == "more": continue - await ctx.send(file=discord.File(StringIO("\n".join(messages)), filename="output.txt")) + discord_file = discord.File( + StringIO("\n".join(messages)), filename="output.txt" # type: ignore + ) + + await ctx.send(file=discord_file) break @@ -322,10 +331,12 @@ async def create_model(model, identifier, values=None, fields_only=False): case "ForeignKeyFieldInstance": casing_field = Utils.pascal_case(field) - instance = await Utils.fetch_model(casing_field).first() + instance = Utils.fetch_model(casing_field) if instance is None: raise Exception(f"Could not find default {casing_field}") + + instance = await instance.first() fields[f"{field}_id"] = instance.pk @@ -444,7 +455,12 @@ def extract_str_attr(object: Any) -> str: The class you want to fetch the `__str__` attribute from. """ source = inspect.getsource(object.__str__).replace("str(", "") - extracted = STR_RE.search(source).group(1) + extracted = STR_RE.search(source) + + if extracted is None: + return "id" + + extracted = extracted.group(1) if extracted == "to_string": return "id" diff --git a/poetry.lock b/poetry.lock index 9ff107d..1cc6225 100644 --- a/poetry.lock +++ b/poetry.lock @@ -570,30 +570,30 @@ files = [ [[package]] name = "ruff" -version = "0.11.6" +version = "0.11.13" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "ruff-0.11.6-py3-none-linux_armv6l.whl", hash = "sha256:d84dcbe74cf9356d1bdb4a78cf74fd47c740bf7bdeb7529068f69b08272239a1"}, - {file = "ruff-0.11.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9bc583628e1096148011a5d51ff3c836f51899e61112e03e5f2b1573a9b726de"}, - {file = "ruff-0.11.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2959049faeb5ba5e3b378709e9d1bf0cab06528b306b9dd6ebd2a312127964a"}, - {file = "ruff-0.11.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63c5d4e30d9d0de7fedbfb3e9e20d134b73a30c1e74b596f40f0629d5c28a193"}, - {file = "ruff-0.11.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a4b9a4e1439f7d0a091c6763a100cef8fbdc10d68593df6f3cfa5abdd9246e"}, - {file = "ruff-0.11.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5edf270223dd622218256569636dc3e708c2cb989242262fe378609eccf1308"}, - {file = "ruff-0.11.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f55844e818206a9dd31ff27f91385afb538067e2dc0beb05f82c293ab84f7d55"}, - {file = "ruff-0.11.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d8f782286c5ff562e4e00344f954b9320026d8e3fae2ba9e6948443fafd9ffc"}, - {file = "ruff-0.11.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01c63ba219514271cee955cd0adc26a4083df1956d57847978383b0e50ffd7d2"}, - {file = "ruff-0.11.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15adac20ef2ca296dd3d8e2bedc6202ea6de81c091a74661c3666e5c4c223ff6"}, - {file = "ruff-0.11.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4dd6b09e98144ad7aec026f5588e493c65057d1b387dd937d7787baa531d9bc2"}, - {file = "ruff-0.11.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:45b2e1d6c0eed89c248d024ea95074d0e09988d8e7b1dad8d3ab9a67017a5b03"}, - {file = "ruff-0.11.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bd40de4115b2ec4850302f1a1d8067f42e70b4990b68838ccb9ccd9f110c5e8b"}, - {file = "ruff-0.11.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:77cda2dfbac1ab73aef5e514c4cbfc4ec1fbef4b84a44c736cc26f61b3814cd9"}, - {file = "ruff-0.11.6-py3-none-win32.whl", hash = "sha256:5151a871554be3036cd6e51d0ec6eef56334d74dfe1702de717a995ee3d5b287"}, - {file = "ruff-0.11.6-py3-none-win_amd64.whl", hash = "sha256:cce85721d09c51f3b782c331b0abd07e9d7d5f775840379c640606d3159cae0e"}, - {file = "ruff-0.11.6-py3-none-win_arm64.whl", hash = "sha256:3567ba0d07fb170b1b48d944715e3294b77f5b7679e8ba258199a250383ccb79"}, - {file = "ruff-0.11.6.tar.gz", hash = "sha256:bec8bcc3ac228a45ccc811e45f7eb61b950dbf4cf31a67fa89352574b01c7d79"}, + {file = "ruff-0.11.13-py3-none-linux_armv6l.whl", hash = "sha256:4bdfbf1240533f40042ec00c9e09a3aade6f8c10b6414cf11b519488d2635d46"}, + {file = "ruff-0.11.13-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aef9c9ed1b5ca28bb15c7eac83b8670cf3b20b478195bd49c8d756ba0a36cf48"}, + {file = "ruff-0.11.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:53b15a9dfdce029c842e9a5aebc3855e9ab7771395979ff85b7c1dedb53ddc2b"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab153241400789138d13f362c43f7edecc0edfffce2afa6a68434000ecd8f69a"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c51f93029d54a910d3d24f7dd0bb909e31b6cd989a5e4ac513f4eb41629f0dc"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1808b3ed53e1a777c2ef733aca9051dc9bf7c99b26ece15cb59a0320fbdbd629"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:d28ce58b5ecf0f43c1b71edffabe6ed7f245d5336b17805803312ec9bc665933"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55e4bc3a77842da33c16d55b32c6cac1ec5fb0fbec9c8c513bdce76c4f922165"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:633bf2c6f35678c56ec73189ba6fa19ff1c5e4807a78bf60ef487b9dd272cc71"}, + {file = "ruff-0.11.13-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ffbc82d70424b275b089166310448051afdc6e914fdab90e08df66c43bb5ca9"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a9ddd3ec62a9a89578c85842b836e4ac832d4a2e0bfaad3b02243f930ceafcc"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d237a496e0778d719efb05058c64d28b757c77824e04ffe8796c7436e26712b7"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26816a218ca6ef02142343fd24c70f7cd8c5aa6c203bca284407adf675984432"}, + {file = "ruff-0.11.13-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:51c3f95abd9331dc5b87c47ac7f376db5616041173826dfd556cfe3d4977f492"}, + {file = "ruff-0.11.13-py3-none-win32.whl", hash = "sha256:96c27935418e4e8e77a26bb05962817f28b8ef3843a6c6cc49d8783b5507f250"}, + {file = "ruff-0.11.13-py3-none-win_amd64.whl", hash = "sha256:29c3189895a8a6a657b7af4e97d330c8a3afd2c9c8f46c81e2fc5a31866517e3"}, + {file = "ruff-0.11.13-py3-none-win_arm64.whl", hash = "sha256:b4385285e9179d608ff1d2fb9922062663c658605819a6876d8beef0c30b7f3b"}, + {file = "ruff-0.11.13.tar.gz", hash = "sha256:26fa247dc68d1d4e72c179e08889a25ac0c7ba4d78aecfc835d49cbfd60bf514"}, ] [[package]] @@ -718,4 +718,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.12" -content-hash = "acca86dc3528062287dee36e3a3ee37e3d8c303c4943a7c75411c05e5e519366" +content-hash = "aed82117906c27eee3590872dcdf92c74dd8e1df5917aa2802d83dc97bc4c435" diff --git a/pyproject.toml b/pyproject.toml index 1696b4f..d7162f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ license = "MIT" [tool.poetry.dependencies] python = ">=3.12" "discord.py" = "^2.5.0" -ruff = "^0.11.6" +ruff = "^0.11.13" [build-system] requires = ["poetry-core>=1.0.0"] From e96d877f7446c2cfa251a215bde65c9f02ebc2c2 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 9 Jun 2025 11:52:41 -0400 Subject: [PATCH 47/77] Add indentation to DexScript --- DexScript/package/commands.py | 51 ++++++++++++++-------- DexScript/package/parser.py | 82 ++++++++++++++++++++--------------- 2 files changed, 79 insertions(+), 54 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index dd4cede..ef0e2fb 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -42,9 +42,9 @@ def attribute_error(self, model, attribute): Parameters ---------- - model: Value + model: The model you want to check in. - attribute: Value + attribute: The attribute you want to check. """ if model.value is None or hasattr(model.value(), attribute): @@ -62,11 +62,11 @@ def type_error(self, value, name: str, allowed_types: list[Types]): Parameters ---------- - value: Value + value: The value that has the original type. - name: str + name: The name of the value. - allowed_types: list[Types] + allowed_types: A list of types that are allowed. """ if value is None or value.type in allowed_types: @@ -86,11 +86,11 @@ async def create(self, ctx, model, identifier, values=None): Parameters ------------- - model: Model + model: The model you want to create an instance for. - identifier: Any + identifier: The identifier (name) of the model instance. - values: dict? + values: The values you want to provide to the newly created model instance. Documentation @@ -108,9 +108,9 @@ async def delete(self, ctx, model, identifiers): Parameters ------------- - model: Model + model: The model you want to delete the instance from. - identifiers: Any | Array[Any] + identifiers: The identifier(s) of the instance(s) you want to delete. Documentation @@ -137,13 +137,13 @@ async def update(self, ctx, model, identifier, attribute, value=None): Parameters ------------- - model: Model + model: The model you want to update the instance from. - identifier: Any + identifier: The identifier of the model instance. - attribute: Any + attribute: The attribute you want to update. - value: Any + value: The new value of the specified attribute. If blank, it will search for attachments. Documentation @@ -369,6 +369,11 @@ async def save(self, ctx, name): """ Saves an eval preset. + Parameters + ---------- + name: + The name of the eval preset you want to save. + Documentation ------------- EVAL > SAVE > NAME @@ -415,6 +420,11 @@ async def remove(self, ctx, name): """ Removes an eval preset. + Parameters + ---------- + name: + The name of the eval preset you want to remove. + Documentation ------------- EVAL > REMOVE > NAME @@ -444,6 +454,11 @@ async def run(self, ctx, name): """ Runs an eval preset. + Parameters + ---------- + name: + The name of the eval preset you want to run. + Documentation ------------- EVAL > RUN > NAME @@ -597,9 +612,9 @@ async def new(self, ctx, name, image=None): Parameters ------------- - name: Any + name: The name of the emoji you want to create. - image: Any? + image: The image link you want to use if there are no attachments. Documentation @@ -628,7 +643,7 @@ async def delete(self, ctx, name): Parameters ------------- - name: Any + name: The name of the emoji you want to delete. Documentation @@ -653,7 +668,7 @@ async def info(self, ctx, name): Parameters ------------- - name: Any + name: The name of the emoji you want to view. Documentation diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index f5fb977..7f09443 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -112,51 +112,61 @@ def create_value(self, line): return value - def parse_split(self, input_string): - result = [] - buffer = "" - - persist = False - - for character in input_string: - if character == "{": - persist = True - buffer += character - continue - - if character == "}" and persist: - persist = False - buffer += character - continue - - if character == ">" and not persist: - if buffer.strip(): - result.append(buffer.strip()) - - buffer = "" - continue - - buffer += character - - if buffer.strip(): - result.append(buffer.strip()) - - return result - def error(self, message, log): return (message, log)[config.debug] async def execute(self, code: str, run_commands=True): shared_instance = commands.Shared(self.ctx.message.attachments) + data = [] + new_data = [] + + entered_indent = False + iteration_level = 0 + original_index = -1 + split_code = [x for x in code.split("\n") if x.strip() != ""] - parsed_code = [] - - for line in split_code: - if line.strip().startswith("--"): + + offset = len([x for x in split_code if x.strip().startswith("|")]) + + for index, line in enumerate(split_code): + line_new = line.strip() + + if line_new.startswith("--"): + continue + + if not line_new.startswith("|"): + data.append([line, {}]) + original_index = -1 + + if entered_indent: + iteration_level += 1 + + continue + + if original_index == -1: + entered_indent = True + original_index = iteration_level + + split_line = line.split("|")[1].split(">") + key = split_line[0].strip() + + data[original_index][1][key] = split_line[1].strip() + + for item in data: + if item[1] == {}: + new_data.append(item[0]) continue - parsed_code.append([self.create_value(x.strip()) for x in self.parse_split(line)]) + statement = item[0] + + for key, value in item[1].items(): + new_data.append(f"{statement} > {key} > {value}") + + parsed_code = [] + + for line in new_data: + parsed_code.append([self.create_value(x.strip()) for x in line.split(">")]) if not run_commands: return parsed_code From d4ebd85b0600072978f174ae26a6eb8dc4f3bcc1 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 9 Jun 2025 12:03:07 -0400 Subject: [PATCH 48/77] Support single indent values --- DexScript/package/parser.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 7f09443..ac86b61 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -147,6 +147,12 @@ async def execute(self, code: str, run_commands=True): if original_index == -1: entered_indent = True original_index = iteration_level + + split_pipe = line.split("|")[1].strip() + + if ">" not in split_pipe: + data[original_index][1][split_pipe] = split_pipe + continue split_line = line.split("|")[1].split(">") key = split_line[0].strip() @@ -161,7 +167,12 @@ async def execute(self, code: str, run_commands=True): statement = item[0] for key, value in item[1].items(): - new_data.append(f"{statement} > {key} > {value}") + new_statement = f"{statement} > {key}" + + if key != value: + new_statement += f" > {value}" + + new_data.append(new_statement) parsed_code = [] From d4f1adc242d91b62d02cf9c7ed94331c76f82d57 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 9 Jun 2025 19:04:54 -0400 Subject: [PATCH 49/77] Add simple `mkdir` function --- DexScript/package/commands.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index ef0e2fb..13e8476 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -543,6 +543,23 @@ async def listdir(self, ctx, file_path=None): await Utils.message_list(ctx, os.listdir(path)) + async def mkdir(self, ctx, file_path): + """ + Creates a new directory. + + Parameters + ------------- + file_path: + The file path of the directory. + + Documentation + ------------- + FILE > MKDIR > FILE_PATH + """ + os.mkdir(file_path) + + await ctx.send(f"Created `{file_path}` directory") + async def delete(self, ctx, file_path): """ Deletes a file or directory based on the specified file path. From 7efd903298f50cb006a088f8e712acdff75bad35 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 9 Jun 2025 21:11:52 -0400 Subject: [PATCH 50/77] Fix path error --- DexScript/package/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 13e8476..dac73e2 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -556,7 +556,7 @@ async def mkdir(self, ctx, file_path): ------------- FILE > MKDIR > FILE_PATH """ - os.mkdir(file_path) + os.mkdir(file_path.value) await ctx.send(f"Created `{file_path}` directory") From 65f939b7c96d0009b7500423814ad7b054f7e2c7 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 9 Jun 2025 21:15:01 -0400 Subject: [PATCH 51/77] Remove dict and fix checks --- DexScript/package/parser.py | 18 +----------------- DexScript/package/utils.py | 1 - 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index ac86b61..008c571 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -58,7 +58,6 @@ def create_value(self, line): Types.BOOLEAN: lower in ["true", "false"], Types.HEX: lower.startswith("#"), Types.ARRAY: lower.startswith("[") and lower.endswith("]"), - Types.DICT: lower.startswith("{") and lower.endswith("}") } for key, operation in type_dict.items(): @@ -96,19 +95,6 @@ def create_value(self, line): case Types.ARRAY: value.value = [self.create_value(x.strip()) for x in line[1:-1].split("|")] - - case Types.DICT: - value_dict = {} - new_line = line[1:-1] - - for item in new_line.split("|"): - keyitems = item.strip().split(">") - - value = self.create_value(keyitems[1].strip()) - - value_dict[keyitems[0].strip()] = value - - value.value = value_dict return value @@ -127,9 +113,7 @@ async def execute(self, code: str, run_commands=True): split_code = [x for x in code.split("\n") if x.strip() != ""] - offset = len([x for x in split_code if x.strip().startswith("|")]) - - for index, line in enumerate(split_code): + for line in split_code: line_new = line.strip() if line_new.startswith("--"): diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index a43cd1b..6a7b5d1 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -49,7 +49,6 @@ class Types(Enum): DATETIME = 5 HEX = 6 ARRAY = 7 - DICT = 8 @dataclass From cb44ac17868bd54923606247e0c2ce8f19f3aa8c Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 24 Jun 2025 04:09:19 -0400 Subject: [PATCH 52/77] Add NONE type --- DexScript/package/parser.py | 4 ++++ DexScript/package/utils.py | 1 + 2 files changed, 5 insertions(+) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 008c571..49de61c 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -58,6 +58,7 @@ def create_value(self, line): Types.BOOLEAN: lower in ["true", "false"], Types.HEX: lower.startswith("#"), Types.ARRAY: lower.startswith("[") and lower.endswith("]"), + Types.NONE: lower == "NIL" } for key, operation in type_dict.items(): @@ -96,6 +97,9 @@ def create_value(self, line): case Types.ARRAY: value.value = [self.create_value(x.strip()) for x in line[1:-1].split("|")] + case Types.NONE: + value.value = None + return value def error(self, message, log): diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 6a7b5d1..c9faa2f 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -49,6 +49,7 @@ class Types(Enum): DATETIME = 5 HEX = 6 ARRAY = 7 + NONE = 8 @dataclass From b31b696d2f820eb75193a202ebcffa7b590b59a8 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Wed, 25 Jun 2025 10:05:16 -0400 Subject: [PATCH 53/77] Attempt to fix attachment handling --- DexScript/package/commands.py | 29 ++++++++++++----------------- DexScript/package/parser.py | 4 +--- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index dac73e2..86766a5 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -12,23 +12,14 @@ EMOJI_RE = re.compile(r"^[a-zA-Z0-9_]{2,32}$") -@dataclass -class Shared: - """ - Values that will be retained throughout the entire code execution. - """ - - attachments: list = datafield(default_factory=list) - - class DexCommand: """ Default class for all dex commands. """ - def __init__(self, bot, shared): + def __init__(self, bot, attachments): self.bot = bot - self.shared = shared + self.attachments = attachments def __loaded__(self): """ @@ -169,7 +160,7 @@ async def update(self, ctx, model, identifier, attribute, value=None): if new_value is not None and new_value.startswith("https://"): file = Utils.from_link(new_value) else: - file = self.shared.attachments.pop(0) + file = self.attachments.pop(0) image_path = await Utils.save_file(file) @@ -410,9 +401,9 @@ async def file(self, ctx): ------------- EVAL > FILE """ - content = await ctx.message.attachments[0].read() + content = await self.attachments[0].read() - ctx.message.attachments.pop(0) + self.attachments.pop(0) await ctx.invoke(self.bot.get_command("eval"), body=content.decode()) @@ -507,13 +498,15 @@ async def write(self, ctx, file_path): ------------- FILE > WRITE > FILE_PATH """ - new_file = ctx.message.attachments[0] + new_file = self.attachments[0] + + self.attachments.pop(0) with open(file_path.name, "w") as opened_file: contents = await new_file.read() opened_file.write(contents.decode("utf-8")) - await ctx.send(f"Wrote to `{file_path}`") + await ctx.send(f"Wrote from `{new_file.filename}` to `{file_path}`") async def clear(self, ctx, file_path): """ @@ -641,7 +634,9 @@ async def new(self, ctx, name, image=None): image_content = None if image is None: - image_content = await ctx.message.attachments[0].read() + image_content = await self.attachments[0].read() + + self.attachments.pop(0) else: image_content = Utils.from_link(image.value)[1] diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 49de61c..1e5ddbe 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -106,8 +106,6 @@ def error(self, message, log): return (message, log)[config.debug] async def execute(self, code: str, run_commands=True): - shared_instance = commands.Shared(self.ctx.message.attachments) - data = [] new_data = [] @@ -190,7 +188,7 @@ async def execute(self, code: str, run_commands=True): line2.pop(0) class_loaded = commands.Global if method[0] == commands.Global else method[0] - class_loaded = class_loaded(self.bot, shared_instance) + class_loaded = class_loaded(self.bot, self.ctx.message.attachments) class_loaded.__loaded__() method_call = getattr(class_loaded, method[1].name.lower()) From 6aa71690a01da14e7980c9064f9d42be1c97064b Mon Sep 17 00:00:00 2001 From: Dotsian Date: Wed, 25 Jun 2025 10:11:05 -0400 Subject: [PATCH 54/77] Fix checks --- DexScript/package/commands.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 86766a5..5fb78d7 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -2,8 +2,6 @@ import os import re import shutil -from dataclasses import dataclass -from dataclasses import field as datafield import discord From 27c1a8f310959d8bfd8a6b57b1cfb03fb1144b34 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 28 Jun 2025 07:21:32 -0400 Subject: [PATCH 55/77] Overhaul `WRITE` function --- DexScript/package/commands.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index 5fb78d7..f424fe6 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -488,23 +488,28 @@ async def read(self, ctx, file_paths): await ctx.send(files=files) - async def write(self, ctx, file_path): + async def create(self, ctx, file_path): """ - Writes to a file using the attached file's contents. + Creates to a file. If an attachment is added, it saves the attachment. Documentation ------------- - FILE > WRITE > FILE_PATH + FILE > CREATE > FILE_PATH """ + if self.attachments == []: + with open(file_path.name, "w"): + pass + + await ctx.send(f"Created `{file_path}`") + return + new_file = self.attachments[0] self.attachments.pop(0) - with open(file_path.name, "w") as opened_file: - contents = await new_file.read() - opened_file.write(contents.decode("utf-8")) + await new_file.save(file_path.name) - await ctx.send(f"Wrote from `{new_file.filename}` to `{file_path}`") + await ctx.send(f"Created `{file_path}` from `{new_file.filename}`") async def clear(self, ctx, file_path): """ From d21bf81283dc1499dce10c71e14a035f2556a9f7 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 28 Jun 2025 07:25:09 -0400 Subject: [PATCH 56/77] Finish `WRITE` function, revert name change --- DexScript/package/commands.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index f424fe6..d28e104 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -488,15 +488,18 @@ async def read(self, ctx, file_paths): await ctx.send(files=files) - async def create(self, ctx, file_path): + async def write(self, ctx, file_path): """ - Creates to a file. If an attachment is added, it saves the attachment. + Writes to a file. If an attachment is added, it will save the attachment. Documentation ------------- - FILE > CREATE > FILE_PATH + FILE > WRITE > FILE_PATH """ if self.attachments == []: + if os.path.isfile(file_path.name): + raise Exception(f"{file_path} already exists.") + with open(file_path.name, "w"): pass @@ -509,7 +512,7 @@ async def create(self, ctx, file_path): await new_file.save(file_path.name) - await ctx.send(f"Created `{file_path}` from `{new_file.filename}`") + await ctx.send(f"Wrote to `{file_path}` from `{new_file.filename}`") async def clear(self, ctx, file_path): """ From 20e65afb942a78809750d2cf222ff3be16eb3eee Mon Sep 17 00:00:00 2001 From: Dotsian Date: Mon, 30 Jun 2025 03:51:41 -0400 Subject: [PATCH 57/77] Fix create cmd --- DexScript/package/commands.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/DexScript/package/commands.py b/DexScript/package/commands.py index d28e104..1701bca 100644 --- a/DexScript/package/commands.py +++ b/DexScript/package/commands.py @@ -86,8 +86,6 @@ async def create(self, ctx, model, identifier, values=None): ------------- CREATE > MODEL > IDENTIFIER > VALUES(?) """ - self.type_error(values, "values", [Types.DICT]) - await Utils.create_model(model.value, identifier, values) await ctx.send(f"Created `{identifier}` {model.name.lower()}") From 1b6e37598283609de3beb5eaf7d5f54866449a12 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Thu, 10 Jul 2025 00:13:32 -0400 Subject: [PATCH 58/77] Add modules to config --- DexScript/package/parser.py | 4 ++++ DexScript/package/utils.py | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 1e5ddbe..f70edd2 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -38,11 +38,15 @@ def __init__(self, ctx, bot): and issubclass(o, commands.DexCommand) and not issubclass(o, commands.Global) and o.__name__ != "DexCommand" + and o.__name__ in config.modules ), ) self.global_methods = [x for x in dir(commands.Global) if not x.startswith("__")] + if "Global" not in config.modules: + self.global_methods = [] + def create_value(self, line): value = Value(line) value.value = line diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index c9faa2f..e563ddd 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -3,7 +3,7 @@ import inspect import os import re -from dataclasses import dataclass +from dataclasses import dataclass, field from difflib import get_close_matches from enum import Enum from io import StringIO @@ -61,6 +61,14 @@ class Settings: debug: bool = False versioncheck: bool = False reference: str = "main" + modules: list[str] = field(default_factory=lambda: [ + "Global", + "Emoji", + "Eval", + "File", + "Filter", + "Template" + ]) config = Settings() From e3687241157596ce0d2eeac871b055802eefea73 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 12 Jul 2025 20:44:01 -0400 Subject: [PATCH 59/77] Add button --- DexScript/github/installer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index ab50579..7c1e463 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -177,6 +177,10 @@ async def uninstall_button(self, interaction: discord.Interaction, _: discord.ui await interaction.message.edit(**self.installer.interface.fields) await interaction.response.defer() + @discord.ui.button(style=discord.ButtonStyle.secondary, label="Config") + async def config_button(self, interaction: discord.Interaction, _: discord.ui.Button): + pass + @discord.ui.button(style=discord.ButtonStyle.red, label="Exit") async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Button): self.install_button.disabled = True From 36889886b0c21622465c8b1dbd532ec212a8a13f Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 12 Jul 2025 21:03:25 -0400 Subject: [PATCH 60/77] More work on config revamp --- DexScript/github/installer.py | 39 ++++++++++++++++++++++++----------- DexScript/package/cog.py | 4 ++-- DexScript/package/config.toml | 18 ++++++++++++++++ DexScript/package/parser.py | 4 ++-- DexScript/package/utils.py | 34 +++++++++++++++++++----------- 5 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 DexScript/package/config.toml diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index 7c1e463..c8ccf3f 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -35,7 +35,7 @@ class InstallerConfig: """ github = ["Dotsian/DexScript", "dev"] - files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py"] + files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py", "config.toml"] appearance = { "logo": f"{ASSET_PATH}/DexScriptLogo.png", "logo_error": f"{ASSET_PATH}/DexScriptLogoError.png", @@ -71,15 +71,10 @@ def __init__(self, installer, embed_type="setup"): self.installer = installer - match embed_type: - case "setup": - self.setup() - case "error": - self.error() - case "installed": - self.installed() - case "uninstalled": - self.uninstalled() + if not hasattr(self, embed_type): + return + + getattr(self, embed_type)() def setup(self): self.title = "DexScript Installation" @@ -139,6 +134,15 @@ def uninstalled(self): self.set_thumbnail(url=config.appearance["logo"]) + def config(self): + with open(f"{config.path}/config.toml") as file: + file_contents = file.read() + + self.title = "DexScript Configuration" + self.description = f"```toml\n{file_contents}\n```" + self.color = discord.Color.from_str("#03BAFC") + self.timestamp = datetime.now() + class InstallerView(discord.ui.View): def __init__(self, installer): @@ -177,9 +181,16 @@ async def uninstall_button(self, interaction: discord.Interaction, _: discord.ui await interaction.message.edit(**self.installer.interface.fields) await interaction.response.defer() - @discord.ui.button(style=discord.ButtonStyle.secondary, label="Config") + @discord.ui.button( + style=discord.ButtonStyle.secondary, + label="Config", + disabled=not os.path.isfile(f"{config.path}/config.toml") + ) async def config_button(self, interaction: discord.Interaction, _: discord.ui.Button): - pass + self.installer.interface.embed = InstallerEmbed(self.installer, "config") + + await interaction.message.edit(**self.installer.interface.fields) + await interaction.response.defer() @discord.ui.button(style=discord.ButtonStyle.red, label="Exit") async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Button): @@ -283,6 +294,10 @@ async def install(self): os.makedirs(config.path, exist_ok=True) for file in config.files: + if file.endswith(".toml") and os.path.isfile(f"{config.path}/{file}"): + logger.log(f"{file} already exists, skipping", "INFO") + continue + logger.log(f"Fetching {file} from '{link}/DexScript/package'", "INFO") request = requests.get(f"{link}/DexScript/package/{file}", {"ref": config.github[1]}) diff --git a/DexScript/package/cog.py b/DexScript/package/cog.py index ecd9e5d..dfe3292 100644 --- a/DexScript/package/cog.py +++ b/DexScript/package/cog.py @@ -25,12 +25,12 @@ def __init__(self, bot): @staticmethod def check_version(): - if not config.versioncheck: + if not config.version_warning: return None request = requests.get( "https://api.github.com/repos/Dotsian/DexScript/contents/pyproject.toml", - {"ref": config.reference}, + {"ref": config.branch}, ) if request.status_code != requests.codes.ok: diff --git a/DexScript/package/config.toml b/DexScript/package/config.toml new file mode 100644 index 0000000..616afc4 --- /dev/null +++ b/DexScript/package/config.toml @@ -0,0 +1,18 @@ +# Whether or not DexScript will warn you for running an outdated version. +version-warning = true + +# The command groups that DexScript will load. +command-groups = [ + "Global", + "Emoji", + "Eval", + "File", + "Filter", + "Template" +] + +# Displays additional information for error handling. +debug = false + +# The GitHub branch that will be used for version checking and other misc features. +branch = "main" \ No newline at end of file diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index f70edd2..36e90db 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -38,13 +38,13 @@ def __init__(self, ctx, bot): and issubclass(o, commands.DexCommand) and not issubclass(o, commands.Global) and o.__name__ != "DexCommand" - and o.__name__ in config.modules + and o.__name__ in config.command_groups ), ) self.global_methods = [x for x in dir(commands.Global) if not x.startswith("__")] - if "Global" not in config.modules: + if "Global" not in config.command_groups: self.global_methods = [] def create_value(self, line): diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index e563ddd..444f414 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -3,6 +3,7 @@ import inspect import os import re +import tomllib from dataclasses import dataclass, field from difflib import get_close_matches from enum import Enum @@ -58,20 +59,29 @@ class Settings: Settings class for DexScript. """ - debug: bool = False - versioncheck: bool = False - reference: str = "main" - modules: list[str] = field(default_factory=lambda: [ - "Global", - "Emoji", - "Eval", - "File", - "Filter", - "Template" - ]) + def __init__(self, path): + with open(path, "rb") as f: + data = tomllib.load(f) + + if data is None: + return + + self.version_warning = data.get("version-warning", True) + + self.command_groups = data.get("command-groups", [ + "Global", + "Emoji", + "Eval", + "File", + "Filter", + "Template" + ]) + + self.debug = data.get("debug", False) + self.branch = data.get("branch", "main") -config = Settings() +config = Settings(Path(os.path.dirname(os.path.abspath(__file__)), "./config.toml")) @dataclass From 02273e7f2ce3574f0e3e7c712aa066fd19a6a5ff Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 12 Jul 2025 21:07:34 -0400 Subject: [PATCH 61/77] Small changes --- DexScript/github/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index c8ccf3f..aa237f4 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -34,7 +34,7 @@ class InstallerConfig: Configuration class for the installer. """ - github = ["Dotsian/DexScript", "dev"] + github = ["Dotsian/DexScript", "installer-config"] files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py", "config.toml"] appearance = { "logo": f"{ASSET_PATH}/DexScriptLogo.png", @@ -289,7 +289,7 @@ async def install(self): await bot.remove_cog("DexScript") # type: ignore - link = f"https://api.github.com/repos/{config.github[0]}/contents/" + link = f"https://api.github.com/repos/{config.github[0]}/contents" os.makedirs(config.path, exist_ok=True) From 061aec0fcbf2afc8d07b02780df8502cf52b33c8 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 12 Jul 2025 21:12:18 -0400 Subject: [PATCH 62/77] Add `ConfigView` --- DexScript/github/installer.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index aa237f4..ccef5a7 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -188,6 +188,7 @@ async def uninstall_button(self, interaction: discord.Interaction, _: discord.ui ) async def config_button(self, interaction: discord.Interaction, _: discord.ui.Button): self.installer.interface.embed = InstallerEmbed(self.installer, "config") + self.installer.interface.view = ConfigView(self.installer) await interaction.message.edit(**self.installer.interface.fields) await interaction.response.defer() @@ -202,6 +203,28 @@ async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Butt await interaction.response.defer() +class ConfigView(discord.ui.View): + def __init__(self, installer): + super().__init__() + self.installer = installer + + @discord.ui.button(style=discord.ButtonStyle.primary, label="Back") + async def back_button(self, interaction: discord.Interaction, _: discord.ui.Button): + self.installer.interface.embed = InstallerEmbed(self.installer, "setup") + self.installer.interface.view = InstallerView(self.installer) + + await interaction.message.edit(**self.installer.interface.fields) + await interaction.response.defer() + + @discord.ui.button(style=discord.ButtonStyle.red, label="Exit") + async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Button): + self.back_button.disabled = True + self.quit_button.disabled = True + + await interaction.message.edit(**self.installer.interface.fields) + await interaction.response.defer() + + class InstallerGUI: def __init__(self, installer): self.loaded = False From 694ef77fcbcbf278c48ede104ed864814325a560 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 26 Jul 2025 22:50:06 -0400 Subject: [PATCH 63/77] work on config selection --- DexScript/github/installer.py | 33 +++++++++++++++++++++++++++++++++ DexScript/package/parser.py | 2 +- DexScript/package/utils.py | 4 ++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index ccef5a7..66d6eb1 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -203,11 +203,44 @@ async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Butt await interaction.response.defer() +class ConfigSelect(discord.ui.Select): + def __init__(self, installer): + self.installer = installer + + options = [] + + with open("ballsdex/packages/dexscript/config.toml") as file: + description = "" + + for line in file.readlines(): + if line in ["\n", ""]: + continue + + if line.startswith("#"): + description = (line[2:]) + continue + + name = line.strip(" ")[0] + + options.append( + discord.SelectOption(label=name, value=name, description=description) + ) + + description = "" + + super().__init__(placeholder="Edit setting", max_values=1, min_values=1, options=options) + + async def callback(self, interaction: discord.Interaction): + pass # self.values[0] + + class ConfigView(discord.ui.View): def __init__(self, installer): super().__init__() self.installer = installer + self.add_item(ConfigSelect(installer)) + @discord.ui.button(style=discord.ButtonStyle.primary, label="Back") async def back_button(self, interaction: discord.Interaction, _: discord.ui.Button): self.installer.interface.embed = InstallerEmbed(self.installer, "setup") diff --git a/DexScript/package/parser.py b/DexScript/package/parser.py index 36e90db..c54d06f 100644 --- a/DexScript/package/parser.py +++ b/DexScript/package/parser.py @@ -62,7 +62,7 @@ def create_value(self, line): Types.BOOLEAN: lower in ["true", "false"], Types.HEX: lower.startswith("#"), Types.ARRAY: lower.startswith("[") and lower.endswith("]"), - Types.NONE: lower == "NIL" + Types.NONE: lower == "nil" } for key, operation in type_dict.items(): diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 444f414..bf2b934 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -4,7 +4,7 @@ import os import re import tomllib -from dataclasses import dataclass, field +from dataclasses import dataclass from difflib import get_close_matches from enum import Enum from io import StringIO @@ -81,7 +81,7 @@ def __init__(self, path): self.branch = data.get("branch", "main") -config = Settings(Path(os.path.dirname(os.path.abspath(__file__)), "./config.toml")) +config = Settings("./config.toml") @dataclass From 9557968c16d84a68bafbe70f254e629b37bef477 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 26 Jul 2025 22:57:58 -0400 Subject: [PATCH 64/77] Fix method --- DexScript/github/installer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index 66d6eb1..f64c09b 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -213,14 +213,14 @@ def __init__(self, installer): description = "" for line in file.readlines(): - if line in ["\n", ""]: + if line in ["\n", ""] or line.startswith(" "): continue if line.startswith("#"): - description = (line[2:]) + description = line[2:] continue - name = line.strip(" ")[0] + name = line.split(" ")[0] options.append( discord.SelectOption(label=name, value=name, description=description) From 0d2212a07e02eab7bc1a6e89adc378682ada48b4 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 26 Jul 2025 23:09:05 -0400 Subject: [PATCH 65/77] Add modal --- DexScript/github/installer.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index f64c09b..b03b0bc 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -203,6 +203,20 @@ async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Butt await interaction.response.defer() +class ConfigModal(discord.ui.Modal): + def __init__(self, setting: str): + self.setting = setting + self.value = discord.ui.TextInput(label=f"New {setting} value") + + super().__init__(title=f"Editing `{setting}`") + + async def on_submit(self, interaction: discord.Interaction): + await interaction.response.send_message( + f"Updated `{self.setting}` to `{self.value}`!", + ephemeral=True + ) + + class ConfigSelect(discord.ui.Select): def __init__(self, installer): self.installer = installer @@ -213,7 +227,7 @@ def __init__(self, installer): description = "" for line in file.readlines(): - if line in ["\n", ""] or line.startswith(" "): + if line in ["\n", "", "]"] or line.startswith(" "): continue if line.startswith("#"): @@ -231,7 +245,7 @@ def __init__(self, installer): super().__init__(placeholder="Edit setting", max_values=1, min_values=1, options=options) async def callback(self, interaction: discord.Interaction): - pass # self.values[0] + await interaction.response.send_modal(ConfigModal(self.values[0])) class ConfigView(discord.ui.View): From c155adfb2d17b2a078e82ea6564fd4b663723cd4 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 26 Jul 2025 23:41:51 -0400 Subject: [PATCH 66/77] More development --- DexScript/github/installer.py | 35 +++++++++++++++++++++++++++++------ DexScript/package/config.toml | 11 ++--------- DexScript/package/utils.py | 2 +- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index b03b0bc..dbb58f3 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -204,15 +204,38 @@ async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Butt class ConfigModal(discord.ui.Modal): - def __init__(self, setting: str): + def __init__(self, installer, setting: str): + self.installer = installer self.setting = setting - self.value = discord.ui.TextInput(label=f"New {setting} value") super().__init__(title=f"Editing `{setting}`") + value = discord.ui.TextInput(label=f"New value", required=True) + async def on_submit(self, interaction: discord.Interaction): + with open(f"{config.path}/config.toml") as file: + lines = [x.strip() for x in file.readlines()] + new_lines = [] + + for line in lines: + if not line.startswith(self.setting): + new_lines.append(line + "\n") + continue + + new_value = f'"{self.value.value}"' + + if self.value.value.lower() in ["true", "false"]: + new_value = bool(self.value.value.title()) + + new_lines.append(f"{self.setting} = {new_value}\n") + + with open(f"{config.path}/config.toml", "w") as write_file: + write_file.writelines(new_lines) + + await interaction.message.edit(**self.installer.interface.fields) + await interaction.response.send_message( - f"Updated `{self.setting}` to `{self.value}`!", + f"Updated `{self.setting}` to `{self.value.value}`!", ephemeral=True ) @@ -223,11 +246,11 @@ def __init__(self, installer): options = [] - with open("ballsdex/packages/dexscript/config.toml") as file: + with open(f"{config.path}/config.toml") as file: description = "" for line in file.readlines(): - if line in ["\n", "", "]"] or line.startswith(" "): + if line.rstrip() in ["\n", "", "]"] or line.startswith(" "): continue if line.startswith("#"): @@ -245,7 +268,7 @@ def __init__(self, installer): super().__init__(placeholder="Edit setting", max_values=1, min_values=1, options=options) async def callback(self, interaction: discord.Interaction): - await interaction.response.send_modal(ConfigModal(self.values[0])) + await interaction.response.send_modal(ConfigModal(self.installer, self.values[0])) class ConfigView(discord.ui.View): diff --git a/DexScript/package/config.toml b/DexScript/package/config.toml index 616afc4..ee62b00 100644 --- a/DexScript/package/config.toml +++ b/DexScript/package/config.toml @@ -2,17 +2,10 @@ version-warning = true # The command groups that DexScript will load. -command-groups = [ - "Global", - "Emoji", - "Eval", - "File", - "Filter", - "Template" -] +command-groups = ["Global", "Emoji", "Eval", "File", "Filter", "Template"] # Displays additional information for error handling. debug = false # The GitHub branch that will be used for version checking and other misc features. -branch = "main" \ No newline at end of file +branch = "main" diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index bf2b934..4497704 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -81,7 +81,7 @@ def __init__(self, path): self.branch = data.get("branch", "main") -config = Settings("./config.toml") +config = Settings(Path(os.path.dirname(os.path.abspath(__file__)), "./config.toml")) @dataclass From 1ac824c47fca705e3e5a38faba4c240c375afdb5 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sat, 26 Jul 2025 23:58:00 -0400 Subject: [PATCH 67/77] Final touches --- DexScript/github/installer.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index dbb58f3..10919d9 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -195,9 +195,8 @@ async def config_button(self, interaction: discord.Interaction, _: discord.ui.Bu @discord.ui.button(style=discord.ButtonStyle.red, label="Exit") async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Button): - self.install_button.disabled = True - self.uninstall_button.disabled = True - self.quit_button.disabled = True + for item in self.children: + item.disabled = True await interaction.message.edit(**self.installer.interface.fields) await interaction.response.defer() @@ -222,16 +221,21 @@ async def on_submit(self, interaction: discord.Interaction): new_lines.append(line + "\n") continue - new_value = f'"{self.value.value}"' + full_value = self.value.value + new_value = f'"{full_value}"' - if self.value.value.lower() in ["true", "false"]: - new_value = bool(self.value.value.title()) + if full_value.lower() in ["true", "false"]: + new_value = full_value.lower() + elif full_value.startswith("[") and full_value.endswith("]"): + new_value = full_value new_lines.append(f"{self.setting} = {new_value}\n") with open(f"{config.path}/config.toml", "w") as write_file: write_file.writelines(new_lines) + self.installer.interface.embed = InstallerEmbed(self.installer, "config") + await interaction.message.edit(**self.installer.interface.fields) await interaction.response.send_message( @@ -288,8 +292,8 @@ async def back_button(self, interaction: discord.Interaction, _: discord.ui.Butt @discord.ui.button(style=discord.ButtonStyle.red, label="Exit") async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Button): - self.back_button.disabled = True - self.quit_button.disabled = True + for item in self.children: + item.disabled = True await interaction.message.edit(**self.installer.interface.fields) await interaction.response.defer() From 01c66131fa67a7bc9d39edd830437244e23f01ad Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sun, 27 Jul 2025 01:11:27 -0400 Subject: [PATCH 68/77] Finish configuration editing --- DexScript/github/installer.py | 46 ++++++++++++++++++++++++++++++----- DexScript/package/cog.py | 35 +++++--------------------- DexScript/package/config.toml | 3 +++ README.md | 7 +++++- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index 10919d9..89669e0 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -280,18 +280,52 @@ def __init__(self, installer): super().__init__() self.installer = installer + back_button = discord.ui.Button(label="Back", style=discord.ButtonStyle.primary) + reset_button = discord.ui.Button(label="Reset", style=discord.ButtonStyle.grey) + quit_button = discord.ui.Button(label="Exit", style=discord.ButtonStyle.red) + + back_button.callback = self.back_button + reset_button.callback = self.reset_button + quit_button.callback = self.quit_button + + self.add_item(back_button) self.add_item(ConfigSelect(installer)) + self.add_item(reset_button) + self.add_item(quit_button) - @discord.ui.button(style=discord.ButtonStyle.primary, label="Back") - async def back_button(self, interaction: discord.Interaction, _: discord.ui.Button): + async def back_button(self, interaction: discord.Interaction): self.installer.interface.embed = InstallerEmbed(self.installer, "setup") self.installer.interface.view = InstallerView(self.installer) await interaction.message.edit(**self.installer.interface.fields) await interaction.response.defer() - @discord.ui.button(style=discord.ButtonStyle.red, label="Exit") - async def quit_button(self, interaction: discord.Interaction, _: discord.ui.Button): + async def reset_button(self, interaction: discord.Interaction): + request = requests.get( + f"https://api.github.com/repos/{config.github[0]}/" + "contents/DexScript/package/config.toml", + {"ref": config.github[1]} + ) + + if request.status_code != requests.codes.ok: + await interaction.response.send_message( + f"Failed to reset config file `({request.status_code})`", ephemeral=True + ) + return + + request = request.json() + content = b64decode(request["content"]) + + with open(f"{config.path}/config.toml", "w") as opened_file: + opened_file.write(content.decode()) + + self.installer.interface.embed = InstallerEmbed(self.installer, "config") + + await interaction.message.edit(**self.installer.interface.fields) + + await interaction.response.send_message("Successfully reset config file", ephemeral=True) + + async def quit_button(self, interaction: discord.Interaction): for item in self.children: item.disabled = True @@ -409,7 +443,7 @@ async def install(self): content = b64decode(request["content"]) with open(f"{config.path}/{file}", "w") as opened_file: - opened_file.write(content.decode("UTF-8")) + opened_file.write(content.decode()) logger.log(f"Installed {file} from '{link}/DexScript/package'", "INFO") @@ -450,7 +484,7 @@ def latest_version(self): if pyproject_request.status_code != requests.codes.ok: return - toml_content = b64decode(pyproject_request.json()["content"]).decode("UTF-8") + toml_content = b64decode(pyproject_request.json()["content"]).decode() new_version = re.search(r'version\s*=\s*"(.*?)"', toml_content) if not new_version: diff --git a/DexScript/package/cog.py b/DexScript/package/cog.py index dfe3292..6e273b8 100644 --- a/DexScript/package/cog.py +++ b/DexScript/package/cog.py @@ -15,6 +15,9 @@ ASSET_PATH = "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/main/assets" +def check_dexscript_user(ctx): + return ctx.message.author.id in config.dexscript_user_ids + class DexScript(commands.Cog): """ DexScript commands. @@ -50,6 +53,7 @@ def check_version(): @commands.command() @commands.is_owner() + @commands.check(check_dexscript_user) async def run(self, ctx: commands.Context, *, code: str): """ Executes DexScript code. @@ -84,6 +88,7 @@ async def run(self, ctx: commands.Context, *, code: str): @commands.command() @commands.is_owner() + @commands.check(check_dexscript_user) async def about(self, ctx: commands.Context): """ Displays information about DexScript. @@ -120,6 +125,7 @@ async def about(self, ctx: commands.Context): @commands.command() @commands.is_owner() + @commands.check(check_dexscript_user) async def installer(self, ctx: commands.Context, reference: str = "main"): """ Displays the DexScript installer. @@ -148,32 +154,3 @@ async def installer(self, ctx: commands.Context, reference: str = "main"): case _: await ctx.send(f"Request raised error code `{request.status_code}`.") - - @commands.command() - @commands.is_owner() - async def setting(self, ctx: commands.Context, setting: str, value: str | None = None): - """ - Changes a setting based on the value provided. - - Parameters - ---------- - setting: str - The setting you want to toggle. - value: str | None - The value you want to set the setting to. - """ - setting = setting.lower() - - if setting not in vars(config): - await ctx.send(f"`{setting}` is not a valid setting.") - return - - setting_value = vars(config)[setting] - new_value = value - - if isinstance(setting_value, bool): - new_value = bool(value) if value else not setting_value - - setattr(config, setting, new_value) - - await ctx.send(f"`{setting}` has been set to `{new_value}`") diff --git a/DexScript/package/config.toml b/DexScript/package/config.toml index ee62b00..1306048 100644 --- a/DexScript/package/config.toml +++ b/DexScript/package/config.toml @@ -4,6 +4,9 @@ version-warning = true # The command groups that DexScript will load. command-groups = ["Global", "Emoji", "Eval", "File", "Filter", "Template"] +# A list of user IDs that will be able to execute DexScript commands. +dexscript-user-ids = [] + # Displays additional information for error handling. debug = false diff --git a/README.md b/README.md index 1232b01..496878f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ To install DexScript, you must have the following: ## DexScript Setup -The DexScript installer is a intuitive menu that can allow you to easily update, install, and uninstall DexScript. To bring up the DexScript installer, all you have to do is run one eval command! +The DexScript installer is a intuitive menu that can allow you to easily update, install, configure, and uninstall DexScript. To bring up the DexScript installer, all you have to do is run one eval command! ### Versions @@ -60,6 +60,7 @@ Once you have ran the eval command, the DexScript installer should appear. There * Install [or] Update * Uninstall +* Config * Exit > [!IMPORTANT] @@ -73,6 +74,10 @@ If you are installing DexScript for the first time, you will see a button called If you already have DexScript, you will see a button called "Update". When you click that button, DexScript will update to the latest version. This will instantly update DexScript, which means you don't have to restart your bot. +#### Configuration + +If you have a `config.toml` file already installed within DexScript, the "Config" button will appear. This button will allow you to access the configuration menu, which will let you modify DexScript's internal settings. + #### Uninstalling If you already have DexScript, you will see a button called "Uninstall". Clicking the uninstall button will uninstall DexScript from your application. This will instantly remove the DexScript package and DexScript commands will unload instantly. From 91e702c2ac8d239a9c15a42b774bd6c954469c96 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sun, 27 Jul 2025 01:17:58 -0400 Subject: [PATCH 69/77] Switch back to dev --- DexScript/github/installer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DexScript/github/installer.py b/DexScript/github/installer.py index 89669e0..7a2a4fe 100644 --- a/DexScript/github/installer.py +++ b/DexScript/github/installer.py @@ -34,7 +34,7 @@ class InstallerConfig: Configuration class for the installer. """ - github = ["Dotsian/DexScript", "installer-config"] + github = ["Dotsian/DexScript", "dev"] files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py", "config.toml"] appearance = { "logo": f"{ASSET_PATH}/DexScriptLogo.png", @@ -209,7 +209,7 @@ def __init__(self, installer, setting: str): super().__init__(title=f"Editing `{setting}`") - value = discord.ui.TextInput(label=f"New value", required=True) + value = discord.ui.TextInput(label="New value", required=True) async def on_submit(self, interaction: discord.Interaction): with open(f"{config.path}/config.toml") as file: From d58df9381e002339490a3c5c88baf69433c6e3bb Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sun, 27 Jul 2025 01:20:35 -0400 Subject: [PATCH 70/77] Add missing config field --- DexScript/package/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 4497704..300024e 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -77,6 +77,8 @@ def __init__(self, path): "Template" ]) + self.dexscript_user_ids = data.get("dexscript-user-ids", []) + self.debug = data.get("debug", False) self.branch = data.get("branch", "main") From 6e8945fac0eb7cd02848b70b12a4cf207b95a449 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sun, 27 Jul 2025 01:21:43 -0400 Subject: [PATCH 71/77] Attempt to fix checks --- DexScript/package/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index 300024e..d020b62 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -14,12 +14,12 @@ import discord import requests from ballsdex.core.models import ( - Ball, # noqa: F401 - BallInstance, # noqa: F401 - Economy, # noqa: F401 - Player, # noqa: F401 - Regime, # noqa: F401 - Special, # noqa: F401 + Ball, # noqa: F401, I001 + BallInstance, # noqa: F401, I001 + Economy, # noqa: F401, I001 + Player, # noqa: F401, I001 + Regime, # noqa: F401, I001 + Special, # noqa: F401, I001 ) from dateutil.parser import parse as parse_date From 1fc116bb040e21a6a3e2703240819488ee4c5e09 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sun, 27 Jul 2025 01:22:39 -0400 Subject: [PATCH 72/77] Cry --- DexScript/package/utils.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/DexScript/package/utils.py b/DexScript/package/utils.py index d020b62..300024e 100644 --- a/DexScript/package/utils.py +++ b/DexScript/package/utils.py @@ -14,12 +14,12 @@ import discord import requests from ballsdex.core.models import ( - Ball, # noqa: F401, I001 - BallInstance, # noqa: F401, I001 - Economy, # noqa: F401, I001 - Player, # noqa: F401, I001 - Regime, # noqa: F401, I001 - Special, # noqa: F401, I001 + Ball, # noqa: F401 + BallInstance, # noqa: F401 + Economy, # noqa: F401 + Player, # noqa: F401 + Regime, # noqa: F401 + Special, # noqa: F401 ) from dateutil.parser import parse as parse_date From 679fb85636e9c3090ea58cbe77ebf0635bec0b8a Mon Sep 17 00:00:00 2001 From: Dotsian Date: Tue, 5 Aug 2025 14:48:11 -0400 Subject: [PATCH 73/77] Fix command checks --- DexScript/package/cog.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/DexScript/package/cog.py b/DexScript/package/cog.py index 6e273b8..0ad78c3 100644 --- a/DexScript/package/cog.py +++ b/DexScript/package/cog.py @@ -15,8 +15,10 @@ ASSET_PATH = "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/main/assets" -def check_dexscript_user(ctx): - return ctx.message.author.id in config.dexscript_user_ids +async def check_dexscript_user(ctx): + user = ctx.message.author + + return await ctx.bot.is_owner(user) or user.id in config.dexscript_user_ids class DexScript(commands.Cog): """ @@ -52,7 +54,6 @@ def check_version(): return None @commands.command() - @commands.is_owner() @commands.check(check_dexscript_user) async def run(self, ctx: commands.Context, *, code: str): """ @@ -87,7 +88,6 @@ async def run(self, ctx: commands.Context, *, code: str): await ctx.message.add_reaction("✅") @commands.command() - @commands.is_owner() @commands.check(check_dexscript_user) async def about(self, ctx: commands.Context): """ @@ -124,7 +124,6 @@ async def about(self, ctx: commands.Context): await ctx.send(embed=embed) @commands.command() - @commands.is_owner() @commands.check(check_dexscript_user) async def installer(self, ctx: commands.Context, reference: str = "main"): """ From cadb6a81a33379ce785601d952a5f50274e87a34 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 22 Aug 2025 10:44:01 -0400 Subject: [PATCH 74/77] Remove fork dropdown --- .github/ISSUE_TEMPLATE/bug_report.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 5bb707e..e0a3c46 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -12,7 +12,7 @@ body: label: Error Page description: The [error page](https://github.com/Dotsian/DexScript/wiki/Errors) explains what common errors mean and how to solve them. options: - - label: Did you read the error page and view the common errors? + - label: Did you read the error page and take a look at the common errors? required: true - type: dropdown id: location @@ -46,15 +46,6 @@ body: placeholder: Version number... validations: required: true - - type: dropdown - id: platform - attributes: - label: What fork are you using? - options: - - Ballsdex - - CarFigures - validations: - required: true - type: textarea id: error attributes: From c0dfdc456faf215f4e5e7c89d2993a3cbd4abf13 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Sun, 28 Sep 2025 10:29:15 -0400 Subject: [PATCH 75/77] Convert to DexI system [BETA] --- pyproject.toml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d7162f1..3485793 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,18 @@ -[tool.poetry] +[project] name = "dexscript" version = "1.0" description = "" -authors = ["dotzz "] +requires-python = ">=3.12" license = "MIT" +dependencies = [ + "discord.py>=2.5.0", + "ruff>=0.11.13" +] -[tool.poetry.dependencies] -python = ">=3.12" -"discord.py" = "^2.5.0" -ruff = "^0.11.13" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +[tool.dexi] +requires-ballsdex = ">=2.22.0" [tool.ruff] line-length = 99 lint.ignore = ["E203", "F704", "F706", "F821"] -lint.extend-select = ["E501", "I001"] \ No newline at end of file +lint.extend-select = ["E501", "I001"] From d195dca9b863a8cc4b91bb8c983e0e7706894b1b Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 10 Oct 2025 04:22:06 -0400 Subject: [PATCH 76/77] Add DexI support to DexScript --- DexScript/package/cog.py | 2 +- README.md | 31 ++++++++++++++++++++++++------- pyproject.toml | 10 ++++++++-- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/DexScript/package/cog.py b/DexScript/package/cog.py index 0ad78c3..23055f6 100644 --- a/DexScript/package/cog.py +++ b/DexScript/package/cog.py @@ -10,7 +10,7 @@ from .parser import DexScriptParser from .utils import Utils, config -__version__ = "1.0" +__version__ = "1.0.0" ASSET_PATH = "https://raw.githubusercontent.com/Dotsian/DexScript/refs/heads/main/assets" diff --git a/README.md b/README.md index 496878f..b99dca6 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,12 @@ DexScript has a ton more features too! All of them can be found within our exten To install DexScript, you must have the following: * Ballsdex -* Eval access +* Eval access (if using eval) +* [DexI](https://github.com/Dotsian/DexI) (if using DexI) ## DexScript Setup -The DexScript installer is a intuitive menu that can allow you to easily update, install, configure, and uninstall DexScript. To bring up the DexScript installer, all you have to do is run one eval command! +The DexScript installer is a intuitive menu that can allow you to easily update, install, configure, and uninstall DexScript. To bring up the DexScript installer, all you have to do is run one command! ### Versions @@ -37,21 +38,37 @@ DexScript has two versions, the release version and the development version. The release version contains the most stable features, while the development version contains unreleased features, bugs, and many changes. -To install DexScript, run the following eval command: +To install DexScript, run the following command: #### Release Version -> ```py -> import base64, requests; await ctx.invoke(bot.get_command("eval"), body=base64.b64decode(requests.get("https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py").json()["content"]).decode()) -> ``` +**Using DexI** + +```bash +dexi add Dotsian/DexScript +``` + +**Using Eval** + +```py +import base64, requests; await ctx.invoke(bot.get_command("eval"), body=base64.b64decode(requests.get("https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py").json()["content"]).decode()) +``` #### Development Version +**Using DexI** + +```bash +dexi add Dotsian/DexScript --branch dev +``` + +**Using Eval** + > ```py > import base64, requests; await ctx.invoke(bot.get_command("eval"), body=base64.b64decode(requests.get("https://api.github.com/repos/Dotsian/DexScript/contents/DexScript/github/installer.py", {"ref": "dev"}).json()["content"]).decode()) > ``` -### DexScript Installer +### DexScript Installer (Eval) > [!NOTE] > If DexScript is already installed, you can run `b.installer` to show the DexScript installer, replacing `b.` with your application's prefix. diff --git a/pyproject.toml b/pyproject.toml index 3485793..467981f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "dexscript" -version = "1.0" +version = "1.0.0" description = "" requires-python = ">=3.12" license = "MIT" @@ -10,7 +10,13 @@ dependencies = [ ] [tool.dexi] -requires-ballsdex = ">=2.22.0" +public = true +ballsdex-version = ">=2.22.0" +include-license = true + +[tool.dexi.package] +source = "DexScript/package" +target = "DexScript" [tool.ruff] line-length = 99 From 6f4a222f85d3a38a928cc2fe351e90b931677c39 Mon Sep 17 00:00:00 2001 From: Dotsian Date: Fri, 10 Oct 2025 20:34:20 -0400 Subject: [PATCH 77/77] Update pyproject.toml --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 467981f..9deb925 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ include-license = true [tool.dexi.package] source = "DexScript/package" target = "DexScript" +exclude = [] [tool.ruff] line-length = 99