Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
3f6fa3c
WIP
Caylies May 19, 2025
d6bd29e
Attempt link support
Caylies May 20, 2025
6c56e31
Add `player` model
Caylies May 20, 2025
892eb20
Force `list[str]` during autocorrect
Caylies May 20, 2025
fc803fd
Fix pascal casing detection
Caylies May 20, 2025
593d72b
Fix import mistake
Caylies May 20, 2025
03cc0c1
Force pascal casing
Caylies May 20, 2025
08d92f9
Assume ID
Caylies May 20, 2025
10df93f
Add hex type
Caylies May 20, 2025
025b65d
Fix
Caylies May 20, 2025
f1fa684
Use reference in installer command
Caylies May 20, 2025
a61a80a
Fix #2
Caylies May 20, 2025
361dd6e
Add array type and some other changes
Caylies May 20, 2025
b9b7c6c
Fix missing file
Caylies May 20, 2025
518b522
whoopsie
Caylies May 20, 2025
9431be2
Create values
Caylies May 20, 2025
d7aae72
Bump dev version and some other changes
Caylies May 21, 2025
dd70745
Add `self`
Caylies May 22, 2025
454cac2
Whoops
Caylies May 22, 2025
3ca2be4
Remove `api.py`
Caylies May 22, 2025
1b56c92
Hide utils
Caylies May 22, 2025
027ab3b
Fix smol mistake
Caylies May 23, 2025
874731c
Add utils
Caylies May 23, 2025
5392493
Rename class
Caylies May 23, 2025
449095c
Fix casing
Caylies May 23, 2025
ed00434
Cryign rn
Caylies May 23, 2025
5b60540
Softcode template
Caylies May 23, 2025
6efc16c
Small fix
Caylies May 23, 2025
3efa30f
Add dict type and improve parser
Caylies May 23, 2025
3647c70
Remove string stuff
Caylies May 23, 2025
6647c40
Whoooopss
Caylies May 23, 2025
36fc0e1
Bruh
Caylies May 23, 2025
00169af
Fix typo
Caylies May 23, 2025
17310b1
Remove `re` from parser
Caylies May 23, 2025
8d8b6b3
Add `self`
Caylies May 24, 2025
7363cdf
Add none check
Caylies May 24, 2025
3ddd333
Add none check 2
Caylies May 24, 2025
bf32c86
Create emojis with link
Caylies Jun 2, 2025
55831c6
IndexError fix
Caylies Jun 2, 2025
3e94783
Whoops
Caylies Jun 2, 2025
713215f
More changes
Caylies Jun 2, 2025
7da5149
New emoji class
Caylies Jun 4, 2025
c0bd49d
Add delete method to Emoji
Caylies Jun 5, 2025
5a6d581
Import RegEx
Caylies Jun 5, 2025
f5ea147
Add eval file and emoji info commands.
Caylies Jun 7, 2025
36bd5dd
Fix checks and update dep
Caylies Jun 7, 2025
e96d877
Add indentation to DexScript
Caylies Jun 9, 2025
d4ebd85
Support single indent values
Caylies Jun 9, 2025
d4f1adc
Add simple `mkdir` function
Caylies Jun 9, 2025
7efd903
Fix path error
Caylies Jun 10, 2025
65f939b
Remove dict and fix checks
Caylies Jun 10, 2025
cb44ac1
Add NONE type
Caylies Jun 24, 2025
b31b696
Attempt to fix attachment handling
Caylies Jun 25, 2025
6aa7169
Fix checks
Caylies Jun 25, 2025
27c1a8f
Overhaul `WRITE` function
Caylies Jun 28, 2025
d21bf81
Finish `WRITE` function, revert name change
Caylies Jun 28, 2025
20e65af
Fix create cmd
Caylies Jun 30, 2025
1b6e375
Add modules to config
Caylies Jul 10, 2025
e368724
Add button
Caylies Jul 13, 2025
3688988
More work on config revamp
Caylies Jul 13, 2025
02273e7
Small changes
Caylies Jul 13, 2025
061aec0
Add `ConfigView`
Caylies Jul 13, 2025
694ef77
work on config selection
Caylies Jul 27, 2025
9557968
Fix method
Caylies Jul 27, 2025
0d2212a
Add modal
Caylies Jul 27, 2025
c155adf
More development
Caylies Jul 27, 2025
1ac824c
Final touches
Caylies Jul 27, 2025
01c6613
Finish configuration editing
Caylies Jul 27, 2025
91e702c
Switch back to dev
Caylies Jul 27, 2025
d58df93
Add missing config field
Caylies Jul 27, 2025
6e8945f
Attempt to fix checks
Caylies Jul 27, 2025
1fc116b
Cry
Caylies Jul 27, 2025
0bc814b
[Installer] - New Configuration Menu
Caylies Jul 27, 2025
65a9f71
Merge branch 'main' into dev
Caylies Jul 27, 2025
679fb85
Fix command checks
Caylies Aug 5, 2025
cadb6a8
Remove fork dropdown
Caylies Aug 22, 2025
c0dfdc4
Convert to DexI system [BETA]
Caylies Sep 28, 2025
d195dca
Add DexI support to DexScript
Caylies Oct 10, 2025
6f4a222
Update pyproject.toml
Caylies Oct 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 1 addition & 4 deletions DATAPOLICY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
255 changes: 188 additions & 67 deletions DexScript/github/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# An explanation of the code will be provided below. #
# #
# THIS CODE IS RAN VIA THE `EVAL` COMMAND. #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #


import os
Expand All @@ -15,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

Expand All @@ -25,11 +25,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
Expand All @@ -38,25 +34,13 @@ class InstallerConfig:
Configuration class for the installer.
"""

github = ["Dotsian/DexScript", "main"]
files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py"]
github = ["Dotsian/DexScript", "dev"]
files = ["__init__.py", "cog.py", "commands.py", "parser.py", "utils.py", "config.toml"]
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',
Expand Down Expand Up @@ -87,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"
Expand Down Expand Up @@ -125,7 +104,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")]

Expand All @@ -150,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):
Expand Down Expand Up @@ -188,11 +181,153 @@ 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",
disabled=not os.path.isfile(f"{config.path}/config.toml")
)
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()

@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()


class ConfigModal(discord.ui.Modal):
def __init__(self, installer, setting: str):
self.installer = installer
self.setting = setting

super().__init__(title=f"Editing `{setting}`")

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:
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

full_value = self.value.value
new_value = f'"{full_value}"'

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(
f"Updated `{self.setting}` to `{self.value.value}`!",
ephemeral=True
)


class ConfigSelect(discord.ui.Select):
def __init__(self, installer):
self.installer = installer

options = []

with open(f"{config.path}/config.toml") as file:
description = ""

for line in file.readlines():
if line.rstrip() in ["\n", "", "]"] or line.startswith(" "):
continue

if line.startswith("#"):
description = line[2:]
continue

name = line.split(" ")[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):
await interaction.response.send_modal(ConfigModal(self.installer, self.values[0]))


class ConfigView(discord.ui.View):
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)

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()

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

await interaction.message.edit(**self.installer.interface.fields)
await interaction.response.defer()
Expand Down Expand Up @@ -232,6 +367,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" in lines

def add_package(self, package: str) -> bool:
with open("config.yml", "r") as file:
lines = file.readlines()
Expand Down Expand Up @@ -270,41 +411,24 @@ 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 not self.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")

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)

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]})
Expand All @@ -319,16 +443,13 @@ 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")

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")

Expand Down Expand Up @@ -363,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:
Expand Down
Loading