break up library, move bots to their own repositories
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,3 +4,7 @@ node_modules
|
||||
packages/**/coverage
|
||||
*.tsbuildinfo
|
||||
.vscode/settings.json
|
||||
data
|
||||
db/
|
||||
coverage/
|
||||
logs/
|
||||
3
.husky/pre-commit
Normal file
3
.husky/pre-commit
Normal file
@@ -0,0 +1,3 @@
|
||||
bun test
|
||||
bun run encrypt
|
||||
git add **/.env*
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
@star-kitten:registry=https://git.f302.me/api/packages/jb/npm/
|
||||
23
package.json
23
package.json
@@ -1,26 +1,23 @@
|
||||
{
|
||||
"name": "star-kitten",
|
||||
"version": "0.0.0",
|
||||
"description": "Star Kitten framework",
|
||||
"name": "@star-kitten/monorepo",
|
||||
"version": "0.0.1",
|
||||
"description": "The Star Kitten framework",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "bun --filter '*' build",
|
||||
"co-dev": "bun --filter '!star-kitten' dev",
|
||||
"sk-dev": "bun --filter '!concierge-bot' dev",
|
||||
"link": "bun --filter '*' link",
|
||||
"dev": "bun --filter '*' dev",
|
||||
"test": "bun --filter '*' test",
|
||||
"encrypt": "bun --filter '*' encrypt",
|
||||
"decrypt": "bun --filter '*' decrypt"
|
||||
"decrypt": "bun --filter '*' decrypt",
|
||||
"typecheck": "bun --filter '*' typecheck",
|
||||
"release": "bun --filter '*' release",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ghooks": "^2.0.4",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"config": {
|
||||
"ghooks": {
|
||||
"pre-commit": "bun run encrypt && git add **/.env*"
|
||||
}
|
||||
"husky": "^9.1.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY_DEVELOPMENT="02572da3d4f3a844588a944214c0e142a5a01deaa6551456af146d34b574024416"
|
||||
|
||||
# .env.development
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY="02292a330aa041b5f7efc51504e0c208accba67a6877a217ab43cbb59c3c0c3e66"
|
||||
|
||||
# .env
|
||||
DISCORD_BOT_TOKEN="encrypted:BHWtrKfWm5sdv5MwMVnvwaqsgGlUXQ35/Dxhu+6NqWSdwa0pesu8EmXMZr64fLXEMzsSpbH0rDTj3NUe64jbGyxL2f9LRbMWExj8o7rz9qeM2vdXCBAiOWPYOqLiqqhKvBRvpGuFCZlMeA7dsLxr0t4RPzPbAAUINaLKMmyxTVRsLJO7NVcMK5u/0FczmjoLkz2+woka9RcVZoL2XRMfWM4uLKm8WMrccg=="
|
||||
DEBUG="encrypted:BFqmdpn+2mZ4wU0KT5qhlRJ6J/TMKqhDjiCGEYIdOM9O8CBaSRO5dAduODmr3k/PR3/L9ruk81q0Na4DR0JnnArNB9+mUP9tUkCC/KKBWkQJenwaUpxxZ/Xs1WbTmKCiquTWRVE="
|
||||
PORT="encrypted:BB7uSrGyN0EL1gv/JLl9ltjadMqLLnKg7p6XAS1z96Qt1z+/KuwQ8EQ4BQY+dx19Q4H443vtIWmNSLPWTbZI1Swj6xBwirZ8VmitlnazSYxotkz2nq2sdvALQH5mQlj0iZ0NHLQ="
|
||||
NODE_ENV="encrypted:BFCK2qYG9T55sxJPTGx+xqeGCFx2EPSvnzwX5PM8CoHGmN+6X5Irj1/PZ9m1QbFtQAibDj0CH/i1yHQQ9he4vN0RUqq7uGXsIbSsApvRYSKPaIdi7DKOhMwYRMCd1a4CjHemt2w1n1BizUxf"
|
||||
LOG_LEVEL="encrypted:BFYODyDKQ+uvLGCOk7eZQ8uZJdfI4iy/wUJeC2rNGHJYxIK/2icQ3LvV04DlD8a75kg5zUKC951U0nqT/8vmlp3BCso7CYvOAXISggfMBwFXIaBh2UrDHGOIWDMQWPqjcziCnucj"
|
||||
BASE_URL="encrypted:BNJ+cwBs/2His4iT0aPhsTgxTqkOH28DNsZZ1ctf/Ru4TCr4Q28d9jE/5KSkBlYpWOBItRJ/PK28uJVSdwFuPiCPrpycniLccWS3GvcQFXrMLsfvc9Jo0PoXAFtpmbivlDa7rK6Zhe2y7/16V112hXdeWDGFWlYO9N/iQQM2Vg=="
|
||||
EVE_CLIENT_ID="encrypted:BFv8tNHSS6tb8cWDpnhfC393UcKJZsRgvBTbx4l36+lN3m3oSNmouzRa/KPIXglqkAujt+OyzTyxQ59XxaQ/+ORi6nPNVMr7xDsWwpp24iz509qReOKlTqwk4y2PQ7xVbMskJD65GTg2ZyRwtqc0klBjvvF9frDzlitRpUiL06cS"
|
||||
EVE_CLIENT_SECRET="encrypted:BJIPWclcu2Ok/PQSI8evLfxP7Ze3ULQGoluCCjTQORfzqb8IixKty6lpQurFmt6ez4p1rlf8s73zRjaI6jYKrnPhZ+4sFYfgrCCZUUObsVctFoR7REvYYXD/k/mr5ryRCkR1L7NWihPyQ9fgNViWb+sQPtayf+WrArbqxYRRI5vDD2MMEbakxRCEpg=="
|
||||
EVE_CALLBACK_URL="encrypted:BGiF/m1Zd3jL4Q/p58mE9XUIGoL+Cc7wpxX6PHKt5L0uSat6X74pXC11y0IcO77jSKmpcQ1Zj/pXMz3YLCbHK7toqucZlvTatvka+ad0YAylrLwUBCYeamHmi2CU17qJSPqD9E6Q33yTvqFqf3zBiyZfI8L/PbH9+ok7e8RP+L4xsQ8Jx3piFjS0lFmOC3iznw=="
|
||||
ESI_USER_AGENT="encrypted:BO8zPPwVUKscBZzw8RcePwvuWkYjdCgz9vGrvhFzNJNCNthij3rhN7F5hK1UGPl6RPLOssV1Ke681e6rjldFdi3S1BxWeKQo8QOdjkyHTjmGbOdHoDd6QesMIsx+K9bwJvPl+XFhh+LPnIDzRPEQnTmGOTbvMsxGW9IkJDrm0Oq/aNdO6Blyh4GKzMyW2elgt+s+39/UT5nD2FCe5MlMdLk1/2z8lVV363MfZP1k8qUrbKAvdnZOLJWoWG6Nlg0vDg=="
|
||||
JANICE_KEY="encrypted:BPTKebIZjiHeXHafRCIi7fX2DVYPz5ZwIpPX2GtINQEfrI7DYHhG2NMsxlLaiSFmaGcYA7GAK0DGXt9yTJpIGUBkg73bTE1Pbh1DgtVbpK74xFDbfA7748toAWypDz0B+erh6k6hzz7TywMe+fc80r+OWmQkTpz5pgwg7LaqmT0x"
|
||||
PERPLEXITY_API_KEY="encrypted:BOzm0zV+Nl0sHq9KvoazlqbpgFwj8Gy2crn64Qw5+t4X51OWi1HbCPR82hxuH3CDRKJyuTQMG06s0Z6ovV0N3kMheFlX9qK0SogYympxtVcPd8723IWy6Nr1IioNcajfNW4S38ruRhcrmU4/8rddpyEiWMPB8/Ewb6ycfrnfWHvul4ZqBg6H60IbLDxOQ6AZPWZjRAEy"
|
||||
STAR_KITTEN_KV_DB_PATH="encrypted:BKCUDzngHkpO2Dtqzh++hCsEAAzWfabiF/NdtnP1BzwSN84Y2I+PjUp4mdAiDgPrUWgi3BFS74kZtsutzEF3h3rNi/doKZyOU1cY1DY6aBbQ+WVWPKDiAcENU1GgUp8sUezJNbZ7GXI3Qhnpkz4i"
|
||||
SCOPES=encrypted:BNLqmvLSqQFAMExxaGcx9zUWErl/UO+cbQXOYeQVDgR9JJyo1D8aaPfy06uQDlR+Euv3hy/1NL6tx75KMVChR2p+2fhVcKEC43fx3rt1JxuOvoWck81r6kv/yu5v+YUkw1BVcQNvNp4iiqqOifrro1bylyZkOML6D1DYu7Ns+6srYI4MbJGTDacV6eyRFnGcPdbxCyIophCDEP2iXw==
|
||||
CONCIERGE_DB_PATH="encrypted:BD4/iLtpn7+sqAm/Lm9CSA6ur1AjEZRaYCSy/Z9qCZMPCtoPIeHfCjYs+s+A9PBCxezqJikxbMS8IPhR0EKWQTDzeBsjHRpWGA9mJGKuUYBDtGySH+WQG6+3TEEMU/uqwpLwEUtaULKF9khJdnHlyr5znz5gSw=="
|
||||
ADMIN_USER_ID="encrypted:BFM6XD3HUmF0to4PO8RlGG71OF5aDoV4N3E34/HGwV8Rq4NiIJ+wtxLag8tWjaeIYkTgbknlE8GHiXmJUJMc8eEVhIxNFCC0xmlSJe2NipTLSBbTilfSQPOexURnNXE2I/u+3+il2QvGP7ziReuNM5QiWQ=="
|
||||
@@ -1,23 +0,0 @@
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY_PRODUCTION="02f0469506f6722d8fcc179c199ff159ca32f082000c8e7a1465891adb50a4c031"
|
||||
|
||||
# .env.production
|
||||
DEBUG="encrypted:BK1CLEV0fBG5FnYN581CvgIftBf431wzAqxR6CTQdkpY/FSbLpNtm+7sBCD8pdfOGzqlWO6Vp0WPkd56kSYgFSWyChBiWipUIou6lva/1mzIPW0v/s53azEdPlo2k3VpxVcVSWC8"
|
||||
PORT="encrypted:BIxJSVnlk4yf4r5Yrs3emdnRwO2MuH3YS8cRedHKOpTWtwmxFUyRpr7Psn8McOqhVX1pznNrqbnP8LYAjfmuesYZeon8khD0D2OXepb53J3y+7jf5uoOsNOgTHobH3B/djqKEzY="
|
||||
NODE_ENV="encrypted:BGwvbXEi7Zx49wiBZNE+05VHntm/2zIM3mCQE31J4sthkvW3q5KRR5gPmjyzkzN02juqWC/6lCF6ar73nsIwLzDKZabZiYCxEMcn3B/QYPQ1AiTguu8lVPZWTEdRA2S6kFsiKoV9Rfokry8="
|
||||
LOG_LEVEL="encrypted:BPyfUBe3+m9DP+S48GN9QUNedicrn0ICcUYzQeSfBO25NU60lGRLfyx+iPY1Cn3UmH6izR4V+fJ9UedowHRQYrivAzhk2GUa8p4wbld9HXp2kyo3jZpQXzzUOGtf1cWRSdGr0r4="
|
||||
BASE_URL="encrypted:BAHgrTREjKsjKJCogVttpFTOV84iKFzz8+NAtW9+FEDyZlZPixPYoDYRg2qG+7aOs/YNK7GaxpE6gGyV5EIit9l3V8Fb+p157Ra4tdEevF4xmNZcjnjB6GN2fGDNtTddFwMl71X2NcopxwV5auMhQdC14hk9UIkO"
|
||||
DISCORD_APP_ID="encrypted:BJE5LiHHvSEbz500t8+cew4UPf7khvKKFYy5rlfXeTKahQ/eM94e7MDO3nxMZqnlQ9OO0Ge7N3yOvJEb5x/hKlhqwLJo9sKbVHWS6bKnH9/Ft64rcN+g4pSZJeQBG1jFg/zyPJQq4tVyg7C4htnqQU4TiEI="
|
||||
DISCORD_APP_SECRET="encrypted:BGPATPBLvmFcnZSlyUSkT4hrnM4cH56f92/kQgOHDN+5XvSYq5RFyu0soM6Q6/0wCLb92sdVfKbc5x//84k/im245OCmpsKc7RCMoJNlJ/72uEsFQEADxglYrpevy1gVMI7Rs621gEfaVuoJn5AcQDwpX685ydnlr6Ev0vRtzfLD"
|
||||
DISCORD_PUBLIC_KEY="encrypted:BF3QEgKgm86nnO5LegRHlo/Jr/PNpBoY1BMFNgCJ4LdHTw+XkF//JISwQWxaU0aswwYa4duMSUYjc9DMs/mc1yLpf1DZ2R58X86vTcjCX0SiTiLFhYOpQf0JA+3HfIB/4j7GzM1D99DNK8ZsnzY/YXzHAc+R4aRwl1uu8AlGgtZpv7kgu0QjTNJshz9TUoGUROwbUD7RZABZwom0PYW2Wbw="
|
||||
DISCORD_BOT_TOKEN="encrypted:BMGsG954WBMxPgMgeJX5B9FZmJk4oPrGkDJXPaOfXnqkrOaaSP8Oq2tD+m2Pzfwbkla3xoWp2a3863lumgIsOcjoyFT0jj51tyC7CL9pbLmhLqOmnUwGjl9Vv8MHA369WUFwqm0Sum7bUD4HnMrc5Q+qEKF8zut96hTYmZWn6ACqmoxHqxEp9393NFeUzXUD5tdUhkFCYkxFGvBoy1l/EX7x0aSCkB1iiQ=="
|
||||
EVE_CLIENT_ID="encrypted:BK738Cb0M9aVcW49g7MR9YjjyLXn/if25nCjEBVoT5/+9y6seI2BwxsnXIidQ2253NyuZLzJCf8ual+HN5ouwO4r4ckqKYnk7SwDqj6yTuB47yAqgJDi8SnVManX+oRz6URNiSD7eUTYFxPHfC5XdCYFjMOtGDvLndEbqOiKSvGT"
|
||||
EVE_CLIENT_SECRET="encrypted:BBZdQ+o77YtfV/ic+dfx9nORoFs8iIRwQxToUN65I310uhwMeBWbgjJFu3P1pkR5niHxjkQkyBmEWCBKFNsmNZh/zHyjMpXu5ORIsTpDc45ivu96Cuw5PbtnD+3DwAIdJQIjQR9ufYMIdI6DsrEscJg/XX7Yqk703g0s5e4ODm+jDVlLS6maRW8="
|
||||
EVE_CALLBACK_URL="encrypted:BHrxPwPrv8WoHYPZI7nKyJCnggzxBS4JZKxoWXOKa70fglSvAuujGLWktt1LppJ2qvm8uU+J2qltJq0xCwFjzWB/qkRCQ4mK1tLc+cEh9S9mwKXK+55xntl/cxvfUvNt3dyHTL6Bwtxb1UfUxAkMtvPjtDfFKtJxtZBuXnk4Wv72SHZuGmUCp/Yd"
|
||||
ESI_USER_AGENT="encrypted:BN2TE5wokSxMPBgfek0sxyg3sJHTdTm7vPYnMoyqAux93SzvH06wJ+8sVOLo9dmzHbKzSBn1rVNkoLiZV+fvvbrXMrTGL6Ai2jDzrFoIFTHOU9QNL2dk9LmklgmILhy/RPD5ZJlyFHFPffK9laBVws2aOMepoCgq0wPws5dKEd7SYw8jg/wtmw3EYb68wVghzpdnKIpYRqJAlduDfCJv9FRe5J7XFsZxhVxQSSc="
|
||||
AUTH_DB_PATH="encrypted:BM9HDaWwoYJZrHIQtT2FCMQou8NPbom55bvnNdE916P20UlpDHLkopypRxrnFYn6+ufJVanOl6/1AiD7gHqUP/GfT9mrnnXT8qU9XT43hVtyr+N91t6r4fQkgEs31mFnzrCEvOO8VHx+Ps4Kn3tkhdgnoC798OZv"
|
||||
JANICE_KEY="encrypted:BLrwoFguAtfDtopwRBkaM129x3kq8HLYDHddiNLV8unvaJPXPUHOzL+MySnK0lH2s43f1nqx759C2wT/95PHsU8mk29DUT2FbhwDCtqBccrjTZHJdXNZrzEXhyVUAdgoHdyyGugu/xwhaVqKYyTZJNCviceDcW4c/3iBiRBXpan8"
|
||||
PERPLEXITY_API_KEY="encrypted:BMMVGTrkVOeh5a/iP9dIUMRXmcf0gIhj+KUQeyz4Cm0nKbAS/obENMn24NqmBRQtU/4M5Xj1lIpqV+C5MsBmdY/LW/9LX0z1okvaPRKjrudRyj24Mmj0HZLm+RCv0VTYBL2Yx4dszYCYkxBAEihDR/eQfGIMU7KoRNjMot0uF8BonC6fzSIWI0iwxsRm2k3ZAo/MoaRB"
|
||||
@@ -1,182 +0,0 @@
|
||||
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Caches
|
||||
|
||||
.cache
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
.env.keys
|
||||
.flaskenv*
|
||||
!.env.project
|
||||
!.env.vault
|
||||
|
||||
data/
|
||||
db/
|
||||
litefs/
|
||||
brainstorming/
|
||||
|
||||
# Sentry Config File
|
||||
.sentryclirc
|
||||
|
||||
cloudflared.exe
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 120
|
||||
}
|
||||
4
packages/concierge-bot/data/.gitignore
vendored
4
packages/concierge-bot/data/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "concierge-bot",
|
||||
"type": "module",
|
||||
"module": "src/main.ts",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@dotenvx/dotenvx": "^1.49.0",
|
||||
"@types/bun": "^1.2.21",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@projectdysnomia/dysnomia": "github:projectdysnomia/dysnomia#dev",
|
||||
"@star-kitten/lib": "workspace:*"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "bunx dotenvx run -f .env.development -- bun run --watch src/main.ts",
|
||||
"start": "bunx @dotenvx/dotenvx run -f .env.production -- bun src/main.ts",
|
||||
"build": "mkdirp ./db && bun build --minify --sourcemap src/main.ts --target bun --outdir ./dist",
|
||||
"lint": "prettier --check .",
|
||||
"format": "prettier --write .",
|
||||
"test": "bun test",
|
||||
"encrypt": "bunx dotenvx encrypt -f .env.development && bunx dotenvx encrypt -f .env.production",
|
||||
"decrypt": "bunx dotenvx decrypt -f .env.development && bunx dotenvx decrypt -f .env.production"
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
import { getDB } from '@/lib/db';
|
||||
import { Constants } from '@projectdysnomia/dysnomia';
|
||||
import {
|
||||
createChatCommand,
|
||||
integerOption,
|
||||
stringOption,
|
||||
subCommandGroupOption,
|
||||
subCommandRouter,
|
||||
type CommandContext,
|
||||
type ExecutableInteraction,
|
||||
} from '@star-kitten/lib/discord';
|
||||
import { numberOption, subCommandOption } from '@star-kitten/lib/discord';
|
||||
|
||||
export default createChatCommand(
|
||||
{
|
||||
name: 'route',
|
||||
description: 'Routes',
|
||||
defaultMemberPermissions: Constants.Permissions.administrator,
|
||||
options: [
|
||||
subCommandGroupOption({
|
||||
name: 'group',
|
||||
description: 'a group',
|
||||
options: [
|
||||
subCommandOption({
|
||||
name: 'add',
|
||||
description: 'add a route',
|
||||
options: [
|
||||
stringOption({
|
||||
name: 'start',
|
||||
description: 'starting location',
|
||||
autocomplete: true,
|
||||
required: true,
|
||||
}),
|
||||
stringOption({
|
||||
name: 'end',
|
||||
description: 'end location',
|
||||
autocomplete: true,
|
||||
required: true,
|
||||
}),
|
||||
integerOption({
|
||||
name: 'isk-per-m3',
|
||||
description: 'ISK per m3',
|
||||
required: true,
|
||||
}),
|
||||
numberOption({
|
||||
name: 'collateral-percent',
|
||||
description: 'percentage of collateral to add onto the cost.',
|
||||
required: true,
|
||||
}),
|
||||
integerOption({
|
||||
name: 'max-volume',
|
||||
description: 'Maximum volume allowed for this route',
|
||||
required: true,
|
||||
}),
|
||||
integerOption({
|
||||
name: 'min-reward',
|
||||
description: 'Minimum required reward for this route',
|
||||
required: true,
|
||||
}),
|
||||
integerOption({
|
||||
name: 'expiration',
|
||||
description: 'Expiration the client should set on the contract',
|
||||
required: true,
|
||||
}),
|
||||
integerOption({
|
||||
name: 'completion',
|
||||
description: 'Days to complete time the client should set on the contract',
|
||||
required: true,
|
||||
}),
|
||||
integerOption({
|
||||
name: 'max-collateral',
|
||||
description: 'Maximum collateral allowed for this route',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
subCommandOption({
|
||||
name: 'remove',
|
||||
description: 'remove a route',
|
||||
options: [
|
||||
integerOption({
|
||||
name: 'id',
|
||||
description: 'ID of the route to remove',
|
||||
required: true,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
},
|
||||
subCommandRouter({
|
||||
group: {
|
||||
add: async (interaction, ctx, data) => {
|
||||
console.log(`in add`);
|
||||
if (interaction.isAutocomplete()) {
|
||||
const focused = data.options?.find((opt) => opt.focused);
|
||||
console.log(`focused`, focused);
|
||||
if (focused) {
|
||||
switch (focused.name) {
|
||||
case 'start': {
|
||||
const locations = getDB().getAllLocations();
|
||||
console.log(JSON.stringify(locations.length));
|
||||
return await interaction.result(
|
||||
locations.map((l) => ({ name: l.short_name, value: String(l.location_id) })),
|
||||
);
|
||||
}
|
||||
case 'end': {
|
||||
const locations = getDB().getAllLocations();
|
||||
console.log(JSON.stringify(locations.length));
|
||||
return await interaction.result(
|
||||
locations.map((l) => ({ name: l.short_name, value: String(l.location_id) })),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
remove: (interaction, ctx) => {
|
||||
console.log('remove handler');
|
||||
if (interaction.isApplicationCommand()) {
|
||||
interaction.createMessage(`Thanks`);
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -1,4 +0,0 @@
|
||||
import './locations/command';
|
||||
import './quoute.command';
|
||||
import './time.command';
|
||||
import './create-route.command';
|
||||
@@ -1,48 +0,0 @@
|
||||
import type { PageContext } from '@star-kitten/lib/discord';
|
||||
import type { LocationsState } from './state';
|
||||
import { Page } from './router';
|
||||
import { StructureType } from '@/lib/db/types/routes';
|
||||
|
||||
export default async function (ctx: PageContext<LocationsState>) {
|
||||
const isAdd = ctx.custom_id === Page.addLocationModal;
|
||||
const location = ctx.state.data.selected;
|
||||
return (
|
||||
<modal
|
||||
title={`${isAdd ? 'Add Location' : 'Edit: ' + ctx.state.data.selected?.short_name || ''}`}
|
||||
customId={isAdd ? Page.addLocationModalSubmit : Page.editLocationModalSubmit}
|
||||
>
|
||||
{isAdd && (
|
||||
<label label="Location ID">
|
||||
<textInput customId="loc-id" placeholder="Enter the structure id" required />
|
||||
</label>
|
||||
)}
|
||||
<label label="Location Name">
|
||||
<textInput customId="loc-name" placeholder="Enter the location name" required value={location?.name} />
|
||||
</label>
|
||||
<label label="Location Short Name">
|
||||
<textInput
|
||||
customId="loc-short-name"
|
||||
placeholder="Enter the location short name"
|
||||
required
|
||||
value={location?.short_name}
|
||||
/>
|
||||
</label>
|
||||
<label label="System">
|
||||
<textInput customId="loc-system" placeholder="Enter the system (e.g. Jita)" required value={location?.system} />
|
||||
</label>
|
||||
<label label="Structure Type">
|
||||
<stringSelect
|
||||
customId="loc-structure-type"
|
||||
placeholder="Select Structure Type"
|
||||
minValues={1}
|
||||
maxValues={1}
|
||||
required
|
||||
>
|
||||
{Object.values(StructureType).map((type) => (
|
||||
<option label={type} value={type} default={location?.structure_type === type} />
|
||||
))}
|
||||
</stringSelect>
|
||||
</label>
|
||||
</modal>
|
||||
);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import {
|
||||
createChatCommand,
|
||||
type CommandContext,
|
||||
type ExecutableInteraction,
|
||||
PageType,
|
||||
usePages,
|
||||
hasUserId,
|
||||
PermissionType,
|
||||
} from '@star-kitten/lib/discord';
|
||||
import mainPage from './main.page';
|
||||
import addLocationModal from './add-location.modal';
|
||||
import editServicesModal from './edit-services.modal';
|
||||
import removeLocationModal from './remove-location.modal';
|
||||
import type { LocationsState } from './state';
|
||||
import router, { Page } from './router';
|
||||
import { Constants } from '@projectdysnomia/dysnomia';
|
||||
|
||||
export default createChatCommand(
|
||||
{
|
||||
name: 'locations',
|
||||
description: 'location management',
|
||||
defaultMemberPermissions: Constants.Permissions.administrator,
|
||||
},
|
||||
async (interaction: ExecutableInteraction, commandCtx: CommandContext) => {
|
||||
await usePages<LocationsState>(
|
||||
{
|
||||
pages: {
|
||||
[Page.main]: {
|
||||
key: Page.main,
|
||||
type: PageType.MESSAGE,
|
||||
render: mainPage,
|
||||
},
|
||||
[Page.addLocationModal]: {
|
||||
key: Page.addLocationModal,
|
||||
type: PageType.MODAL,
|
||||
render: addLocationModal,
|
||||
},
|
||||
[Page.editLocationModal]: {
|
||||
key: Page.editLocationModal,
|
||||
type: PageType.MODAL,
|
||||
render: addLocationModal,
|
||||
},
|
||||
[Page.editServicesModal]: {
|
||||
key: Page.editServicesModal,
|
||||
type: PageType.MODAL,
|
||||
render: editServicesModal,
|
||||
},
|
||||
[Page.removeLocationModal]: {
|
||||
key: Page.removeLocationModal,
|
||||
type: PageType.MODAL,
|
||||
render: removeLocationModal,
|
||||
},
|
||||
},
|
||||
router,
|
||||
initialPage: Page.main,
|
||||
ephemeral: true,
|
||||
},
|
||||
interaction,
|
||||
commandCtx,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -1,46 +0,0 @@
|
||||
import type { PageContext } from '@star-kitten/lib/discord';
|
||||
import type { LocationsState } from './state';
|
||||
import { Page } from './router';
|
||||
import { locationSupportsType, RouteType } from '@/lib/db/types/routes';
|
||||
|
||||
export default function (ctx: PageContext<LocationsState>) {
|
||||
const location = ctx.state.data.selected!;
|
||||
return (
|
||||
<modal title={`Services: ${ctx.state.data.selected?.short_name || ''}`} customId={Page.editServicesModalSubmit}>
|
||||
<label label="Can JF">
|
||||
<stringSelect customId="can-jf" placeholder="Can JF" minValues={1} maxValues={1}>
|
||||
<option label="Yes" value={RouteType.JF + ''} default={locationSupportsType(location, RouteType.JF)} />
|
||||
<option label="No" value="0" default={!locationSupportsType(location, RouteType.JF)} />
|
||||
</stringSelect>
|
||||
</label>
|
||||
<label label="Can DST">
|
||||
<stringSelect customId="can-dst" placeholder="Can DST" minValues={1} maxValues={1}>
|
||||
<option label="Yes" value={RouteType.DST + ''} default={locationSupportsType(location, RouteType.DST)} />
|
||||
<option label="No" value="0" default={!locationSupportsType(location, RouteType.DST)} />
|
||||
</stringSelect>
|
||||
</label>
|
||||
<label label="Can BR">
|
||||
<stringSelect customId="can-br" placeholder="Can BR" minValues={1} maxValues={1}>
|
||||
<option label="Yes" value={RouteType.BR + ''} default={locationSupportsType(location, RouteType.BR)} />
|
||||
<option label="No" value="0" default={!locationSupportsType(location, RouteType.BR)} />
|
||||
</stringSelect>
|
||||
</label>
|
||||
<label label="Can SMB">
|
||||
<stringSelect customId="can-smb" placeholder="Can SMB" minValues={1} maxValues={1}>
|
||||
<option label="Yes" value={RouteType.SMB + ''} default={locationSupportsType(location, RouteType.SMB)} />
|
||||
<option label="No" value="0" default={!locationSupportsType(location, RouteType.SMB)} />
|
||||
</stringSelect>
|
||||
</label>
|
||||
<label label="Can Bridge">
|
||||
<stringSelect customId="can-bridge" placeholder="Can Bridge" minValues={1} maxValues={1}>
|
||||
<option
|
||||
label="Yes"
|
||||
value={RouteType.BRIDGE + ''}
|
||||
default={locationSupportsType(location, RouteType.BRIDGE)}
|
||||
/>
|
||||
<option label="No" value="0" default={!locationSupportsType(location, RouteType.BRIDGE)} />
|
||||
</stringSelect>
|
||||
</label>
|
||||
</modal>
|
||||
);
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { ButtonStyle, type PageContext } from '@star-kitten/lib/discord';
|
||||
import type { LocationsState } from './state';
|
||||
import { getDB } from '@/lib/db';
|
||||
import { Page } from './router';
|
||||
import { locationSupportsType, RouteType } from '@/lib/db/types/routes';
|
||||
|
||||
export default function (ctx: PageContext<LocationsState>) {
|
||||
const locations = getDB().getAllLocations();
|
||||
const hasLocations = locations.length > 0;
|
||||
|
||||
const renderLocations = () => {
|
||||
if (locations.length === 0) {
|
||||
return 'No locations added yet.';
|
||||
}
|
||||
return locations
|
||||
.map(
|
||||
(loc) =>
|
||||
`${loc.location_id}\t|\t${loc.short_name}\t|\t${locationSupportsType(loc, RouteType.JF) ? 'JF' : ''}${locationSupportsType(loc, RouteType.DST) ? ', DST' : ''}${locationSupportsType(loc, RouteType.BR) ? ', BR' : ''}${locationSupportsType(loc, RouteType.SMB) ? ', SMB' : ''}${locationSupportsType(loc, RouteType.BRIDGE) ? ', BRIDGE' : ''}`,
|
||||
)
|
||||
.join('\n');
|
||||
};
|
||||
|
||||
if (!hasLocations) {
|
||||
return (
|
||||
<container accent={0x11cc33}>
|
||||
<text>{`# Locations\nNo locations added yet.`}</text>
|
||||
<actionRow>
|
||||
<button customId={Page.addLocationModal} label="Add Location" style={ButtonStyle.PRIMARY} />
|
||||
</actionRow>
|
||||
</container>
|
||||
);
|
||||
}
|
||||
|
||||
const locationOptions = locations.map((loc) => <option label={loc.short_name} value={loc.location_id.toString()} />);
|
||||
|
||||
return (
|
||||
<container accent={0x11cc33}>
|
||||
<text>{`# Locations\n${renderLocations()}`}</text>
|
||||
<actionRow>
|
||||
<button customId={Page.addLocationModal} label="Add Location" style={ButtonStyle.PRIMARY} />
|
||||
</actionRow>
|
||||
|
||||
<text>{`## Edit services at`}</text>
|
||||
<actionRow>
|
||||
<stringSelect
|
||||
customId={Page.editServicesModal}
|
||||
placeholder="Select location to edit services"
|
||||
minValues={1}
|
||||
maxValues={1}
|
||||
>
|
||||
{locationOptions}
|
||||
</stringSelect>
|
||||
</actionRow>
|
||||
<text>{`## Edit location details`}</text>
|
||||
<actionRow>
|
||||
<stringSelect
|
||||
customId={Page.editLocationModal}
|
||||
placeholder="Select location to edit"
|
||||
minValues={1}
|
||||
maxValues={1}
|
||||
>
|
||||
{locationOptions}
|
||||
</stringSelect>
|
||||
</actionRow>
|
||||
<text>{`## Remove location`}</text>
|
||||
<actionRow>
|
||||
<stringSelect
|
||||
customId={Page.removeLocationModal}
|
||||
placeholder="Select location to remove"
|
||||
minValues={1}
|
||||
maxValues={1}
|
||||
>
|
||||
{locationOptions}
|
||||
</stringSelect>
|
||||
</actionRow>
|
||||
</container>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { PageContext } from '@star-kitten/lib/discord';
|
||||
import type { LocationsState } from './state';
|
||||
import { Page } from './router';
|
||||
|
||||
export default function (ctx: PageContext<LocationsState>) {
|
||||
return (
|
||||
<modal title={`Remove ${ctx.state.data.selected?.short_name || ''}?`} customId={Page.removeLocationModalSubmit}>
|
||||
<text>{`# Are you sure?\n\nConfrim that you want remove **${ctx.state.data.selected.name}**.\n\n*This action cannot be undone. Click submit to confirm.*`}</text>
|
||||
</modal>
|
||||
);
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import * as StarKitten from '@star-kitten/lib/discord';
|
||||
import type { LocationsState } from './state';
|
||||
import { getDB } from '@/lib/db';
|
||||
import type { StructureType } from '@/lib/db/types/routes';
|
||||
|
||||
export enum Page {
|
||||
main = 'main',
|
||||
addLocationModal = 'add-location-modal',
|
||||
addLocationModalSubmit = 'add-location-modal-submit',
|
||||
editLocationModal = 'edit-location-modal',
|
||||
editLocationModalSubmit = 'edit-location-modal-submit',
|
||||
editServicesModal = 'edit-services-modal',
|
||||
editServicesModalSubmit = 'edit-services-modal-submit',
|
||||
removeLocationModal = 'remove-location-modal',
|
||||
removeLocationModalSubmit = 'remove-location-modal-submit',
|
||||
}
|
||||
|
||||
export default function (ctx: StarKitten.PageContext<LocationsState>): Page {
|
||||
switch (ctx.custom_id) {
|
||||
case Page.addLocationModal:
|
||||
ctx.state.data.selected = undefined;
|
||||
return Page.addLocationModal;
|
||||
|
||||
case Page.editLocationModalSubmit:
|
||||
case Page.addLocationModalSubmit: {
|
||||
if (!ctx.interaction.isModalSubmit()) {
|
||||
throw new Error('Expected a modal submit interaction.');
|
||||
}
|
||||
|
||||
const location: any = {};
|
||||
for (const component of ctx.interaction.data.components) {
|
||||
if (StarKitten.isModalLabel(component)) {
|
||||
if (StarKitten.isModalTextInput(component.component)) {
|
||||
if (StarKitten.componentHasIdPrefix(component.component, 'loc-id')) {
|
||||
location.location_id = component.component.value.trim();
|
||||
} else if (StarKitten.componentHasIdPrefix(component.component, 'loc-name')) {
|
||||
location.name = component.component.value.trim();
|
||||
} else if (StarKitten.componentHasIdPrefix(component.component, 'loc-short-name')) {
|
||||
location.short_name = component.component.value.trim();
|
||||
} else if (StarKitten.componentHasIdPrefix(component.component, 'loc-system')) {
|
||||
location.system = component.component.value.trim();
|
||||
}
|
||||
} else if (
|
||||
StarKitten.isStringSelectMenu(component.component) &&
|
||||
StarKitten.componentHasIdPrefix(component.component, 'loc-structure-type')
|
||||
) {
|
||||
location.structure_type = component.component.values[0] as StructureType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.custom_id === Page.addLocationModalSubmit ? getDB().addLocation(location) : getDB().updateLocation(location);
|
||||
ctx.state.data.selected = undefined;
|
||||
return Page.main;
|
||||
}
|
||||
|
||||
case Page.editLocationModal: {
|
||||
if (!ctx.interaction.isSelectMenu()) {
|
||||
console.error('Expected a message component interaction.');
|
||||
return Page.main;
|
||||
}
|
||||
const data = ctx.interaction.data;
|
||||
const locationId = Number.parseInt(data.values[0]);
|
||||
const location = getDB().getLocationById(locationId);
|
||||
if (location) {
|
||||
ctx.state.data.selected = location;
|
||||
return Page.editLocationModal;
|
||||
}
|
||||
return Page.main;
|
||||
}
|
||||
|
||||
case Page.editServicesModal: {
|
||||
if (!ctx.interaction.isSelectMenu()) {
|
||||
console.error('Expected a message component interaction.');
|
||||
return Page.main;
|
||||
}
|
||||
const data = ctx.interaction.data;
|
||||
const locationId = Number.parseInt(data.values[0]);
|
||||
const location = getDB().getLocationById(locationId);
|
||||
if (location) {
|
||||
ctx.state.data.selected = location;
|
||||
return Page.editServicesModal;
|
||||
}
|
||||
return Page.main;
|
||||
}
|
||||
|
||||
case Page.editServicesModalSubmit: {
|
||||
if (ctx.interaction.isModalSubmit()) {
|
||||
const location = ctx.state.data.selected;
|
||||
if (!location) {
|
||||
return Page.main;
|
||||
}
|
||||
|
||||
let supported_route_types = 0;
|
||||
for (const component of ctx.interaction.data.components) {
|
||||
if (StarKitten.isModalLabel(component) && StarKitten.isStringSelectMenu(component.component)) {
|
||||
supported_route_types = supported_route_types | parseInt(component.component.values[0]);
|
||||
}
|
||||
}
|
||||
|
||||
getDB().updateLocation({
|
||||
...location,
|
||||
supported_route_types,
|
||||
});
|
||||
}
|
||||
ctx.state.data.selected = undefined;
|
||||
return Page.main;
|
||||
}
|
||||
|
||||
case Page.removeLocationModal: {
|
||||
if (!ctx.interaction.isSelectMenu()) {
|
||||
console.error('Expected a message component interaction.');
|
||||
return Page.main;
|
||||
}
|
||||
const data = ctx.interaction.data;
|
||||
const locationId = Number.parseInt(data.values[0]);
|
||||
const location = getDB().getLocationById(locationId);
|
||||
if (location) {
|
||||
ctx.state.data.selected = location;
|
||||
return Page.removeLocationModal;
|
||||
}
|
||||
return Page.main;
|
||||
}
|
||||
|
||||
case Page.removeLocationModalSubmit: {
|
||||
if (ctx.interaction.isModalSubmit()) {
|
||||
const location = ctx.state.data.selected;
|
||||
if (!location) {
|
||||
return Page.main;
|
||||
}
|
||||
getDB().removeLocation(location.location_id);
|
||||
}
|
||||
ctx.state.data.selected = undefined;
|
||||
return Page.main;
|
||||
}
|
||||
|
||||
default:
|
||||
return Page.main;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { Location } from '@/lib/db';
|
||||
|
||||
export interface LocationsState {
|
||||
selected?: Location;
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
import {
|
||||
Constants,
|
||||
type ChatInputApplicationCommandStructure,
|
||||
type ComponentInteractionSelectMenuData,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
import { appraiseItems, type Appraisal } from '@star-kitten/lib/eve/third-party/janice.js';
|
||||
import { componentHasIdPrefix, isModalLabel, isModalTextInput } from '@star-kitten/lib/discord';
|
||||
import { createChatCommand, type CommandContext, type ExecutableInteraction } from '@star-kitten/lib/discord';
|
||||
import { PageType, usePages } from '@star-kitten/lib/discord/pages';
|
||||
// import { renderAppraisal } from './renderAppraisal';
|
||||
// import { renderAppraisalModal } from './renderAppraisalModal';
|
||||
|
||||
const definition: ChatInputApplicationCommandStructure = {
|
||||
type: Constants.ApplicationCommandTypes.CHAT_INPUT,
|
||||
name: 'quote',
|
||||
nameLocalizations: {
|
||||
de: 'angebot',
|
||||
'es-ES': 'cotización',
|
||||
fr: 'devis',
|
||||
ja: '見積もり',
|
||||
ko: '견적',
|
||||
ru: 'цитата',
|
||||
'zh-CN': '报价',
|
||||
},
|
||||
description: 'Get a quote for moving your items',
|
||||
descriptionLocalizations: {
|
||||
de: 'Holen Sie Sie sich ein Angebot für den Umzug Ihrer Gegenstände',
|
||||
'es-ES': 'Obtén una cotización para mover tus artículos',
|
||||
fr: 'Obtenez un devis pour déplacer vos articles',
|
||||
ja: 'アイテムを移動するための見積もりを取得します',
|
||||
ko: '항목을 이동하기 위한 견적 받기',
|
||||
ru: 'Получите предложение по перемещению ваших предметов',
|
||||
'zh-CN': '获取移动您的物品的报价',
|
||||
},
|
||||
};
|
||||
|
||||
interface QuouteState {
|
||||
serviceType: RouteType;
|
||||
originId?: number;
|
||||
destinationId?: number;
|
||||
items?: string;
|
||||
appraisal?: Appraisal;
|
||||
}
|
||||
|
||||
// Hardcoded routes for now
|
||||
interface RouteType {
|
||||
id: number;
|
||||
short: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const routeTypes: Record<string, RouteType> = {
|
||||
JF: { id: 1, short: 'JF', label: 'Jump Freighter' },
|
||||
DST: { id: 2, short: 'DST', label: 'Deep Space Transport' },
|
||||
SMB: { id: 3, short: 'SMB', label: 'Ship Maintenance Bay' },
|
||||
BR: { id: 4, short: 'BR', label: 'Blockade Runner' },
|
||||
CUSTOM: { id: 5, short: 'CUSTOM', label: 'Custom' },
|
||||
};
|
||||
|
||||
interface Location {
|
||||
id: number;
|
||||
name: string;
|
||||
supported_types: number[]; // RouteType IDs
|
||||
}
|
||||
|
||||
const locations: Location[] = [
|
||||
{ id: 1, name: 'Jita 4-4', supported_types: [1, 2, 3, 4, 5] },
|
||||
{ id: 2, name: 'B-9', supported_types: [1, 2] },
|
||||
{ id: 3, name: '3T7', supported_types: [1, 2, 3, 4, 5] },
|
||||
{ id: 4, name: '4-H', supported_types: [1, 2] },
|
||||
{ id: 5, name: 'Odebeinn', supported_types: [1, 2, 3, 4, 5] },
|
||||
];
|
||||
|
||||
interface Route {
|
||||
origin: number;
|
||||
destination: number;
|
||||
type: number; // RouteType ID
|
||||
}
|
||||
|
||||
const routes: Route[] = [
|
||||
{ origin: 1, destination: 2, type: routeTypes.JF.id },
|
||||
{ origin: 2, destination: 1, type: routeTypes.JF.id },
|
||||
{ origin: 2, destination: 3, type: routeTypes.JF.id },
|
||||
{ origin: 3, destination: 2, type: routeTypes.JF.id },
|
||||
{ origin: 3, destination: 4, type: routeTypes.JF.id },
|
||||
{ origin: 4, destination: 3, type: routeTypes.JF.id },
|
||||
{ origin: 4, destination: 5, type: routeTypes.JF.id },
|
||||
{ origin: 5, destination: 4, type: routeTypes.JF.id },
|
||||
{ origin: 5, destination: 2, type: routeTypes.JF.id },
|
||||
{ origin: 2, destination: 3, type: routeTypes.JF.id },
|
||||
{ origin: 3, destination: 4, type: routeTypes.JF.id },
|
||||
{ origin: 4, destination: 5, type: routeTypes.JF.id },
|
||||
{ origin: 5, destination: 2, type: routeTypes.JF.id },
|
||||
{ origin: 3, destination: 2, type: routeTypes.DST.id },
|
||||
{ origin: 2, destination: 4, type: routeTypes.SMB.id },
|
||||
{ origin: 2, destination: 5, type: routeTypes.BR.id },
|
||||
{ origin: 1, destination: 3, type: routeTypes.DST.id },
|
||||
{ origin: 1, destination: 4, type: routeTypes.SMB.id },
|
||||
{ origin: 1, destination: 5, type: routeTypes.BR.id },
|
||||
{ origin: 2, destination: 1, type: routeTypes.JF.id },
|
||||
{ origin: 3, destination: 1, type: routeTypes.DST.id },
|
||||
{ origin: 4, destination: 1, type: routeTypes.SMB.id },
|
||||
{ origin: 5, destination: 1, type: routeTypes.BR.id },
|
||||
];
|
||||
|
||||
const defaultState: QuouteState = {
|
||||
serviceType: routeTypes.JF,
|
||||
originId: undefined,
|
||||
destinationId: undefined,
|
||||
items: undefined,
|
||||
appraisal: undefined,
|
||||
};
|
||||
|
||||
function uniqueDestinationForOriginAndType(typeId: number, originId?: number) {
|
||||
if (!originId) {
|
||||
locations.filter((loc) => loc.supported_types.includes(typeId));
|
||||
}
|
||||
|
||||
const filtered = routes.filter((r) => r.origin === originId && r.type === typeId);
|
||||
const locSet = new Set<Location>();
|
||||
filtered.forEach((route, index) => {
|
||||
locSet.add(locations.find((l) => l.id === route.destination)!);
|
||||
});
|
||||
return Array.from(locSet);
|
||||
}
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
await usePages<QuouteState>(
|
||||
{
|
||||
pages: {
|
||||
main: {
|
||||
key: 'main',
|
||||
type: PageType.MESSAGE,
|
||||
render: (pageCtx) => {
|
||||
console.log('Rendering main page with state:', pageCtx.state.data);
|
||||
return (
|
||||
<container accent={0x11cc33}>
|
||||
<text>{`# Quote`}</text>
|
||||
<text>{`### Service: ${pageCtx.state.data.serviceType?.label ?? ''}`}</text>
|
||||
<actionRow>
|
||||
{Object.keys(routeTypes).map((key) => (
|
||||
<button
|
||||
customId={`type-${key}`}
|
||||
label={routeTypes[key].short}
|
||||
style={Constants.ButtonStyles.SECONDARY}
|
||||
/>
|
||||
))}
|
||||
</actionRow>
|
||||
<text>{`### Origin: ${locations[pageCtx.state.data.originId - 1]?.name ?? ''}`}</text>
|
||||
<actionRow>
|
||||
<stringSelect customId="route-origin" placeholder="Select Origin">
|
||||
{locations
|
||||
.filter((loc) => loc.supported_types.includes(pageCtx.state.data.serviceType?.id ?? -1))
|
||||
.map((loc) => {
|
||||
return <option label={loc?.name ?? ''} value={loc?.id.toString() ?? ''} />;
|
||||
})}
|
||||
</stringSelect>
|
||||
</actionRow>
|
||||
<text>{`### Destination: ${locations[pageCtx.state.data.destinationId - 1]?.name ?? ''}`}</text>
|
||||
<actionRow>
|
||||
<stringSelect customId="route-destination" placeholder="Select Destination">
|
||||
<option label="Select a destination" value="1" />
|
||||
</stringSelect>
|
||||
</actionRow>
|
||||
<text>{`### Items:\n${pageCtx.state.data.items ?? ''}`}</text>
|
||||
<actionRow>
|
||||
<button customId="addItems" label="Add Items" style={Constants.ButtonStyles.PRIMARY} />
|
||||
</actionRow>
|
||||
</container>
|
||||
);
|
||||
},
|
||||
},
|
||||
addItems: {
|
||||
key: 'add-items',
|
||||
type: PageType.MODAL,
|
||||
render: () => {
|
||||
return (
|
||||
<modal title="Add Items" customId="add-items-modal">
|
||||
<label
|
||||
label="Items"
|
||||
description="Discord limits input to 4000 characters. Add more items by submitting multiple times."
|
||||
>
|
||||
<textInput
|
||||
customId="items-input"
|
||||
placeholder={`e.g. Tritanium 22222
|
||||
Pyerite 8000
|
||||
Mexallon 2444`}
|
||||
isParagraph={true}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</modal>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
initialPage: 'main',
|
||||
initialStateData: defaultState,
|
||||
ephemeral: true,
|
||||
router: (pageCtx) => {
|
||||
if (pageCtx.custom_id.startsWith('type-')) {
|
||||
const key = pageCtx.custom_id.replace('type-', '');
|
||||
pageCtx.state.data.serviceType = routeTypes[key];
|
||||
return 'main';
|
||||
}
|
||||
if (pageCtx.custom_id === 'route-origin' && pageCtx.interaction.isMessageComponent()) {
|
||||
const data = pageCtx.interaction.data as ComponentInteractionSelectMenuData;
|
||||
pageCtx.state.data.originId = Number.parseInt(data.values[0]);
|
||||
return 'main';
|
||||
}
|
||||
if (pageCtx.custom_id === 'route-destination' && pageCtx.interaction.isMessageComponent()) {
|
||||
const data = pageCtx.interaction.data as ComponentInteractionSelectMenuData;
|
||||
pageCtx.state.data.destinationId = Number.parseInt(data.values[0]);
|
||||
return 'main';
|
||||
}
|
||||
if (pageCtx.custom_id === 'addItems') {
|
||||
return 'addItems';
|
||||
}
|
||||
|
||||
if (pageCtx.custom_id === 'add-items-modal' && pageCtx.interaction.isModalSubmit()) {
|
||||
let items = '';
|
||||
pageCtx.interaction.data.components.forEach((comp) => {
|
||||
if (isModalLabel(comp)) {
|
||||
if (isModalTextInput(comp.component) && componentHasIdPrefix(comp.component, 'items-input')) {
|
||||
items = comp.component.value || items;
|
||||
}
|
||||
}
|
||||
});
|
||||
pageCtx.state.data.items = pageCtx.state.data.items ? `${pageCtx.state.data.items}\n${items}` : items;
|
||||
}
|
||||
return 'main';
|
||||
},
|
||||
},
|
||||
interaction,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
|
||||
export default createChatCommand(definition, execute);
|
||||
@@ -1,61 +0,0 @@
|
||||
import { type ExecutableInteraction, type CommandContext, Locale, createChatCommand } from '@star-kitten/lib/discord';
|
||||
|
||||
const eveTimeText = {
|
||||
[Locale.EN_US]: 'EVE Time',
|
||||
[Locale.EN_GB]: 'EVE Time',
|
||||
[Locale.DE]: 'EVE-Zeit',
|
||||
[Locale.ES_ES]: 'Hora EVE',
|
||||
[Locale.FR]: "Heure d'EVE",
|
||||
[Locale.JA]: 'EVE時間',
|
||||
[Locale.KO]: 'EVE 시간',
|
||||
[Locale.RU]: 'Время EVE',
|
||||
[Locale.ZH_CN]: 'EVE时间',
|
||||
};
|
||||
|
||||
export default createChatCommand(
|
||||
{
|
||||
name: 'time',
|
||||
nameLocalizations: {
|
||||
[Locale.DE]: 'zeit',
|
||||
[Locale.ES_ES]: 'hora',
|
||||
[Locale.FR]: 'heure',
|
||||
[Locale.JA]: '時間',
|
||||
[Locale.KO]: '시간',
|
||||
[Locale.RU]: 'время',
|
||||
[Locale.ZH_CN]: '时间',
|
||||
},
|
||||
description: 'Get the current EVE time',
|
||||
descriptionLocalizations: {
|
||||
[Locale.DE]: 'Holen Sie sich die aktuelle EVE-Zeit',
|
||||
[Locale.ES_ES]: 'Obtén la hora actual de EVE',
|
||||
[Locale.FR]: "Obtenez l'heure actuelle d'EVE",
|
||||
[Locale.JA]: '現在のEVE時間を取得します',
|
||||
[Locale.KO]: '현재 EVE 시간을 가져옵니다',
|
||||
[Locale.RU]: 'Получите текущее время EVE',
|
||||
[Locale.ZH_CN]: '获取当前的EVE时间',
|
||||
},
|
||||
},
|
||||
(interaction: ExecutableInteraction, ctx: CommandContext) => {
|
||||
if (!interaction.isApplicationCommand()) return;
|
||||
|
||||
const now = new Date();
|
||||
const eveTime = now.toISOString().split('T')[1].split('.')[0];
|
||||
const eveDate = now.toLocaleDateString(interaction.locale, {
|
||||
timeZone: 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
weekday: 'long',
|
||||
});
|
||||
|
||||
interaction.createJSXMessage(
|
||||
<container>
|
||||
<text>
|
||||
{`### ${eveTimeText[interaction.locale] || eveTimeText[Locale.EN_US]}
|
||||
${eveTime}
|
||||
${eveDate}`}
|
||||
</text>
|
||||
</container>,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -1,61 +0,0 @@
|
||||
import { Database } from 'bun:sqlite';
|
||||
import locationTables, * as locationQueries from './location';
|
||||
export * from './location';
|
||||
|
||||
let db: Database = undefined;
|
||||
const queries = {
|
||||
...locationQueries,
|
||||
};
|
||||
|
||||
function createTables() {
|
||||
locationTables.createTable(db!);
|
||||
}
|
||||
|
||||
function dropTables() {
|
||||
locationTables.dropTable(db!);
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (db) {
|
||||
db.close();
|
||||
db = null;
|
||||
}
|
||||
}
|
||||
|
||||
function initializeDatabase(dbPath: string = process.env.CONCIERGE_DB_PATH || ':memory:') {
|
||||
db = new Database(dbPath);
|
||||
createTables();
|
||||
|
||||
Object.keys(queries).forEach((key) => {
|
||||
if (typeof queries[key] === 'function') {
|
||||
queries[key] = queries[key].bind(null, db);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
type OmitFirstArg<F> = F extends (arg1: any, ...args: infer R) => infer Ret ? (...args: R) => Ret : never;
|
||||
|
||||
type CurriedObject<T, O> = {
|
||||
[K in keyof T]: T[K] extends (arg1: O, ...args: infer R) => infer Ret ? OmitFirstArg<T[K]> : T[K];
|
||||
};
|
||||
|
||||
export type DB = CurriedObject<Omit<typeof queries, 'default'>, Database> & {
|
||||
db: Database;
|
||||
createTables: () => void;
|
||||
dropTables: () => void;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
export function getDB(): DB {
|
||||
if (!db) {
|
||||
initializeDatabase();
|
||||
}
|
||||
return {
|
||||
...(queries as any),
|
||||
default: undefined,
|
||||
createTables,
|
||||
dropTables,
|
||||
close,
|
||||
db,
|
||||
} as DB;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import type { Database } from 'bun:sqlite';
|
||||
import { dynamicInsert, dynamicUpdate, remove, select, type QueryOptions } from '@star-kitten/lib/util/sqlite.js';
|
||||
import type { Location } from './types/routes';
|
||||
|
||||
const TABLE_NAME = 'locations';
|
||||
|
||||
export default {
|
||||
createTable: (db: Database) => {
|
||||
db.run(
|
||||
`CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (
|
||||
location_id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
short_name TEXT NOT NULL,
|
||||
structure_type TEXT NOT NULL,
|
||||
system TEXT NOT NULL,
|
||||
supported_route_types INT NOT NULL DEFAULT 0
|
||||
)`,
|
||||
);
|
||||
},
|
||||
|
||||
dropTable: (db: Database) => {
|
||||
db.run(`DROP TABLE IF EXISTS ${TABLE_NAME}`);
|
||||
},
|
||||
};
|
||||
|
||||
export function addLocation(db: Database, location: Location) {
|
||||
return dynamicInsert(db, TABLE_NAME, location);
|
||||
}
|
||||
|
||||
export function updateLocation(db: Database, location: Location) {
|
||||
const id = location.location_id;
|
||||
delete location.location_id;
|
||||
return dynamicUpdate(db, TABLE_NAME, location, 'location_id', id);
|
||||
}
|
||||
|
||||
export function getLocationById(db: Database, location_id: number) {
|
||||
return select<Location>(db, TABLE_NAME, {}, '*', { location_id })?.[0];
|
||||
}
|
||||
|
||||
export function getAllLocations(db: Database, options?: QueryOptions<Location>): Location[] {
|
||||
return select<Location>(db, TABLE_NAME, options);
|
||||
}
|
||||
|
||||
export function removeLocation(db: Database, location_id: number) {
|
||||
return remove<Location>(db, TABLE_NAME, { location_id });
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { Database } from 'bun:sqlite';
|
||||
import { dynamicInsert, dynamicUpdate, remove, select, type QueryOptions } from '@star-kitten/lib/util/sqlite.js';
|
||||
import type { Route } from './types/routes';
|
||||
|
||||
const TABLE_NAME = 'routes';
|
||||
|
||||
export default {
|
||||
createTable: (db: Database) => {
|
||||
db.run(
|
||||
`CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (
|
||||
route_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
start_location_id INTEGER NOT NULL,
|
||||
end_location_id INTEGER NOT NULL,
|
||||
isk_per_m3 INTEGER NOT NULL,
|
||||
collat_pct REAL NOT NULL,
|
||||
max_volume INTEGER NOT NULL,
|
||||
min_reward INTEGER NOT NULL,
|
||||
expiration INTEGER NOT NULL,
|
||||
completion INTEGER NOT NULL,
|
||||
max_collateral INTEGER
|
||||
)
|
||||
`,
|
||||
);
|
||||
},
|
||||
|
||||
dropTable: (db: Database) => {
|
||||
db.run(`DROP TABLE IF EXISTS ${TABLE_NAME}`);
|
||||
},
|
||||
};
|
||||
|
||||
export function addRoute(db: Database, route: Omit<Route, 'route_id'>) {
|
||||
return dynamicInsert(db, TABLE_NAME, route);
|
||||
}
|
||||
|
||||
export function updateLocation(db: Database, location: Route) {
|
||||
const id = location.route_id;
|
||||
delete location.route_id;
|
||||
return dynamicUpdate(db, TABLE_NAME, location, 'route_id', id);
|
||||
}
|
||||
|
||||
export function getRoutes(db: Database, options?: QueryOptions<Route>) {
|
||||
return select<Route>(db, TABLE_NAME, options);
|
||||
}
|
||||
|
||||
export function getRoute(db: Database, route_id: number) {
|
||||
return select<Route>(db, TABLE_NAME, {}, '*', { route_id })?.[0];
|
||||
}
|
||||
|
||||
export function removeRoute(db: Database, route_id: number) {
|
||||
return remove<Route>(db, TABLE_NAME, { route_id });
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { ContractStatus } from '@star-kitten/lib/eve';
|
||||
|
||||
export interface CourierContract {
|
||||
concierge_id: number; // internal id to track this db record
|
||||
contract_id: number; // id of the contract for this courier
|
||||
assigned_to: number;
|
||||
received: string;
|
||||
last_updated: string;
|
||||
status: ContractStatus;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
export enum RouteType {
|
||||
JF = 1 << 0,
|
||||
DST = 1 << 1,
|
||||
BR = 1 << 2,
|
||||
SMB = 1 << 3,
|
||||
BRIDGE = 1 << 4,
|
||||
}
|
||||
|
||||
export enum StructureType {
|
||||
NPC = 'NPC',
|
||||
Keepstar = 'Keepstar',
|
||||
Fortizar = 'Fortizar',
|
||||
Astrahus = 'Astrahus',
|
||||
Sotiyo = 'Sotiyo',
|
||||
Azbel = 'Azbel',
|
||||
Raitaru = 'Raitaru',
|
||||
Athanor = 'Athanor',
|
||||
Tatara = 'Tatara',
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
location_id: number;
|
||||
name: string;
|
||||
short_name: string;
|
||||
structure_type: StructureType;
|
||||
system: string;
|
||||
supported_route_types: number;
|
||||
}
|
||||
|
||||
export interface Route {
|
||||
route_id: number;
|
||||
start_location_id: number;
|
||||
end_location_id: number;
|
||||
isk_per_m3: number;
|
||||
collat_pct: number; // collateral percent as a float, 1.5 = 1.5%
|
||||
max_volume: number;
|
||||
min_reward: number;
|
||||
expiration: number;
|
||||
completion: number;
|
||||
max_collateral: number;
|
||||
}
|
||||
|
||||
export function locationSupportsType(loc: Location, rt: RouteType) {
|
||||
return (loc.supported_route_types & rt) !== 0;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import { startBot } from '@star-kitten/lib/discord';
|
||||
import './commands';
|
||||
|
||||
startBot();
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@star-kitten/lib/discord",
|
||||
"moduleResolution": "bundler",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"typeRoots": ["src/types", "./node_modules/@types"]
|
||||
},
|
||||
"references": [{ "path": "../lib" }],
|
||||
"include": ["src", "types"],
|
||||
"exclude": ["node_modules", "dist", "build", "**/*.test.ts"]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# concierge-bot
|
||||
# discord
|
||||
|
||||
To install dependencies:
|
||||
|
||||
7
packages/discord/fixtures/commands/test1.command.ts
Normal file
7
packages/discord/fixtures/commands/test1.command.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createChatCommand } from '@/commands';
|
||||
|
||||
export default createChatCommand({
|
||||
name: 'test1', description: 'Test command 1' },
|
||||
async () => {},
|
||||
);
|
||||
|
||||
7
packages/discord/fixtures/commands/test2.command.ts
Normal file
7
packages/discord/fixtures/commands/test2.command.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createChatCommand } from '@/commands';
|
||||
|
||||
export default createChatCommand({
|
||||
name: 'test2', description: 'Test command 2' },
|
||||
async () => {},
|
||||
);
|
||||
|
||||
74
packages/discord/package.json
Normal file
74
packages/discord/package.json
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "@star-kitten/discord",
|
||||
"version": "0.0.1",
|
||||
"description": "Star Kitten Discord Library.",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.f302.me/jb/star-kitten#readme",
|
||||
"bugs": {
|
||||
"url": "https://git.f302.me/jb/star-kitten/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://git.f302.me/jb/star-kitten.git"
|
||||
},
|
||||
"author": "JB <j-b-3.deviate267@passmail.net>",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index*.d.ts"
|
||||
},
|
||||
"./commands": {
|
||||
"require": "./dist/commands/index.js",
|
||||
"import": "./dist/commands/index.js",
|
||||
"types": "./dist/types/commands/index.d.ts"
|
||||
},
|
||||
"./components": {
|
||||
"types": "./dist/types/components/index.d.ts",
|
||||
"require": "./dist/components/index.js",
|
||||
"import": "./dist/components/index.js"
|
||||
},
|
||||
"./pages": {
|
||||
"require": "./dist/pages/index.js",
|
||||
"import": "./dist/pages/index.js",
|
||||
"types": "./dist/types/pages/index.d.ts"
|
||||
},
|
||||
"./common": {
|
||||
"require": "./dist/common/index.js",
|
||||
"import": "./dist/common/index.js"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.5",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"bumpp": "^10.1.0",
|
||||
"prettier-plugin-multiline-arrays": "^4.0.3",
|
||||
"tsdown": "^0.14.2",
|
||||
"typescript": "beta"
|
||||
},
|
||||
"dependencies": {
|
||||
"@star-kitten/util": "link:@star-kitten/util",
|
||||
"@projectdysnomia/dysnomia": "github:projectdysnomia/dysnomia#dev",
|
||||
"date-fns": "^4.1.0",
|
||||
"fp-filters": "^0.5.4",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"dev": "tsdown --watch",
|
||||
"link": "bun link",
|
||||
"test": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"release": "bumpp && npm publish"
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,6 @@ export function injectInteraction(interaction: ExecutableInteraction, ctx: Parti
|
||||
return _originalCreateModal(content);
|
||||
};
|
||||
|
||||
interaction.createJSXModal = async (component) => {
|
||||
return interaction.createModal(component as any);
|
||||
};
|
||||
}
|
||||
|
||||
if ('createMessage' in interaction) {
|
||||
@@ -28,13 +25,6 @@ export function injectInteraction(interaction: ExecutableInteraction, ctx: Parti
|
||||
return _originalCreateMessage(content);
|
||||
};
|
||||
|
||||
interaction.createJSXMessage = async (component) => {
|
||||
const messageContent = {
|
||||
flags: Constants.MessageFlags.IS_COMPONENTS_V2,
|
||||
components: [component],
|
||||
};
|
||||
return interaction.createMessage(messageContent as any);
|
||||
};
|
||||
}
|
||||
|
||||
if ('editMessage' in interaction) {
|
||||
@@ -47,13 +37,6 @@ export function injectInteraction(interaction: ExecutableInteraction, ctx: Parti
|
||||
return _originalEditMessage(messageID, content);
|
||||
};
|
||||
|
||||
interaction.editJSXMessage = async (messageID, component) => {
|
||||
const messageContent = {
|
||||
flags: Constants.MessageFlags.IS_COMPONENTS_V2,
|
||||
components: [component],
|
||||
};
|
||||
return interaction.editMessage(messageID, messageContent as any);
|
||||
};
|
||||
}
|
||||
|
||||
if ('createFollowup' in interaction) {
|
||||
@@ -66,13 +49,6 @@ export function injectInteraction(interaction: ExecutableInteraction, ctx: Parti
|
||||
return _originalCreateFollowup(content);
|
||||
};
|
||||
|
||||
interaction.createJSXFollowup = async (component) => {
|
||||
const messageContent = {
|
||||
flags: Constants.MessageFlags.IS_COMPONENTS_V2,
|
||||
components: [component],
|
||||
};
|
||||
return interaction.createFollowup(messageContent as any);
|
||||
};
|
||||
}
|
||||
}
|
||||
return [interaction, ctx as CommandContext];
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createReactiveState } from '@/util/reactive-state.js';
|
||||
import { createReactiveState } from '@star-kitten/util/reactive-state.js';
|
||||
import { isApplicationCommand, isAutocomplete } from './command-helpers';
|
||||
import type { CommandState, ExecutableInteraction, PartialContext } from '../types';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getCommandState } from './command-state';
|
||||
import { type ExecutableInteraction } from '../types/interaction.type';
|
||||
import type { CommandContext, CommandHandler, CommandOptions, PartialContext } from '../types';
|
||||
import { augmentInteraction, getCommandName } from './command-helpers';
|
||||
import { awaitMaybePromise } from '@/util/promise';
|
||||
import { awaitMaybePromise } from '@star-kitten/util/promise';
|
||||
|
||||
export async function handleCommands(
|
||||
interaction: ExecutableInteraction,
|
||||
@@ -1,3 +1,6 @@
|
||||
// Exports that will be made publicly available outside this library
|
||||
export * from './create';
|
||||
export * from './option-builders';
|
||||
|
||||
import * as options from './option-builders';
|
||||
export { options };
|
||||
@@ -1,339 +1,367 @@
|
||||
import {
|
||||
Constants,
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
type ModalSubmitInteractionData,
|
||||
type FileUploadComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type ActionRowItem = Button | StringSelectMenu | UserSelectMenu | RoleSelectMenu | MentionableSelectMenu | ChannelSelectMenu;
|
||||
export const actionRow = (...components: ActionRowItem[]): ActionRow => ({
|
||||
type: Constants.ComponentTypes.ACTION_ROW,
|
||||
components: components.filter((c) => c),
|
||||
});
|
||||
|
||||
export enum ButtonStyle {
|
||||
PRIMARY = 1,
|
||||
SECONDARY = 2,
|
||||
SUCCESS = 3,
|
||||
DANGER = 4,
|
||||
}
|
||||
|
||||
export interface ButtonOptions {
|
||||
style?: ButtonStyle;
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const button = (label: string, custom_id: string, options?: ButtonOptions): InteractionButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: options?.style ?? Constants.ButtonStyles.PRIMARY,
|
||||
label,
|
||||
custom_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface URLButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const urlButton = (label: string, url: string, options?: URLButtonOptions): URLButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.LINK,
|
||||
label,
|
||||
url,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface PremiumButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const premiumButton = (sku_id: string, options?: PremiumButtonOptions): PremiumButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.PREMIUM,
|
||||
sku_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface StringSelectOpts {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
}
|
||||
|
||||
export interface StringSelectOption {
|
||||
label: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
emoji?: PartialEmoji;
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
export interface StringSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export const stringSelect = (custom_id: string, selectOpts: StringSelectOpts, ...options: StringSelectOption[]): StringSelectMenu => ({
|
||||
type: Constants.ComponentTypes.STRING_SELECT,
|
||||
custom_id,
|
||||
options,
|
||||
placeholder: selectOpts.placeholder,
|
||||
min_values: selectOpts.min_values ?? 1,
|
||||
max_values: selectOpts.max_values ?? 1,
|
||||
disabled: selectOpts.disabled ?? false,
|
||||
required: selectOpts.required ?? false, // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
});
|
||||
|
||||
export interface InputOptions {
|
||||
isParagraph?: boolean;
|
||||
label?: string;
|
||||
min_length?: number;
|
||||
max_length?: number;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export const input = (custom_id: string, options?: InputOptions): TextInput => ({
|
||||
type: Constants.ComponentTypes.TEXT_INPUT,
|
||||
custom_id,
|
||||
style: options.isParagraph ? Constants.TextInputStyles.PARAGRAPH : Constants.TextInputStyles.SHORT,
|
||||
label: options?.label,
|
||||
min_length: options?.min_length ?? 0,
|
||||
max_length: options?.max_length ?? 4000,
|
||||
required: options?.required ?? false,
|
||||
value: options?.value,
|
||||
placeholder: options?.placeholder,
|
||||
});
|
||||
|
||||
export interface UserSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'user' }>;
|
||||
}
|
||||
|
||||
export const userSelect = (custom_id: string, options?: UserSelectOptions): UserSelectMenu => ({
|
||||
type: Constants.ComponentTypes.USER_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface RoleSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'role' }>;
|
||||
}
|
||||
|
||||
export const roleSelect = (custom_id: string, options?: RoleSelectOptions): RoleSelectMenu => ({
|
||||
type: Constants.ComponentTypes.ROLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface MentionableSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'user' | 'role' }>;
|
||||
}
|
||||
|
||||
export const mentionableSelect = (custom_id: string, options?: MentionableSelectOptions): MentionableSelectMenu => ({
|
||||
type: Constants.ComponentTypes.MENTIONABLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface ChannelSelectOptions {
|
||||
channel_types?: GuildChannelTypes[];
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'channel' }>;
|
||||
}
|
||||
|
||||
export const channelSelect = (custom_id: string, options?: ChannelSelectOptions): ChannelSelectMenu => ({
|
||||
type: Constants.ComponentTypes.CHANNEL_SELECT,
|
||||
custom_id,
|
||||
channel_types: options?.channel_types ?? [],
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface SectionOptions {
|
||||
components: Array<TextDisplayComponent>;
|
||||
accessory: Button | ThumbnailComponent;
|
||||
}
|
||||
|
||||
export const section = (accessory: Button | ThumbnailComponent, ...components: Array<TextDisplayComponent>): SectionComponent => ({
|
||||
type: Constants.ComponentTypes.SECTION,
|
||||
accessory,
|
||||
components: components.filter((c) => c),
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a text display component where the text will be displayed similar to a message: supports markdown
|
||||
* @param content The text content to display.
|
||||
* @returns The created text display component.
|
||||
*/
|
||||
export const text = (content: string) => ({
|
||||
type: Constants.ComponentTypes.TEXT_DISPLAY,
|
||||
content,
|
||||
});
|
||||
|
||||
export interface ThumbnailOptions {
|
||||
media: {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
};
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const thumbnail = (url: string, description?: string, spoiler?: boolean): ThumbnailComponent => ({
|
||||
type: Constants.ComponentTypes.THUMBNAIL,
|
||||
media: {
|
||||
url,
|
||||
},
|
||||
description,
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export interface MediaItem {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const gallery = (...items: MediaItem[]): MediaGalleryComponent => ({
|
||||
type: Constants.ComponentTypes.MEDIA_GALLERY,
|
||||
items: items.map((item) => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
media: { url: item.url },
|
||||
description: item.description,
|
||||
spoiler: item.spoiler,
|
||||
})),
|
||||
});
|
||||
|
||||
export interface FileOptions {
|
||||
url: string; // Supports only attachment://<filename> references
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const file = (url: string, spoiler?: boolean): FileComponent => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
file: {
|
||||
url,
|
||||
},
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export enum Padding {
|
||||
SMALL = 1,
|
||||
LARGE = 2,
|
||||
}
|
||||
|
||||
export interface SeparatorOptions {
|
||||
divider?: boolean;
|
||||
spacing?: Padding;
|
||||
}
|
||||
export const separator = (spacing?: Padding, divider?: boolean): SeparatorComponent => ({
|
||||
type: Constants.ComponentTypes.SEPARATOR,
|
||||
divider,
|
||||
spacing: spacing ?? Padding.SMALL,
|
||||
});
|
||||
|
||||
export interface ContainerOptions {
|
||||
accent_color?: number;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export type ContainerItems =
|
||||
| ActionRow
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent;
|
||||
|
||||
export const container = (options: ContainerOptions, ...components: ContainerItems[]): ContainerComponent => ({
|
||||
type: Constants.ComponentTypes.CONTAINER,
|
||||
...options,
|
||||
components: components.filter((c) => c),
|
||||
});
|
||||
|
||||
// Modals
|
||||
|
||||
export interface LabelOptions {
|
||||
label: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const label = (options: LabelOptions, component: LabelComponent['component']): LabelComponent => ({
|
||||
type: Constants.ComponentTypes.LABEL,
|
||||
label: options.label,
|
||||
description: options.description,
|
||||
component,
|
||||
});
|
||||
|
||||
export const modal = (
|
||||
options: { custom_id?: string; title?: string },
|
||||
...components: Array<LabelComponent | ActionRow | TextDisplayComponent>
|
||||
): ModalSubmitInteractionData =>
|
||||
({
|
||||
type: 9 as any, // Modal type
|
||||
custom_id: options.custom_id ?? '',
|
||||
title: options.title ?? '',
|
||||
components: components.filter((c) => c),
|
||||
} as any);
|
||||
import {
|
||||
Constants,
|
||||
type ActionRow,
|
||||
type Button,
|
||||
type ChannelSelectMenu,
|
||||
type GuildChannelTypes,
|
||||
type MentionableSelectMenu,
|
||||
type PartialEmoji,
|
||||
type RoleSelectMenu,
|
||||
type StringSelectMenu,
|
||||
type TextInput,
|
||||
type UserSelectMenu,
|
||||
type LabelComponent,
|
||||
type ContainerComponent,
|
||||
type TextDisplayComponent,
|
||||
type SectionComponent,
|
||||
type MediaGalleryComponent,
|
||||
type SeparatorComponent,
|
||||
type FileComponent,
|
||||
type InteractionButton,
|
||||
type URLButton,
|
||||
type PremiumButton,
|
||||
type ThumbnailComponent,
|
||||
type ModalSubmitInteractionData,
|
||||
type FileUploadComponent,
|
||||
} from '@projectdysnomia/dysnomia';
|
||||
|
||||
export type ActionRowItem = Button | StringSelectMenu | UserSelectMenu | RoleSelectMenu | MentionableSelectMenu | ChannelSelectMenu;
|
||||
export const actionRow = (...components: ActionRowItem[]): ActionRow => ({
|
||||
type: Constants.ComponentTypes.ACTION_ROW,
|
||||
components: components.filter((c) => c),
|
||||
});
|
||||
|
||||
export enum ButtonStyle {
|
||||
PRIMARY = 1,
|
||||
SECONDARY = 2,
|
||||
SUCCESS = 3,
|
||||
DANGER = 4,
|
||||
}
|
||||
|
||||
export interface ButtonOptions {
|
||||
style?: ButtonStyle;
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const button = (label: string, custom_id: string, options?: ButtonOptions): InteractionButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: options?.style ?? Constants.ButtonStyles.PRIMARY,
|
||||
label,
|
||||
custom_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface URLButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const urlButton = (label: string, url: string, options?: URLButtonOptions): URLButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.LINK,
|
||||
label,
|
||||
url,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface PremiumButtonOptions {
|
||||
emoji?: PartialEmoji;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const premiumButton = (sku_id: string, options?: PremiumButtonOptions): PremiumButton => ({
|
||||
type: Constants.ComponentTypes.BUTTON,
|
||||
style: Constants.ButtonStyles.PREMIUM,
|
||||
sku_id,
|
||||
...options,
|
||||
});
|
||||
|
||||
export interface StringSelectOpts {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
}
|
||||
|
||||
export interface StringSelectOption {
|
||||
label: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
emoji?: PartialEmoji;
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
export const option = (option: StringSelectOption): StringSelectOption => option;
|
||||
|
||||
export interface StringSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export const stringSelect = (custom_id: string, selectOpts: StringSelectOpts, ...options: StringSelectOption[]): StringSelectMenu => ({
|
||||
type: Constants.ComponentTypes.STRING_SELECT,
|
||||
custom_id,
|
||||
options,
|
||||
placeholder: selectOpts.placeholder,
|
||||
min_values: selectOpts.min_values ?? 1,
|
||||
max_values: selectOpts.max_values ?? 1,
|
||||
disabled: selectOpts.disabled ?? false,
|
||||
required: selectOpts.required ?? false, // Note: not actually a property of StringSelectMenu, but useful for modals
|
||||
});
|
||||
|
||||
export interface InputOptions {
|
||||
isParagraph?: boolean;
|
||||
label?: string;
|
||||
min_length?: number;
|
||||
max_length?: number;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export const input = (custom_id: string, options?: InputOptions): TextInput => ({
|
||||
type: Constants.ComponentTypes.TEXT_INPUT,
|
||||
custom_id,
|
||||
style: options.isParagraph ? Constants.TextInputStyles.PARAGRAPH : Constants.TextInputStyles.SHORT,
|
||||
label: options?.label,
|
||||
min_length: options?.min_length ?? 0,
|
||||
max_length: options?.max_length ?? 4000,
|
||||
required: options?.required ?? false,
|
||||
value: options?.value,
|
||||
placeholder: options?.placeholder,
|
||||
});
|
||||
|
||||
export interface UserSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'user' }>;
|
||||
}
|
||||
|
||||
export const userSelect = (custom_id: string, options?: UserSelectOptions): UserSelectMenu => ({
|
||||
type: Constants.ComponentTypes.USER_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface RoleSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'role' }>;
|
||||
}
|
||||
|
||||
export const roleSelect = (custom_id: string, options?: RoleSelectOptions): RoleSelectMenu => ({
|
||||
type: Constants.ComponentTypes.ROLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface MentionableSelectOptions {
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'user' | 'role' }>;
|
||||
}
|
||||
|
||||
export const mentionableSelect = (custom_id: string, options?: MentionableSelectOptions): MentionableSelectMenu => ({
|
||||
type: Constants.ComponentTypes.MENTIONABLE_SELECT,
|
||||
custom_id,
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface ChannelSelectOptions {
|
||||
channel_types?: GuildChannelTypes[];
|
||||
placeholder?: string;
|
||||
min_values?: number;
|
||||
max_values?: number;
|
||||
disabled?: boolean;
|
||||
required?: boolean; // if on a modal
|
||||
default_values?: Array<{ id: string; type: 'channel' }>;
|
||||
}
|
||||
|
||||
export const channelSelect = (custom_id: string, options?: ChannelSelectOptions): ChannelSelectMenu => ({
|
||||
type: Constants.ComponentTypes.CHANNEL_SELECT,
|
||||
custom_id,
|
||||
channel_types: options?.channel_types ?? [],
|
||||
placeholder: options?.placeholder ?? '',
|
||||
min_values: options?.min_values ?? 1,
|
||||
max_values: options?.max_values ?? 1,
|
||||
disabled: options?.disabled ?? false,
|
||||
default_values: options?.default_values ?? [],
|
||||
});
|
||||
|
||||
export interface SectionOptions {
|
||||
components: Array<TextDisplayComponent>;
|
||||
accessory: Button | ThumbnailComponent;
|
||||
}
|
||||
|
||||
export const section = (accessory: Button | ThumbnailComponent, ...components: Array<TextDisplayComponent>): SectionComponent => ({
|
||||
type: Constants.ComponentTypes.SECTION,
|
||||
accessory,
|
||||
components: components.filter((c) => c),
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a text display component where the text will be displayed similar to a message: supports markdown
|
||||
* @param content The text content to display.
|
||||
* @returns The created text display component.
|
||||
*/
|
||||
export const text = (content: string) => ({
|
||||
type: Constants.ComponentTypes.TEXT_DISPLAY,
|
||||
content,
|
||||
});
|
||||
|
||||
export interface ThumbnailOptions {
|
||||
media: {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
};
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const thumbnail = (url: string, description?: string, spoiler?: boolean): ThumbnailComponent => ({
|
||||
type: Constants.ComponentTypes.THUMBNAIL,
|
||||
media: {
|
||||
url,
|
||||
},
|
||||
description,
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export interface MediaItem {
|
||||
url: string; // Supports arbitrary urls and attachment://<filename> references
|
||||
description?: string;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const gallery = (...items: MediaItem[]): MediaGalleryComponent => ({
|
||||
type: Constants.ComponentTypes.MEDIA_GALLERY,
|
||||
items: items.map((item) => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
media: { url: item.url },
|
||||
description: item.description,
|
||||
spoiler: item.spoiler,
|
||||
})),
|
||||
});
|
||||
|
||||
export interface FileOptions {
|
||||
url: string; // Supports only attachment://<filename> references
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export const file = (url: string, spoiler?: boolean): FileComponent => ({
|
||||
type: Constants.ComponentTypes.FILE,
|
||||
file: {
|
||||
url,
|
||||
},
|
||||
spoiler,
|
||||
});
|
||||
|
||||
export enum Padding {
|
||||
SMALL = 1,
|
||||
LARGE = 2,
|
||||
}
|
||||
|
||||
export interface SeparatorOptions {
|
||||
divider?: boolean;
|
||||
spacing?: Padding;
|
||||
}
|
||||
export const separator = (spacing?: Padding, divider?: boolean): SeparatorComponent => ({
|
||||
type: Constants.ComponentTypes.SEPARATOR,
|
||||
divider,
|
||||
spacing: spacing ?? Padding.SMALL,
|
||||
});
|
||||
|
||||
export interface ContainerOptions {
|
||||
accent_color?: number;
|
||||
spoiler?: boolean;
|
||||
}
|
||||
|
||||
export type ContainerItems =
|
||||
| ActionRow
|
||||
| TextDisplayComponent
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent;
|
||||
|
||||
export const container = (options: ContainerOptions, ...components: ContainerItems[]): ContainerComponent => ({
|
||||
type: Constants.ComponentTypes.CONTAINER,
|
||||
...options,
|
||||
components: components.filter((c) => c),
|
||||
});
|
||||
|
||||
// Modals
|
||||
|
||||
export interface LabelOptions {
|
||||
label: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const label = (options: LabelOptions, component: LabelComponent['component']): LabelComponent => ({
|
||||
type: Constants.ComponentTypes.LABEL,
|
||||
label: options.label,
|
||||
description: options.description,
|
||||
component,
|
||||
});
|
||||
|
||||
export const modal = (
|
||||
options: { custom_id?: string; title?: string },
|
||||
...components: Array<LabelComponent | ActionRow | TextDisplayComponent>
|
||||
): ModalSubmitInteractionData =>
|
||||
({
|
||||
type: 9 as any, // Modal type
|
||||
custom_id: options.custom_id ?? '',
|
||||
title: options.title ?? '',
|
||||
components: components.filter((c) => c),
|
||||
} as any);
|
||||
|
||||
export interface ComponentsV2Options {
|
||||
ephemeral?: boolean;
|
||||
}
|
||||
|
||||
export const componentsV2 = (
|
||||
options: ComponentsV2Options,
|
||||
...components: Array<
|
||||
| ContainerComponent
|
||||
| ActionRow
|
||||
| TextDisplayComponent
|
||||
| Button
|
||||
| SectionComponent
|
||||
| MediaGalleryComponent
|
||||
| SeparatorComponent
|
||||
| FileComponent
|
||||
| UserSelectMenu
|
||||
| RoleSelectMenu
|
||||
| MentionableSelectMenu
|
||||
| ChannelSelectMenu
|
||||
| StringSelectMenu
|
||||
>
|
||||
) => ({
|
||||
flags: Constants.MessageFlags.IS_COMPONENTS_V2 | (options.ephemeral ? Constants.MessageFlags.EPHEMERAL : 0),
|
||||
components: components.filter((c) => c),
|
||||
});
|
||||
5
packages/discord/src/components/index.ts
Normal file
5
packages/discord/src/components/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './helpers';
|
||||
export * from './builders';
|
||||
|
||||
import * as components from './builders';
|
||||
export { components };
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Client as DJSClient } from '@projectdysnomia/dysnomia';
|
||||
import kv, { asyncKV } from '@/util/kv.js';
|
||||
import kv, { asyncKV } from '@star-kitten/util/kv.js';
|
||||
import type { KVStore } from './kv-store.type.ts.ts';
|
||||
import type { Cache } from './cache.type.ts';
|
||||
import { registerCommands } from '../commands/handle-commands.ts';
|
||||
@@ -1,7 +1,6 @@
|
||||
export * from './constants';
|
||||
export * from './commands';
|
||||
export * from './core';
|
||||
export * from './jsx';
|
||||
export * from './components';
|
||||
export * from './pages';
|
||||
export * from './types';
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Constants, type InteractionContentEdit, type InteractionModalContent } from '@projectdysnomia/dysnomia';
|
||||
import type { CommandContext, ExecutableInteraction } from '../types';
|
||||
import type { StarKittenElement } from '../jsx';
|
||||
|
||||
export enum PageType {
|
||||
MODAL = 'modal',
|
||||
@@ -15,8 +14,8 @@ export interface Page<T> {
|
||||
render: (
|
||||
ctx: PageContext<T>,
|
||||
) =>
|
||||
| (InteractionModalContent | InteractionContentEdit | StarKittenElement)
|
||||
| Promise<InteractionModalContent | InteractionContentEdit | StarKittenElement>;
|
||||
| (InteractionModalContent | InteractionContentEdit)
|
||||
| Promise<InteractionModalContent | InteractionContentEdit>;
|
||||
}
|
||||
|
||||
export interface PagesOptions<T> {
|
||||
@@ -132,7 +131,7 @@ export async function usePages<T>(options: PagesOptions<T>, interaction: Executa
|
||||
const content = isPromise(maybePromise) ? await maybePromise : maybePromise;
|
||||
return await pagesInteraction.createFollowup({
|
||||
flags,
|
||||
...wrapJSXContent(content),
|
||||
...content as any,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -140,7 +139,7 @@ export async function usePages<T>(options: PagesOptions<T>, interaction: Executa
|
||||
await pagesInteraction.deferUpdate();
|
||||
const maybePromise = page.render(pageContext);
|
||||
const content = isPromise(maybePromise) ? await maybePromise : maybePromise;
|
||||
return await pagesInteraction.editMessage(pageState.messageId, wrapJSXContent(content));
|
||||
return await pagesInteraction.editMessage(pageState.messageId, content as any);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -149,7 +148,7 @@ export async function usePages<T>(options: PagesOptions<T>, interaction: Executa
|
||||
const content = isPromise(maybePromise) ? await maybePromise : maybePromise;
|
||||
const message = await pagesInteraction.createFollowup({
|
||||
flags: getFlags(options),
|
||||
...wrapJSXContent(content),
|
||||
...content as any,
|
||||
});
|
||||
pageState.messageId = message.id;
|
||||
pageState.channelId = message.channel?.id;
|
||||
@@ -161,9 +160,3 @@ function isPromise<T>(value: T | Promise<T>): value is Promise<T> {
|
||||
return typeof (value as Promise<T>)?.then === 'function';
|
||||
}
|
||||
|
||||
function wrapJSXContent(content: any) {
|
||||
if ('type' in content) {
|
||||
return { components: [content] };
|
||||
}
|
||||
return content;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PartialEmoji } from '@projectdysnomia/dysnomia';
|
||||
import { actionRow, button, gallery, type ButtonOptions, type ContainerItems } from '@/discord/components';
|
||||
import { actionRow, button, gallery, type ButtonOptions, type ContainerItems } from '@/components';
|
||||
import type { PageContext } from './pages';
|
||||
|
||||
export function getSubrouteKey(prefix: string, subroutes: string[]) {
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { ChatInputApplicationCommandStructure, ApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
import type { ExecutableInteraction } from './interaction.type';
|
||||
import type { Cache } from '@/discord/core/cache.type';
|
||||
import type { KVStore } from '@/discord/core/kv-store.type.ts';
|
||||
import type { Client } from '@/discord/core/client';
|
||||
import type { Cache } from '@/core/cache.type';
|
||||
import type { KVStore } from '@/core/kv-store.type.ts';
|
||||
import type { Client } from '@/core/client';
|
||||
|
||||
export interface CommandState<T = any> {
|
||||
id: string; // unique id for this command instance
|
||||
@@ -1,5 +1,4 @@
|
||||
import type Dysnomia from '@projectdysnomia/dysnomia';
|
||||
import type { StarKittenElement } from '../jsx';
|
||||
|
||||
export type Interaction = CommandInteraction | ModalSubmitInteraction | ComponentInteraction | AutocompleteInteraction | PingInteraction;
|
||||
|
||||
@@ -14,10 +13,6 @@ export interface InteractionAugments {
|
||||
isAutocomplete: () => this is Dysnomia.AutocompleteInteraction;
|
||||
isPing: () => this is Dysnomia.PingInteraction;
|
||||
isExecutable: () => this is ExecutableInteraction;
|
||||
createJSXMessage: (component: StarKittenElement) => Promise<Dysnomia.Message>;
|
||||
editJSXMessage: (messageID: string, component: StarKittenElement) => Promise<Dysnomia.Message>;
|
||||
createJSXFollowup: (component: StarKittenElement) => Promise<Dysnomia.Message>;
|
||||
createJSXModal: (component: StarKittenElement) => Promise<void>;
|
||||
}
|
||||
|
||||
export type CommandInteraction = Dysnomia.CommandInteraction & InteractionAugments;
|
||||
23
packages/discord/tsconfig.json
Normal file
23
packages/discord/tsconfig.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"strict": false,
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"lib": ["ESNext"],
|
||||
"typeRoots": ["src/types", "./node_modules/@types"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
},
|
||||
"emitDeclarationOnly": true,
|
||||
"noEmit": false,
|
||||
"noEmitOnError": false,
|
||||
"declaration": true,
|
||||
"outDir": "dist/types",
|
||||
"rootDir": ".",
|
||||
"allowImportingTsExtensions": true
|
||||
},
|
||||
"include": ["src", "fixtures"],
|
||||
"exclude": ["node_modules", "build", "**/*.test.ts"]
|
||||
}
|
||||
19
packages/discord/tsdown.config.ts
Normal file
19
packages/discord/tsdown.config.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { defineConfig } from 'tsdown';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: [
|
||||
'./src/index.ts',
|
||||
'./src/commands/index.ts',
|
||||
'./src/components/index.ts',
|
||||
'./src/pages/index.ts',
|
||||
'./src/common/index.ts',
|
||||
],
|
||||
platform: 'node',
|
||||
dts: true,
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
unbundle: true,
|
||||
external: ['bun:sqlite', 'bun'],
|
||||
},
|
||||
]);
|
||||
33
packages/eve-data/.env.development
Normal file
33
packages/eve-data/.env.development
Normal file
@@ -0,0 +1,33 @@
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY_DEVELOPMENT="02572da3d4f3a844588a944214c0e142a5a01deaa6551456af146d34b574024416"
|
||||
|
||||
# .env.development
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY="02292a330aa041b5f7efc51504e0c208accba67a6877a217ab43cbb59c3c0c3e66"
|
||||
|
||||
# .env
|
||||
DEBUG="encrypted:BL9a2Dle847wiqvjnB1ryC8L5vJy0pn/5BZJdEMSWBIzcxPN3MhXoHsQ4KQcRXW4xuWLpHq00aAH5kNda3Bu7kPPbnltvhcYoqQSz3giItL8th1T/qm4zc+gM7lNHl/+5G0xJFE="
|
||||
PORT="encrypted:BKVA322kVuBfcDbptjVrvp9O8+1w0iCNT7Sv777MfCuLHTdJdUZr5jZbcKBD8yxPGpkc6sNdB4j6n2flC5NwExHdO/mdGD1VEBpdcZQCcSO7MYOYt1j0pfLNkvEqpuHnD1tkxT8="
|
||||
NODE_ENV="encrypted:BLNNdnKOW0f8d614oe1ur73es5z/2MhyJIXmeMNvU/VlOBL1dg/mT4vHosXG908eCyM2Nx1v7GxAfXmdpVSzWltfODwJzPw/lPZV3JFB2yIt4f8zJIHvkHKPMFLupK9bDUUS2m63uvZ4IE0v"
|
||||
LOG_LEVEL="encrypted:BOZXADbkv9vkzwx7/JC/EcBC00W5li6rbluRVGwy5t0fIj2WaoWXkwlTmHf5NiczQLPCgB+HD6LJxAeef8uEMgN0hwDCBXVGl53HWwDjkmxmmDAdfBmOeldJhDQbFNXumyrmKIOu"
|
||||
BASE_URL="encrypted:BFjoUMvbNCwqcd8j74dNe+4MZn1STXKTVq8LoP+CPuunNfL9QQkjyR+ryH0rgOH0W0LVLQBszEEpEiFFOohq3/uZlN/UTsqkQXntINvBMvT2S2dTTTafB4jgSoNrS6VDSx3mipO15IzQZ/QlADQrqVI5Y+iB7Y2lAx7M"
|
||||
EVE_CLIENT_ID="encrypted:BPpft3gC8xuWAg5SzhnoG009FhlJ9K65Ku7QoRrjZFAm7VaR2WDbH7lPMXoXY1/tu2sFln2+iCA0J8VMwqnvmHgocQtE2qPmRHdZUUSuGph76uIU4CNaIRICpXyn3jOfPIjsyBXcsKrMwLEdfeEwkxazDJ8ZAw/r+WHre9xr+iwK"
|
||||
EVE_CLIENT_SECRET="encrypted:BNXhx7jO6z2IfGU0TLUxMnONUXYWlQvM+RxUYDuOb+Tsncr32aJ4j6pFMHMeUYcqrQIogads6GDNb73trhk9XvpPu8kBgrksdkCtErp47u5++6yRGLkqTmkdpZeja2KCaGx0Bcu4EfP5DS/H9Mc6nvb6+wctj8s/w2MguF6O5gg/2LNDNT3tbrI="
|
||||
EVE_CALLBACK_URL="encrypted:BPzYFYlfnfLhPQuwRyUZs3F3wxjy6TEHQKNjbu+XnztSS6rJh8eAuW0ThYxee8lBAzLSGgKmjIt1PkYljnTekrR2CKyp3hMhag6W08tzxJDs4qyAsi+X91mEHXrd+ekmr4d5iAZu+sr/rHiK9jkzAsVWA8e8ttAVtW5Us7sb6ovZ1bjWM20oXk+4e5Gg"
|
||||
ESI_USER_AGENT="encrypted:BI/g/h+eCwT468Io0A9DhVU+z1iWiJwOI75mNDqZMJB1LWyhd/+pneYlcEAJJEsAPP4+YN66qAZ/8PzgH6rVcXS5H27FqwC+/mJFsMfC9jAkFDl38nxDogpKJUAimpG53ERM7LegMMcxAsnr085JduloijL5ylCwEwpiewzWwN+xqOOcQrW6UcQgsgbyonIlqaYstN4NdplvdSC2zvrYN5OnToEoQ2YFiAtU7dTxSx0="
|
||||
DISCORD_APP_ID="encrypted:BIiCdiy6ypeM3LK9pvkqoNIn0Js7XS5rUQqBNzZcywm9eH21gSit2rb+cwYGBGzYmkv/xG6Zf/CnUA7STrtRFfOD57vF46DVam0now4IVrTWr7X3CZLgBa0TBJgph72rTSTg8HKz8OheH/y7u730uN0OLhw="
|
||||
DISCORD_APP_SECRET="encrypted:BHNfy9BO/85OpyEQgehnwAByFz0jyE3b/YiK69aVFFKotBjeh9rj/0eg2/LsJvPsAHW3NDHmSWIvo+0gqTMuFbbA52wyLk4HeZmX5yNV6sQfGLitwJVCiyfXWpW81dkwTWF4bPNu/zTDlN7hJVuOTz9o6QN5WRSlbCiuP3n7Ojk8"
|
||||
DISCORD_PUBLIC_KEY="encrypted:BMzCllTpNrIQy9DkWGqE6tW7XxIEdui58eV5PqmMtwizEptjZVGp+IRtin9SUXBOtugzUUn51KP4+8AQauyFP4/oiEpiCa04lPLy2OF0pTzF61zC6jBb+gGL6fbvA2DWORXkR98sK6IAO5+OaGXy1SnHU1qjM7J5GFBaAywdPmDdLpuD4zJLvXggpooepaHHKer4ViyBAev9QU2Zb9/Ants="
|
||||
DISCORD_BOT_TOKEN="encrypted:BKJPnAvriqdaKV5eirAAs6TgtmtJMj7ZMjf8Y8sbD+T3WK/tbh8mgNNlyPk+ztre5mQD46vuWMa9F9uYCZcBmlk3XcRiYFwtyIGeaUPw2hT5kswk+jcaj66HKTknF09KwOhjvg3O2394d9p0jNFsx6ioT1ddzoSEKcQkyeBuJAsOTGnPVQKkfUNCRCWXMf6C3pxqVmFP/azfFFQNy8yevXnhZU97Zk9Ohg=="
|
||||
DISCORD_TEST_GUILD_ID="encrypted:BP+e5/IUX1gJP+/ZtG1LxwWcLeYsYwhM6boSbJv1YDhg3FF/qDM6A3OXsneSBP3PTDwW7e14M5U0jRIMT9YemyuF9r7895fMgBnFbPfSY+WaRDruibFI6QW1RvEcTuFjoU2/kRT6s69C/I1frYEMja2rfw=="
|
||||
JANICE_KEY="encrypted:BIb85AGnz4hgENGxJYhDmtEE6rEPjfBKZBtJ4KuDNtAjyIEI4kRKQNkOudVxc4BXQ6MXIqhIPIuTE6DdCN03olEfk3Fo5/FmwOr1bWF6pYFbwMxdVgjc9Cj9Lie3+tAnr3MaOfT0y+P+zUiAMWRJELpLv0TzSNhmTAF1LS+9zjUV"
|
||||
PERPLEXITY_API_KEY="encrypted:BNy5gd/Ku1Dilcfc9VftzIJn5xxDCT0NapIw5i+4h1srz2nNVgDc8w6GvmdBBz/NiPut9x+7Wv5hKVuXh2N07HaO67bTugzZ7dwabQdDJGlRp8mo/TEJMIN9Pm1ztI5HZW4UGfMY4xNILHRJb+JRBNdI4GZSh13i6vyc8ym9BgOXLVTR3ZSCJwFqzeDTgR9R3NhIjUa3"
|
||||
STAR_KITTEN_KV_DB_PATH="encrypted:BPZyMGyF1A+mQQ+OKNtHJLM+EGyrgkfhO6hHs1YHaoifLsMbJz2NDZmOzDg+ubPTUyjOJ+XbjcVdEFLn23QoOqKtW/WSk6WPPNAgWnWrTl+5XJI7bfGKaaZ1LR1PaB4ftPyMLFyoWVZ2iPHBP1xv"
|
||||
POSTGRES_URL="encrypted:BLUN+6NZpCkc+8WvJkhB3v8sYoX88maPYzkBi2WXItAOtA2r/1a65yZThcumddnba7WzaaN7xPv0CUA6LZpszP8t59LJ2ewKNJJyi9Z5bHINoedLh57kwlu0TcGsoO/EUPS81V0FZAlxLnmuVvMn/xYxmXJWiTeQRCe4vxLSctMNWgvcWXuQivGrLFh6AMtgfi+FxtkaH5zYXIjDn1Q/U2gKTwyLxmPBrXxFnYx8aGI9Leios605d/4lugra"
|
||||
NEO4J_URL="encrypted:BGphrNITLIGqF5aVoY9F2IaDVP+IRXuPvtfTRKtNqL8u5JIqkbEBpRknfB3wA2XoYiDsQ2ADfwwSuw5uvj1IWU2/ZS9AEukGJGYa6RP1dI/R0XvRAqS7O9waOH8nuH3KPaTSiEfeLqd+c+3J8MAKgpaysNp1nKGsZa6RzjhYEWxsKw3Yp4LY5d3yRloqxWsAJsd3WnH8cJWvJi0gT04PEJ73gX4TyHlPuduH37uo6os="
|
||||
23
packages/eve-data/.env.production
Normal file
23
packages/eve-data/.env.production
Normal file
@@ -0,0 +1,23 @@
|
||||
#/-------------------[DOTENV_PUBLIC_KEY]--------------------/
|
||||
#/ public-key encryption for .env files /
|
||||
#/ [how it works](https://dotenvx.com/encryption) /
|
||||
#/----------------------------------------------------------/
|
||||
DOTENV_PUBLIC_KEY_PRODUCTION="02f0469506f6722d8fcc179c199ff159ca32f082000c8e7a1465891adb50a4c031"
|
||||
|
||||
# .env.production
|
||||
DEBUG="encrypted:BK1eaaKoyGmDyiuLd+fIdaEdCGyYgLyUv5tsNcXvZ8CTMMXi7SBcNSy0K5hI0VC0ptEIX7iO/sZQcQ1K0V9gBFCoNDpuTvO+c3bFKU2nKQ0BBULlXhJNDoETmZ2p9L7KLNDFYQ6d"
|
||||
PORT="encrypted:BOpbRvBpbB3ra5St/8dv2h/93aL1vu1rVbc9u/V0YZyvP6nJv88rToz2/zfyNDeqsjBA2IO4s3+v3ZtxSJ6XXwd3+or3++yGs7A8t7bGoxVRyzb0uluHh6hWx+DKdxZE7HIYAh0="
|
||||
NODE_ENV="encrypted:BORW/YY5Y5EV7Lh5L3fSOfKsbyH5/ZBtMH/iuurGnJZlvFhBsZTBm5ms02dLJgFN/F1HFYskR8xLyEnnCCndK3YpdzrPYbVYSPr5EuWsVeX6DbCROi7RY1Zd27kYBSA5QjxHJOxWCIL0djY="
|
||||
LOG_LEVEL="encrypted:BHAO8Gg4Y3bo9Cwi1+YOvtc0dSgYUS6+UWNTcHfWISsrv4XX1sj/T9wjEGb8Y6dCmenqPSSI0DaBaEoLXDG3S31mdhTPmMfNBWRQQFbIxrz17WtqIDN6MGiI7/6ki5qryzup6ME="
|
||||
BASE_URL="encrypted:BAAw6UxbwTW7snM4pjPLGsYtZGbyOSDR9BwIo2oPDGPernDYD5YS80ffc7N1cRpLt807Vg41YoDsuj5PnwjmU9Zg7rl6cUnFKnLcvuFHNWdiULcl7bPQjHgZq1/wXwmYRt8p1DOI1nAgZIaxf58hi/c/9pUy5IpE"
|
||||
DISCORD_APP_ID="encrypted:BEneta1lCVd+uZCdIEOCtilukJb8W/57BZRvSzg7a6MnFkNxqlT2CXiZH6kvO9k+s3qj8zCUdsb6vQP0BzLlmDmmFLQGMn6q/NOlYArn0WlIUz+81JvMOZnv1YH6uFnJczJugtPoBy8d9Jjvn1u6vT+6pfM="
|
||||
DISCORD_APP_SECRET="encrypted:BG4R0eH2AHda/W+EkwT82dhO1UAjQZAbrXfn0+vOeNT/E3EQZ3VH/o5N1Ed3Hjg/mf90jv+ey3HBimt747dN+IBgkzkYpbs44h/TVZukSPwN5wJR14nVRwtU+ZZYX9UUsb/wjWFn7n5DCCOIs2N5Z/bqbRw4mPruKwBzJ5SD8JgD"
|
||||
DISCORD_PUBLIC_KEY="encrypted:BBTLdwJa7/tbzH4bddef00+PpJN9ZKCnSKxIWLuc10oKkg5g5TRSt9cnTRZvAsWGeacKxrECyl2WjoPYYI0qqDPZv/OHRejQ1EwDpfEVJVVoL7Xdm3c5Ncs0Vog03al3gEMbc2Zs6X3Teyt4K96PybwZ/fyhqsoHwmJ0mSvafD+2ASYii3tP23TkFewtnOenlZTBblXGkLy5C9Vkvodu1Qg="
|
||||
DISCORD_BOT_TOKEN="encrypted:BKIDa8iPhX1BKarlpoJOd/qhn/wLz0LsSI3joaCKWcEWpRynWAU1e1lfAA85j1EzVsX2BE2AcUbDS+FDzuacRooOgXk1fcywf2RSA+skDqdSGwNXDUQvOlptTdGyb8Eq10MkQOtUCHn50npULOCFkoGVHunzHCOtLAKzq0n758X8SOswfdWgDLQP8vtB9BF6SfkCu93bDDeZU+gJvYU3x/GwLqlaaJdF0A=="
|
||||
EVE_CLIENT_ID="encrypted:BA6dxhE8M8b3pDjcXDFBPdMw8aIBd1CUlN+NqnzC9XCZUc5Y7g4n2le0NPk6BwlgbrM9iRRdxzNkfmXiopJ1C33N4t095F/qd2cHAmya05//XAEOnOjA8MYA889rA/Jn1TXY2dSrLfp8UAMFo2pdu7r8QDAXdRUWoefAW/CmD0vR"
|
||||
EVE_CLIENT_SECRET="encrypted:BP7JP9rGeAWdVBashqAcGmB9v2WstOu7jJiB72jyzEK8PtSXA8eZK4MdFjwkfilZJr9k0kQcIYH4E/koKEW6BANDr5Ph8pYRz+r/lS11fsoJ1HZhujPKyuKIaIVEHxGb94FU7bJb6tAE0ozEZPZzeEZFGKJe5x9dR8qbAuD3oRC9JbidH4Lu2kQ="
|
||||
EVE_CALLBACK_URL="encrypted:BBOyw5Un+FeDukVE387oHdUZ6k3rMn69WX0Wi8qVWhyW1koCKTAot5jgZA24tKq4JPI71kYb1/5Z7baZCNekfOSUQsVbuFH8g9I9KffWeIIW7AhKA0TWPZCVxGCEFIj/x54DqmXtCnd7YRxYxXaWaSVrCos7lZAR5mnoTN2hBEEQFCWOhA1cX4iP"
|
||||
ESI_USER_AGENT="encrypted:BADtioS3QPCdtq+RvA90WTk61cnHPadpaeTE4JJDEaWuFygvtbouvrcbPhp6MmkXTrVb1E6Fs6dS74ocCMP+4wSfE4UuJpDos2lTo5eHp/Y65ERqdByQbAUVPQ1tf1vuqrDqBYFiM3LfiQUnulASsmhGsDUgroxPTv0g4hv+4m5/DMR4mClgn5Cb+UoguJRRptWyZi5jCvXwxQzTtRQ1NYqr8WNNMA65oiGCkw=="
|
||||
AUTH_DB_PATH="encrypted:BOgXzNmmct1BCPLRgrKbrCtERDEUCxyv1gJPEQupSgVmxyjoDCZLu+7dgfMvB7AeDfy2BtelFDpT3geWg+zazyUZO2aN/EvEv56YShh6iTDFONXHWx6l+8uu+l7Dmj9saL8MCJRcTBv4AtYseu7Y4AErK4wbNAEF"
|
||||
JANICE_KEY="encrypted:BKdgDHUyDpAx5e72lhaOA33hszd6OTCz5p3cDk8AkkLK4JumLtKYJSelkKeHsl+jSSpYgLbwDgS42RL5bYBOJZUXmTCSn01PlxQguoseHzjtyd4HqdRtXtK3AucQOX27ZbY0tEZFzW467suq9NpDKie2cB3mdSJrfS/CJX58VgRi"
|
||||
PERPLEXITY_API_KEY="encrypted:BL8MWGlcOic+n/V0yWSWai2Pwo8lbYbjcRkD2N7uDf38ogp7dUUdye/1dNWYzjm+ICsWXi5SQNEDkSdysegsJmoUtxBtEnX0Y0GJrQZaY5MEFobSicV5sjSdlh2y1gDcRrGPmvgsDXL/ehs3PWzz2DI0J71BnsziR4CmumkzwSxqot/mtTQMJQaO3UKFXB0RL5bwCWwv"
|
||||
15
packages/eve-data/README.md
Normal file
15
packages/eve-data/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# eve-utils
|
||||
|
||||
To install dependencies:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.3.5. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||
7
packages/eve-data/bunfig.toml
Normal file
7
packages/eve-data/bunfig.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[test]
|
||||
coverage = true
|
||||
coverageSkipTestFiles = true
|
||||
coverageReporter = ["text", "lcov"]
|
||||
|
||||
[run]
|
||||
bun = true
|
||||
29
packages/eve-data/package.json
Normal file
29
packages/eve-data/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@star-kitten/eve-data",
|
||||
"module": "src/main.ts",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@dotenvx/dotenvx": "^1.52.0",
|
||||
"@types/bun": "^1.3.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@duckdb/node-api": "^1.4.4-r.1",
|
||||
"neo4j-driver": "^6.0.1",
|
||||
"pg": "^8.18.0",
|
||||
"redis": "^5.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"link": "bun link",
|
||||
"test": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"release": "bumpp && npm publish",
|
||||
"dev": "bunx @dotenvx/dotenvx run -f .env.development -- bun run src/main.ts && tsdown --watch",
|
||||
"build": "bunx @dotenvx/dotenvx run -f .env.production -- bun src/main.ts && tsdown",
|
||||
"encrypt": "bunx dotenvx encrypt -f .env.development && bunx dotenvx encrypt -f .env.production",
|
||||
"decrypt": "bunx dotenvx decrypt -f .env.development && bunx dotenvx decrypt -f .env.production"
|
||||
}
|
||||
}
|
||||
72
packages/eve-data/src/download-and-extract.ts
Normal file
72
packages/eve-data/src/download-and-extract.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { Readable } from "stream";
|
||||
import { exec } from "child_process";
|
||||
|
||||
export async function downloadAndExtract(
|
||||
url: string,
|
||||
outputDir: string
|
||||
): Promise<void> {
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
console.log(`Starting download from ${url}...`);
|
||||
const response = await fetch(url);
|
||||
if (!response.ok || !response.body)
|
||||
throw new Error(`Failed to download ${url}`);
|
||||
const nodeStream = Readable.fromWeb(response.body as any);
|
||||
|
||||
const compressedFilePath = path.join(outputDir, "archive.tar.xz");
|
||||
const fileStream = fs.createWriteStream(compressedFilePath);
|
||||
|
||||
nodeStream.pipe(fileStream);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fileStream.on("finish", () => {
|
||||
// Use native tar command to extract files
|
||||
exec(
|
||||
`tar -xJf ${compressedFilePath} -C ${outputDir}`,
|
||||
(error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`Extraction error: ${stderr}`);
|
||||
reject(error);
|
||||
} else {
|
||||
console.log("Extraction complete");
|
||||
|
||||
// Clean up the archive file
|
||||
fs.unlink(compressedFilePath, (err) => {
|
||||
if (err) {
|
||||
console.error(`Error removing archive: ${err.message}`);
|
||||
reject(err);
|
||||
} else {
|
||||
console.log("Archive cleaned up");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
fileStream.on("error", (err) => {
|
||||
console.error("File stream error", err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// CLI execution (only runs when file is executed directly)
|
||||
if (import.meta.main) {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length !== 2) {
|
||||
console.error("Usage: bun run downloadAndExtract.ts <url> <outputDir>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const [url, outputDir] = args;
|
||||
|
||||
downloadAndExtract(url, outputDir).catch((err) =>
|
||||
console.error("Download failed", err)
|
||||
);
|
||||
}
|
||||
26
packages/eve-data/src/main.ts
Normal file
26
packages/eve-data/src/main.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { downloadAndExtract } from "./download-and-extract";
|
||||
|
||||
async function main() {
|
||||
const referenceDataUrl =
|
||||
"https://data.everef.net/reference-data/reference-data-latest.tar.xz";
|
||||
const hoboleaksSDEUrl =
|
||||
"https://data.everef.net/hoboleaks-sde/hoboleaks-sde-latest.tar.xz";
|
||||
const referenceDataOutputDir = "./data/reference-data";
|
||||
const hoboleaksSDEOutputDir = "./data/hoboleaks-sde";
|
||||
|
||||
try {
|
||||
await downloadAndExtract(referenceDataUrl, referenceDataOutputDir);
|
||||
console.log("Reference data downloaded and extracted successfully");
|
||||
} catch (err) {
|
||||
console.error("Error during download and extraction", err);
|
||||
}
|
||||
|
||||
try {
|
||||
await downloadAndExtract(hoboleaksSDEUrl, hoboleaksSDEOutputDir);
|
||||
console.log("Hoboleaks SDE downloaded and extracted successfully");
|
||||
} catch (err) {
|
||||
console.error("Error during download and extraction", err);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -5,9 +5,7 @@
|
||||
"strict": false,
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@star-kitten/lib/discord",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"lib": ["ESNext"],
|
||||
"typeRoots": ["src/types", "./node_modules/@types"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
@@ -22,6 +20,6 @@
|
||||
"rootDir": ".",
|
||||
"allowImportingTsExtensions": true
|
||||
},
|
||||
"include": ["src", "types", "src/jsx/types.d.ts", "scripts"],
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "build", "**/*.test.ts"]
|
||||
}
|
||||
22
packages/eve-data/tsconfig.json
Normal file
22
packages/eve-data/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"strict": false,
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"lib": ["ESNext"],
|
||||
"typeRoots": ["src/types", "./node_modules/@types"],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"emitDeclarationOnly": true,
|
||||
"noEmit": false,
|
||||
"noEmitOnError": false,
|
||||
"declaration": true,
|
||||
"rootDir": "src",
|
||||
"allowImportingTsExtensions": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "build", "**/*.test.ts"]
|
||||
}
|
||||
16
packages/eve-data/tsdown.config.ts
Normal file
16
packages/eve-data/tsdown.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from 'tsdown';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: [
|
||||
'./src/**/*.ts',
|
||||
'!./src/**/*.test.ts',
|
||||
],
|
||||
platform: 'node',
|
||||
dts: true,
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
unbundle: true,
|
||||
external: ['bun:sqlite', 'bun'],
|
||||
},
|
||||
]);
|
||||
23
packages/eve-discord/README.md
Normal file
23
packages/eve-discord/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# tsdown-starter
|
||||
|
||||
A starter for creating a TypeScript package.
|
||||
|
||||
## Development
|
||||
|
||||
- Install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
- Run the unit tests:
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
- Build the library:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
59
packages/eve-discord/package.json
Normal file
59
packages/eve-discord/package.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "@star-kitten/eve-discord",
|
||||
"version": "0.0.1",
|
||||
"description": "Star Kitten EVE Discord Library.",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.f302.me/jb/star-kitten#readme",
|
||||
"bugs": {
|
||||
"url": "https://git.f302.me/jb/star-kitten/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://git.f302.me/jb/star-kitten.git"
|
||||
},
|
||||
"author": "JB <j-b-3.deviate267@passmail.net>",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index*.d.ts"
|
||||
},
|
||||
"./commands/*.js": {
|
||||
"require": "./dist/commands/*.js",
|
||||
"import": "./dist/commands/*.js",
|
||||
"types": "./dist/types/commands/*.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@projectdysnomia/dysnomia": "github:projectdysnomia/dysnomia#dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.5",
|
||||
"prettier-plugin-multiline-arrays": "^4.0.3",
|
||||
"tsdown": "^0.14.2",
|
||||
"typescript": "beta"
|
||||
},
|
||||
"dependencies": {
|
||||
"@star-kitten/util": "link:@star-kitten/util",
|
||||
"@star-kitten/eve": "link:@star-kitten/eve",
|
||||
"@star-kitten/discord": "link:@star-kitten/discord"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"dev": "tsdown --watch",
|
||||
"link": "bun link",
|
||||
"test": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"release": "bumpp && npm publish"
|
||||
}
|
||||
}
|
||||
136
packages/eve-discord/src/commands/appraise.command.ts
Normal file
136
packages/eve-discord/src/commands/appraise.command.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { appraiseItems, type Appraisal } from '@star-kitten/eve/third-party/janice.js';
|
||||
import {
|
||||
componentHasIdPrefix,
|
||||
isModalLabel,
|
||||
isModalSelect,
|
||||
isModalTextInput,
|
||||
} from '@star-kitten/discord/components';
|
||||
import { createChatCommand, type CommandContext, type ExecutableInteraction, components as c, type PageContext, ButtonStyle } from '@star-kitten/discord';
|
||||
import { PageType, usePages } from '@star-kitten/discord/pages';
|
||||
import { Constants } from '@projectdysnomia/dysnomia';
|
||||
import { markets } from '@star-kitten/eve/third-party/janice.js';
|
||||
import { formatNumberToShortForm } from '@star-kitten/util';
|
||||
|
||||
|
||||
export interface AppraisalState {
|
||||
appraisal?: Appraisal;
|
||||
}
|
||||
|
||||
|
||||
function renderAppraisalModal() {
|
||||
return c.modal({ custom_id: 'appraisalResult', title: 'Appraise Items' },
|
||||
c.label({ label: 'Select a market' },
|
||||
c.stringSelect('market', { placeholder: 'Select a market', min_values: 1, max_values: 1 },
|
||||
...markets.map((m) => c.option({ label: m.name, value: m.id.toString(), default: m.id === 2 }))
|
||||
),
|
||||
),
|
||||
c.label({ label: 'Enter items to appraise' },
|
||||
c.input('input',{
|
||||
isParagraph: true,
|
||||
placeholder: `e.g. Tritanium 22222\nPyerite 8000\nMexallon 2444`,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function renderAppraisalFollowUp(appraisal: Appraisal,
|
||||
pageCtx: PageContext<AppraisalState>,
|
||||
interaction: ExecutableInteraction
|
||||
) {
|
||||
|
||||
const content = `
|
||||
# [Appraisal ${appraisal.id} @ ${appraisal.market.name}](https://janice.e-351.com/a/${appraisal.id})
|
||||
### Buy: \`${formatNumberToShortForm(appraisal.effectivePrices.totalBuyPrice)}\` ISK
|
||||
### Split: \`${formatNumberToShortForm(appraisal.effectivePrices.totalSplitPrice)}\` ISK
|
||||
### Sell: \`${formatNumberToShortForm(appraisal.effectivePrices.totalSellPrice)}\` ISK
|
||||
-# Volume: ${formatNumberToShortForm(appraisal.totalPackagedVolume)} m³
|
||||
\`\`\`
|
||||
Buy: Sell: Qty: Item:
|
||||
${appraisal.items.map((i) => `${formatNumberToShortForm(i.effectivePrices.buyPrice).padEnd(10)}${formatNumberToShortForm(i.effectivePrices.sellPrice).padEnd(10)}${formatNumberToShortForm(i.amount).padEnd(10)}${i.itemType.name}`).join('\n')}
|
||||
\`\`\`
|
||||
-# https://janice.e-351.com/a/${appraisal.id}
|
||||
`;
|
||||
|
||||
return c.container({ accent: 0x1da57a },
|
||||
c.text(content),
|
||||
pageCtx.state.currentPage !== 'share' && c.actionRow(
|
||||
c.button({ custom_id: 'share', label: 'Share in Channel', disabled: !interaction.channel?.id, style: ButtonStyle.PRIMARY }),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
return await usePages<AppraisalState>(
|
||||
{
|
||||
pages: {
|
||||
appraiseModal: {
|
||||
key: 'appraiseModal',
|
||||
type: PageType.MODAL,
|
||||
render: () => renderAppraisalModal(),
|
||||
},
|
||||
appraisalResult: {
|
||||
key: 'appraisalResult',
|
||||
render: async (pageCtx) => {
|
||||
if (!interaction.isModalSubmit()) {
|
||||
throw new Error('Expected a modal submit interaction for appraisalResult page');
|
||||
}
|
||||
let marketId = 2; // Default to Jita
|
||||
let items = '';
|
||||
|
||||
interaction.data.components.forEach((comp) => {
|
||||
if (isModalLabel(comp)) {
|
||||
if (isModalSelect(comp.component) && componentHasIdPrefix(comp.component, `market`)) {
|
||||
marketId = Number.parseInt(comp.component.values[0]) || marketId;
|
||||
} else if (isModalTextInput(comp.component) && componentHasIdPrefix(comp.component, `input`)) {
|
||||
items = comp.component.value || items;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const appraisal = await appraiseItems(items, marketId);
|
||||
pageCtx.state.data.appraisal = appraisal;
|
||||
return renderAppraisalFollowUp(appraisal, pageCtx, interaction);
|
||||
},
|
||||
},
|
||||
share: {
|
||||
key: 'share',
|
||||
type: PageType.FOLLOWUP,
|
||||
followUpFlags: Constants.MessageFlags.IS_COMPONENTS_V2,
|
||||
render: (pageCtx) => {
|
||||
const rendered = renderAppraisalFollowUp(pageCtx.state.data.appraisal!, pageCtx, interaction);
|
||||
return rendered;
|
||||
},
|
||||
},
|
||||
},
|
||||
initialPage: 'appraiseModal',
|
||||
timeout: 300, // 5 minutes
|
||||
ephemeral: true,
|
||||
},
|
||||
interaction,
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
|
||||
export default createChatCommand({
|
||||
name: 'appraise',
|
||||
nameLocalizations: {
|
||||
de: 'bewerten',
|
||||
'es-ES': 'tasar',
|
||||
fr: 'estimer',
|
||||
ja: '査定',
|
||||
ko: '감정',
|
||||
ru: 'оценить',
|
||||
'zh-CN': '评估',
|
||||
},
|
||||
description: 'Evaluate the worth of your space junk',
|
||||
descriptionLocalizations: {
|
||||
de: 'Bewerten Sie den Wert Ihres Weltraumschrotts',
|
||||
'es-ES': 'Evalúa el valor de tu chatarra espacial',
|
||||
fr: 'Évaluez la valeur de vos déchets spatiaux',
|
||||
ja: 'あなたの宇宙のガラクタの価値を評価します',
|
||||
ko: '우주 쓰레기의 가치를 평가하십시오',
|
||||
ru: 'Оцените стоимость вашего космического мусора',
|
||||
'zh-CN': '评估您宇宙垃圾的价值',
|
||||
},
|
||||
}, execute);
|
||||
78
packages/eve-discord/src/commands/time-from-now.command.ts
Normal file
78
packages/eve-discord/src/commands/time-from-now.command.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
type ExecutableInteraction,
|
||||
type CommandContext,
|
||||
createChatCommand,
|
||||
components,
|
||||
options,
|
||||
} from '@star-kitten/discord';
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, context: CommandContext) {
|
||||
if (!interaction.isApplicationCommand()) return;
|
||||
|
||||
const days = (interaction.data.options.find((option) => option.name === 'days')?.value || 0) as number;
|
||||
const hours = (interaction.data.options.find((option) => option.name === 'hours')?.value || 0) as number;
|
||||
const minutes = (interaction.data.options.find((option) => option.name === 'minutes')?.value || 0) as number;
|
||||
const ephemeral = !(interaction.data.options.find((option) => option.name === 'public')?.value || false) as boolean;
|
||||
|
||||
const totalSeconds = days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60;
|
||||
|
||||
const now = new Date();
|
||||
const futureTime = new Date(now.getTime() + totalSeconds * 1000);
|
||||
|
||||
const eveTime = futureTime.toISOString().split('T')[1].split('.')[0];
|
||||
const eveDate = futureTime.toLocaleDateString(interaction.locale, {
|
||||
timeZone: 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
weekday: 'long',
|
||||
});
|
||||
|
||||
const discordTimestamp = Math.floor(futureTime.getTime() / 1000);
|
||||
const timestampCodes = [
|
||||
`<t:${discordTimestamp}:f>`, // Full date and time
|
||||
`<t:${discordTimestamp}:F>`, // Long date and time
|
||||
`<t:${discordTimestamp}:T>`, // 24-hour time
|
||||
`<t:${discordTimestamp}:R>`, // Relative time
|
||||
];
|
||||
|
||||
const textContent = `In ${days ? `**${days}** day${days !== 1 ? 's' : ''}, ` : ''}${hours ? `**${hours}** hour${hours !== 1 ? 's' : ''}, ` : ''}${minutes ? `**${minutes}** minute${minutes !== 1 ? 's' : ''} ` : ''}from now, the EVE time will be:
|
||||
${eveDate} at ${eveTime}
|
||||
### Discord timestamp codes:
|
||||
${timestampCodes.map((code) => `${code}\n\`\`\`${code}\`\`\``).join('\n')}
|
||||
`;
|
||||
|
||||
interaction.createMessage(
|
||||
components.componentsV2({ ephemeral }, components.container({}, components.text(textContent))),
|
||||
);
|
||||
}
|
||||
|
||||
export default createChatCommand(
|
||||
{
|
||||
name: 'time_from_now',
|
||||
description: 'Calculate time from now in EVE format with Discord timestamp codes',
|
||||
options: [
|
||||
options.numberOption({
|
||||
name: 'days',
|
||||
description: 'Number of days to add',
|
||||
required: false,
|
||||
}),
|
||||
options.numberOption({
|
||||
name: 'hours',
|
||||
description: 'Number of hours to add',
|
||||
required: false,
|
||||
}),
|
||||
options.numberOption({
|
||||
name: 'minutes',
|
||||
description: 'Number of minutes to add',
|
||||
required: false,
|
||||
}),
|
||||
options.booleanOption({
|
||||
name: 'public',
|
||||
description: 'Whether the response should be public (visible to everyone)',
|
||||
required: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
execute,
|
||||
);
|
||||
@@ -1,13 +1,44 @@
|
||||
import { Constants, type ChatInputApplicationCommandStructure } from '@projectdysnomia/dysnomia';
|
||||
import {
|
||||
type ExecutableInteraction,
|
||||
type CommandContext,
|
||||
isApplicationCommand,
|
||||
Locale,
|
||||
} from '@star-kitten/lib/discord';
|
||||
componentsV2,
|
||||
container,
|
||||
text,
|
||||
createChatCommand,
|
||||
} from '@star-kitten/discord';
|
||||
|
||||
const definition: ChatInputApplicationCommandStructure = {
|
||||
type: Constants.ApplicationCommandTypes.CHAT_INPUT,
|
||||
const eveTimeText = {
|
||||
[Locale.EN_US]: 'EVE Time',
|
||||
[Locale.EN_GB]: 'EVE Time',
|
||||
[Locale.DE]: 'EVE-Zeit',
|
||||
[Locale.ES_ES]: 'Hora EVE',
|
||||
[Locale.FR]: "Heure d'EVE",
|
||||
[Locale.JA]: 'EVE時間',
|
||||
[Locale.KO]: 'EVE 시간',
|
||||
[Locale.RU]: 'Время EVE',
|
||||
[Locale.ZH_CN]: 'EVE时间',
|
||||
};
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
if (!interaction.isApplicationCommand()) return;
|
||||
|
||||
const now = new Date();
|
||||
const eveTime = now.toISOString().split('T')[1].split('.')[0];
|
||||
const eveDate = now.toLocaleDateString(interaction.locale, {
|
||||
timeZone: 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
weekday: 'long',
|
||||
});
|
||||
|
||||
interaction.createMessage(componentsV2({}, container({}, text(`### ${eveTimeText[interaction.locale] || eveTimeText[Locale.EN_US]}
|
||||
${eveTime}
|
||||
${eveDate}`))));
|
||||
}
|
||||
|
||||
export default createChatCommand({
|
||||
name: 'time',
|
||||
nameLocalizations: {
|
||||
[Locale.DE]: 'zeit',
|
||||
@@ -28,51 +59,4 @@ const definition: ChatInputApplicationCommandStructure = {
|
||||
[Locale.RU]: 'Получите текущее время EVE',
|
||||
[Locale.ZH_CN]: '获取当前的EVE时间',
|
||||
},
|
||||
};
|
||||
|
||||
const eveTimeText = {
|
||||
[Locale.EN_US]: 'EVE Time',
|
||||
[Locale.EN_GB]: 'EVE Time',
|
||||
[Locale.DE]: 'EVE-Zeit',
|
||||
[Locale.ES_ES]: 'Hora EVE',
|
||||
[Locale.FR]: "Heure d'EVE",
|
||||
[Locale.JA]: 'EVE時間',
|
||||
[Locale.KO]: 'EVE 시간',
|
||||
[Locale.RU]: 'Время EVE',
|
||||
[Locale.ZH_CN]: 'EVE时间',
|
||||
};
|
||||
|
||||
function renderTimeDisplay(locale: string = 'en-US') {
|
||||
const now = new Date();
|
||||
const eveTime = now.toISOString().split('T')[1].split('.')[0];
|
||||
const eveDate = now.toLocaleDateString(locale, {
|
||||
timeZone: 'UTC',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: '2-digit',
|
||||
weekday: 'long',
|
||||
});
|
||||
return {
|
||||
flags: Constants.MessageFlags.IS_COMPONENTS_V2,
|
||||
components: [
|
||||
<container>
|
||||
<text>
|
||||
{`### ${eveTimeText[locale] || eveTimeText[Locale.EN_US]}
|
||||
${eveTime}
|
||||
${eveDate}`}
|
||||
</text>
|
||||
</container>,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
async function execute(interaction: ExecutableInteraction, ctx: CommandContext) {
|
||||
if (!isApplicationCommand(interaction)) return;
|
||||
|
||||
interaction.createMessage(renderTimeDisplay(interaction.locale));
|
||||
}
|
||||
|
||||
export default {
|
||||
definition,
|
||||
execute,
|
||||
};
|
||||
}, execute);
|
||||
3
packages/eve-discord/src/index.ts
Normal file
3
packages/eve-discord/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function() {
|
||||
console.log('Hello from eve-discord!');
|
||||
}
|
||||
21
packages/eve-discord/tsconfig.json
Normal file
21
packages/eve-discord/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"strict": false,
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@data/*": ["./data/*"],
|
||||
},
|
||||
"emitDeclarationOnly": true,
|
||||
"noEmit": false,
|
||||
"noEmitOnError": false,
|
||||
"declaration": true,
|
||||
"rootDir": ".",
|
||||
"allowImportingTsExtensions": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "build", "**/*.test.ts"]
|
||||
}
|
||||
16
packages/eve-discord/tsdown.config.ts
Normal file
16
packages/eve-discord/tsdown.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from 'tsdown';
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
entry: [
|
||||
'./src/**/*.ts',
|
||||
'!./src/**/*.test.ts',
|
||||
],
|
||||
platform: 'node',
|
||||
dts: true,
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
unbundle: true,
|
||||
external: ['bun:sqlite', 'bun'],
|
||||
},
|
||||
]);
|
||||
23
packages/eve/README.md
Normal file
23
packages/eve/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# tsdown-starter
|
||||
|
||||
A starter for creating a TypeScript package.
|
||||
|
||||
## Development
|
||||
|
||||
- Install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
- Run the unit tests:
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
- Build the library:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
115
packages/eve/package.json
Normal file
115
packages/eve/package.json
Normal file
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"name": "@star-kitten/eve",
|
||||
"version": "0.0.1",
|
||||
"description": "Star Kitten EVE Library.",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.f302.me/jb/star-kitten#readme",
|
||||
"bugs": {
|
||||
"url": "https://git.f302.me/jb/star-kitten/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://git.f302.me/jb/star-kitten.git"
|
||||
},
|
||||
"author": "JB <j-b-3.deviate267@passmail.net>",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index*.d.ts"
|
||||
},
|
||||
"./esi": {
|
||||
"import": "./dist/esi/index.js",
|
||||
"types": "./dist/esi/index*.d.ts",
|
||||
"require": "./dist/esi/index.js"
|
||||
},
|
||||
"./db": {
|
||||
"import": "./dist/db/index.js",
|
||||
"types": "./dist/db/index*.d.ts",
|
||||
"require": "./dist/db/index.js"
|
||||
},
|
||||
"./ref": {
|
||||
"import": "./dist/ref/index.js",
|
||||
"types": "./dist/ref/index*.d.ts",
|
||||
"require": "./dist/ref/index.js"
|
||||
},
|
||||
"./third-party/janice.js": {
|
||||
"import": "./dist/third-party/janice.js",
|
||||
"types": "./dist/types/third-party/janice.d.ts",
|
||||
"require": "./dist/third-party/janice.js"
|
||||
},
|
||||
"./models": {
|
||||
"import": "./dist/models/index.js",
|
||||
"types": "./dist/models/index*.d.ts",
|
||||
"require": "./dist/models/index.js"
|
||||
},
|
||||
"./data/*": "./data/*",
|
||||
"./discord": {
|
||||
"import": "./dist/discord/index.js",
|
||||
"require": "./dist/discord/index.js",
|
||||
"types": "./dist/types/discord/index.d.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.5",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/jwk-to-pem": "^2.0.3",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/node": "^22.15.17",
|
||||
"@types/node-cache": "^4.2.5",
|
||||
"@types/stream-chain": "^2.1.0",
|
||||
"@types/stream-json": "^1.7.8",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"bumpp": "^10.1.0",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"openapi-fetch": "^0.15.0",
|
||||
"openapi-typescript": "^7.10.1",
|
||||
"prettier-plugin-multiline-arrays": "^4.0.3",
|
||||
"tsdown": "^0.14.2",
|
||||
"typescript": "beta"
|
||||
},
|
||||
"dependencies": {
|
||||
"@star-kitten/util": "link:@star-kitten/util",
|
||||
"@orama/orama": "^3.1.13",
|
||||
"@oslojs/encoding": "^1.1.0",
|
||||
"cron-parser": "^5.3.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"drizzle-orm": "^0.44.5",
|
||||
"elysia": "^1.4.20",
|
||||
"fp-filters": "^0.5.4",
|
||||
"html-dom-parser": "^5.1.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwk-to-pem": "^2.0.7",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"node-cache": "^5.1.2",
|
||||
"stream-chain": "^3.4.0",
|
||||
"stream-json": "^1.9.1",
|
||||
"winston": "^3.17.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"dev": "tsdown --watch",
|
||||
"link": "bun link",
|
||||
"test": "bun test",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"release": "bumpp && npm publish",
|
||||
"generate-migrations": "bunx drizzle-kit generate --dialect sqlite --schema ./src/db/schema.ts",
|
||||
"migrate": "bun run ./src/db/migrate.ts",
|
||||
"everef-api": "bunx openapi-typescript https://raw.githubusercontent.com/autonomouslogic/eve-ref/refs/heads/main/spec/eve-ref-api.yaml -o src/eve/everef/schema.d.ts",
|
||||
"get-data": "bun run refresh:reference-data && bun run refresh:hoboleaks",
|
||||
"refresh:reference-data": "bun run scripts/download-and-extract.ts https://data.everef.net/reference-data/reference-data-latest.tar.xz ./data/reference-data",
|
||||
"refresh:hoboleaks": "bun run scripts/download-and-extract.ts https://data.everef.net/hoboleaks-sde/hoboleaks-sde-latest.tar.xz ./data/hoboleaks",
|
||||
"static-export": "bun run scripts/export-solar-systems.ts"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user