diff --git a/package.json b/package.json index 1dba940..27d4392 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord-tickets", - "version": "4.0.0-beta.8", + "version": "4.0.0-beta.9", "private": "true", "description": "An open-source Discord bot for ticket management", "main": "src/", @@ -40,7 +40,7 @@ "node": ">=18" }, "dependencies": { - "@discord-tickets/settings": "^2.1.1", + "@discord-tickets/settings": "^2.1.3", "@eartharoid/dbf": "^0.3.3", "@eartharoid/dtf": "^2.0.1", "@eartharoid/i18n": "^1.2.1", @@ -65,6 +65,7 @@ "object-diffy": "^1.0.4", "prisma": "^4.11.0", "semver": "^7.3.8", + "spacetime": "^7.4.1", "terminal-link": "^2.1.1", "yaml": "^1.10.2" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3378b7b..4a54282 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,7 +3,7 @@ lockfileVersion: 5.4 specifiers: '@commitlint/cli': ^17.4.4 '@commitlint/config-conventional': ^17.4.4 - '@discord-tickets/settings': ^2.1.1 + '@discord-tickets/settings': ^2.1.3 '@eartharoid/dbf': ^0.3.3 '@eartharoid/dtf': ^2.0.1 '@eartharoid/i18n': ^1.2.1 @@ -37,13 +37,14 @@ specifiers: object-diffy: ^1.0.4 prisma: ^4.11.0 semver: ^7.3.8 + spacetime: ^7.4.1 terminal-link: ^2.1.1 utf-8-validate: ^5.0.10 yaml: ^1.10.2 zlib-sync: ^0.1.8 dependencies: - '@discord-tickets/settings': 2.1.1_svelte@3.56.0 + '@discord-tickets/settings': 2.1.3_svelte@3.57.0 '@eartharoid/dbf': 0.3.3_3cxu5zja4e2r5wmvge7mdcljwq '@eartharoid/dtf': 2.0.1 '@eartharoid/i18n': 1.2.1 @@ -68,6 +69,7 @@ dependencies: object-diffy: 1.0.4 prisma: 4.11.0 semver: 7.3.8 + spacetime: 7.4.1 terminal-link: 2.1.1 yaml: 1.10.2 @@ -204,15 +206,15 @@ packages: '@commitlint/execute-rule': 17.4.0 '@commitlint/resolve-extends': 17.4.4 '@commitlint/types': 17.4.4 - '@types/node': 18.15.1 + '@types/node': 18.15.3 chalk: 4.1.2 - cosmiconfig: 8.1.0 - cosmiconfig-typescript-loader: 4.3.0_jad34rn52rvsukepwt6d7357fa + cosmiconfig: 8.1.2 + cosmiconfig-typescript-loader: 4.3.0_kpxsywhbgzx5buqgdnmsfobuwi lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.1_r2vohjtqb453xa4ljp4dw3sqb4 + ts-node: 10.9.1_cbfmry4sbbh4vatmdrsmatfg5a typescript: 4.9.5 transitivePeerDependencies: - '@swc/core' @@ -293,8 +295,8 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@discord-tickets/settings/2.1.1_svelte@3.56.0: - resolution: {integrity: sha512-fqxK0SRyFPBddtccu6iFiJJvfToOQvkjazlYVguekEiYdmMdT61vi83r1SXYx3Bs0qKyXMUnmWGOA/+4jIOMGA==} + /@discord-tickets/settings/2.1.3_svelte@3.57.0: + resolution: {integrity: sha512-rKwt87oXBydudfnKkSmgbnNtCFbkYM0ZBuZLqSfl+H8N1kT+bylGoyXfAF2OLoEc5vK/EyBCEP9trZMO60774g==} dependencies: '@fortawesome/fontawesome-free': 6.3.0 '@skyra/discord-components-core': 3.6.0 @@ -304,7 +306,7 @@ packages: ms: 2.1.3 postcss: 8.4.21 sortablejs: 1.15.0 - svelte-modals: 1.2.1_svelte@3.56.0 + svelte-modals: 1.2.1_svelte@3.57.0 svelte-toasts: 1.1.2 transitivePeerDependencies: - svelte @@ -317,7 +319,7 @@ packages: '@discordjs/formatters': 0.2.0 '@discordjs/util': 0.2.0 '@sapphire/shapeshift': 3.8.1 - discord-api-types: 0.37.35 + discord-api-types: 0.37.36 fast-deep-equal: 3.1.3 ts-mixer: 6.0.3 tslib: 2.5.0 @@ -332,7 +334,7 @@ packages: resolution: {integrity: sha512-vn4oMSXuMZUm8ITqVOtvE7/fMMISj4cI5oLsR09PEQXHKeKDAMLltG/DWeeIs7Idfy6V8Fk3rn1e69h7NfzuNA==} engines: {node: '>=16.9.0'} dependencies: - discord-api-types: 0.37.35 + discord-api-types: 0.37.36 dev: false /@discordjs/rest/1.6.0: @@ -343,7 +345,7 @@ packages: '@discordjs/util': 0.2.0 '@sapphire/async-queue': 1.5.0 '@sapphire/snowflake': 3.4.0 - discord-api-types: 0.37.35 + discord-api-types: 0.37.36 file-type: 18.2.1 tslib: 2.5.0 undici: 5.21.0 @@ -376,8 +378,8 @@ packages: resolution: {integrity: sha512-nMQdHrGgpw+vNL5DbivELW2K3KAUGaMvTjjmFsEPf8mUW8+LAgRjvfFn2gkJq1mnlD6HoqUaHqEL4YpWr2T5MA==} dev: false - /@eslint-community/eslint-utils/4.2.0_eslint@8.36.0: - resolution: {integrity: sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==} + /@eslint-community/eslint-utils/4.3.0_eslint@8.36.0: + resolution: {integrity: sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -639,13 +641,13 @@ packages: resolution: {integrity: sha512-13aVNoleHyAMqd4lcfcfjEduV4QavlHn9P2TUlTUPQC8m8cd8n8wSUTDUxKhQbYpzJ0hn7AHnlmLixfiqQT4FQ==} engines: {node: '>=v14.0.0'} dependencies: - '@stencil/core': 2.22.2 + '@stencil/core': 2.22.3 clsx: 1.2.1 hex-to-rgba: 2.0.1 dev: false - /@stencil/core/2.22.2: - resolution: {integrity: sha512-r+vbxsGNcBaV1VDOYW25lv4QfXTlNoIb5GpUX7rZ+cr59yqYCZC5tlV+IzX6YgHKW62ulCc9M3RYtTfHtNbNNw==} + /@stencil/core/2.22.3: + resolution: {integrity: sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng==} engines: {node: '>=12.10.0', npm: '>=6.0.0'} hasBin: true dev: false @@ -674,8 +676,8 @@ packages: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true - /@types/node/18.15.1: - resolution: {integrity: sha512-U2TWca8AeHSmbpi314QBESRk7oPjSZjDsR+c+H4ECC1l+kFgpZf8Ydhv3SJpPy51VyZHHqxlb6mTTqYNNRVAIw==} + /@types/node/18.15.3: + resolution: {integrity: sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==} /@types/normalize-package-data/2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} @@ -684,7 +686,7 @@ packages: /@types/ws/8.5.4: resolution: {integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==} dependencies: - '@types/node': 18.15.1 + '@types/node': 18.15.3 dev: false /JSONStream/1.3.5: @@ -1313,7 +1315,7 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: true - /cosmiconfig-typescript-loader/4.3.0_jad34rn52rvsukepwt6d7357fa: + /cosmiconfig-typescript-loader/4.3.0_kpxsywhbgzx5buqgdnmsfobuwi: resolution: {integrity: sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==} engines: {node: '>=12', npm: '>=6'} peerDependencies: @@ -1322,14 +1324,14 @@ packages: ts-node: '>=10' typescript: '>=3' dependencies: - '@types/node': 18.15.1 - cosmiconfig: 8.1.0 - ts-node: 10.9.1_r2vohjtqb453xa4ljp4dw3sqb4 + '@types/node': 18.15.3 + cosmiconfig: 8.1.2 + ts-node: 10.9.1_cbfmry4sbbh4vatmdrsmatfg5a typescript: 4.9.5 dev: true - /cosmiconfig/8.1.0: - resolution: {integrity: sha512-0tLZ9URlPGU7JsKq0DQOQ3FoRsYX8xDZ7xMiATQfaiGMz7EHowNkbU9u1coAOmnh9p/1ySpm0RB3JNWRXM5GCg==} + /cosmiconfig/8.1.2: + resolution: {integrity: sha512-rmpUFKMZiawLfug8sP4NbpBSOpWftZB6UACOLEiNbnRAYM1TzgQuTWlMYFRuPgmoTCkcOxSMwQJQpJmiXv/eHw==} engines: {node: '>=14'} dependencies: import-fresh: 3.3.0 @@ -1423,8 +1425,8 @@ packages: engines: {node: '>=0.3.1'} dev: true - /discord-api-types/0.37.35: - resolution: {integrity: sha512-iyKZ/82k7FX3lcmHiAvvWu5TmyfVo78RtghBV/YsehK6CID83k5SI03DKKopBcln+TiEIYw5MGgq7SJXSpNzMg==} + /discord-api-types/0.37.36: + resolution: {integrity: sha512-Nlxmp10UpVr/utgZ9uODQvG2Or+5w7LFrvFMswyeKC9l/+UaqGT6H0OVgEFhu9GEO4U6K7NNO5W8Carv7irnCA==} dev: false /discord.js/14.8.0_3cxu5zja4e2r5wmvge7mdcljwq: @@ -1438,7 +1440,7 @@ packages: '@discordjs/util': 0.2.0 '@sapphire/snowflake': 3.4.0 '@types/ws': 8.5.4 - discord-api-types: 0.37.35 + discord-api-types: 0.37.36 fast-deep-equal: 3.1.3 lodash.snakecase: 4.1.1 tslib: 2.5.0 @@ -1553,7 +1555,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true dependencies: - '@eslint-community/eslint-utils': 4.2.0_eslint@8.36.0 + '@eslint-community/eslint-utils': 4.3.0_eslint@8.36.0 '@eslint-community/regexpp': 4.4.0 '@eslint/eslintrc': 2.0.1 '@eslint/js': 8.36.0 @@ -1655,8 +1657,8 @@ packages: strip-final-newline: 2.0.0 dev: true - /execa/7.1.0: - resolution: {integrity: sha512-T6nIJO3LHxUZ6ahVRaxXz9WLEruXLqdcluA+UuTptXmLM7nDAn9lx9IfkxPyzEL21583qSt4RmL44pO71EHaJQ==} + /execa/7.1.1: + resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} dependencies: cross-spawn: 7.0.3 @@ -1765,7 +1767,7 @@ packages: rfdc: 1.3.0 secure-json-parse: 2.7.0 semver: 7.3.8 - tiny-lru: 10.1.0 + tiny-lru: 10.2.1 transitivePeerDependencies: - supports-color dev: false @@ -1882,7 +1884,7 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 dev: false @@ -1891,7 +1893,7 @@ packages: resolution: {integrity: sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==} engines: {node: '>=14.14'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 dev: true @@ -2007,8 +2009,8 @@ packages: type-fest: 0.20.2 dev: true - /graceful-fs/4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + /graceful-fs/4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} /grapheme-splitter/1.0.4: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} @@ -2309,7 +2311,7 @@ packages: dependencies: universalify: 2.0.0 optionalDependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 /jsonparse/1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} @@ -2378,7 +2380,7 @@ packages: cli-truncate: 3.1.0 commander: 10.0.0 debug: 4.3.4 - execa: 7.1.0 + execa: 7.1.1 lilconfig: 2.1.0 listr2: 5.0.8 micromatch: 4.0.5 @@ -2415,7 +2417,7 @@ packages: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} dependencies: - graceful-fs: 4.2.10 + graceful-fs: 4.2.11 parse-json: 4.0.0 pify: 3.0.0 strip-bom: 3.0.0 @@ -3380,6 +3382,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /spacetime/7.4.1: + resolution: {integrity: sha512-khQpvLNMhHFzfFJslMfunqqsjOxdmoDDYX5Wh4qYb8N6f8vBPI6HvbT7Wb2wcMa+oP1Xh1HpEcwfPFrj8UfvHQ==} + dev: false + /spdx-correct/3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: @@ -3544,20 +3550,20 @@ packages: engines: {node: '>= 0.4'} dev: true - /svelte-modals/1.2.1_svelte@3.56.0: + /svelte-modals/1.2.1_svelte@3.57.0: resolution: {integrity: sha512-7MEKUx5wb5YppkXWFGeRlYM5FMGEnpix39u9Y6GtCNTMXRDZ7DB2Z50IYLMRTMW5lOsCdtJgFbB0E3iZMKmsAA==} peerDependencies: svelte: ^3.0.0 dependencies: - svelte: 3.56.0 + svelte: 3.57.0 dev: false /svelte-toasts/1.1.2: resolution: {integrity: sha512-m+yL4eEKXyJoyjTYaH1j1GFwF0Pi8YDqnVfwWPDmwi4712iZesv+TNCmToSNlav3R5Vkmc8ZBRkT8DOcu3sywQ==} dev: false - /svelte/3.56.0: - resolution: {integrity: sha512-LvXiJbjdvJKwB/0CQyYpDX0q+hFqCyWmybzC2G6eK1tJJA/RSRCytTfNmjHv+RHlLuA70vWG7nXp6gbeErYvRA==} + /svelte/3.57.0: + resolution: {integrity: sha512-WMXEvF+RtAaclw0t3bPDTUe19pplMlfyKDsixbHQYgCWi9+O9VN0kXU1OppzrB9gPAvz4NALuoca2LfW2bOjTQ==} engines: {node: '>= 8'} dev: false @@ -3614,8 +3620,8 @@ packages: readable-stream: 3.6.2 dev: true - /tiny-lru/10.1.0: - resolution: {integrity: sha512-8nOOqPPLrvE4gcBQZcvPZbJANNIwY0Mwu1h3kklq7mraCGG/JDNcu/QBZt4HQArGClrGxmQ3V/wgHy9xmK38gQ==} + /tiny-lru/10.2.1: + resolution: {integrity: sha512-cxcNyX4O50FDnB5x9jdEJupYC+9PPutt6wT16TaOtXeHfV9t41aFqg0VniB9YK5KmBeqmZaYriNMajm/5TtA7Q==} engines: {node: '>=12'} dev: false @@ -3666,7 +3672,7 @@ packages: resolution: {integrity: sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==} dev: false - /ts-node/10.9.1_r2vohjtqb453xa4ljp4dw3sqb4: + /ts-node/10.9.1_cbfmry4sbbh4vatmdrsmatfg5a: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -3685,7 +3691,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 - '@types/node': 18.15.1 + '@types/node': 18.15.3 acorn: 8.8.2 acorn-walk: 8.2.0 arg: 4.1.3 diff --git a/src/i18n/en-GB.yml b/src/i18n/en-GB.yml index 26e09d8..b2c267e 100644 --- a/src/i18n/en-GB.yml +++ b/src/i18n/en-GB.yml @@ -420,3 +420,14 @@ ticket: topic: Topic title: ℹī¸ Reference released: â™ģī¸ {user} has released this ticket. + working_hours: + next: + description: + We'll be back at (), although + you may receive a response before then. + title: 🕗 We're not working at the moment + today: + description: + You may receive a response before, but we don't start working until + today (). + title: 🕗 We're not working at the moment diff --git a/src/lib/tickets/manager.js b/src/lib/tickets/manager.js index f744d54..a6d9d6a 100644 --- a/src/lib/tickets/manager.js +++ b/src/lib/tickets/manager.js @@ -18,6 +18,7 @@ const ExtendedEmbedBuilder = require('../embed'); const { logTicketEvent } = require('../logging'); const { isStaff } = require('../users'); const { Collection } = require('discord.js'); +const spacetime = require('spacetime'); const Cryptr = require('cryptr'); const { decrypt, @@ -440,8 +441,6 @@ module.exports = class TicketManager { ), ]; - // TODO: !staff || workingHours - if (answers) { embeds.push( new ExtendedEmbedBuilder() @@ -618,6 +617,7 @@ module.exports = class TicketManager { }; if (referencesTicketId) data.referencesTicket = { connect: { id: referencesTicketId } }; if (answers) data.questionAnswers = { createMany: { data: answers } }; + await interaction.editReply({ components: [], embeds: [ @@ -645,7 +645,7 @@ module.exports = class TicketManager { if (category.guild.archive && message) { if ( - await this.client.prisma.archivedMessage.findUnique({ where: { id: message.id } })|| + await this.client.prisma.archivedMessage.findUnique({ where: { id: message.id } }) || await this.archiver.saveMessage(ticket.id, message, true) ) { await this.client.prisma.ticket.update({ @@ -682,6 +682,47 @@ module.exports = class TicketManager { ], }); } + + const workingHours = category.guild.workingHours; + const timezone = workingHours[0]; + workingHours.shift(); // remove timezone + const now = spacetime.now(timezone); + const currentHours = workingHours[now.day()]; + const start = now.time(currentHours[0]); + const end = now.time(currentHours[1]); + + if (currentHours[0] === currentHours[1] || now.isAfter(end)) { // staff have the day off or have finished for the day + // first look for the next working day *this* week (after today) + let nextIndex = workingHours.findIndex((hours, i) => i > now.day() && hours[0] !== hours[1]); + // if there isn't one, look for the next working day *next* week (before and including today's weekday) + if (!nextIndex) nextIndex = workingHours.findIndex((hours, i) => i <= now.day() && hours[0] !== hours[1]); + if (nextIndex) { + const next = workingHours[nextIndex]; + let then = now.add(nextIndex - now.day(), 'day'); + if (nextIndex <= now.day()) then = then.add(1, 'week'); + const timestamp = Math.ceil(then.time(next[0]).d.getTime() / 1000); // in seconds + await channel.send({ + embeds: [ + new ExtendedEmbedBuilder() + .setColor(category.guild.primaryColour) + .setTitle(getMessage('ticket.working_hours.next.title')) + .setDescription(getMessage('ticket.working_hours.next.description', { timestamp })), + ], + }); + } + } else if (now.isBefore(start)) { // staff haven't started working yet + const timestamp = Math.ceil(start.d.getTime() / 1000); // in seconds + await channel.send({ + embeds: [ + new ExtendedEmbedBuilder() + .setColor(category.guild.primaryColour) + .setTitle(getMessage('ticket.working_hours.today.title')) + .setDescription(getMessage('ticket.working_hours.today.description', { timestamp })), + ], + }); + } + + // TODO: !staff } /** @@ -1226,4 +1267,4 @@ module.exports = class TicketManager { } } -}; \ No newline at end of file +};