From 14185dc280d8de8896006551d1796ec602e0ffa8 Mon Sep 17 00:00:00 2001 From: connorbutsmaller Date: Fri, 14 Feb 2025 13:26:41 -0500 Subject: [PATCH 01/16] all sage setup until step 7 --- config.ts | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/config.ts b/config.ts index 8b137891..ec66e4a0 100644 --- a/config.ts +++ b/config.ts @@ -1 +1,80 @@ +export const BOT = { + TOKEN: 'df432bec2d87c3a7149e000dbb84299d7417bd6185e73477f3bdaa1fcf4e5aa2', // Bot token here + CLIENT_ID: '1340018861654806588', // Client ID here + NAME: 'Ian_Sage'// Bot Name. NEEDS TO BE LESS THAN 11 CHARACTERS +}; + +export const MONGO = ''; + +export const DB = { + CONNECTION: 'mongodb+srv://connorbutsmaller:connorbutsage@cluster0.3ps8b.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0', // Mongo connection string here + USERS: 'users', + PVQ: 'pvQuestions', + QTAGS: 'questionTags', + ASSIGNABLE: 'assignable', + COURSES: 'courses', + REMINDERS: 'reminders', + CLIENT_DATA: 'clientData', + POLLS: 'polls', + JOB_FORMS: 'jobForms' +}; + +export const GUILDS = { // Guild IDs for each guild + MAIN: '1339665566373384233', + GATEWAY: '1339665566373384233', + GATEWAY_INVITE: '1339665566373384233' +}; + +export const ROLES = { // Role IDS for each role + ADMIN: '', + STUDENT_ADMIN: '', + STAFF: '', + VERIFIED: '', + MUTED: '', + LEVEL_ONE: '' +}; + +export const EMAIL = { + SENDER: 'ianduffy@udel.edu', // The email address all emails should be sent from + REPLY_TO: 'ianduffy@udel.edu', // The replyto address for all emails + REPORT_ADDRESSES: [ // A list of all the email address to get the weekly report + 'ianduffy@udel.edu' // Add your email here + ] +}; + +export const CHANNELS = { // Channel IDs + ERROR_LOG: '1339670678214934679', + SERVER_LOG: '1339670734795964578', + MEMBER_LOG: '1339670791855538227', + MOD_LOG: '1339670814957764751', + FEEDBACK: '1339670947325546628', + SAGE: '1339670961951215678', + ANNOUNCEMENTS: '1339670980087251065', + ARCHIVE: '1339670989709115524', + ROLE_SELECT: '1339670989709115524' +}; + +export const ROLE_DROPDOWNS = { + COURSE_ROLES: '', + ASSIGN_ROLES: '' +}; + +export const LEVEL_TIER_ROLES = [ + '', + '', + '', + '', + '' +]; + +export const FIRST_LEVEL = 10; +export const GITHUB_TOKEN = ''; +export const GITHUB_PROJECT = ''; +export const PREFIX = 's;'; +export const MAINTAINERS = 'Ian S25';// The current maintainers of this bot +export const SEMESTER_ID = '';// The current semester ID. i.e. s21 +export const BLACKLIST = []; + +export const APP_ID = ''; // Adzuna API app ID +export const APP_KEY = ''; // Adzuna API key From ca574ed0043d71a81ea8b727dd7c57c85efb9ed1 Mon Sep 17 00:00:00 2001 From: John Aromando Date: Fri, 14 Feb 2025 13:55:13 -0500 Subject: [PATCH 02/16] Update package.json Gets rid of the 97 errors, update to TS version. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4e0da389..cbf41a9e 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,6 @@ "@typescript-eslint/parser": "^4.23.0", "eslint": "^7.26.0", "tsc-watch": "^4.6.2", - "typescript": "^4.9.5" + "typescript": "^5.0.2" } } From c184376efc408b11aa3ff6b306554377de69db80 Mon Sep 17 00:00:00 2001 From: John Aromando Date: Sun, 16 Feb 2025 15:12:15 -0500 Subject: [PATCH 03/16] Update package.json Configure project to use node-pre-gyp --- package.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/package.json b/package.json index cbf41a9e..950af9a9 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "main": "dist/src/sage.js", "scripts": { "start": "node dist/src/sage.js", + "install": "node-pre-gyp install --fallback-to-build", "build": "tsc -p .", "clean": "rm -rf dist", "test": "eslint src --ext .ts", @@ -18,6 +19,11 @@ "nudge": "node dist/onboard/nudge.js", "autodoc": "node dist/autodoc/writecommands.js && autodoc/movemd.sh" }, + "binary": { + "module_name": "your_module", + "module_path": "./lib/binding/", + "host": "https://your_module.s3-us-west-1.amazonaws.com" + }, "repository": { "type": "git", "url": "git+https://github.com/ud-cis-discord/SageV2.git" @@ -34,6 +40,7 @@ "canvas": "^2.11.2", "console-stamp": "^3.0.2", "discord.js": "^14.16.3", + "@discordjs/node-pre-gyp": "0.4.5", "module-alias": "^2.2.2", "moment": "^2.29.1", "mongodb": "^3.6.3", From d4fe756c751046b06d18e173f0d54c19a2e8d843 Mon Sep 17 00:00:00 2001 From: qbettin Date: Mon, 17 Feb 2025 13:08:51 -0500 Subject: [PATCH 04/16] config setup --- config.ts | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/config.ts b/config.ts index 8b137891..4088c86f 100644 --- a/config.ts +++ b/config.ts @@ -1 +1,81 @@ +export const BOT = { + TOKEN: '60d8126899463552490c8dcee88a1a025b371dcd7a8a942dc7ecdd4b577fd6ec', // Bot token here + CLIENT_ID: '1340018471676809247', // Client ID here + NAME: 'qbettin-Sage'// Bot Name +}; + +export const MONGO = ''; + +export const DB = { + CONNECTION: 'mongodb+srv://quinten:1qaz2wsx@cluster0.3ps8b.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0', // Mongo connection string here + USERS: 'users', + PVQ: 'pvQuestions', + QTAGS: 'questionTags', + ASSIGNABLE: 'assignable', + COURSES: 'courses', + REMINDERS: 'reminders', + CLIENT_DATA: 'clientData', + POLLS: 'polls', + JOB_FORMS: 'jobForms' +}; + +export const GUILDS = { // Guild IDs for each guild + MAIN: '1339665566373384233', + GATEWAY: '1339665566373384233', + GATEWAY_INVITE: '1339665566373384233' +}; + +export const ROLES = { // Role IDS for each role + ADMIN: '', + STUDENT_ADMIN: '', + STAFF: '', + VERIFIED: '', + MUTED: '', + LEVEL_ONE: '' +}; + +export const EMAIL = { + SENDER: 'pgsweet@udel.edu', // The email address all emails should be sent from + REPLY_TO: 'pgsweet@udel.edu', // The replyto address for all emails + REPORT_ADDRESSES: [ // A list of all the email address to get the weekly report + 'pgsweet@udel.edu' + ] +}; + +export const CHANNELS = { // Channel IDs + ERROR_LOG: '1339670678214934679', + SERVER_LOG: '1339670734795964578', + MEMBER_LOG: '1339670791855538227', + MOD_LOG: '1339670814957764751', + FEEDBACK: '1339670947325546628', + SAGE: '1339670961951215678', + ANNOUNCEMENTS: '1339670980087251065', + ARCHIVE: '1339670989709115524', + ROLE_SELECT: '1339671002170265650' +}; + +export const ROLE_DROPDOWNS = { + COURSE_ROLES: '', + ASSIGN_ROLES: '' +}; + +export const LEVEL_TIER_ROLES = [ + '', + '', + '', + '', + '' +]; + +export const FIRST_LEVEL = 10; +export const GITHUB_TOKEN = ''; +export const GITHUB_PROJECT = ''; +export const PREFIX = 's;'; +export const MAINTAINERS = 'Patrick Sweet';// The current maintainers of this bot +export const SEMESTER_ID = 's25';// The current semester ID. i.e. s21 +export const BLACKLIST = []; + +export const APP_ID = ''; // Adzuna API app ID +export const APP_KEY = ''; // Adzuna API key + From bb3cd80a03a6be034fbf68ffcd46d604457a55c1 Mon Sep 17 00:00:00 2001 From: John Aromando Date: Wed, 19 Feb 2025 13:12:15 -0500 Subject: [PATCH 05/16] Create install-deps-linux.sh --- install-deps-linux.sh | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 install-deps-linux.sh diff --git a/install-deps-linux.sh b/install-deps-linux.sh new file mode 100644 index 00000000..8b8a88ae --- /dev/null +++ b/install-deps-linux.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e +echo "Installing system dependencies for Linux..." +sudo apt-get update +sudo apt-get install -y libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev \ + build-essential g++ pkg-config python3 python3-pip From 2bfac06e27038fc6fa7f1c70cb630c7ecb3611e9 Mon Sep 17 00:00:00 2001 From: John Aromando Date: Wed, 19 Feb 2025 13:12:38 -0500 Subject: [PATCH 06/16] Create install-deps-mac.sh --- install-deps-mac.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 install-deps-mac.sh diff --git a/install-deps-mac.sh b/install-deps-mac.sh new file mode 100644 index 00000000..226bde1f --- /dev/null +++ b/install-deps-mac.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e +echo "Installing system dependencies for macOS..." +brew install pkg-config cairo pango jpeg giflib + +echo "Checking for binding.gyp..." +BINDING_GYP="binding.gyp" +MODULE_NAME="sage" +SRC_FILE="src/sage.ts" + +if [ ! -f "$BINDING_GYP" ]; then + echo "Creating missing binding.gyp..." + cat < $BINDING_GYP +{ + "targets": [ + { + "target_name": "$MODULE_NAME", + "sources": ["$SRC_FILE"], + } + ] +} +EOF + echo "binding.gyp created successfully." From ace6d6e3ee42a0876f09fa15075f5c230309a57c Mon Sep 17 00:00:00 2001 From: John Aromando Date: Wed, 19 Feb 2025 13:12:58 -0500 Subject: [PATCH 07/16] Create install-deps-windows.ps1 --- install-deps-windows.ps1 | 75 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 install-deps-windows.ps1 diff --git a/install-deps-windows.ps1 b/install-deps-windows.ps1 new file mode 100644 index 00000000..5ad08195 --- /dev/null +++ b/install-deps-windows.ps1 @@ -0,0 +1,75 @@ +#Runs as administrator +$adminCheck = [Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent() +if (-not $adminCheck.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Host "This script requires administrative privileges. Restarting..." + Start-Process powershell -ArgumentList "-NoExit -NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs -Wait +} + +Write-Host "Installing Windows dependencies for node-canvas and node-gyp..." + +#Check if Chocolatey is installed, install if not +if (!(Test-Path "$env:ProgramData\chocolatey\bin\choco.exe")) { + Write-Host "Installing Chocolatey..." + Set-ExecutionPolicy Bypass -Scope Process -Force + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 + Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) + RefreshEnv +} + +#Check for pending reboot BEFORE installation +$pendingReboot = Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" +if ($pendingReboot) { + Write-Host "Pending system reboot detected. Please restart your computer and rerun this script." +} + +#Enable Chocolatey to ignore pending reboots (if needed) +Write-Host "Configuring Chocolatey to ignore pending reboots..." +choco feature enable --name="exitOnRebootDetected" + +#Force reinstall Visual Studio Build Tools with necessary components +Write-Host "Installing Visual Studio 2022 tools..." +choco install python visualstudio2022-workload-vctools -y + +#Set GYP_MSVS_VERSION for node-gyp +Write-Host "Setting GYP_MSVS_VERSION for node-gyp..." +[System.Environment]::SetEnvironmentVariable("GYP_MSVS_VERSION", "2022", "Machine") + +#Install MSYS2 +Write-Host "Installing MSYS2..." +choco install -y msys2 + +#Force refresh pacman package database +Write-Host "Updating MSYS2 package database..." +& "$msys2Path" -lc "pacman -Sy --noconfirm" + +#Install required libraries using pacman +Write-Host "Installing required libraries for node-canvas..." +& "$msys2Path" -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-cairo mingw-w64-x86_64-pango mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-giflib" + +#Define the path for binding.gyp +$bindingGypPath = "$PSScriptRoot\binding.gyp" + +#Ensure binding.gyp is created without BOM +if (!(Test-Path $bindingGypPath)) { + Write-Host "Creating missing binding.gyp..." + + $bindingContent = @" +{ + "targets": [ + { + "target_name": "sage", + "sources": ["src/sage.ts"] + } + ] +} +"@ + + # Write file in UTF-8 without BOM + [System.Text.Encoding]::UTF8.GetBytes($bindingContent) | Set-Content -Path $bindingGypPath -Encoding Byte +} + +#Verify Installation +Write-Host "Verifying installed packages..." +& "$msys2Path" -lc "pacman -Q mingw-w64-x86_64-cairo mingw-w64-x86_64-pango mingw-w64-x86_64-libjpeg-turbo mingw-w64-x86_64-giflib" + +Write-Host "All dependencies installed successfully!" From 00b2ea7512d239bf358580fd75cdd294ab143a67 Mon Sep 17 00:00:00 2001 From: John Aromando Date: Wed, 19 Feb 2025 13:13:19 -0500 Subject: [PATCH 08/16] Create install-deps.js --- install-deps.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 install-deps.js diff --git a/install-deps.js b/install-deps.js new file mode 100644 index 00000000..36140120 --- /dev/null +++ b/install-deps.js @@ -0,0 +1,65 @@ +const { spawnSync } = require("child_process"); +const path = require("path"); + +const isWindows = process.platform === "win32"; + +//Function to check if running as Administrator +function isAdmin() { + if (!isWindows) return true; + const result = spawnSync("powershell.exe", [ + "-NoProfile", "-ExecutionPolicy", "Bypass", + "-Command", "([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)" + ], { encoding: "utf8" }); + + return result.stdout.trim() === "True"; +} + +if (isWindows) { + console.log("🟣 Installing Windows dependencies..."); + + if (!isAdmin()) { + console.log("🔴 Not running as admin. Restarting as administrator..."); + + //Relaunch as admin to keep window open + const scriptPath = path.join(__dirname, "install-deps-windows.ps1"); + const result = spawnSync("powershell.exe", [ + "-NoProfile", "-ExecutionPolicy", "Bypass", + "-Command", `Start-Process powershell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -File \\"${scriptPath}\\"' -Verb RunAs -Wait` + ], { stdio: "inherit", shell: true }); + + if (result.error) { + console.error("⚠️ Failed to elevate PowerShell:", result.error); + process.exit(1); + } + + console.log("✅ Dependency installation completed!"); + process.exit(result.status); + } + + //Run PowerShell script synchronously (when already elevated) + const scriptPath = path.join(__dirname, "install-deps-windows.ps1"); + const result = spawnSync("powershell.exe", [ + "-NoProfile", "-ExecutionPolicy", "Bypass", + "-File", scriptPath + ], { stdio: "inherit", shell: true }); + + if (result.status !== 0) { + console.error("⚠️ PowerShell script failed with exit code:", result.status); + process.exit(1); + } + + console.log("✅ Windows dependency installation complete."); + +} else if (os === "linux") { + console.log("🟢 Installing Linux dependencies..."); + spawnSync("bash", ["./install-deps-linux.sh"], { stdio: "inherit" }); + +} else if (os === "darwin") { + console.log("🍎 Installing macOS dependencies..."); + spawnSync("bash", ["./install-deps-mac.sh"], { stdio: "inherit" }); + +} else { + console.log("❌ Unsupported OS detected."); + process.exit(1); + +} From f33908abe65698a168c830a13ae253b72f00078e Mon Sep 17 00:00:00 2001 From: John Aromando Date: Wed, 19 Feb 2025 13:13:33 -0500 Subject: [PATCH 09/16] Update package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 950af9a9..270b55ac 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ "description": "A purpose build Discord bot to manage the UD CIS Discord server.", "main": "dist/src/sage.js", "scripts": { + "preinstall": "node install-deps.js", "start": "node dist/src/sage.js", - "install": "node-pre-gyp install --fallback-to-build", + "install": "node-pre-gyp install --fallback-to-build --build-from-source", "build": "tsc -p .", "clean": "rm -rf dist", "test": "eslint src --ext .ts", From b7865559fb0d9279e761d9d09b4cad77d4b68d4b Mon Sep 17 00:00:00 2001 From: connorbutsmaller <112649955+connorbutsmaller@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:18:36 -0500 Subject: [PATCH 10/16] removed ian from config.ts --- config.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config.ts b/config.ts index ec66e4a0..23ff9d74 100644 --- a/config.ts +++ b/config.ts @@ -1,13 +1,13 @@ export const BOT = { - TOKEN: 'df432bec2d87c3a7149e000dbb84299d7417bd6185e73477f3bdaa1fcf4e5aa2', // Bot token here - CLIENT_ID: '1340018861654806588', // Client ID here - NAME: 'Ian_Sage'// Bot Name. NEEDS TO BE LESS THAN 11 CHARACTERS + TOKEN: '', // Bot token here + CLIENT_ID: '', // Client ID here + NAME: ''// Bot Name. NEEDS TO BE LESS THAN 11 CHARACTERS }; export const MONGO = ''; export const DB = { - CONNECTION: 'mongodb+srv://connorbutsmaller:connorbutsage@cluster0.3ps8b.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0', // Mongo connection string here + CONNECTION: '', // Mongo connection string here USERS: 'users', PVQ: 'pvQuestions', QTAGS: 'questionTags', @@ -35,10 +35,10 @@ export const ROLES = { // Role IDS for each role }; export const EMAIL = { - SENDER: 'ianduffy@udel.edu', // The email address all emails should be sent from - REPLY_TO: 'ianduffy@udel.edu', // The replyto address for all emails + SENDER: '', // The email address all emails should be sent from + REPLY_TO: '', // The replyto address for all emails REPORT_ADDRESSES: [ // A list of all the email address to get the weekly report - 'ianduffy@udel.edu' // Add your email here + '' // Add your email here ] }; @@ -71,7 +71,7 @@ export const FIRST_LEVEL = 10; export const GITHUB_TOKEN = ''; export const GITHUB_PROJECT = ''; export const PREFIX = 's;'; -export const MAINTAINERS = 'Ian S25';// The current maintainers of this bot +export const MAINTAINERS = '';// The current maintainers of this bot export const SEMESTER_ID = '';// The current semester ID. i.e. s21 export const BLACKLIST = []; From 3880ffe215f3e997ea16c76703f10525321e4fce Mon Sep 17 00:00:00 2001 From: John Aromando Date: Wed, 19 Feb 2025 14:15:13 -0500 Subject: [PATCH 11/16] Update install-deps-mac.sh --- install-deps-mac.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install-deps-mac.sh b/install-deps-mac.sh index 226bde1f..92aa6c2e 100644 --- a/install-deps-mac.sh +++ b/install-deps-mac.sh @@ -15,9 +15,10 @@ if [ ! -f "$BINDING_GYP" ]; then "targets": [ { "target_name": "$MODULE_NAME", - "sources": ["$SRC_FILE"], + "sources": ["$SRC_FILE"] } ] } EOF echo "binding.gyp created successfully." +fi From c1dbd8b946c70d7b8c1ce1701edc59aa28bd6c5f Mon Sep 17 00:00:00 2001 From: John Aromando Date: Thu, 20 Feb 2025 21:11:46 -0500 Subject: [PATCH 12/16] Update install-deps.js --- install-deps.js | 1 + 1 file changed, 1 insertion(+) diff --git a/install-deps.js b/install-deps.js index 36140120..9597137b 100644 --- a/install-deps.js +++ b/install-deps.js @@ -1,5 +1,6 @@ const { spawnSync } = require("child_process"); const path = require("path"); +const os = process.platform; const isWindows = process.platform === "win32"; From 2d8f5a07a76f17e0ee4f5abf9f43b7a84007c820 Mon Sep 17 00:00:00 2001 From: Quinten Bettin <122491208+qbettin@users.noreply.github.com> Date: Mon, 17 Mar 2025 12:36:52 -0400 Subject: [PATCH 13/16] Delete config.ts --- config.ts | 81 ------------------------------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 config.ts diff --git a/config.ts b/config.ts deleted file mode 100644 index 4088c86f..00000000 --- a/config.ts +++ /dev/null @@ -1,81 +0,0 @@ -export const BOT = { - TOKEN: '60d8126899463552490c8dcee88a1a025b371dcd7a8a942dc7ecdd4b577fd6ec', // Bot token here - CLIENT_ID: '1340018471676809247', // Client ID here - NAME: 'qbettin-Sage'// Bot Name -}; - -export const MONGO = ''; - -export const DB = { - CONNECTION: 'mongodb+srv://quinten:1qaz2wsx@cluster0.3ps8b.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0', // Mongo connection string here - USERS: 'users', - PVQ: 'pvQuestions', - QTAGS: 'questionTags', - ASSIGNABLE: 'assignable', - COURSES: 'courses', - REMINDERS: 'reminders', - CLIENT_DATA: 'clientData', - POLLS: 'polls', - JOB_FORMS: 'jobForms' -}; - -export const GUILDS = { // Guild IDs for each guild - MAIN: '1339665566373384233', - GATEWAY: '1339665566373384233', - GATEWAY_INVITE: '1339665566373384233' -}; - -export const ROLES = { // Role IDS for each role - ADMIN: '', - STUDENT_ADMIN: '', - STAFF: '', - VERIFIED: '', - MUTED: '', - LEVEL_ONE: '' -}; - -export const EMAIL = { - SENDER: 'pgsweet@udel.edu', // The email address all emails should be sent from - REPLY_TO: 'pgsweet@udel.edu', // The replyto address for all emails - REPORT_ADDRESSES: [ // A list of all the email address to get the weekly report - 'pgsweet@udel.edu' - ] -}; - -export const CHANNELS = { // Channel IDs - ERROR_LOG: '1339670678214934679', - SERVER_LOG: '1339670734795964578', - MEMBER_LOG: '1339670791855538227', - MOD_LOG: '1339670814957764751', - FEEDBACK: '1339670947325546628', - SAGE: '1339670961951215678', - ANNOUNCEMENTS: '1339670980087251065', - ARCHIVE: '1339670989709115524', - ROLE_SELECT: '1339671002170265650' -}; - -export const ROLE_DROPDOWNS = { - COURSE_ROLES: '', - ASSIGN_ROLES: '' -}; - -export const LEVEL_TIER_ROLES = [ - '', - '', - '', - '', - '' -]; - -export const FIRST_LEVEL = 10; -export const GITHUB_TOKEN = ''; -export const GITHUB_PROJECT = ''; -export const PREFIX = 's;'; -export const MAINTAINERS = 'Patrick Sweet';// The current maintainers of this bot -export const SEMESTER_ID = 's25';// The current semester ID. i.e. s21 -export const BLACKLIST = []; - -export const APP_ID = ''; // Adzuna API app ID -export const APP_KEY = ''; // Adzuna API key - - From fb9c87b6b8f426a14b2178ae2656128c13463c18 Mon Sep 17 00:00:00 2001 From: qbettin Date: Mon, 17 Mar 2025 12:45:10 -0400 Subject: [PATCH 14/16] changes --- .gitignore | 1 + src/pieces/blacklist.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 23593b77..b6a7618b 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,4 @@ dist # TernJS port file .tern-port +config.ts diff --git a/src/pieces/blacklist.ts b/src/pieces/blacklist.ts index 6f698939..36257c21 100644 --- a/src/pieces/blacklist.ts +++ b/src/pieces/blacklist.ts @@ -88,7 +88,7 @@ async function register(bot: Client): Promise { if (msg.partial) { msg = await msg.fetch(); } - msg = msg as Message; + msg = msg as Message; filterMessages(msg).catch(async error => bot.emit('error', error)); }); From 347d4a3cdaa011af3b88398ee2ecd86152cecf57 Mon Sep 17 00:00:00 2001 From: qbettin Date: Tue, 18 Mar 2025 14:19:31 -0400 Subject: [PATCH 15/16] notes command for fetchinig file given a course name --- src/commands/lecturehelper/notes.ts | 83 +++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/commands/lecturehelper/notes.ts diff --git a/src/commands/lecturehelper/notes.ts b/src/commands/lecturehelper/notes.ts new file mode 100644 index 00000000..dc8ba39c --- /dev/null +++ b/src/commands/lecturehelper/notes.ts @@ -0,0 +1,83 @@ +import { ApplicationCommandOptionData, ApplicationCommandOptionType, ChatInputCommandInteraction, EmbedBuilder, InteractionResponse } from 'discord.js'; +import { Command } from '@lib/types/Command'; +import axios from 'axios'; + +export default class extends Command { + description = 'Fetch the latest file from a Canvas course'; + runInDM?: true; + options: ApplicationCommandOptionData[] = [ + { + name: 'course', + description: 'The name of the course', + type: ApplicationCommandOptionType.String, + required: true + } + ]; + + async run(interaction: ChatInputCommandInteraction): Promise | void> { + const courseName = interaction.options.getString('course'); + console.log(`Received course name: ${courseName}`); + + const canvasToken = '25~n29E3YGf3YD6rtGxyTWy7MkFrehA7UwZVk3xmvaUN7mGtz9UJTYTuH4EtwQANVE8'; + const baseUrl = 'https://udel.instructure.com/api/v1/courses?page=1&per_page=100'; + + try { + // Step 1: Fetch courses with pagination parameters + console.log('Fetching courses from Canvas with page=1 and per_page=100...'); + const response = await axios.get(baseUrl, { + headers: { Authorization: `Bearer ${canvasToken}` } + }); + + const allCourses = response.data; + console.log(`Fetched ${allCourses.length} courses`); + + // Step 2: Find the best-matching course ID + console.log(courseName); + const matchedCourse = allCourses.find(course => + (course.name?.toLowerCase() ?? "").includes(courseName.toLowerCase()) + ); + + if (!matchedCourse) { + console.log(`No matching course found for: ${courseName}`); + return interaction.reply({ content: `No course found matching "${courseName}".`, ephemeral: true }); + } + + const courseId = matchedCourse.id; + console.log(`Matched course: ${matchedCourse.name} (ID: ${courseId})`); + + // Step 3: Fetch files for the matched course + console.log(`Fetching files for course ID: ${courseId}`); + const filesResponse = await axios.get(`https://udel.instructure.com/api/v1/courses/${courseId}/files`, { + headers: { Authorization: `Bearer ${canvasToken}` } + }); + + const files = filesResponse.data; + console.log(`Fetched ${files.length} files from course`); + + if (files.length === 0) { + return interaction.reply({ content: 'No files found for this course.', ephemeral: true }); + } + console.log(files) + + // Step 4: Get the first file's public URL + console.log(`Fetching file URL for: ${files[0].display_name}`); + const fileUrlResponse = await axios.get(files[0].url, { + headers: { Authorization: `Bearer ${canvasToken}` } + }); + + console.log("File URL retrieved:", fileUrlResponse.config.url); + + // Step 5: Reply with the first file's link + const embed = new EmbedBuilder() + .setColor('#3CD6A3') + .setTitle(files[0].display_name) + .setDescription(`[Download File](${fileUrlResponse.config.url})`); + + return interaction.reply({ embeds: [embed], ephemeral: true }); + + } catch (error) { + console.error('Error fetching course files:', error.response ? error.response.data : error.message); + return interaction.reply({ content: 'Failed to fetch course files.', ephemeral: true }); + } + } +} From 56589a627cf79b6c7b63bd57b085f613330fe807 Mon Sep 17 00:00:00 2001 From: qbettin Date: Wed, 2 Apr 2025 19:32:38 -0400 Subject: [PATCH 16/16] course dropdown functionality with start of search term stuff --- src/commands/lecturehelper/notes.ts | 214 ++++++++++++++++++---------- 1 file changed, 136 insertions(+), 78 deletions(-) diff --git a/src/commands/lecturehelper/notes.ts b/src/commands/lecturehelper/notes.ts index dc8ba39c..634c83b1 100644 --- a/src/commands/lecturehelper/notes.ts +++ b/src/commands/lecturehelper/notes.ts @@ -1,83 +1,141 @@ -import { ApplicationCommandOptionData, ApplicationCommandOptionType, ChatInputCommandInteraction, EmbedBuilder, InteractionResponse } from 'discord.js'; +import { + ApplicationCommandOptionData, + ApplicationCommandOptionType, + ChatInputCommandInteraction, + EmbedBuilder, + ActionRowBuilder, + StringSelectMenuBuilder, + StringSelectMenuInteraction, + Client, + Interaction +} from 'discord.js'; import { Command } from '@lib/types/Command'; import axios from 'axios'; export default class extends Command { - description = 'Fetch the latest file from a Canvas course'; - runInDM?: true; - options: ApplicationCommandOptionData[] = [ - { - name: 'course', - description: 'The name of the course', - type: ApplicationCommandOptionType.String, - required: true - } - ]; - - async run(interaction: ChatInputCommandInteraction): Promise | void> { - const courseName = interaction.options.getString('course'); - console.log(`Received course name: ${courseName}`); - - const canvasToken = '25~n29E3YGf3YD6rtGxyTWy7MkFrehA7UwZVk3xmvaUN7mGtz9UJTYTuH4EtwQANVE8'; - const baseUrl = 'https://udel.instructure.com/api/v1/courses?page=1&per_page=100'; - - try { - // Step 1: Fetch courses with pagination parameters - console.log('Fetching courses from Canvas with page=1 and per_page=100...'); - const response = await axios.get(baseUrl, { - headers: { Authorization: `Bearer ${canvasToken}` } - }); - - const allCourses = response.data; - console.log(`Fetched ${allCourses.length} courses`); - - // Step 2: Find the best-matching course ID - console.log(courseName); - const matchedCourse = allCourses.find(course => - (course.name?.toLowerCase() ?? "").includes(courseName.toLowerCase()) - ); - - if (!matchedCourse) { - console.log(`No matching course found for: ${courseName}`); - return interaction.reply({ content: `No course found matching "${courseName}".`, ephemeral: true }); - } - - const courseId = matchedCourse.id; - console.log(`Matched course: ${matchedCourse.name} (ID: ${courseId})`); - - // Step 3: Fetch files for the matched course - console.log(`Fetching files for course ID: ${courseId}`); - const filesResponse = await axios.get(`https://udel.instructure.com/api/v1/courses/${courseId}/files`, { - headers: { Authorization: `Bearer ${canvasToken}` } - }); - - const files = filesResponse.data; - console.log(`Fetched ${files.length} files from course`); - - if (files.length === 0) { - return interaction.reply({ content: 'No files found for this course.', ephemeral: true }); - } - console.log(files) - - // Step 4: Get the first file's public URL - console.log(`Fetching file URL for: ${files[0].display_name}`); - const fileUrlResponse = await axios.get(files[0].url, { - headers: { Authorization: `Bearer ${canvasToken}` } - }); - - console.log("File URL retrieved:", fileUrlResponse.config.url); - - // Step 5: Reply with the first file's link - const embed = new EmbedBuilder() - .setColor('#3CD6A3') - .setTitle(files[0].display_name) - .setDescription(`[Download File](${fileUrlResponse.config.url})`); - - return interaction.reply({ embeds: [embed], ephemeral: true }); - - } catch (error) { - console.error('Error fetching course files:', error.response ? error.response.data : error.message); - return interaction.reply({ content: 'Failed to fetch course files.', ephemeral: true }); - } - } + description = 'Fetch the latest file from a Canvas course'; + runInDM?: true; + options: ApplicationCommandOptionData[] = [ + { + name: 'search_term', + description: 'Search term to filter files', + type: ApplicationCommandOptionType.String, // Required string option for search term + required: true + } + ]; + + async run(interaction: ChatInputCommandInteraction): Promise { + const canvasToken = '25~n29E3YGf3YD6rtGxyTWy7MkFrehA7UwZVk3xmvaUN7mGtz9UJTYTuH4EtwQANVE8'; + const baseUrl = 'https://udel.instructure.com/api/v1/courses?page=1&per_page=100'; + + try { + // Get search term from interaction + const searchTerm = interaction.options.getString('search_term'); + console.log('Search Term:', searchTerm); // Log the search term + + // No longer ephemeral, so remove the `ephemeral: true` here + await interaction.deferReply(); + + console.log('Fetching all courses...'); + const response = await axios.get(baseUrl, { + headers: { Authorization: `Bearer ${canvasToken}` } + }); + const allCourses = response.data; + console.log(`Fetched ${allCourses.length} courses`); + + const validCourses = []; + for (const course of allCourses) { + const enrollmentUrl = `https://udel.instructure.com/api/v1/courses/${course.id}/enrollments?type[]=StudentEnrollment&include[]=enrollments&page=1&per_page=1`; + try { + await axios.get(enrollmentUrl, { + headers: { Authorization: `Bearer ${canvasToken}` } + }); + validCourses.push({ id: course.id, name: course.name }); + } catch (error) { + if (error.response?.status !== 403) { + console.error(`Error checking enrollment for course ${course.id}:`, error.message); + } + } + } + + if (validCourses.length === 0) { + await interaction.editReply({ content: 'No active courses found.' }); + return; + } + + const courseOptions = validCourses.map(course => ({ + label: course.name, + value: course.id.toString() + })); + + const selectMenu = new StringSelectMenuBuilder() + .setCustomId('course_select') + .setPlaceholder('Select a course') + .addOptions(courseOptions); + + const row = new ActionRowBuilder().addComponents(selectMenu); + await interaction.editReply({ content: 'Select a course:', components: [row] }); + + // Pass the searchTerm along with the interaction handler + // Now call setupInteractionHandler here by passing the client and searchTerm + setupInteractionHandler(interaction.client, searchTerm); + + } catch (error) { + console.error('Error fetching courses:', error.response ? error.response.data : error.message); + await interaction.editReply({ content: 'Failed to fetch courses.' }); + } + } +} + +export async function handleCourseSelection(interaction: StringSelectMenuInteraction, searchTerm: string) { + try { + // Make this reply visible to the chat (no ephemeral) + await interaction.deferReply(); + + console.log('Search Term inside handleCourseSelection:', searchTerm); // Log search term + + const courseId = interaction.values[0]; + const canvasToken = '25~n29E3YGf3YD6rtGxyTWy7MkFrehA7UwZVk3xmvaUN7mGtz9UJTYTuH4EtwQANVE8'; + const filesUrl = `https://udel.instructure.com/api/v1/courses/${courseId}/files`; + + console.log(`Fetching files for course ID: ${courseId}`); + console.log(`Search Term: ${searchTerm}`); // Log search term with each file fetch + const filesResponse = await axios.get(filesUrl, { + headers: { Authorization: `Bearer ${canvasToken}` } + }); + + const files = filesResponse.data; + console.log(`Fetched ${files.length} files from course`); + + if (files.length === 0) { + await interaction.editReply({ content: 'No files found for this course.' }); + return; + } + + const fileUrlResponse = await axios.get(files[0].url, { + headers: { Authorization: `Bearer ${canvasToken}` } + }); + + const embed = new EmbedBuilder() + .setColor('#3CD6A3') + .setTitle(files[0].display_name) + .setDescription(`[Download File](${fileUrlResponse.config.url})`); + + console.log("Sending embed response..."); + await interaction.editReply({ embeds: [embed] }); + + } catch (error) { + console.error('Error fetching course files:', error.response ? error.response.data : error.message); + await interaction.editReply({ content: 'Failed to fetch course files.' }); + } +} + +// Ensure the bot listens for the course selection interaction +// Now accepts the client instance +export function setupInteractionHandler(client: Client, searchTerm?: string) { + client.on('interactionCreate', async (interaction: Interaction) => { + if (interaction.isStringSelectMenu() && interaction.customId === 'course_select') { + await handleCourseSelection(interaction as StringSelectMenuInteraction, searchTerm); // Pass search term to the handler + } + }); }