From 9dc18b86a2e8073440b019d88df3173b1cba146a Mon Sep 17 00:00:00 2001 From: Tabby <41929769+tabby-cat-nya@users.noreply.github.com> Date: Sun, 2 Nov 2025 14:55:36 +1100 Subject: [PATCH] refactoring and stuff --- .gitignore | 1 + README.md | 88 ++++++++++++++++++++++++------ TODO.md | 23 ++++++++ application_cmds/help.gd | 62 +++++++++++++++++++++ application_cmds/help.gd.uid | 1 + application_cmds/printer-create.gd | 2 +- application_cmds/printer-delete.gd | 27 ++------- application_cmds/printer-load.gd | 6 +- application_cmds/printer-nozzle.gd | 23 +------- application_cmds/printer-slots.gd | 23 +------- application_cmds/printer-unload.gd | 8 +-- application_cmds/spool-create.gd | 2 +- application_cmds/spool-delete.gd | 27 ++------- application_cmds/spool-edit.gd | 77 ++++++++++++++++++++++++++ application_cmds/spool-edit.gd.uid | 1 + application_cmds/test_appcmd.gd | 4 +- 16 files changed, 260 insertions(+), 115 deletions(-) create mode 100644 TODO.md create mode 100644 application_cmds/help.gd create mode 100644 application_cmds/help.gd.uid create mode 100644 application_cmds/spool-edit.gd create mode 100644 application_cmds/spool-edit.gd.uid diff --git a/.gitignore b/.gitignore index 0af181c..edadb58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Godot 4+ specific ignores .godot/ /android/ +environment.env diff --git a/README.md b/README.md index 5f40b30..fcb3ae4 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,73 @@ -# FabsocBot +# Fabcat! +meow meow! welcome to fabcat! a discord bot for Fabsoc made by Tabby +`Version 0.1` +## Basic Commands +This commands can be used by anyone! +- `/help` + displays this help screen +- `/printer-status` + displays the currently loaded spools and nozzle for each printer +- `/spool-list` + displays a list of both loaded and available spools -## Todo -- [x] want to redo spool management so when a spool is in a printer it leaves leaves the library - only exists in one place at a time - - [x] spool list should show loaded spools in printer, then unloaded and available spools - - [X] unloading a spool returns it to the library -- [ ] job management? -- [x] shoot some of the printers can hold multiple fillaments: endermixer 2, ams: 4 -- [x] handling of spools when printer is deleted -- [x] command permissions -- [ ] refactoring -- [ ] documentation - - [ ] readme - - [ ] fabsoc docs - - [ ] help command -> points to fabsoc docs? -- [x] offer to delete /unloaded spools -- [ ] bugtesting / fixing +## Technician Only Commands +Since these commands edit data, only users with the `technician` role may use them +### Printer Management +- `/printer-create ` + Creates a new printer with the provided name +- `/printer-delete ` + Deletes the printer with the matching name +- `/printer-slots ` + Changes the amount of spool slots a printer has (for example: if an AMS is attached) +- `/printer-nozzle ` + Changes the nozzle attached to the printer +- `/printer-load ` + Loads a spool into a printer, If the printer only has one spool slot and it is filled then this command will replace it and return the old spool to the library (offers option to delete old spool) +- `/printer-unload ` + Unloads the spool from the printer, If there are multiple a dropdown asks which to unload. The unloaded spool can then be kept in the library or deleted +### Spool Management +- `/spool-create [link]` + Creates a new spool with the given name and optionally a link to it +- `/spool-delete ` + Deletes the spool with the matching name, can only delete a spool if it is in the library (not loaded into a printer) +- `/spool-edit [new-link]` + Changes the name and/or link of a spool, only works if the spool is in the library (not loaded into a printer) +## Contributing to fabcat + +> [!Warning] Work in progress + +fabcat is made in Godot v4.5 with the discord.gd plugin. To edit the project follow these steps: +1. Download Godot v4.5 +2. Clone the fabcat repo to your computer +3. Open the project folder with Godot + +### Project Structure +- `main.gd` - main script which manages everything at the top level, you shouldn't need to touch this +- `Tools.gd` - global script that contains helper functions usable from anywhere, currently just has a function to check if the user has the right permissions +- `AutocompleteTools.gd` - global script that contains helper functions for command autocomplete +- `library.gd` - global script that manages the printer and spool library +- `datatypes` + - `librarySave` - class used by `library.gd` to save data to storage + - `printer` - class used to represent a printer + - `spool` - class used to represent a spool +- `application_cmds` - folder that holds all the commands for Fabcat, do not rename or place commands elsewhere as they will not be picked up +### Application Command Structure +Each application command file is made up of up to 5 key parts, only `execute()` and `data` are required for a minimal command: +- `on_ready()` - this function runs once when the bot starts up, useful for connecting signals +- `on_autocomplete()` - this function is run every time the bot receives a new partially typed argument and can return a list of entries for the user to pick from +- `execute()` - function that is run when the user runs the command +- `on_interaction_create()` - gets run whenever the user interacts with components in a message such as buttons or a dropdown, requires a signal connection to be made in `ready()` +- `data` - defines the structure and arguments of the application command + +### Useful Resources +- [Godot Documentation](https://docs.godotengine.org/en/4.5/) +- [discord.gd Documentation](https://3ddelano.github.io/discord.gd/#quick-tips-to-browse-through-the-documentation) +- [discord.gd Youtube Tutorial](https://www.youtube.com/playlist?list=PL5t0hR7ADzuk4M_GDeGcW7cDjG_xp710p) +- [Example discord.gd Project](https://github.com/3ddelano/discord-bot-v2-godot/tree/main) + +### Running an instance of fabcat +1. export to linux x86_64 +2. move to your server +3. give it executable permissions +4. run with the --headless argument +5. then do something else to keep it running in background, restarting as needed diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..1b1b188 --- /dev/null +++ b/TODO.md @@ -0,0 +1,23 @@ +# FabsocBot + +## Todo +- [x] want to redo spool management so when a spool is in a printer it leaves leaves the library - only exists in one place at a time + - [x] spool list should show loaded spools in printer, then unloaded and available spools + - [X] unloading a spool returns it to the library +- [x] shoot some of the printers can hold multiple fillaments: endermixer 2, ams: 4 +- [x] handling of spools when printer is deleted +- [x] command permissions +- [x] refactoring +- [x] documentation + - [x] readme + - [x] fabsoc docs + - [x] contributing guide + - [x] help command -> points to fabsoc docs? +- [x] offer to delete /unloaded spools +- [?] bugtesting / fixing +- [x] spool editing +- [ ] seperate place to store bot access key +- [x] update test command + +### Possible furture development +- job management diff --git a/application_cmds/help.gd b/application_cmds/help.gd new file mode 100644 index 0000000..9f303ab --- /dev/null +++ b/application_cmds/help.gd @@ -0,0 +1,62 @@ +extends RefCounted + +var help : String = ( +"meow meow! welcome to fabcat! a discord bot for Fabsoc made by Tabby <:tabby:1434381576694665378> +`Version 0.1` +## Basic Commands +This commands can be used by anyone! +- `/help` + displays this help screen +- `/printer-status` + displays the currently loaded spools and nozzle for each printer +- `/spool-list` + displays a list of both loaded and available spools + +## Technician Only Commands +Since these commands edit data, only users with the `technician` role may use them +### Printer Management +- `/printer-create ` + Creates a new printer with the provided name +- `/printer-delete ` + Deletes the printer with the matching name +- `/printer-slots ` + Changes the amount of spool slots a printer has (for example: if an AMS is attached) +- `/printer-nozzle ` + Changes the nozzle attached to the printer +- `/printer-load ` + Loads a spool into a printer, If the printer only has one spool slot and it is filled then this command will replace it and return the old spool to the library (offers option to delete old spool) +- `/printer-unload ` + Unloads the spool from the printer, If there are multiple a dropdown asks which to unload. The unloaded spool can then be kept in the library or deleted +### Spool Management +- `/spool-create [link]` + Creates a new spool with the given name and optionally a link to it +- `/spool-delete ` + Deletes the spool with the matching name, can only delete a spool if it is in the library (not loaded into a printer) +- `/spool-edit [new-link]` + Changes the name and/or link of a spool, only works if the spool is in the library (not loaded into a printer)" +) +var link : String = "\n\nInterested in improving fabcat? more information is available in the [Fabsoc docs](https://utsfabsoc.github.io/Docs/Technician-Zone/Fabcat-Discord-Bot)" + +#func on_ready(main, bot: DiscordBot) -> void: +# pass +# +#func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: +# pass + +func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: + + var response : String = "displaying help..." + var embed = Embed.new().set_description(help + link) + embed.set_color("#CB5DFF") + + interaction.reply({ + "content": response, + "embeds":[embed], + "ephemeral" : true, + }) + pass + +var data = ApplicationCommand.new()\ + .set_name("help")\ + .set_description("read the manual")\ + diff --git a/application_cmds/help.gd.uid b/application_cmds/help.gd.uid new file mode 100644 index 0000000..30e6d54 --- /dev/null +++ b/application_cmds/help.gd.uid @@ -0,0 +1 @@ +uid://cvrvkvt45ugls diff --git a/application_cmds/printer-create.gd b/application_cmds/printer-create.gd index 259833a..6e5055d 100644 --- a/application_cmds/printer-create.gd +++ b/application_cmds/printer-create.gd @@ -7,7 +7,7 @@ extends RefCounted # pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) diff --git a/application_cmds/printer-delete.gd b/application_cmds/printer-delete.gd index 5302948..ce921b4 100644 --- a/application_cmds/printer-delete.gd +++ b/application_cmds/printer-delete.gd @@ -12,30 +12,11 @@ func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, opt # The part of string which the user is typing var part = options[0].value - #print("received autocomplete: ", part) - - # The final Array of choices for the autocomplete response - var result = [] - for hint in Library.printer_choices_string(): - # If the user hasn't typed anything, add all the hints - if part == "": - result.append(ApplicationCommand.choice(hint, hint)) - else: - # If the user has typed some part of string, - # add only those hints which have the part as a substring - if hint.findn(part) > -1: - result.append(ApplicationCommand.choice(hint, hint)) - - # Limit the number of results to 25 (Discord's limit is 25) - if result.size() > 25: - result = result.slice(0, 24) - - # Respond with the results - interaction.respond_autocomplete(result) + AutocompleteTools.printer_autocomplete(part, interaction) pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) @@ -84,7 +65,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): print(interaction.data.custom_id) if(interaction.data.custom_id == "delete-printer"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return #print("deleting: " + endangered_printer) for printer in Library.save.printers: @@ -105,7 +86,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): }) elif(interaction.data.custom_id == "keep-printer"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return endangered_printer = "" var embed = Embed.new().set_description("cancelled deletion") diff --git a/application_cmds/printer-load.gd b/application_cmds/printer-load.gd index e34c0f4..804ab3d 100644 --- a/application_cmds/printer-load.gd +++ b/application_cmds/printer-load.gd @@ -19,7 +19,7 @@ func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, opt pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) @@ -117,7 +117,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): print(interaction.data.custom_id) if(interaction.data.custom_id == "delete-oldspool"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return #print("deleting: " + endangered_printer) @@ -135,7 +135,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): }) elif(interaction.data.custom_id == "keep-oldspool"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return endangered_spool = null diff --git a/application_cmds/printer-nozzle.gd b/application_cmds/printer-nozzle.gd index efde925..57c14c1 100644 --- a/application_cmds/printer-nozzle.gd +++ b/application_cmds/printer-nozzle.gd @@ -10,30 +10,11 @@ func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, opt # The part of string which the user is typing var part = options[0].value - #print("received autocomplete: ", part) - - # The final Array of choices for the autocomplete response - var result = [] - for hint in Library.printer_choices_string(): - # If the user hasn't typed anything, add all the hints - if part == "": - result.append(ApplicationCommand.choice(hint, hint)) - else: - # If the user has typed some part of string, - # add only those hints which have the part as a substring - if hint.findn(part) > -1: - result.append(ApplicationCommand.choice(hint, hint)) - - # Limit the number of results to 25 (Discord's limit is 25) - if result.size() > 25: - result = result.slice(0, 24) - - # Respond with the results - interaction.respond_autocomplete(result) + AutocompleteTools.printer_autocomplete(part, interaction) pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) diff --git a/application_cmds/printer-slots.gd b/application_cmds/printer-slots.gd index b14b1ff..b20343d 100644 --- a/application_cmds/printer-slots.gd +++ b/application_cmds/printer-slots.gd @@ -10,30 +10,11 @@ func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, opt # The part of string which the user is typing var part = options[0].value - #print("received autocomplete: ", part) - - # The final Array of choices for the autocomplete response - var result = [] - for hint in Library.printer_choices_string(): - # If the user hasn't typed anything, add all the hints - if part == "": - result.append(ApplicationCommand.choice(hint, hint)) - else: - # If the user has typed some part of string, - # add only those hints which have the part as a substring - if hint.findn(part) > -1: - result.append(ApplicationCommand.choice(hint, hint)) - - # Limit the number of results to 25 (Discord's limit is 25) - if result.size() > 25: - result = result.slice(0, 24) - - # Respond with the results - interaction.respond_autocomplete(result) + AutocompleteTools.printer_autocomplete(part, interaction) pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) diff --git a/application_cmds/printer-unload.gd b/application_cmds/printer-unload.gd index 201e5f3..ab5c067 100644 --- a/application_cmds/printer-unload.gd +++ b/application_cmds/printer-unload.gd @@ -16,7 +16,7 @@ func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, opt pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) @@ -104,7 +104,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): return if(interaction.data.custom_id == "spool-select"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(interaction.data.values[0]) @@ -135,7 +135,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): break if(interaction.data.custom_id == "delete-unloadspool"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return #print("deleting: " + endangered_printer) @@ -153,7 +153,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): }) elif(interaction.data.custom_id == "keep-unloadspool"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return endangered_spool = null diff --git a/application_cmds/spool-create.gd b/application_cmds/spool-create.gd index ed0b1ff..22440de 100644 --- a/application_cmds/spool-create.gd +++ b/application_cmds/spool-create.gd @@ -7,7 +7,7 @@ extends RefCounted # pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) diff --git a/application_cmds/spool-delete.gd b/application_cmds/spool-delete.gd index b092bc6..842cd57 100644 --- a/application_cmds/spool-delete.gd +++ b/application_cmds/spool-delete.gd @@ -12,30 +12,11 @@ func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, opt # The part of string which the user is typing var part = options[0].value - #print("received autocomplete: ", part) - - # The final Array of choices for the autocomplete response - var result = [] - for hint in Library.spool_choices_string(): - # If the user hasn't typed anything, add all the hints - if part == "": - result.append(ApplicationCommand.choice(hint, hint)) - else: - # If the user has typed some part of string, - # add only those hints which have the part as a substring - if hint.findn(part) > -1: - result.append(ApplicationCommand.choice(hint, hint)) - - # Limit the number of results to 25 (Discord's limit is 25) - if result.size() > 25: - result = result.slice(0, 24) - - # Respond with the results - interaction.respond_autocomplete(result) + AutocompleteTools.spool_autocomplete(part, interaction) pass func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return print(options) @@ -84,7 +65,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): print(interaction.data.custom_id) if(interaction.data.custom_id == "delete-spool"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return #print("deleting: " + endangered_printer) for spool in Library.save.spools: @@ -101,7 +82,7 @@ func on_interaction_create(bot: DiscordBot, interaction : DiscordInteraction): }) elif(interaction.data.custom_id == "keep-spool"): - if not Tools.check_perms(interaction): + if not Tools.check_perms(interaction): #limit access to technicians only return endangered_spool = "" var embed = Embed.new().set_description("cancelled deletion") diff --git a/application_cmds/spool-edit.gd b/application_cmds/spool-edit.gd new file mode 100644 index 0000000..8b679dd --- /dev/null +++ b/application_cmds/spool-edit.gd @@ -0,0 +1,77 @@ +extends RefCounted + + +#func on_ready(main, bot: DiscordBot) -> void: + #pass +# +func on_autocomplete(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: + #print(options) + #interaction.respond_autocomplete(Library.printer_choies()) + + # The part of string which the user is typing + var part = options[0].value + AutocompleteTools.spool_autocomplete(part, interaction) + pass + +func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Array) -> void: + if not Tools.check_perms(interaction): #limit access to technicians only + return + + print(options) + var spool_name : String = options[0].value + var new_name : String = options[1].value + var new_link : String + if options.size() > 2: + new_link = options[2].value + + var spool_exists : bool = false + + + for spool : Spool in Library.save.spools: + if spool.name == spool_name: + spool_exists = true + if new_name != "0": + spool.name = new_name + if new_link: + spool.link = new_link + #Library.save.printers.erase(printer) + + Library.save_data() + + if not spool_exists: + interaction.reply({ + "content" : "unable to find " + spool_name + }) + return + + var response : String = "finished editing `" + new_name + "`\n" + var embed = Embed.new().set_description(Library.list_spools(new_name)) + + + interaction.reply({ + "content": response, + "embeds":[embed], + }) + pass + + +var data = ApplicationCommand.new()\ + .set_name("spool-edit")\ + .set_description("edit a spool (IN THE LIBRARY)")\ + .add_option(ApplicationCommand.string_option("spool_name", "the spools name (MUST BE UNLOADED)", + { + "required":true, + "autocomplete":true, + #"choices" : Library.printer_choies() + }))\ + .add_option(ApplicationCommand.string_option("new_name","new name for the spool (type '0' to keep as is)", + { + "required" : true, + }))\ + .add_option(ApplicationCommand.string_option("new_link","new link for the spool", + { + "required" : false, + }))\ + + +## lesson learnt: option name must not have spaces diff --git a/application_cmds/spool-edit.gd.uid b/application_cmds/spool-edit.gd.uid new file mode 100644 index 0000000..1782594 --- /dev/null +++ b/application_cmds/spool-edit.gd.uid @@ -0,0 +1 @@ +uid://bp5ameady5mdn diff --git a/application_cmds/test_appcmd.gd b/application_cmds/test_appcmd.gd index ccae219..2202766 100644 --- a/application_cmds/test_appcmd.gd +++ b/application_cmds/test_appcmd.gd @@ -10,9 +10,9 @@ func execute(main, bot: DiscordBot, interaction: DiscordInteraction, options: Ar print("hiya!") interaction.reply( { - "content" : "meow meow! hearing you loud and clear tabby!" + "content" : "meow meow!" } ) pass -var data = ApplicationCommand.new().set_name("meow_test").set_description("meow_desc") +var data = ApplicationCommand.new().set_name("meow_test").set_description("meow meow meow!")