Compare commits

...

77 commits

Author SHA1 Message Date
c36b2fa7d8 Merge pull request 'v3.1.0' (#317) from develop into main
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/tag Build is failing
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/317
2023-07-08 13:30:22 +01:00
995564cb5b Deprecate mute and unmute commands (#314)
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
- Add a deprecation message to the mute and unmute commands

#252

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/314
2023-07-07 12:28:06 +01:00
400b4de304 feature/98-timeout-command-2 (#306)
Some checks failed
continuous-integration/drone/push Build is failing
- Prevent user from trying to time out a bot
- Update time length input to ignore commas

#98

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/306
2023-06-30 17:33:04 +01:00
eb774aa280 Fix bot not replying to the interaction on warn (#312)
Some checks failed
continuous-integration/drone/push Build is failing
- Fix bot not replying to the interaction when warning a user

#301

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/312
2023-06-30 17:19:40 +01:00
9749f9c9c9 Update drone deployment script to only deploy when a tag is created (#313)
Some checks failed
continuous-integration/drone/push Build is failing
- Update drone deployment script to only deploy when a tag is created

#292

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/313
2023-06-30 17:16:46 +01:00
e84f871329 Update dependency typeorm to v0.3.17 (#310)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [typeorm](https://typeorm.io) ([source](https://github.com/typeorm/typeorm)) | dependencies | patch | [`0.3.16` -> `0.3.17`](https://renovatebot.com/diffs/npm/typeorm/0.3.16/0.3.17) |

---

### Release Notes

<details>
<summary>typeorm/typeorm</summary>

### [`v0.3.17`](https://github.com/typeorm/typeorm/releases/tag/0.3.17)

[Compare Source](https://github.com/typeorm/typeorm/compare/0.3.16...0.3.17)

##### Bug Fixes

-   [#&#8203;10040](https://github.com/typeorm/typeorm/issues/10040) TypeORM synchronize database even if it is up to date ([#&#8203;10041](https://github.com/typeorm/typeorm/issues/10041)) ([b1a3a39](b1a3a39504))
-   add missing await ([#&#8203;10084](https://github.com/typeorm/typeorm/issues/10084)) ([f5d4397](f5d43975db))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/310
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-06-26 17:58:24 +01:00
a23028b40c Update dependency minimatch to v9.0.2 (#309)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [minimatch](https://github.com/isaacs/minimatch) | dependencies | patch | [`9.0.1` -> `9.0.2`](https://renovatebot.com/diffs/npm/minimatch/9.0.1/9.0.2) |

---

### Release Notes

<details>
<summary>isaacs/minimatch</summary>

### [`v9.0.2`](https://github.com/isaacs/minimatch/compare/v9.0.1...v9.0.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v9.0.1...v9.0.2)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/309
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-06-26 17:51:49 +01:00
915efc226e Remove github workflows (#308)
Some checks failed
continuous-integration/drone/push Build is failing
Removing the github action workflows.

This is because we no longer use github, and this just causes
unnecessary compute minutes being used over on github.

#251

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/308
2023-06-23 17:39:40 +01:00
a56076e118 Update rules for Vylpes' Den with gitea link (#307)
Some checks failed
continuous-integration/drone/push Build is failing
- Update rules file with:
	- Gitea link
    - Short url for server invite
    - New youtube link
    - Remove old vylbot core link as deprecated
    - Remove blog site as deprecated

#234

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/307
2023-06-23 17:37:31 +01:00
4f2c186244 Create timeout command (#302)
Some checks failed
continuous-integration/drone/push Build is failing
- Create timeout command

#98

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/302
2023-06-16 18:01:45 +01:00
46226d37a7 Add PR and Issue templates (#303)
Some checks failed
continuous-integration/drone/push Build is failing
- Add PR and Issue templates

#250

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/303
2023-06-16 17:58:49 +01:00
3f34ff15dd Add licence (#304)
Some checks failed
continuous-integration/drone/push Build is failing
- Add licence file

#249

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/304
2023-06-16 17:56:37 +01:00
6c334cea81 Update dependency typeorm to v0.3.16 (#237)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [typeorm](https://typeorm.io) ([source](https://github.com/typeorm/typeorm)) | dependencies | patch | [`0.3.14` -> `0.3.16`](https://renovatebot.com/diffs/npm/typeorm/0.3.14/0.3.16) |

---

### Release Notes

<details>
<summary>typeorm/typeorm</summary>

### [`v0.3.16`](https://github.com/typeorm/typeorm/blob/HEAD/CHANGELOG.md#&#8203;0316-httpsgithubcomtypeormtypeormcompare03150316-2023-05-09)

[Compare Source](https://github.com/typeorm/typeorm/compare/0.3.15...0.3.16)

##### Bug Fixes

-   add `trustServerCertificate` option to `SqlServerConnectionOptions` ([#&#8203;9985](https://github.com/typeorm/typeorm/issues/9985)) ([0305805](03058055df)), closes [#&#8203;8093](https://github.com/typeorm/typeorm/issues/8093)
-   add directConnection options to MongoDB connection ([#&#8203;9955](https://github.com/typeorm/typeorm/issues/9955)) ([e0165e7](e0165e75ee))
-   add onDelete option validation for oracle ([#&#8203;9786](https://github.com/typeorm/typeorm/issues/9786)) ([938f94b](938f94bded)), closes [#&#8203;9189](https://github.com/typeorm/typeorm/issues/9189)
-   added instanceName to options ([#&#8203;9968](https://github.com/typeorm/typeorm/issues/9968)) ([7c5627f](7c5627f272))
-   added transaction retry logic in cockroachdb ([#&#8203;10032](https://github.com/typeorm/typeorm/issues/10032)) ([607d6f9](607d6f9595))
-   allow json as alias for longtext mariadb ([#&#8203;10018](https://github.com/typeorm/typeorm/issues/10018)) ([2a2bb4b](2a2bb4bdc1))
-   convert the join table ID to the referenceColumn ID type ([#&#8203;9887](https://github.com/typeorm/typeorm/issues/9887)) ([9460296](9460296147))
-   correct encode mongodb auth credentials ([#&#8203;10024](https://github.com/typeorm/typeorm/issues/10024)) ([96b7ee4](96b7ee44b2)), closes [#&#8203;9885](https://github.com/typeorm/typeorm/issues/9885)
-   create correct children during cascade saving entities with STI ([#&#8203;9034](https://github.com/typeorm/typeorm/issues/9034)) ([06c1e98](06c1e98ae2)), closes [#&#8203;7758](https://github.com/typeorm/typeorm/issues/7758) [#&#8203;7758](https://github.com/typeorm/typeorm/issues/7758) [#&#8203;9033](https://github.com/typeorm/typeorm/issues/9033) [#&#8203;9033](https://github.com/typeorm/typeorm/issues/9033) [#&#8203;7758](https://github.com/typeorm/typeorm/issues/7758) [#&#8203;7758](https://github.com/typeorm/typeorm/issues/7758)
-   express option bug in init command ([#&#8203;10022](https://github.com/typeorm/typeorm/issues/10022)) ([5be20e2](5be20e2bcd))
-   for running cli-ts-node-esm use exit code from child process ([#&#8203;10030](https://github.com/typeorm/typeorm/issues/10030)) ([a188b1d](a188b1d9f4)), closes [#&#8203;10029](https://github.com/typeorm/typeorm/issues/10029)
-   mongodb typings breaks the browser version ([#&#8203;9962](https://github.com/typeorm/typeorm/issues/9962)) ([99bef49](99bef49128)), closes [#&#8203;9959](https://github.com/typeorm/typeorm/issues/9959)
-   RelationIdLoader has access to queryPlanner when wrapped in transaction ([#&#8203;9990](https://github.com/typeorm/typeorm/issues/9990)) ([21a9d67](21a9d67fcf)), closes [#&#8203;9988](https://github.com/typeorm/typeorm/issues/9988)
-   resolve duplicate subscriber updated columns ([#&#8203;9958](https://github.com/typeorm/typeorm/issues/9958)) ([3d67901](3d67901fde)), closes [#&#8203;9948](https://github.com/typeorm/typeorm/issues/9948)
-   select + addOrderBy broke in 0.3.14 ([#&#8203;9961](https://github.com/typeorm/typeorm/issues/9961)) ([0e56f0f](0e56f0fcf8)), closes [#&#8203;9960](https://github.com/typeorm/typeorm/issues/9960)
-   support More/LessThanOrEqual in relations  ([#&#8203;9978](https://github.com/typeorm/typeorm/issues/9978)) ([8795c86](8795c864e8))

##### Features

-   mariadb uuid inet4 inet6 column data type support ([#&#8203;9845](https://github.com/typeorm/typeorm/issues/9845)) ([d8a2e37](d8a2e3730f))

##### Reverts

-   "refactor: remove date-fns package ([#&#8203;9634](https://github.com/typeorm/typeorm/issues/9634))" ([54f4f89](54f4f8986a))

### [`v0.3.15`](https://github.com/typeorm/typeorm/blob/HEAD/CHANGELOG.md#&#8203;0315-httpsgithubcomtypeormtypeormcompare03140315-2023-04-15)

[Compare Source](https://github.com/typeorm/typeorm/compare/0.3.14...0.3.15)

##### Bug Fixes

-   make cache optional fields optional ([#&#8203;9942](https://github.com/typeorm/typeorm/issues/9942)) ([159c60a](159c60a6e8))
-   prevent unique index identical to primary key (all sql dialects) ([#&#8203;9940](https://github.com/typeorm/typeorm/issues/9940)) ([51eecc2](51eecc2aa0))
-   SelectQueryBuilder builds incorrectly escaped alias in Oracle when used on entity with composite key ([#&#8203;9668](https://github.com/typeorm/typeorm/issues/9668)) ([83c6c0e](83c6c0ed80))

##### Features

-   support for the latest mongodb v5 ([#&#8203;9925](https://github.com/typeorm/typeorm/issues/9925)) ([f6a3ce7](f6a3ce732d)), closes [#&#8203;7907](https://github.com/typeorm/typeorm/issues/7907) [#&#8203;7907](https://github.com/typeorm/typeorm/issues/7907)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/237
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-06-12 17:19:58 +01:00
e6c845e3b2 Switch to TypeORM's DataSource API (#299)
Some checks failed
continuous-integration/drone/push Build is failing
- Switch to TypeORM's DataSource API, rather than using the now deprecated ormconfig.json
- This will fix stage deployment not knowing how to deploy the database migrations

#297

> **NOTE:** This change requires the deployment scripts to be updated, please update them on the server before merging

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/299
2023-05-26 17:59:22 +01:00
c2418381ea Update dependency minimatch to v9 (#286)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [minimatch](https://github.com/isaacs/minimatch) | dependencies | major | [`7.4.6` -> `9.0.1`](https://renovatebot.com/diffs/npm/minimatch/7.4.6/9.0.1) |

---

### Release Notes

<details>
<summary>isaacs/minimatch</summary>

### [`v9.0.1`](https://github.com/isaacs/minimatch/compare/v9.0.0...v9.0.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v9.0.0...v9.0.1)

### [`v9.0.0`](https://github.com/isaacs/minimatch/compare/v8.0.4...v9.0.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v8.0.4...v9.0.0)

### [`v8.0.4`](https://github.com/isaacs/minimatch/compare/v8.0.3...v8.0.4)

[Compare Source](https://github.com/isaacs/minimatch/compare/v8.0.3...v8.0.4)

### [`v8.0.3`](https://github.com/isaacs/minimatch/compare/v8.0.2...v8.0.3)

[Compare Source](https://github.com/isaacs/minimatch/compare/v8.0.2...v8.0.3)

### [`v8.0.2`](https://github.com/isaacs/minimatch/compare/v8.0.1...v8.0.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v8.0.1...v8.0.2)

### [`v8.0.1`](https://github.com/isaacs/minimatch/compare/v8.0.0...v8.0.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v8.0.0...v8.0.1)

### [`v8.0.0`](https://github.com/isaacs/minimatch/compare/v7.4.6...v8.0.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.4.6...v8.0.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/286
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-05-22 18:07:24 +01:00
61633277ef Update dependency @types/node to v20 (#294)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped)) | devDependencies | major | [`^18.0.0` -> `^20.0.0`](https://renovatebot.com/diffs/npm/@types%2fnode/18.16.5/20.1.4) |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/294
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-05-15 18:24:57 +01:00
40182c8777 Update dependency minimatch to v7.4.6 (#284)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [minimatch](https://github.com/isaacs/minimatch) | dependencies | patch | [`7.4.3` -> `7.4.6`](https://renovatebot.com/diffs/npm/minimatch/7.4.3/7.4.6) |

---

### Release Notes

<details>
<summary>isaacs/minimatch</summary>

### [`v7.4.6`](https://github.com/isaacs/minimatch/compare/v7.4.5...v7.4.6)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.4.5...v7.4.6)

### [`v7.4.5`](https://github.com/isaacs/minimatch/compare/v7.4.4...v7.4.5)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.4.4...v7.4.5)

### [`v7.4.4`](https://github.com/isaacs/minimatch/compare/v7.4.3...v7.4.4)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.4.3...v7.4.4)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/284
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-05-15 18:23:54 +01:00
14a1981b94 Update dependency ts-jest to v29 (#244)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [ts-jest](https://kulshekhar.github.io/ts-jest) ([source](https://github.com/kulshekhar/ts-jest)) | dependencies | major | [`^27.1.2` -> `^29.0.0`](https://renovatebot.com/diffs/npm/ts-jest/27.1.5/29.1.0) |

---

### Release Notes

<details>
<summary>kulshekhar/ts-jest</summary>

### [`v29.1.0`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2910-httpsgithubcomkulshekharts-jestcomparev2905v2910-2023-03-26)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.0.5...v29.1.0)

##### Features

-   Support TypeScript 5.x ([#&#8203;4064](https://github.com/kulshekhar/ts-jest/issues/4064)) ([db98cc9](87f27821db)), closes [#&#8203;4048](https://github.com/kulshekhar/ts-jest/issues/4048)

#### [29.0.5](https://github.com/kulshekhar/ts-jest/compare/v29.0.4...v29.0.5) (2023-01-13)

##### Reverts

-   Revert "fix(transformer): don't use cache when `tsJestConfig` is different ([#&#8203;3966](https://github.com/kulshekhar/ts-jest/issues/3966))" ([185eb18](185eb189d7)), closes [#&#8203;3966](https://github.com/kulshekhar/ts-jest/issues/3966)

#### [29.0.4](https://github.com/kulshekhar/ts-jest/compare/v29.0.3...v29.0.4) (2023-01-10)

##### Bug Fixes

-   **transformer:** don't use cache when `tsJestConfig` is different ([#&#8203;3966](https://github.com/kulshekhar/ts-jest/issues/3966)) ([a445638](a445638ca6))
-   bump `json5` to `2.2.3` ([#&#8203;3976](b9f7809948))

#### [29.0.3](https://github.com/kulshekhar/ts-jest/compare/v29.0.2...v29.0.3) (2022-09-28)

##### Bug Fixes

-   merge config from `globals` with transformer config correctly ([#&#8203;3842](https://github.com/kulshekhar/ts-jest/issues/3842)) ([9c9fd60](9c9fd6097a)), closes [#&#8203;3841](https://github.com/kulshekhar/ts-jest/issues/3841)
-   **presets:** allow merging transform config when using presets ([#&#8203;3833](https://github.com/kulshekhar/ts-jest/issues/3833)) ([afc6a94](afc6a948b1))

##### Features

-   add `useESM` option to `pathsToModuleNameMapper` options ([#&#8203;3792](https://github.com/kulshekhar/ts-jest/issues/3792)) ([eabe906](eabe906e1d))

#### [29.0.2](https://github.com/kulshekhar/ts-jest/compare/v29.0.1...v29.0.2) (2022-09-23)

##### Bug Fixes

-   mark `ts-jest` as optional in `ConfigGlobals` ([#&#8203;3816](https://github.com/kulshekhar/ts-jest/issues/3816)) ([cbb88bb](cbb88bba34)), closes [#&#8203;3815](https://github.com/kulshekhar/ts-jest/issues/3815)
-   use correct typings for `config:init` command ([#&#8203;3825](https://github.com/kulshekhar/ts-jest/issues/3825)) ([21b94db](21b94dbca2))

#### [29.0.2](https://github.com/kulshekhar/ts-jest/compare/v29.0.1...v29.0.2) (2022-09-22)

##### Bug Fixes

-   mark `ts-jest` as optional in `ConfigGlobals` ([#&#8203;3816](https://github.com/kulshekhar/ts-jest/issues/3816)) ([cbb88bb](cbb88bba34)), closes [#&#8203;3815](https://github.com/kulshekhar/ts-jest/issues/3815)

#### [29.0.1](https://github.com/kulshekhar/ts-jest/compare/v29.0.0...v29.0.1) (2022-09-13)

##### Bug Fixes

-   **legacy:** include existing globals config in cached config ([#&#8203;3803](https://github.com/kulshekhar/ts-jest/issues/3803)) ([e79be47](e79be47d2b))

##### Features

-   add typings for `ts-jest` options via `transform` config ([#&#8203;3805](https://github.com/kulshekhar/ts-jest/issues/3805)) ([664b0f2](664b0f2b44))

### [`v29.0.5`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2905-httpsgithubcomkulshekharts-jestcomparev2904v2905-2023-01-13)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.0.4...v29.0.5)

##### Reverts

-   Revert "fix(transformer): don't use cache when `tsJestConfig` is different ([#&#8203;3966](https://github.com/kulshekhar/ts-jest/issues/3966))" ([185eb18](185eb189d7)), closes [#&#8203;3966](https://github.com/kulshekhar/ts-jest/issues/3966)

### [`v29.0.4`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2904-httpsgithubcomkulshekharts-jestcomparev2903v2904-2023-01-10)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.0.3...v29.0.4)

##### Bug Fixes

-   **transformer:** don't use cache when `tsJestConfig` is different ([#&#8203;3966](https://github.com/kulshekhar/ts-jest/issues/3966)) ([a445638](a445638ca6))
-   bump `json5` to `2.2.3` ([#&#8203;3976](b9f7809948))

### [`v29.0.3`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2903-httpsgithubcomkulshekharts-jestcomparev2902v2903-2022-09-28)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.0.2...v29.0.3)

##### Bug Fixes

-   merge config from `globals` with transformer config correctly ([#&#8203;3842](https://github.com/kulshekhar/ts-jest/issues/3842)) ([9c9fd60](9c9fd6097a)), closes [#&#8203;3841](https://github.com/kulshekhar/ts-jest/issues/3841)
-   **presets:** allow merging transform config when using presets ([#&#8203;3833](https://github.com/kulshekhar/ts-jest/issues/3833)) ([afc6a94](afc6a948b1))

##### Features

-   add `useESM` option to `pathsToModuleNameMapper` options ([#&#8203;3792](https://github.com/kulshekhar/ts-jest/issues/3792)) ([eabe906](eabe906e1d))

### [`v29.0.2`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2902-httpsgithubcomkulshekharts-jestcomparev2901v2902-2022-09-23)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.0.1...v29.0.2)

##### Bug Fixes

-   mark `ts-jest` as optional in `ConfigGlobals` ([#&#8203;3816](https://github.com/kulshekhar/ts-jest/issues/3816)) ([cbb88bb](cbb88bba34)), closes [#&#8203;3815](https://github.com/kulshekhar/ts-jest/issues/3815)
-   use correct typings for `config:init` command ([#&#8203;3825](https://github.com/kulshekhar/ts-jest/issues/3825)) ([21b94db](21b94dbca2))

### [`v29.0.1`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2901-httpsgithubcomkulshekharts-jestcomparev2900v2901-2022-09-13)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v29.0.0...v29.0.1)

##### Bug Fixes

-   **legacy:** include existing globals config in cached config ([#&#8203;3803](https://github.com/kulshekhar/ts-jest/issues/3803)) ([e79be47](e79be47d2b))

##### Features

-   add typings for `ts-jest` options via `transform` config ([#&#8203;3805](https://github.com/kulshekhar/ts-jest/issues/3805)) ([664b0f2](664b0f2b44))

### [`v29.0.0`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2900-httpsgithubcomkulshekharts-jestcomparev2900-next1v2900-2022-09-08)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.8...v29.0.0)

##### Features

-   drop Node 12 and Node 17 support ([#&#8203;3787](https://github.com/kulshekhar/ts-jest/issues/3787)) ([0f1de16](0f1de16608))
-   migrate globals config to transformer config ([#&#8203;3780](https://github.com/kulshekhar/ts-jest/issues/3780)) ([31e5843](31e5843554))
-   support Jest 29 ([#&#8203;3767](https://github.com/kulshekhar/ts-jest/issues/3767)) ([94b553b](94b553ba08))

##### DEPRECATIONS

-   Define `ts-jest` config under `globals` is now deprecated. Please define the config via transformer config instead.

##### BREAKING CHANGES

-   Only Node 14, 16 and 18 are supported
-   **Jest 29** is required.

### [`v28.0.8`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2808-httpsgithubcomkulshekharts-jestcomparev2807v2808-2022-08-14)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.7...v28.0.8)

##### Bug Fixes

-   allow `.mts` to be processed ([#&#8203;3713](https://github.com/kulshekhar/ts-jest/issues/3713)) ([effae71](effae71736)), closes [#&#8203;3702](https://github.com/kulshekhar/ts-jest/issues/3702)

### [`v28.0.7`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2807-httpsgithubcomkulshekharts-jestcomparev2806v2807-2022-07-15)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.6...v28.0.7)

##### Bug Fixes

-   update `@jest/types` to be an optional peer dependency ([#&#8203;3690](https://github.com/kulshekhar/ts-jest/issues/3690)) ([8a8c3fa](8a8c3fafec)), closes [#&#8203;3689](https://github.com/kulshekhar/ts-jest/issues/3689)

### [`v28.0.6`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2806-httpsgithubcomkulshekharts-jestcomparev2805v2806-2022-07-13)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.5...v28.0.6)

##### Bug Fixes

-   **config:** don't show diagnostics warning with `diagnostics: false` ([#&#8203;3647](https://github.com/kulshekhar/ts-jest/issues/3647)) ([9a9bc02](9a9bc02935)), closes [#&#8203;3638](https://github.com/kulshekhar/ts-jest/issues/3638)
-   **legacy:** add `useCaseSensitiveFileNames` wherever needed ([#&#8203;3683](https://github.com/kulshekhar/ts-jest/issues/3683)) ([c40bc34](c40bc34625)), closes [#&#8203;3665](https://github.com/kulshekhar/ts-jest/issues/3665)
-   set `@jest/types` as peer dependency ([#&#8203;3633](https://github.com/kulshekhar/ts-jest/issues/3633)) ([24567e1](24567e13d2))

### [`v28.0.5`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2805-httpsgithubcomkulshekharts-jestcomparev2804v2805-2022-06-11)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.4...v28.0.5)

##### Bug Fixes

-   **legacy:** throw type check error in ESM mode with `reject` ([#&#8203;3618](https://github.com/kulshekhar/ts-jest/issues/3618)) ([7dd01ff](7dd01ffe0c)), closes [#&#8203;3507](https://github.com/kulshekhar/ts-jest/issues/3507)

### [`v28.0.4`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2804-httpsgithubcomkulshekharts-jestcomparev2803v2804-2022-06-02)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.3...v28.0.4)

##### Bug Fixes

-   remove `@types/jest` from peer deps ([#&#8203;3592](https://github.com/kulshekhar/ts-jest/issues/3592)) ([b66b656](b66b656e0f))

### [`v28.0.3`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2803-httpsgithubcomkulshekharts-jestcomparev2802v2803-2022-05-23)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.2...v28.0.3)

##### Bug Fixes

-   **security:** update version of `json5` ([#&#8203;3528](https://github.com/kulshekhar/ts-jest/issues/3528)) ([b31f5ba](b31f5bab14))

### [`v28.0.2`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2802-httpsgithubcomkulshekharts-jestcomparev2801v2802-2022-05-07)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.1...v28.0.2)

##### Bug Fixes

-   **transformers:** use `Array.sort` in hoisting transformer ([#&#8203;3498](https://github.com/kulshekhar/ts-jest/issues/3498)) ([e400a6e](e400a6ec0e)), closes [#&#8203;3476](https://github.com/kulshekhar/ts-jest/issues/3476)

### [`v28.0.1`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2801-httpsgithubcomkulshekharts-jestcomparev2800v2801-2022-05-03)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v28.0.0...v28.0.1)

##### Bug Fixes

-   lower the required node version to ^16.10 ([#&#8203;3495](https://github.com/kulshekhar/ts-jest/issues/3495)) ([3a4e48a](3a4e48afff)), closes [#&#8203;3494](https://github.com/kulshekhar/ts-jest/issues/3494)

### [`v28.0.0`](https://github.com/kulshekhar/ts-jest/blob/HEAD/CHANGELOG.md#&#8203;2800-httpsgithubcomkulshekharts-jestcomparev2800-next3v2800-2022-05-02)

[Compare Source](https://github.com/kulshekhar/ts-jest/compare/v27.1.5...v28.0.0)

##### Bug Fixes

-   **legacy** invoke Babel `processAsync` for `babel-jest` in ESM mode instead of `process` ([#&#8203;3430](https://github.com/kulshekhar/ts-jest/issues/3430)) ([0d7356c](0d7356cd76))

##### Features

-   **presets:** add presets for legacy mode ([#&#8203;3465](https://github.com/kulshekhar/ts-jest/issues/3465)) ([543b4ad](543b4ad729))
-   mark `ConfigSet` as legacy ([#&#8203;3456](https://github.com/kulshekhar/ts-jest/issues/3456)) ([a986729](a98672977a))
-   mark `TsCompiler` and `TsJestCompiler` as legacy ([#&#8203;3457](https://github.com/kulshekhar/ts-jest/issues/3457)) ([0f2fe30](0f2fe30676))
-   remove `path-mapping` AST transformer ([#&#8203;3455](https://github.com/kulshekhar/ts-jest/issues/3455)) ([f566869](f5668698f8))
-   set Jest peer dependencies to v28 ([#&#8203;3454](https://github.com/kulshekhar/ts-jest/issues/3454)) ([1e880ff](1e880fffe8))
-   **core:** drop support for Node.js 10 ([#&#8203;3332](https://github.com/kulshekhar/ts-jest/issues/3332)) ([7a9aa61](7a9aa615ea))
-   **core:** remove `mocked` testing util ([#&#8203;3333](https://github.com/kulshekhar/ts-jest/issues/3333)) ([2d9017d](2d9017ddfe))
-   **core:** remove `ts-jest/utils` sub path export ([#&#8203;3334](https://github.com/kulshekhar/ts-jest/issues/3334)) ([9f253d3](9f253d31df))
-   mark `TsJestTransformer` as legacy ([#&#8203;3451](https://github.com/kulshekhar/ts-jest/issues/3451)) ([b090179](b0901799ad))

##### BREAKING CHANGES

-   `path-mapping` AST transformer is no longer shipped in `ts-jest` v28. Please use an alternative one like https://github.com/LeDDGroup/typescript-transform-paths instead.
-   Any imports `ts-jest/dist/compiler/ts-compiler` should change to `ts-jest/dist/legacy/compiler/ts-compiler`
-   Any imports `ts-jest/dist/compiler/ts-jest-compiler` should change to `ts-jest/dist/legacy/compiler/ts-jest-compiler`
-   Any imports `ts-jest/dist/config/config-set` should change to `ts-jest/dist/legacy/config/config-set`
-   Minimum support `TypeScript` version is now **4.3** since Jest 28 requires it.
-   **Jest 28** is required.
-   **core:** Any imports `ts-jest/utils` should be replaced with `ts-jest`.
-   **core:** Starting from Jest 27.4, `mocked` has been integrated into Jest repo.
-   **core:** Support for Node.js v10 has been removed as Jest drops support for it.

#### [27.1.5](https://github.com/kulshekhar/ts-jest/compare/v27.1.3...v27.1.4) (2022-05-17)

##### Bug Fixes

-   **transformers** use `Array.sort` in hoisting transformer ([#&#8203;3498](https://github.com/kulshekhar/ts-jest/pull/3498)) ([e400a6e](e400a6ec0e)), fixes [#&#8203;3476](https://github.com/kulshekhar/ts-jest/issues/3476)

#### [27.1.4](https://github.com/kulshekhar/ts-jest/compare/v27.1.3...v27.1.4) (2022-03-24)

##### Bug Fixes

-   **compiler:** revert [#&#8203;3194](https://github.com/kulshekhar/ts-jest/issues/3194) ([#&#8203;3362](https://github.com/kulshekhar/ts-jest/issues/3362)) ([2b7dffe](2b7dffeac9)), closes [#&#8203;3272](https://github.com/kulshekhar/ts-jest/issues/3272)
-   remove `esbuild` from peer dependency ([#&#8203;3360](https://github.com/kulshekhar/ts-jest/issues/3360)) ([8c8c1ca](8c8c1ca615)), closes [#&#8203;3346](https://github.com/kulshekhar/ts-jest/issues/3346)
-   support Babel config file with `.cjs` extension ([#&#8203;3361](https://github.com/kulshekhar/ts-jest/issues/3361)) ([5e5ac4a](5e5ac4ac28)), closes [#&#8203;3335](https://github.com/kulshekhar/ts-jest/issues/3335)

#### [27.1.3](https://github.com/kulshekhar/ts-jest/compare/v27.1.2...v27.1.3) (2022-01-14)

##### Bug Fixes

-   **compiler:** update memory cache for compiler using received file content ([#&#8203;3194](https://github.com/kulshekhar/ts-jest/issues/3194)) ([e4d9541](e4d9541f26))

#### [27.1.2](https://github.com/kulshekhar/ts-jest/compare/v27.1.1...v27.1.2) (2021-12-15)

##### Bug Fixes

-   stimulate `esbuild` type to avoid importing `esbuild` directly ([#&#8203;3147](https://github.com/kulshekhar/ts-jest/issues/3147)) ([9ace0a9](9ace0a9991))

#### [27.1.1](https://github.com/kulshekhar/ts-jest/compare/v27.1.0...v27.1.1) (2021-12-07)

##### Bug Fixes

-   bring back `afterProcess` hook ([#&#8203;3132](https://github.com/kulshekhar/ts-jest/issues/3132)) ([2b6b86e](2b6b86e01d))
-   make `esbuild` as optional peer dependency ([#&#8203;3129](https://github.com/kulshekhar/ts-jest/pull/3129)) ([20258de](20258de54c))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/244
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-05-15 18:20:36 +01:00
6fb448fc28 Update dependency emoji-regex to v10 (#240)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [emoji-regex](https://mths.be/emoji-regex) ([source](https://github.com/mathiasbynens/emoji-regex)) | dependencies | major | [`^9.2.0` -> `^10.0.0`](https://renovatebot.com/diffs/npm/emoji-regex/9.2.2/10.2.1) |

---

### Release Notes

<details>
<summary>mathiasbynens/emoji-regex</summary>

### [`v10.2.1`](https://github.com/mathiasbynens/emoji-regex/compare/v10.2.0...v10.2.1)

[Compare Source](https://github.com/mathiasbynens/emoji-regex/compare/v10.2.0...v10.2.1)

### [`v10.2.0`](https://github.com/mathiasbynens/emoji-regex/compare/v10.1.0...v10.2.0)

[Compare Source](https://github.com/mathiasbynens/emoji-regex/compare/v10.1.0...v10.2.0)

### [`v10.1.0`](https://github.com/mathiasbynens/emoji-regex/compare/v10.0.1...v10.1.0)

[Compare Source](https://github.com/mathiasbynens/emoji-regex/compare/v10.0.1...v10.1.0)

### [`v10.0.1`](https://github.com/mathiasbynens/emoji-regex/compare/v10.0.0...v10.0.1)

[Compare Source](https://github.com/mathiasbynens/emoji-regex/compare/v10.0.0...v10.0.1)

### [`v10.0.0`](https://github.com/mathiasbynens/emoji-regex/compare/v9.2.2...v10.0.0)

[Compare Source](https://github.com/mathiasbynens/emoji-regex/compare/v9.2.2...v10.0.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/240
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-05-15 18:19:12 +01:00
c12644d537 Fix build errors from merge
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-08 18:37:51 +01:00
efd213d085 Merge branch 'main' into develop 2023-05-08 18:33:23 +01:00
c02dbb30d7 Update dependency jest to v29 (#241)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [jest](https://jestjs.io/) ([source](https://github.com/facebook/jest)) | dependencies | major | [`^27.4.5` -> `^29.0.0`](https://renovatebot.com/diffs/npm/jest/27.4.5/29.5.0) |
| [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jest) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped)) | dependencies | major | [`^27.0.3` -> `^29.0.0`](https://renovatebot.com/diffs/npm/@types%2fjest/27.0.3/29.5.0) |

---

### Release Notes

<details>
<summary>facebook/jest</summary>

### [`v29.5.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2950)

[Compare Source](https://github.com/facebook/jest/compare/v29.4.3...v29.5.0)

##### Features

-   `[jest-changed-files]` Support Sapling ([#&#8203;13941](https://github.com/facebook/jest/pull/13941))
-   `[jest-circus, @&#8203;jest/cli, jest-config]` Add feature to randomize order of tests via CLI flag or through the config file([#&#8203;12922](https://github.com/facebook/jest/pull/12922))
-   `[jest-cli, jest-config, @&#8203;jest/core, jest-haste-map, @&#8203;jest/reporters, jest-runner, jest-runtime, @&#8203;jest/types]` Add `workerThreads` configuration option to allow using [worker threads](https://nodejs.org/dist/latest/docs/api/worker_threads.html) for parallelization ([#&#8203;13939](https://github.com/facebook/jest/pull/13939))
-   `[jest-cli]` Export `yargsOptions` ([#&#8203;13970](https://github.com/facebook/jest/pull/13970))
-   `[jest-config]` Add `openHandlesTimeout` option to configure possible open handles warning. ([#&#8203;13875](https://github.com/facebook/jest/pull/13875))
-   `[@jest/create-cache-key-function]` Allow passing `length` argument to `createCacheKey()` function and set its default value to `16` on Windows ([#&#8203;13827](https://github.com/facebook/jest/pull/13827))
-   `[jest-message-util]` Add support for [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) ([#&#8203;13946](https://github.com/facebook/jest/pull/13946) & [#&#8203;13947](https://github.com/facebook/jest/pull/13947))
-   `[jest-message-util]` Add support for [Error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) in `test` and `it` ([#&#8203;13935](https://github.com/facebook/jest/pull/13935) & [#&#8203;13966](https://github.com/facebook/jest/pull/13966))
-   `[jest-reporters]` Add `summaryThreshold` option to summary reporter to allow overriding the internal threshold that is used to print the summary of all failed tests when the number of test suites surpasses it ([#&#8203;13895](https://github.com/facebook/jest/pull/13895))
-   `[jest-runtime]` Expose `@sinonjs/fake-timers` async APIs functions `advanceTimersByTimeAsync(msToRun)` (`tickAsync(msToRun)`), `advanceTimersToNextTimerAsync(steps)` (`nextAsync`), `runAllTimersAsync` (`runAllAsync`), and `runOnlyPendingTimersAsync` (`runToLastAsync`) ([#&#8203;13981](https://github.com/facebook/jest/pull/13981))
-   `[jest-runtime, @&#8203;jest/transform]` Allow V8 coverage provider to collect coverage from files which were not loaded explicitly ([#&#8203;13974](https://github.com/facebook/jest/pull/13974))
-   `[jest-snapshot]` Add support to `cts` and `mts` TypeScript files to inline snapshots ([#&#8203;13975](https://github.com/facebook/jest/pull/13975))
-   `[jest-worker]` Add `start` method to worker farms ([#&#8203;13937](https://github.com/facebook/jest/pull/13937))
-   `[jest-worker]` Support passing a URL as path to worker ([#&#8203;13982](https://github.com/facebook/jest/pull/13982))

##### Fixes

-   `[babel-plugin-jest-hoist]` Fix unwanted hoisting of nested `jest` usages ([#&#8203;13952](https://github.com/facebook/jest/pull/13952))
-   `[jest-circus]` Send test case results for `todo` tests ([#&#8203;13915](https://github.com/facebook/jest/pull/13915))
-   `[jest-circus]` Update message printed on test timeout ([#&#8203;13830](https://github.com/facebook/jest/pull/13830))
-   `[jest-circus]` Avoid creating the word "testfalse" when `takesDoneCallback` is `false` in the message printed on test timeout AND updated timeouts test ([#&#8203;13954](https://github.com/facebook/jest/pull/13954))
-   `[jest-environment-jsdom]` Stop setting `document` to `null` on teardown ([#&#8203;13972](https://github.com/facebook/jest/pull/13972))
-   `[@jest/expect-utils]` Update `toStrictEqual()` to be able to check `jest.fn().mock.calls` ([#&#8203;13960](https://github.com/facebook/jest/pull/13960))
-   `[@jest/test-result]` Allow `TestResultsProcessor` type to return a Promise ([#&#8203;13950](https://github.com/facebook/jest/pull/13950))

##### Chore & Maintenance

-   `[jest-snapshot]` Remove dependency on `jest-haste-map` ([#&#8203;13977](https://github.com/facebook/jest/pull/13977))

### [`v29.4.3`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2943)

[Compare Source](https://github.com/facebook/jest/compare/v29.4.2...v29.4.3)

##### Features

-   `[expect]` Update `toThrow()` to be able to use error `cause`s ([#&#8203;13606](https://github.com/facebook/jest/pull/13606))
-   `[jest-core]` allow to use `workerIdleMemoryLimit` with only 1 worker or `runInBand` option ([#&#8203;13846](https://github.com/facebook/jest/pull/13846))
-   `[jest-message-util]` Add support for [error `cause`s](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) ([#&#8203;13868](https://github.com/facebook/jest/pull/13868) & [#&#8203;13912](https://github.com/facebook/jest/pull/13912))
-   `[jest-runtime]` Revert `import assertions` for JSON modules as it's been relegated to Stage 2 ([#&#8203;13911](https://github.com/facebook/jest/pull/13911))

##### Fixes

-   `[@jest/expect-utils]` `subsetEquality` should consider also an object's inherited string keys ([#&#8203;13824](https://github.com/facebook/jest/pull/13824))
-   `[jest-mock]` Clear mock state when `jest.restoreAllMocks()` is called ([#&#8203;13867](https://github.com/facebook/jest/pull/13867))
-   `[jest-mock]` Prevent `mockImplementationOnce` and `mockReturnValueOnce` bleeding into `withImplementation` ([#&#8203;13888](https://github.com/facebook/jest/pull/13888))
-   `[jest-mock]` Do not restore mocks when `jest.resetAllMocks()` is called ([#&#8203;13866](https://github.com/facebook/jest/pull/13866))

### [`v29.4.2`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2942)

[Compare Source](https://github.com/facebook/jest/compare/v29.4.1...v29.4.2)

##### Features

-   `[@jest/core]` Instrument significant lifecycle events with [`performance.mark()`](https://nodejs.org/docs/latest-v16.x/api/perf_hooks.html#performancemarkname-options) ([#&#8203;13859](https://github.com/facebook/jest/pull/13859))

##### Fixes

-   `[expect, @&#8203;jest/expect]` Provide type of `actual` as a generic argument to `Matchers` to allow better-typed extensions ([#&#8203;13848](https://github.com/facebook/jest/pull/13848))
-   `[jest-circus]` Added explicit mention of test failing because `done()` is not being called in error message ([#&#8203;13847](https://github.com/facebook/jest/pull/13847))
-   `[jest-runtime]` Handle CJS re-exports of node core modules from ESM ([#&#8203;13856](https://github.com/facebook/jest/pull/13856))
-   `[jest-transform]` Downgrade `write-file-atomic` to v4 ([#&#8203;13853](https://github.com/facebook/jest/pull/13853))
-   `[jest-worker]` Ignore IPC messages not intended for Jest ([#&#8203;13543](https://github.com/facebook/jest/pull/13543))

##### Chore & Maintenance

-   `[*]` make sure to exclude `.eslintcache` from published module ([#&#8203;13832](https://github.com/facebook/jest/pull/13832))
-   `[docs]` Cleanup incorrect links in CHANGELOG.md ([#&#8203;13857](https://github.com/facebook/jest/pull/13857))

### [`v29.4.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2941)

[Compare Source](https://github.com/facebook/jest/compare/v29.4.0...v29.4.1)

##### Features

-   `[expect, jest-circus, @&#8203;jest/types]` Implement `numPassingAsserts` of testResults to track the number of passing asserts in a test ([#&#8203;13795](https://github.com/facebook/jest/pull/13795))
-   `[jest-core]` Add newlines to JSON output ([#&#8203;13817](https://github.com/facebook/jest/pull/13817))
-   `[@jest/reporters]` Automatic log folding in GitHub Actions Reporter ([#&#8203;13626](https://github.com/facebook/jest/pull/13626))

##### Fixes

-   `[@jest/expect-utils]` `toMatchObject` diffs should include `Symbol` properties ([#&#8203;13810](https://github.com/facebook/jest/pull/13810))
-   `[jest-runtime]` Handle missing `replaceProperty` ([#&#8203;13823](https://github.com/facebook/jest/pull/13823))
-   `[@jest/types]` Add partial support for `done` callbacks in typings of `each` ([#&#8203;13756](https://github.com/facebook/jest/pull/13756))

### [`v29.4.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2940)

[Compare Source](https://github.com/facebook/jest/compare/v29.3.1...v29.4.0)

##### Features

-   `[expect, @&#8203;jest/expect-utils]` Support custom equality testers ([#&#8203;13654](https://github.com/facebook/jest/pull/13654))
-   `[jest-config, jest-worker]` Use `os.availableParallelism` if available to calculate number of workers to spawn ([#&#8203;13738](https://github.com/facebook/jest/pull/13738))
-   `[@jest/globals, jest-mock]` Add `jest.replaceProperty()` that replaces property value ([#&#8203;13496](https://github.com/facebook/jest/pull/13496))
-   `[jest-haste-map]` ignore Sapling vcs directories (`.sl/`) ([#&#8203;13674](https://github.com/facebook/jest/pull/13674))
-   `[jest-resolve]` Support subpath imports ([#&#8203;13705](https://github.com/facebook/jest/pull/13705), [#&#8203;13723](https://github.com/facebook/jest/pull/13723), [#&#8203;13777](https://github.com/facebook/jest/pull/13777))
-   `[jest-runtime]` Add `jest.isolateModulesAsync` for scoped module initialization of asynchronous functions ([#&#8203;13680](https://github.com/facebook/jest/pull/13680))
-   `[jest-runtime]` Add `jest.isEnvironmentTornDown` function ([#&#8203;13741](https://github.com/facebook/jest/pull/13741))
-   `[jest-test-result]` Added `skipped` and `focused` status to `FormattedTestResult` ([#&#8203;13700](https://github.com/facebook/jest/pull/13700))
-   `[jest-transform]` Support for asynchronous `createTransformer` ([#&#8203;13762](https://github.com/facebook/jest/pull/13762))

##### Fixes

-   `[jest-environment-node]` Fix non-configurable globals ([#&#8203;13687](https://github.com/facebook/jest/pull/13687))
-   `[@jest/expect-utils]` `toMatchObject` should handle `Symbol` properties ([#&#8203;13639](https://github.com/facebook/jest/pull/13639))
-   `[jest-mock]` Fix `mockReset` and `resetAllMocks` `undefined` return value([#&#8203;13692](https://github.com/facebook/jest/pull/13692))
-   `[jest-resolve]` Add global paths to `require.resolve.paths` ([#&#8203;13633](https://github.com/facebook/jest/pull/13633))
-   `[jest-resolve]` Correct node core module detection when using `node:` specifiers ([#&#8203;13806](https://github.com/facebook/jest/pull/13806))
-   `[jest-runtime]` Support WASM files that import JS resources ([#&#8203;13608](https://github.com/facebook/jest/pull/13608))
-   `[jest-runtime]` Use the `scriptTransformer` cache in `jest-runner` ([#&#8203;13735](https://github.com/facebook/jest/pull/13735))
-   `[jest-runtime]` Enforce import assertions when importing JSON in ESM ([#&#8203;12755](https://github.com/facebook/jest/pull/12755) & [#&#8203;13805](https://github.com/facebook/jest/pull/13805))
-   `[jest-snapshot]` Make sure to import `babel` outside of the sandbox ([#&#8203;13694](https://github.com/facebook/jest/pull/13694))
-   `[jest-transform]` Ensure the correct configuration is passed to preprocessors specified multiple times in the `transform` option ([#&#8203;13770](https://github.com/facebook/jest/pull/13770))

##### Chore & Maintenance

-   `[@jest/fake-timers]` Update `@sinonjs/fake-timers` ([#&#8203;13612](https://github.com/facebook/jest/pull/13612))
-   `[docs]` Improve custom puppeteer example to prevent worker warnings ([#&#8203;13619](https://github.com/facebook/jest/pull/13619))

### [`v29.3.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2931)

[Compare Source](https://github.com/facebook/jest/compare/v29.3.0...v29.3.1)

##### Fixes

-   `[jest-config]` Do not warn about `preset` in `ProjectConfig` ([#&#8203;13583](https://github.com/facebook/jest/pull/13583))

##### Performance

-   `[jest-transform]` Defer creation of cache directory ([#&#8203;13420](https://github.com/facebook/jest/pull/13420))

### [`v29.3.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2930)

[Compare Source](https://github.com/facebook/jest/compare/v29.2.2...v29.3.0)

##### Features

-   `[jest-runtime]` Support WebAssembly (Wasm) imports in ESM modules ([#&#8203;13505](https://github.com/facebook/jest/pull/13505))

##### Fixes

-   `[jest-config]` Add config validation for `projects` option ([#&#8203;13565](https://github.com/facebook/jest/pull/13565))
-   `[jest-mock]` Treat cjs modules as objects so they can be mocked ([#&#8203;13513](https://github.com/facebook/jest/pull/13513))
-   `[jest-worker]` Throw an error instead of hanging when jest workers terminate unexpectedly ([#&#8203;13566](https://github.com/facebook/jest/pull/13566))

##### Chore & Maintenance

-   `[@jest/transform]` Update `convert-source-map` ([#&#8203;13509](https://github.com/facebook/jest/pull/13509))
-   `[docs]` Mention `toStrictEqual` in UsingMatchers docs. ([#&#8203;13560](https://github.com/facebook/jest/pull/13560))

### [`v29.2.2`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2922)

[Compare Source](https://github.com/facebook/jest/compare/v29.2.1...v29.2.2)

##### Fixes

-   `[@jest/test-sequencer]` Make sure sharding does not produce empty groups ([#&#8203;13476](https://github.com/facebook/jest/pull/13476))
-   `[jest-circus]` Test marked as `todo` are shown as todo when inside a focussed describe ([#&#8203;13504](https://github.com/facebook/jest/pull/13504))
-   `[jest-mock]` Ensure mock resolved and rejected values are promises from correct realm ([#&#8203;13503](https://github.com/facebook/jest/pull/13503))
-   `[jest-snapshot]` Don't highlight passing asymmetric property matchers in snapshot diff ([#&#8203;13480](https://github.com/facebook/jest/issues/13480))

##### Chore & Maintenance

-   `[docs]` Update link to Jest 28 upgrade guide in error message ([#&#8203;13483](https://github.com/facebook/jest/pull/13483))
-   `[jest-runner, jest-watcher]` Update `emittery` ([#&#8203;13490](https://github.com/facebook/jest/pull/13490))

### [`v29.2.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2921)

[Compare Source](https://github.com/facebook/jest/compare/v29.2.0...v29.2.1)

##### Features

-   `[@jest/globals, jest-mock]` Add `jest.Spied*` utility types ([#&#8203;13440](https://github.com/facebook/jest/pull/13440))

##### Fixes

-   `[jest-environment-node]` make `globalThis.performance` writable for Node 19 and fake timers ([#&#8203;13467](https://github.com/facebook/jest/pull/13467))
-   `[jest-mock]` Revert [#&#8203;13398](https://github.com/facebook/jest/pull/13398) to restore mocking of setters ([#&#8203;13472](https://github.com/facebook/jest/pull/13472))

##### Performance

-   `[*]` Use sha1 instead of sha256 for hashing ([#&#8203;13421](https://github.com/facebook/jest/pull/13421))

### [`v29.2.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2920)

[Compare Source](https://github.com/facebook/jest/compare/v29.1.2...v29.2.0)

##### Features

-   `[@jest/cli, jest-config]` A seed for the test run will be randomly generated, or set by a CLI option ([#&#8203;13400](https://github.com/facebook/jest/pull/13400))
-   `[@jest/cli, jest-config]` `--show-seed` will display the seed value in the report, and can be set via a CLI flag or through the config file ([#&#8203;13400](https://github.com/facebook/jest/pull/13400))
-   `[jest-config]` Add `readInitialConfig` utility function ([#&#8203;13356](https://github.com/facebook/jest/pull/13356))
-   `[jest-core]` Allow `testResultsProcessor` to be async ([#&#8203;13343](https://github.com/facebook/jest/pull/13343))
-   `[@jest/environment, jest-environment-node, jest-environment-jsdom, jest-runtime]` Add `getSeed()` to the `jest` object ([#&#8203;13400](https://github.com/facebook/jest/pull/13400))
-   `[expect, @&#8203;jest/expect-utils]` Allow `isA` utility to take a type argument ([#&#8203;13355](https://github.com/facebook/jest/pull/13355))
-   `[expect]` Expose `AsyncExpectationResult` and `SyncExpectationResult` types ([#&#8203;13411](https://github.com/facebook/jest/pull/13411))

##### Fixes

-   `[babel-plugin-jest-hoist]` Ignore `TSTypeQuery` when checking for hoisted references ([#&#8203;13367](https://github.com/facebook/jest/pull/13367))
-   `[jest-core]` Fix `detectOpenHandles` false positives for some special objects such as `TLSWRAP` ([#&#8203;13414](https://github.com/facebook/jest/pull/13414))
-   `[jest-mock]` Fix mocking of getters and setters on classes ([#&#8203;13398](https://github.com/facebook/jest/pull/13398))
-   `[jest-reporters]` Revert: Transform file paths into hyperlinks ([#&#8203;13399](https://github.com/facebook/jest/pull/13399))
-   `[@jest/types]` Infer type of `each` table correctly when the table is a tuple or array ([#&#8203;13381](https://github.com/facebook/jest/pull/13381))
-   `[@jest/types]` Rework typings to allow the `*ReturnedWith` matchers to be called with no argument ([#&#8203;13385](https://github.com/facebook/jest/pull/13385))

##### Chore & Maintenance

-   `[*]` Update `@babel/*` deps, resulting in slightly different stack traces for `each` ([#&#8203;13422](https://github.com/facebook/jest/pull/13422))

##### Performance

-   `[jest-runner]` Do not instrument v8 coverage data if coverage should not be collected ([#&#8203;13282](https://github.com/facebook/jest/pull/13282))

### [`v29.1.2`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2912)

[Compare Source](https://github.com/facebook/jest/compare/v29.1.1...v29.1.2)

##### Fixes

-   `[expect, @&#8203;jest/expect]` Revert buggy inference of argument types for `*CalledWith` and `*ReturnedWith` matchers introduced in 29.1.0 ([#&#8203;13339](https://github.com/facebook/jest/pull/13339))
-   `[jest-worker]` Add missing dependency on `jest-util` ([#&#8203;13341](https://github.com/facebook/jest/pull/13341))

### [`v29.1.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2911)

[Compare Source](https://github.com/facebook/jest/compare/v29.1.0...v29.1.1)

##### Fixes

-   `[jest-mock]` Revert [#&#8203;13145](https://github.com/facebook/jest/pull/13145) which broke mocking of transpiled ES modules

### [`v29.1.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2910)

[Compare Source](https://github.com/facebook/jest/compare/v29.0.3...v29.1.0)

##### Features

-   `[expect, @&#8203;jest/expect]` Support type inference for function parameters in `CalledWith` assertions ([#&#8203;13268](https://github.com/facebook/jest/pull/13268))
-   `[expect, @&#8203;jest/expect]` Infer type of `*ReturnedWith` matchers argument ([#&#8203;13278](https://github.com/facebook/jest/pull/13278))
-   `[@jest/environment, jest-runtime]` Allow `jest.requireActual` and `jest.requireMock` to take a type argument ([#&#8203;13253](https://github.com/facebook/jest/pull/13253))
-   `[@jest/environment]` Allow `jest.mock` and `jest.doMock` to take a type argument ([#&#8203;13254](https://github.com/facebook/jest/pull/13254))
-   `[@jest/fake-timers]` Add `jest.now()` to return the current fake clock time ([#&#8203;13244](https://github.com/facebook/jest/pull/13244), [#&#8203;13246](https://github.com/facebook/jest/pull/13246))
-   `[@jest/mock]` Add `withImplementation` method for temporarily overriding a mock ([#&#8203;13281](https://github.com/facebook/jest/pull/13281))
-   `[expect]` Export `toThrow*` matchers ([#&#8203;13328](https://github.com/facebook/jest/pull/13328))

##### Fixes

-   `[jest-circus, jest-jasmine2]` Fix error messages for Node's `assert.throes` ([#&#8203;13322](https://github.com/facebook/jest/pull/13322))
-   `[jest-haste-map]` Remove `__proto__` usage ([#&#8203;13256](https://github.com/facebook/jest/pull/13256))
-   `[jest-mock]` Improve `spyOn` typings to handle optional properties ([#&#8203;13247](https://github.com/facebook/jest/pull/13247))
-   `[jest-mock]` Fix mocking of getters and setters on classes ([#&#8203;13145](https://github.com/facebook/jest/pull/13145))
-   `[jest-snapshot]` Throw useful error when an array is passed as property matchers ([#&#8203;13263](https://github.com/facebook/jest/pull/13263))
-   `[jest-snapshot]` Prioritize parser used in the project ([#&#8203;13323](https://github.com/facebook/jest/pull/13323))
-   `[jest-transform]` Attempt to work around issues with atomic writes on Windows ([#&#8203;11423](https://github.com/facebook/jest/pull/11423))

### [`v29.0.3`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2903)

[Compare Source](https://github.com/facebook/jest/compare/v29.0.2...v29.0.3)

##### Features

-   `[@jest/environment, jest-runtime]` Allow passing a generic type argument to `jest.createMockFromModule<T>()` method ([#&#8203;13202](https://github.com/facebook/jest/pull/13202))
-   `[expect]` Expose `ExpectationResult` type ([#&#8203;13240](https://github.com/facebook/jest/pull/13240))
-   `[jest-snapshot]` Expose `Context` type ([#&#8203;13240](https://github.com/facebook/jest/pull/13240))
-   `[@jest/globals]` Add `jest.Mock` type helper ([#&#8203;13235](https://github.com/facebook/jest/pull/13235))

##### Fixes

-   `[jest-core]` Capture `execError` during `TestScheduler.scheduleTests` and dispatch to reporters ([#&#8203;13203](https://github.com/facebook/jest/pull/13203))
-   `[jest-resolve]` Make sure to resolve module paths after looking at `exports` ([#&#8203;13242](https://github.com/facebook/jest/pull/13242))
-   `[jest-resolve]` Improve error on module not found deep in the `require` stack ([#&#8203;8704](https://github.com/facebook/jest/pull/8704))
-   `[jest-snapshot]` Fix typings of snapshot matchers ([#&#8203;13240](https://github.com/facebook/jest/pull/13240))

##### Chore & Maintenance

-   `[*]` Fix inconsistent workspace prefixes ([#&#8203;13217](https://github.com/facebook/jest/pull/13217))
-   `[jest-haste-map]` Expose a minimal public API to TypeScript ([#&#8203;13023](https://github.com/facebook/jest/pull/13023))

### [`v29.0.2`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2902)

[Compare Source](https://github.com/facebook/jest/compare/v29.0.1...v29.0.2)

##### Features

-   `[jest-transform]` Expose `TransformFactory` type ([#&#8203;13184](https://github.com/facebook/jest/pull/13184))

##### Fixes

-   `[babel-plugin-jest-hoist]` Support imported `jest` in mock factory ([#&#8203;13188](https://github.com/facebook/jest/pull/13188))
-   `[jest-mock]` Align the behavior and return type of `generateFromMetadata` method ([#&#8203;13207](https://github.com/facebook/jest/pull/13207))
-   `[jest-runtime]` Support `jest.resetModules()` with ESM ([#&#8203;13211](https://github.com/facebook/jest/pull/13211))

### [`v29.0.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2901)

[Compare Source](https://github.com/facebook/jest/compare/v29.0.0...v29.0.1)

##### Fixes

-   `[jest-snapshot]` Pass `snapshotFormat` through when diffing snapshots ([#&#8203;13181](https://github.com/facebook/jest/pull/13181))

### [`v29.0.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2900)

[Compare Source](https://github.com/facebook/jest/compare/v28.1.3...v29.0.0)

##### Features

-   `[expect]` \[**BREAKING**] Differentiate between `MatcherContext` `MatcherUtils` and `MatcherState` types ([#&#8203;13141](https://github.com/facebook/jest/pull/13141))
-   `[jest-circus]` Add support for `test.failing.each` ([#&#8203;13142](https://github.com/facebook/jest/pull/13142))
-   `[jest-config]` \[**BREAKING**] Make `snapshotFormat` default to `escapeString: false` and `printBasicPrototype: false` ([#&#8203;13036](https://github.com/facebook/jest/pull/13036))
-   `[jest-config]` \[**BREAKING**] Remove undocumented `collectCoverageOnlyFrom` option ([#&#8203;13156](https://github.com/facebook/jest/pull/13156))
-   `[jest-environment-jsdom]` \[**BREAKING**] Upgrade to `jsdom@20` ([#&#8203;13037](https://github.com/facebook/jest/pull/13037), [#&#8203;13058](https://github.com/facebook/jest/pull/13058))
-   `[@jest/globals]` Add `jest.Mocked`, `jest.MockedClass`, `jest.MockedFunction` and `jest.MockedObject` utility types ([#&#8203;12727](https://github.com/facebook/jest/pull/12727))
-   `[jest-mock]` \[**BREAKING**] Refactor `Mocked*` utility types. `MaybeMockedDeep` and `MaybeMocked` became `Mocked` and `MockedShallow` respectively; only deep mocked variants of `MockedClass`, `MockedFunction` and `MockedObject` are exported ([#&#8203;13123](https://github.com/facebook/jest/pull/13123), [#&#8203;13124](https://github.com/facebook/jest/pull/13124))
-   `[jest-mock]` \[**BREAKING**] Change the default `jest.mocked` helper’s behavior to deep mocked ([#&#8203;13125](https://github.com/facebook/jest/pull/13125))
-   `[jest-snapshot]` \[**BREAKING**] Let `babel` find config when updating inline snapshots ([#&#8203;13150](https://github.com/facebook/jest/pull/13150))
-   `[@jest/test-result, @&#8203;jest/types]` \[**BREAKING**] Replace `Bytes` and `Milliseconds` types with `number` ([#&#8203;13155](https://github.com/facebook/jest/pull/13155))
-   `[jest-worker]` Adds `workerIdleMemoryLimit` option which is used as a check for worker memory leaks >= Node 16.11.0 and recycles child workers as required ([#&#8203;13056](https://github.com/facebook/jest/pull/13056), [#&#8203;13105](https://github.com/facebook/jest/pull/13105), [#&#8203;13106](https://github.com/facebook/jest/pull/13106), [#&#8203;13107](https://github.com/facebook/jest/pull/13107))
-   `[pretty-format]` \[**BREAKING**] Remove `ConvertAnsi` plugin in favour of `jest-serializer-ansi-escapes` ([#&#8203;13040](https://github.com/facebook/jest/pull/13040))
-   `[pretty-format]` Allow to opt out from sorting object keys with `compareKeys: null` ([#&#8203;12443](https://github.com/facebook/jest/pull/12443))

##### Fixes

-   `[jest-config]` Fix testing multiple projects with TypeScript config files ([#&#8203;13099](https://github.com/facebook/jest/pull/13099))
-   `[@jest/expect-utils]` Fix deep equality of ImmutableJS Record ([#&#8203;13055](https://github.com/facebook/jest/pull/13055))
-   `[jest-haste-map]` Increase the maximum possible file size that jest-haste-map can handle ([#&#8203;13094](https://github.com/facebook/jest/pull/13094))
-   `[jest-runtime]` Properly support CJS re-exports from dual packages ([#&#8203;13170](https://github.com/facebook/jest/pull/13170))
-   `[jest-snapshot]` Make `prettierPath` optional in `SnapshotState` ([#&#8203;13149](https://github.com/facebook/jest/pull/13149))
-   `[jest-snapshot]` Fix parsing error from inline snapshot files with `JSX` ([#&#8203;12760](https://github.com/facebook/jest/pull/12760))
-   `[jest-worker]` When a process runs out of memory worker exits correctly and doesn't spin indefinitely ([#&#8203;13054](https://github.com/facebook/jest/pull/13054))

##### Chore & Maintenance

-   `[*]` \[**BREAKING**] Drop support for Node v12 and v17 ([#&#8203;13033](https://github.com/facebook/jest/pull/13033))
-   `[docs]` Fix webpack name ([#&#8203;13049](https://github.com/facebook/jest/pull/13049))
-   `[docs]` Explicit how to set `n` for `--bail` ([#&#8203;13128](https://github.com/facebook/jest/pull/13128))
-   `[docs]` Update Enzyme URL ([#&#8203;13166](https://github.com/facebook/jest/pull/13166))
-   `[jest-leak-detector]` Remove support for `weak-napi` ([#&#8203;13035](https://github.com/facebook/jest/pull/13035))
-   `[jest-snapshot]` \[**BREAKING**] Require `rootDir` as argument to `SnapshotState` ([#&#8203;13150](https://github.com/facebook/jest/pull/13150))

### [`v28.1.3`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2813)

[Compare Source](https://github.com/facebook/jest/compare/v28.1.2...v28.1.3)

##### Features

-   `[jest-leak-detector]` Use native `FinalizationRegistry` when it exists to get rid of external C dependency ([#&#8203;12973](https://github.com/facebook/jest/pull/12973))

##### Fixes

-   `[jest-changed-files]` Fix a lock-up after repeated invocations ([#&#8203;12757](https://github.com/facebook/jest/issues/12757))
-   `[@jest/expect-utils]` Fix deep equality of ImmutableJS OrderedSets ([#&#8203;12977](https://github.com/facebook/jest/pull/12977))
-   `[jest-mock]` Add index signature support for `spyOn` types ([#&#8203;13013](https://github.com/facebook/jest/pull/13013), [#&#8203;13020](https://github.com/facebook/jest/pull/13020))
-   `[jest-snapshot]` Fix indentation of awaited inline snapshots ([#&#8203;12986](https://github.com/facebook/jest/pull/12986))

##### Chore & Maintenance

-   `[*]` Replace internal usage of `pretty-format/ConvertAnsi` with `jest-serializer-ansi-escapes` ([#&#8203;12935](https://github.com/facebook/jest/pull/12935), [#&#8203;13004](https://github.com/facebook/jest/pull/13004))
-   `[docs]` Update spyOn docs ([#&#8203;13000](https://github.com/facebook/jest/pull/13000))

### [`v28.1.2`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2812)

[Compare Source](https://github.com/facebook/jest/compare/v28.1.1...v28.1.2)

##### Fixes

-   `[jest-runtime]` Avoid star type import from `@jest/globals` ([#&#8203;12949](https://github.com/facebook/jest/pull/12949))

##### Chore & Maintenance

-   `[docs]` Mention that jest-codemods now supports Sinon ([#&#8203;12898](https://github.com/facebook/jest/pull/12898))

### [`v28.1.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2811)

[Compare Source](https://github.com/facebook/jest/compare/v28.1.0...v28.1.1)

##### Features

-   `[jest]` Expose `Config` type ([#&#8203;12848](https://github.com/facebook/jest/pull/12848))
-   `[@jest/reporters]` Improve `GitHubActionsReporter`s annotation format ([#&#8203;12826](https://github.com/facebook/jest/pull/12826))
-   `[@jest/types]` Infer argument types passed to `test` and `describe` callback functions from `each` tables ([#&#8203;12885](https://github.com/facebook/jest/pull/12885), [#&#8203;12905](https://github.com/facebook/jest/pull/12905))

##### Fixes

-   `[@jest/expect-utils]` Fix deep equality of ImmutableJS OrderedMaps ([#&#8203;12899](https://github.com/facebook/jest/pull/12899))
-   `[jest-docblock]` Handle multiline comments in parseWithComments ([#&#8203;12845](https://github.com/facebook/jest/pull/12845))
-   `[jest-mock]` Improve `spyOn` error messages ([#&#8203;12901](https://github.com/facebook/jest/pull/12901))
-   `[jest-runtime]` Correctly report V8 coverage with `resetModules: true` ([#&#8203;12912](https://github.com/facebook/jest/pull/12912))
-   `[jest-worker]` Make `JestWorkerFarm` helper type to include methods of worker module that take more than one argument ([#&#8203;12839](https://github.com/facebook/jest/pull/12839))

##### Chore & Maintenance

-   `[docs]` Updated docs to indicate that `jest-environment-jsdom` is a separate package [#&#8203;12828](https://github.com/facebook/jest/issues/12828)
-   `[docs]` Document the comments used by coverage providers [#&#8203;12835](https://github.com/facebook/jest/issues/12835)
-   `[docs]` Use `docusaurus-remark-plugin-tab-blocks` to format tabs with code examples ([#&#8203;12859](https://github.com/facebook/jest/pull/12859))
-   `[jest-haste-map]` Bump `walker` version ([#&#8203;12324](https://github.com/facebook/jest/pull/12324))

### [`v28.1.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2810)

[Compare Source](https://github.com/facebook/jest/compare/v28.0.3...v28.1.0)

##### Features

-   `[jest-circus]` Add `failing` test modifier that inverts the behavior of tests ([#&#8203;12610](https://github.com/facebook/jest/pull/12610))
-   `[jest-environment-node, jest-environment-jsdom]` Allow specifying `customExportConditions` ([#&#8203;12774](https://github.com/facebook/jest/pull/12774))

##### Fixes

-   `[expect]` Adjust typings of `lastCalledWith`, `nthCalledWith`, `toBeCalledWith` matchers to allow a case there a mock was called with no arguments ([#&#8203;12807](https://github.com/facebook/jest/pull/12807))
-   `[@jest/expect-utils]` Fix deep equality of ImmutableJS Lists ([#&#8203;12763](https://github.com/facebook/jest/pull/12763))
-   `[jest-core]` Do not collect `SIGNREQUEST` as open handles ([#&#8203;12789](https://github.com/facebook/jest/pull/12789))

##### Chore & Maintenance

-   `[docs]` Specified documentation about `--filter` CLI docs ([#&#8203;12799](https://github.com/facebook/jest/pull/12799))
-   `[@jest-reporters]` Move helper functions from `utils.ts` into separate files ([#&#8203;12782](https://github.com/facebook/jest/pull/12782))
-   `[jest-resolve]` Replace `process.versions.pnp` type declaration with `@types/pnpapi` devDependency ([#&#8203;12783](https://github.com/facebook/jest/pull/12783))

### [`v28.0.3`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2803)

[Compare Source](https://github.com/facebook/jest/compare/v28.0.2...v28.0.3)

##### Fixes

-   `[jest-config]` Normalize `reporters` option defined in presets ([#&#8203;12769](https://github.com/facebook/jest/pull/12769))
-   `[@jest/reporters]` Fix trailing slash in matching `coverageThreshold` key ([#&#8203;12714](https://github.com/facebook/jest/pull/12714))
-   `[jest-resolve]` Fix (experimental) ESM module mocking for re-exports ([#&#8203;12766](https://github.com/facebook/jest/pull/12766))
-   `[@jest/transform]` Throw better error if an invalid return value if encountered ([#&#8203;12764](https://github.com/facebook/jest/pull/12764))

##### Chore & Maintenance

-   `[docs]` Fix typo in `--shard` CLI docs ([#&#8203;12761](https://github.com/facebook/jest/pull/12761))

### [`v28.0.2`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2802)

[Compare Source](https://github.com/facebook/jest/compare/v28.0.1...v28.0.2)

##### Features

-   `[jest-worker]` Add `JestWorkerFarm` helper type ([#&#8203;12753](https://github.com/facebook/jest/pull/12753))

##### Fixes

-   `[*]` Lower Node 16 requirement to 16.10 from 16.13 due to a [Node bug](https://github.com/nodejs/node/issues/40014) that causes memory and performance issues ([#&#8203;12754](https://github.com/facebook/jest/pull/12754))

### [`v28.0.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2801)

[Compare Source](https://github.com/facebook/jest/compare/v28.0.0...v28.0.1)

##### Features

-   `[jest-resolve]` Expose `ResolverOptions` type ([#&#8203;12736](https://github.com/facebook/jest/pull/12736))

##### Fixes

-   `[expect]` Add missing dependency `jest-util` ([#&#8203;12744](https://github.com/facebook/jest/pull/12744))
-   `[jest-circus]` Improve `test.concurrent` ([#&#8203;12748](https://github.com/facebook/jest/pull/12748))
-   `[jest-resolve]` Correctly throw an error if `jsdom` test environment is used, but not installed ([#&#8203;12749](https://github.com/facebook/jest/pull/12749))

##### Chore & Maintenance

-   `[jest-serializer]` Remove deprecated module from source tree ([#&#8203;12735](https://github.com/facebook/jest/pull/12735))

### [`v28.0.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2800)

[Compare Source](https://github.com/facebook/jest/compare/v27.5.1...v28.0.0)

##### Features

-   `[babel-jest]` Export `createTransformer` function ([#&#8203;12399](https://github.com/facebook/jest/pull/12399))
-   `[expect]` Expose `AsymmetricMatchers`, `MatcherFunction` and `MatcherFunctionWithState` interfaces ([#&#8203;12363](https://github.com/facebook/jest/pull/12363), [#&#8203;12376](https://github.com/facebook/jest/pull/12376))
-   `[jest-circus]` Support error logging before retry ([#&#8203;12201](https://github.com/facebook/jest/pull/12201))
-   `[jest-circus, jest-jasmine2]` Allowed classes and functions as `describe` and `it`/`test` names ([#&#8203;12484](https://github.com/facebook/jest/pull/12484))
-   `[jest-cli, jest-config]` \[**BREAKING**] Remove `testURL` config, use `testEnvironmentOptions.url` instead ([#&#8203;10797](https://github.com/facebook/jest/pull/10797))
-   `[jest-cli, jest-core]` Add `--shard` parameter for distributed parallel test execution ([#&#8203;12546](https://github.com/facebook/jest/pull/12546))
-   `[jest-cli]` \[**BREAKING**] Remove undocumented `--timers` option ([#&#8203;12572](https://github.com/facebook/jest/pull/12572))
-   `[jest-config]` \[**BREAKING**] Stop shipping `jest-environment-jsdom` by default ([#&#8203;12354](https://github.com/facebook/jest/pull/12354))
-   `[jest-config]` \[**BREAKING**] Stop shipping `jest-jasmine2` by default ([#&#8203;12355](https://github.com/facebook/jest/pull/12355))
-   `[jest-config, @&#8203;jest/types]` Add `ci` to `GlobalConfig` ([#&#8203;12378](https://github.com/facebook/jest/pull/12378))
-   `[jest-config]` \[**BREAKING**] Rename `moduleLoader` to `runtime` ([#&#8203;10817](https://github.com/facebook/jest/pull/10817))
-   `[jest-config]` \[**BREAKING**] Rename `extraGlobals` to `sandboxInjectedGlobals` ([#&#8203;10817](https://github.com/facebook/jest/pull/10817))
-   `[jest-config]` \[**BREAKING**] Throw an error instead of showing a warning if multiple configs are used ([#&#8203;12510](https://github.com/facebook/jest/pull/12510))
-   `[jest-config]` \[**BREAKING**] Do not normalize long deprecated configuration options `preprocessorIgnorePatterns`, `scriptPreprocessor`, `setupTestFrameworkScriptFile` and `testPathDirs` ([#&#8203;12701](https://github.com/facebook/jest/pull/12701))
-   `[jest-cli, jest-core]` Add `--ignoreProjects` CLI argument to ignore test suites by project name ([#&#8203;12620](https://github.com/facebook/jest/pull/12620))
-   `[jest-core]` Pass project config to `globalSetup`/`globalTeardown` function as second argument ([#&#8203;12440](https://github.com/facebook/jest/pull/12440))
-   `[jest-core]` Stabilize test runners with event emitters ([#&#8203;12641](https://github.com/facebook/jest/pull/12641))
-   `[jest-core, jest-watcher]` \[**BREAKING**] Move `TestWatcher` class to `jest-watcher` package ([#&#8203;12652](https://github.com/facebook/jest/pull/12652))
-   `[jest-core]` Allow using Summary Reporter as stand-alone reporter ([#&#8203;12687](https://github.com/facebook/jest/pull/12687))
-   `[jest-environment-jsdom]` \[**BREAKING**] Upgrade jsdom to 19.0.0 ([#&#8203;12290](https://github.com/facebook/jest/pull/12290))
-   `[jest-environment-jsdom]` \[**BREAKING**] Add default `browser` condition to `exportConditions` for `jsdom` environment ([#&#8203;11924](https://github.com/facebook/jest/pull/11924))
-   `[jest-environment-jsdom]` \[**BREAKING**] Pass global config to Jest environment constructor for `jsdom` environment ([#&#8203;12461](https://github.com/facebook/jest/pull/12461))
-   `[jest-environment-jsdom]` \[**BREAKING**] Second argument `context` to constructor is mandatory ([#&#8203;12469](https://github.com/facebook/jest/pull/12469))
-   `[jest-environment-node]` \[**BREAKING**] Add default `node` and `node-addon` conditions to `exportConditions` for `node` environment ([#&#8203;11924](https://github.com/facebook/jest/pull/11924))
-   `[jest-environment-node]` \[**BREAKING**] Pass global config to Jest environment constructor for `node` environment ([#&#8203;12461](https://github.com/facebook/jest/pull/12461))
-   `[jest-environment-node]` \[**BREAKING**] Second argument `context` to constructor is mandatory ([#&#8203;12469](https://github.com/facebook/jest/pull/12469))
-   `[jest-environment-node]` Add all available globals to test globals, not just explicit ones ([#&#8203;12642](https://github.com/facebook/jest/pull/12642), [#&#8203;12696](https://github.com/facebook/jest/pull/12696))
-   `[@jest/expect]` New module which extends `expect` with `jest-snapshot` matchers ([#&#8203;12404](https://github.com/facebook/jest/pull/12404), [#&#8203;12410](https://github.com/facebook/jest/pull/12410), [#&#8203;12418](https://github.com/facebook/jest/pull/12418))
-   `[@jest/expect-utils]` New module exporting utils for `expect` ([#&#8203;12323](https://github.com/facebook/jest/pull/12323))
-   `[@jest/fake-timers]` \[**BREAKING**] Rename `timers` configuration option to `fakeTimers` ([#&#8203;12572](https://github.com/facebook/jest/pull/12572))
-   `[@jest/fake-timers]` \[**BREAKING**] Allow `jest.useFakeTimers()` and `projectConfig.fakeTimers` to take an options bag ([#&#8203;12572](https://github.com/facebook/jest/pull/12572))
-   `[jest-haste-map]` \[**BREAKING**] `HasteMap.create` now returns a promise ([#&#8203;12008](https://github.com/facebook/jest/pull/12008))
-   `[jest-haste-map]` Add support for `dependencyExtractor` written in ESM ([#&#8203;12008](https://github.com/facebook/jest/pull/12008))
-   `[jest-mock]` \[**BREAKING**] Rename exported utility types `ClassLike`, `FunctionLike`, `ConstructorLikeKeys`, `MethodLikeKeys`, `PropertyLikeKeys`; remove exports of utility types `ArgumentsOf`, `ArgsType`, `ConstructorArgumentsOf` - TS builtin utility types `ConstructorParameters` and `Parameters` should be used instead ([#&#8203;12435](https://github.com/facebook/jest/pull/12435), [#&#8203;12489](https://github.com/facebook/jest/pull/12489))
-   `[jest-mock]` Improve `isMockFunction` to infer types of passed function ([#&#8203;12442](https://github.com/facebook/jest/pull/12442))
-   `[jest-mock]` \[**BREAKING**] Improve the usage of `jest.fn` generic type argument ([#&#8203;12489](https://github.com/facebook/jest/pull/12489))
-   `[jest-mock]` Add support for auto-mocking async generator functions ([#&#8203;11080](https://github.com/facebook/jest/pull/11080))
-   `[jest-mock]` Add `contexts` member to mock functions ([#&#8203;12601](https://github.com/facebook/jest/pull/12601))
-   `[@jest/reporters]` Add GitHub Actions reporter ([#&#8203;11320](https://github.com/facebook/jest/pull/11320), [#&#8203;12658](https://github.com/facebook/jest/pull/12658))
-   `[@jest/reporters]` Pass `reporterContext` to custom reporter constructors as third argument ([#&#8203;12657](https://github.com/facebook/jest/pull/12657))
-   `[jest-resolve]` \[**BREAKING**] Add support for `package.json` `exports` ([#&#8203;11961](https://github.com/facebook/jest/pull/11961), [#&#8203;12373](https://github.com/facebook/jest/pull/12373))
-   `[jest-resolve]` Support package self-reference ([#&#8203;12682](https://github.com/facebook/jest/pull/12682))
-   `[jest-resolve, jest-runtime]` Add support for `data:` URI import and mock ([#&#8203;12392](https://github.com/facebook/jest/pull/12392))
-   `[jest-resolve, jest-runtime]` Add support for async resolver ([#&#8203;11540](https://github.com/facebook/jest/pull/11540))
-   `[jest-resolve]` \[**BREAKING**] Remove `browser?: boolean` from resolver options, `conditions: ['browser']` should be used instead ([#&#8203;12707](https://github.com/facebook/jest/pull/12707))
-   `[jest-resolve]` Expose `JestResolver`, `AsyncResolver`, `SyncResolver`, `PackageFilter`, `PathFilter` and `PackageJSON` types ([#&#8203;12707](https://github.com/facebook/jest/pull/12707), ([#&#8203;12712](https://github.com/facebook/jest/pull/12712))
-   `[jest-runner]` Allow `setupFiles` module to export an async function ([#&#8203;12042](https://github.com/facebook/jest/pull/12042))
-   `[jest-runner]` Allow passing `testEnvironmentOptions` via docblocks ([#&#8203;12470](https://github.com/facebook/jest/pull/12470))
-   `[jest-runner]` Expose `CallbackTestRunner`, `EmittingTestRunner` abstract classes and `CallbackTestRunnerInterface`, `EmittingTestRunnerInterface` to help typing third party runners ([#&#8203;12646](https://github.com/facebook/jest/pull/12646), [#&#8203;12715](https://github.com/facebook/jest/pull/12715))
-   `[jest-runner]` Lock version of `source-map-support` to 0.5.13 ([#&#8203;12720](https://github.com/facebook/jest/pull/12720))
-   `[jest-runtime]` \[**BREAKING**] `Runtime.createHasteMap` now returns a promise ([#&#8203;12008](https://github.com/facebook/jest/pull/12008))
-   `[jest-runtime]` Calling `jest.resetModules` function will clear FS and transform cache ([#&#8203;12531](https://github.com/facebook/jest/pull/12531))
-   `[jest-runtime]` \[**BREAKING**] Remove `Context` type export, it must be imported from `@jest/test-result` ([#&#8203;12685](https://github.com/facebook/jest/pull/12685))
-   `[jest-runtime]` Add `import.meta.jest` ([#&#8203;12698](https://github.com/facebook/jest/pull/12698))
-   `[@jest/schemas]` New module for JSON schemas for Jest's config ([#&#8203;12384](https://github.com/facebook/jest/pull/12384))
-   `[@jest/source-map]` Migrate from `source-map` to `@jridgewell/trace-mapping` ([#&#8203;12692](https://github.com/facebook/jest/pull/12692))
-   `[jest-transform]` \[**BREAKING**] Make it required for `process()` and `processAsync()` methods to always return structured data ([#&#8203;12638](https://github.com/facebook/jest/pull/12638))
-   `[jest-test-result]` Add duration property to JSON test output ([#&#8203;12518](https://github.com/facebook/jest/pull/12518))
-   `[jest-watcher]` \[**BREAKING**] Make `PatternPrompt` class to take `entityName` as third constructor parameter instead of `this._entityName` ([#&#8203;12591](https://github.com/facebook/jest/pull/12591))
-   `[jest-worker]` \[**BREAKING**] Allow only absolute `workerPath` ([#&#8203;12343](https://github.com/facebook/jest/pull/12343))
-   `[jest-worker]` \[**BREAKING**] Default to advanced serialization when using child process workers ([#&#8203;10983](https://github.com/facebook/jest/pull/10983))
-   `[pretty-format]` New `maxWidth` parameter ([#&#8203;12402](https://github.com/facebook/jest/pull/12402))

##### Fixes

-   `[*]` Use `sha256` instead of `md5` as hashing algortihm for compatibility with FIPS systems ([#&#8203;12722](https://github.com/facebook/jest/pull/12722))
-   `[babel-jest]` \[**BREAKING**] Pass `rootDir` as `root` in Babel's options ([#&#8203;12689](https://github.com/facebook/jest/pull/12689))
-   `[expect]` Move typings of `.not`, `.rejects` and `.resolves` modifiers outside of `Matchers` interface ([#&#8203;12346](https://github.com/facebook/jest/pull/12346))
-   `[expect]` Throw useful error if `expect.extend` is called with invalid matchers ([#&#8203;12488](https://github.com/facebook/jest/pull/12488))
-   `[expect]` Fix `iterableEquality` ignores other properties ([#&#8203;8359](https://github.com/facebook/jest/pull/8359))
-   `[expect]` Fix print for the `closeTo` matcher ([#&#8203;12626](https://github.com/facebook/jest/pull/12626))
-   `[jest-changed-files]` Improve `changedFilesWithAncestor` pattern for Mercurial SCM ([#&#8203;12322](https://github.com/facebook/jest/pull/12322))
-   `[jest-circus, @&#8203;jest/types]` Disallow undefined value in `TestContext` type ([#&#8203;12507](https://github.com/facebook/jest/pull/12507))
-   `[jest-config]` Correctly detect CI environment and update snapshots accordingly ([#&#8203;12378](https://github.com/facebook/jest/pull/12378))
-   `[jest-config]` Pass `moduleTypes` to `ts-node` to enforce CJS when transpiling ([#&#8203;12397](https://github.com/facebook/jest/pull/12397))
-   `[jest-config]` \[**BREAKING**] Add `mjs` and `cjs` to default `moduleFileExtensions` config ([#&#8203;12578](https://github.com/facebook/jest/pull/12578))
-   `[jest-config, jest-haste-map]` Allow searching for tests in `node_modules` by exposing `retainAllFiles` ([#&#8203;11084](https://github.com/facebook/jest/pull/11084))
-   `[jest-core]` \[**BREAKING**] Exit with status `1` if no tests are found with `--findRelatedTests` flag ([#&#8203;12487](https://github.com/facebook/jest/pull/12487))
-   `[jest-core]` Do not report unref-ed subprocesses as open handles ([#&#8203;12705](https://github.com/facebook/jest/pull/12705))
-   `[jest-each]` `%#` is not replaced with index of the test case ([#&#8203;12517](https://github.com/facebook/jest/pull/12517))
-   `[jest-each]` Fixes error message with incorrect count of missing arguments ([#&#8203;12464](https://github.com/facebook/jest/pull/12464))
-   `[jest-environment-jsdom]` Make `jsdom` accessible to extending environments again ([#&#8203;12232](https://github.com/facebook/jest/pull/12232))
-   `[jest-environment-jsdom]` Log JSDOM errors more cleanly ([#&#8203;12386](https://github.com/facebook/jest/pull/12386))
-   `[jest-environment-node]` Add `MessageChannel`, `MessageEvent` to globals ([#&#8203;12553](https://github.com/facebook/jest/pull/12553))
-   `[jest-environment-node]` Add `structuredClone` to globals ([#&#8203;12631](https://github.com/facebook/jest/pull/12631))
-   `[@jest/expect-utils]` \[**BREAKING**] Fix false positives when looking for `undefined` prop ([#&#8203;8923](https://github.com/facebook/jest/pull/8923))
-   `[jest-haste-map]` Don't use partial results if file crawl errors ([#&#8203;12420](https://github.com/facebook/jest/pull/12420))
-   `[jest-haste-map]` Make watchman existence check lazy+async ([#&#8203;12675](https://github.com/facebook/jest/pull/12675))
-   `[jest-jasmine2, jest-types]` \[**BREAKING**] Move all `jasmine` specific types from `@jest/types` to its own package ([#&#8203;12125](https://github.com/facebook/jest/pull/12125))
-   `[jest-jasmine2]` Do not set `duration` to `0` for skipped tests ([#&#8203;12518](https://github.com/facebook/jest/pull/12518))
-   `[jest-matcher-utils]` Pass maxWidth to `pretty-format` to avoid printing every element in arrays by default ([#&#8203;12402](https://github.com/facebook/jest/pull/12402))
-   `[jest-mock]` Fix function overloads for `spyOn` to allow more correct type inference in complex object ([#&#8203;12442](https://github.com/facebook/jest/pull/12442))
-   `[jest-mock]` Handle overridden `Function.name` property ([#&#8203;12674](https://github.com/facebook/jest/pull/12674))
-   `[@jest/reporters]` Notifications generated by the `--notify` flag are no longer persistent in GNOME Shell. ([#&#8203;11733](https://github.com/facebook/jest/pull/11733))
-   `[@jest/reporters]` Move missing icon file which is needed for `NotifyReporter` class. ([#&#8203;12593](https://github.com/facebook/jest/pull/12593))
-   `[@jest/reporters]` Update `v8-to-istanbul` ([#&#8203;12697](https://github.com/facebook/jest/pull/12697))
-   `[jest-resolver]` Call custom resolver with core node.js modules ([#&#8203;12654](https://github.com/facebook/jest/pull/12654))
-   `[jest-runner]` Correctly resolve `source-map-support` ([#&#8203;12706](https://github.com/facebook/jest/pull/12706))
-   `[jest-worker]` Fix `Farm` execution results memory leak ([#&#8203;12497](https://github.com/facebook/jest/pull/12497))

##### Chore & Maintenance

-   `[*]` \[**BREAKING**] Drop support for Node v10 and v15 and target first LTS `16.13.0` ([#&#8203;12220](https://github.com/facebook/jest/pull/12220))
-   `[*]` \[**BREAKING**] Drop support for `typescript@3.8`, minimum version is now `4.3` ([#&#8203;11142](https://github.com/facebook/jest/pull/11142), [#&#8203;12648](https://github.com/facebook/jest/pull/12648))
-   `[*]` Bundle all `.d.ts` files into a single `index.d.ts` per module ([#&#8203;12345](https://github.com/facebook/jest/pull/12345))
-   `[*]` Use `globalThis` instead of `global` ([#&#8203;12447](https://github.com/facebook/jest/pull/12447))
-   `[babel-jest]` \[**BREAKING**] Only export `createTransformer` ([#&#8203;12407](https://github.com/facebook/jest/pull/12407))
-   `[docs]` Add note about not mixing `done()` with Promises ([#&#8203;11077](https://github.com/facebook/jest/pull/11077))
-   `[docs, examples]` Update React examples to match with the new React guidelines for code examples ([#&#8203;12217](https://github.com/facebook/jest/pull/12217))
-   `[docs]` Add clarity for module factory hoisting limitations ([#&#8203;12453](https://github.com/facebook/jest/pull/12453))
-   `[docs]` Add more information about how code transformers work ([#&#8203;12407](https://github.com/facebook/jest/pull/12407))
-   `[docs]` Add upgrading guide ([#&#8203;12633](https://github.com/facebook/jest/pull/12633))
-   `[expect]` \[**BREAKING**] Remove support for importing `build/utils` ([#&#8203;12323](https://github.com/facebook/jest/pull/12323))
-   `[expect]` \[**BREAKING**] Migrate to ESM ([#&#8203;12344](https://github.com/facebook/jest/pull/12344))
-   `[expect]` \[**BREAKING**] Snapshot matcher types are moved to `@jest/expect` ([#&#8203;12404](https://github.com/facebook/jest/pull/12404))
-   `[jest-cli]` Update `yargs` to v17 ([#&#8203;12357](https://github.com/facebook/jest/pull/12357))
-   `[jest-config]` \[**BREAKING**] Remove `getTestEnvironment` export ([#&#8203;12353](https://github.com/facebook/jest/pull/12353))
-   `[jest-config]` \[**BREAKING**] Rename config option `name` to `id` ([#&#8203;11981](https://github.com/facebook/jest/pull/11981))
-   `[jest-create-cache-key-function]` Added README.md file with basic usage instructions ([#&#8203;12492](https://github.com/facebook/jest/pull/12492))
-   `[@jest/core]` Use `index.ts` instead of `jest.ts` as main export ([#&#8203;12329](https://github.com/facebook/jest/pull/12329))
-   `[jest-environment-jsdom]` \[**BREAKING**] Migrate to ESM ([#&#8203;12340](https://github.com/facebook/jest/pull/12340))
-   `[jest-environment-node]` \[**BREAKING**] Migrate to ESM ([#&#8203;12340](https://github.com/facebook/jest/pull/12340))
-   `[jest-haste-map]` Remove legacy `isRegExpSupported` ([#&#8203;12676](https://github.com/facebook/jest/pull/12676))
-   `[@jest/fake-timers]` Update `@sinonjs/fake_timers` to v9 ([#&#8203;12357](https://github.com/facebook/jest/pull/12357))
-   `[jest-jasmine2, jest-runtime]` \[**BREAKING**] Use `Symbol` to pass `jest.setTimeout` value instead of `jasmine` specific logic ([#&#8203;12124](https://github.com/facebook/jest/pull/12124))
-   `[jest-phabricator]` \[**BREAKING**] Migrate to ESM ([#&#8203;12341](https://github.com/facebook/jest/pull/12341))
-   `[jest-resolve]` \[**BREAKING**] Make `requireResolveFunction` argument mandatory ([#&#8203;12353](https://github.com/facebook/jest/pull/12353))
-   `[jest-runner]` \[**BREAKING**] Remove some type exports from `@jest/test-result` ([#&#8203;12353](https://github.com/facebook/jest/pull/12353))
-   `[jest-runner]` \[**BREAKING**] Second argument to constructor (`Context`) is not optional ([#&#8203;12640](https://github.com/facebook/jest/pull/12640))
-   `[jest-serializer]` \[**BREAKING**] Deprecate package in favour of using `v8` APIs directly ([#&#8203;12391](https://github.com/facebook/jest/pull/12391))
-   `[jest-snapshot]` \[**BREAKING**] Migrate to ESM ([#&#8203;12342](https://github.com/facebook/jest/pull/12342))
-   `[jest-transform]` Update `write-file-atomic` to v4 ([#&#8203;12357](https://github.com/facebook/jest/pull/12357))
-   `[jest-types]` \[**BREAKING**] Remove `Config.Glob` and `Config.Path` ([#&#8203;12406](https://github.com/facebook/jest/pull/12406))
-   `[jest]` Use `index.ts` instead of `jest.ts` as main export ([#&#8203;12329](https://github.com/facebook/jest/pull/12329))

##### Performance

-   `[jest-haste-map]` \[**BREAKING**] Default to `node` crawler over shelling out to `find` if `watchman` is not enabled ([#&#8203;12320](https://github.com/facebook/jest/pull/12320))

### [`v27.5.1`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2751)

[Compare Source](https://github.com/facebook/jest/compare/v27.5.0...v27.5.1)

##### Features

-   `[jest-config]` Support comments in JSON config file ([#&#8203;12316](https://github.com/facebook/jest/pull/12316))
-   `[pretty-format]` Expose `ConvertAnsi` plugin ([#&#8203;12308](https://github.com/facebook/jest/pull/12308))

##### Fixes

-   `[expect]` Add type definitions for asymmetric `closeTo` matcher ([#&#8203;12304](https://github.com/facebook/jest/pull/12304))
-   `[jest-cli]` Load binary via exported API ([#&#8203;12315](https://github.com/facebook/jest/pull/12315))
-   `[jest-config]` Replace `jsonlint` with `parse-json` ([#&#8203;12316](https://github.com/facebook/jest/pull/12316))
-   `[jest-repl]` Make module importable ([#&#8203;12311](https://github.com/facebook/jest/pull/12311) & [#&#8203;12315](https://github.com/facebook/jest/pull/12315))

##### Chore & Maintenance

-   `[*]` Avoid anonymous default exports ([#&#8203;12313](https://github.com/facebook/jest/pull/12313))

### [`v27.5.0`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2750)

[Compare Source](https://github.com/facebook/jest/compare/v27.4.7...v27.5.0)

##### Features

-   `[expect]` Add asymmetric matcher `expect.closeTo` ([#&#8203;12243](https://github.com/facebook/jest/pull/12243))
-   `[jest-mock]` Added `mockFn.mock.lastCall` to retrieve last argument ([#&#8203;12285](https://github.com/facebook/jest/pull/12285))

##### Fixes

-   `[expect]` Add a fix for `.toHaveProperty('')` ([#&#8203;12251](https://github.com/facebook/jest/pull/12251))
-   `[jest-each, @&#8203;jest/globals]` Allow passing `ReadonlyArray` type of a table to `describe.each` and `test.each` ([#&#8203;12297](https://github.com/facebook/jest/pull/12297))
-   `[@jest/globals]` Add missing `options` argument to `jest.doMock` typing ([#&#8203;12292](https://github.com/facebook/jest/pull/12292))
-   `[jest-environment-node]` Add `atob` and `btoa` ([#&#8203;12269](https://github.com/facebook/jest/pull/12269))
-   `[jest-matcher-utils]` Correct diff for expected asymmetric matchers ([#&#8203;12264](https://github.com/facebook/jest/pull/12264))
-   `[jest-message-util]` Fix `.getTopFrame()` (and `toMatchInlineSnapshot()`) with `mjs` files ([#&#8203;12277](https://github.com/facebook/jest/pull/12277))

##### Chore & Maintenance

-   `[*]` Update `graceful-fs` to `^4.2.9` ([#&#8203;11749](https://github.com/facebook/jest/pull/11749))

##### Performance

-   `[jest-resolve]` perf: skip error creation on not found `stat` calls ([#&#8203;11749](https://github.com/facebook/jest/pull/11749))

### [`v27.4.7`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2747)

[Compare Source](https://github.com/facebook/jest/compare/v27.4.6...v27.4.7)

##### Fixes

-   `jest-config` Add missing `@babel/core` dependency ([#&#8203;12216](https://github.com/facebook/jest/pull/12216))

### [`v27.4.6`](https://github.com/facebook/jest/blob/HEAD/CHANGELOG.md#&#8203;2746)

[Compare Source](https://github.com/facebook/jest/compare/v27.4.5...v27.4.6)

##### Fixes

-   `[jest-environment-node]` Add `AbortSignal` ([#&#8203;12157](https://github.com/facebook/jest/pull/12157))
-   `[jest-environment-node]` Add Missing node global `performance` ([#&#8203;12002](https://github.com/facebook/jest/pull/12002))
-   `[jest-runtime]` Handle missing `mocked` property ([#&#8203;12213](https://github.com/facebook/jest/pull/12213))
-   `[@jest/transform]` Update dependency package `pirates` to 4.0.4 ([#&#8203;12002](https://github.com/facebook/jest/pull/12002))

##### Performance

-   `jest-config` perf: only register ts-node once when loading TS config files ([#&#8203;12160](https://github.com/facebook/jest/pull/12160))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/241
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-03-27 18:06:33 +01:00
ec1763be26 Update dependency minimatch to v7.4.3 (#282)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [minimatch](https://github.com/isaacs/minimatch) | dependencies | patch | [`7.4.2` -> `7.4.3`](https://renovatebot.com/diffs/npm/minimatch/7.4.2/7.4.3) |

---

### Release Notes

<details>
<summary>isaacs/minimatch</summary>

### [`v7.4.3`](https://github.com/isaacs/minimatch/compare/v7.4.2...v7.4.3)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.4.2...v7.4.3)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/282
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-03-27 17:59:54 +01:00
1c00fdae5c Merge branch 'main' into develop
Some checks failed
continuous-integration/drone/push Build is failing
2023-03-25 13:37:17 +00:00
580fd497a2 Update dependency typescript to v5 (#275)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [typescript](https://www.typescriptlang.org/) ([source](https://github.com/Microsoft/TypeScript)) | devDependencies | major | [`^4.5.2` -> `^5.0.0`](https://renovatebot.com/diffs/npm/typescript/4.5.2/5.0.2) |

---

### Release Notes

<details>
<summary>Microsoft/TypeScript</summary>

### [`v5.0.2`](https://github.com/microsoft/TypeScript/releases/tag/v5.0.2): TypeScript 5.0

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.9.5...v5.0.2)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/).

For new features, check out the [What's new in TypeScript v5.0.2]().

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript v5.0.0 (Beta)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=is%3Aissue+milestone%3A%22TypeScript+5.0.0%22+is%3Aclosed+).
-   [fixed issues query for Typescript v5.0.1 (RC)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=is%3Aissue+milestone%3A%22TypeScript+5.0.1%22+is%3Aclosed+).
-   [fixed issues query for Typescript v5.0.2 (Stable)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=is%3Aissue+milestone%3A%22TypeScript+5.0.2%22+is%3Aclosed+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.9.5`](https://github.com/microsoft/TypeScript/releases/tag/v4.9.5): TypeScript 4.9.5

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.9.4...v4.9.5)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

#### Changes:

-   [`69e88ef`](69e88ef551) Port ignore deprecations to 4.9 ([#&#8203;52419](https://github.com/Microsoft/TypeScript/issues/52419))
-   [`daf4e81`](daf4e817a1) Port timestamp fix to 4.9 ([#&#8203;52426](https://github.com/Microsoft/TypeScript/issues/52426))

### [`v4.9.4`](https://github.com/microsoft/TypeScript/releases/tag/v4.9.4): TypeScript 4.9.4

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.9.3...v4.9.4)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript v4.9.4](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=is%3Aissue+milestone%3A%22TypeScript+4.9.4%22+is%3Aclosed+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

#### Changes:

-   [`e286821`](e2868216f6) Bump version to 4.9.4 and LKG.
-   [`eb5419f`](eb5419fc8d) Cherry-pick [#&#8203;51704](https://github.com/Microsoft/TypeScript/issues/51704) to release 4.9 ([#&#8203;51712](https://github.com/Microsoft/TypeScript/issues/51712))
-   [`b4d382b`](b4d382b9b1) Cherry-pick changes for narrowing to tagged literal types.
-   [`e7a02f4`](e7a02f43fc) Port of [#&#8203;51626](https://github.com/Microsoft/TypeScript/issues/51626) and [#&#8203;51689](https://github.com/Microsoft/TypeScript/issues/51689) to release-4.9 ([#&#8203;51627](https://github.com/Microsoft/TypeScript/issues/51627))
-   [`1727912`](1727912f04) Cherry-pick fix around `visitEachChild` to release-4.9. ([#&#8203;51544](https://github.com/Microsoft/TypeScript/issues/51544))

This list of changes was [auto generated](https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/\_release?releaseId=117&\_a=release-summary).

### [`v4.9.3`](https://github.com/microsoft/TypeScript/releases/tag/v4.9.3): TypeScript 4.9

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.8.4...v4.9.3)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

#### Changes:

-   [`93bd577`](93bd577458) Bump version to 4.9.3 and LKG.
-   [`107f832`](107f832b80) Update LKG.
-   [`31bee56`](31bee5682d) Cherry-pick PR [#&#8203;50977](https://github.com/Microsoft/TypeScript/issues/50977) into release-4.9 ([#&#8203;51363](https://github.com/Microsoft/TypeScript/issues/51363)) \[ [#&#8203;50872](https://github.com/Microsoft/TypeScript/issues/50872) ]
-   [`1e2fa7a`](1e2fa7ae15) Update version to 4.9.2-rc and LKG.
-   [`7ab89e5`](7ab89e5c6e) Merge remote-tracking branch 'origin/main' into release-4.9
-   [`e5cd686`](e5cd686def) Update package-lock.json
-   [`8d40dc1`](8d40dc15d1) Update package-lock.json
-   [`5cfb3a2`](5cfb3a2fe3) Only call return() for an abrupt completion in user code ([#&#8203;51297](https://github.com/Microsoft/TypeScript/issues/51297))
-   [`a7a9d15`](a7a9d158e8) Fix for broken baseline in yieldInForInInDownlevelGenerator ([#&#8203;51345](https://github.com/Microsoft/TypeScript/issues/51345))
-   [`7f8426f`](7f8426f4df) fix for-in enumeration containing yield in generator ([#&#8203;51295](https://github.com/Microsoft/TypeScript/issues/51295))

<details><summary><b>See More</b></summary>

-   [`3d2b401`](3d2b4017eb) Fix assertion functions accessed via wildcard imports ([#&#8203;51324](https://github.com/Microsoft/TypeScript/issues/51324))
-   [`64d0d5a`](64d0d5ae14) fix(51301): Fixing an unused import at the end of a line removes the newline ([#&#8203;51320](https://github.com/Microsoft/TypeScript/issues/51320))
-   [`754eeb2`](754eeb2986) Update CodeQL workflow and configuration, fix found bugs ([#&#8203;51263](https://github.com/Microsoft/TypeScript/issues/51263))
-   [`d8aad26`](d8aad26200) Update package-lock.json
-   [`d4f26c8`](d4f26c840b) fix(51245): Class with parameter decorator in arrow function causes "convert to default export" refactoring failure ([#&#8203;51256](https://github.com/Microsoft/TypeScript/issues/51256))
-   [`16faf45`](16faf45682) Update package-lock.json
-   [`8b1ecdb`](8b1ecdb701) fix(50654): "Move to a new file" breaks the declaration of referenced variable ([#&#8203;50681](https://github.com/Microsoft/TypeScript/issues/50681))
-   [`170a17f`](170a17fad5) Dom update 2022-10-25 ([#&#8203;51300](https://github.com/Microsoft/TypeScript/issues/51300))
-   [`9c4e14d`](9c4e14d751) Remove "No type information for this code" from baseline ([#&#8203;51311](https://github.com/Microsoft/TypeScript/issues/51311))
-   [`88d25b4`](88d25b4f23) fix(50068): Refactors trigger debug failure when JSX text has a ' and a tag on the same line. ([#&#8203;51299](https://github.com/Microsoft/TypeScript/issues/51299))
-   [`8bee69a`](8bee69acf4) Update package-lock.json
-   [`702de1e`](702de1eeaa) Fix early call to return/throw on generator ([#&#8203;51294](https://github.com/Microsoft/TypeScript/issues/51294))
-   [`2c12b14`](2c12b14999) Add a GH Action to file a new issue if we go a week without seeing a typescript-error-deltas issue ([#&#8203;51271](https://github.com/Microsoft/TypeScript/issues/51271))
-   [`6af270d`](6af270dee0) Update package-lock.json
-   [`2cc4c16`](2cc4c16a26) Update package-lock.json
-   [`6093491`](60934915d9) Fix apparent typo in getStringMappingType ([#&#8203;51248](https://github.com/Microsoft/TypeScript/issues/51248))
-   [`61c2609`](61c26096e3) Update package-lock.json
-   [`ef69116`](ef69116c41) Generate shortest `rootDirs` module specifier instead of first possible ([#&#8203;51244](https://github.com/Microsoft/TypeScript/issues/51244))
-   [`bbb42f4`](bbb42f453d) Fix typo in canWatchDirectoryOrFile found by CodeQL ([#&#8203;51262](https://github.com/Microsoft/TypeScript/issues/51262))
-   [`a56b254`](a56b254ad3) Include 'this' type parameter in isRelatedTo fast path ([#&#8203;51230](https://github.com/Microsoft/TypeScript/issues/51230))
-   [`3abd351`](3abd351c0e) Fix super property transform in async arrow in method ([#&#8203;51240](https://github.com/Microsoft/TypeScript/issues/51240))
-   [`eed0511`](eed0511218) Update package-lock.json
-   [`2625c1f`](2625c1feae) Make the init config category order predictable ([#&#8203;51247](https://github.com/Microsoft/TypeScript/issues/51247))
-   [`1ca99b3`](1ca99b3402) fix(50551): Destructuring assignment with var bypasses "variable is used before being assigned" check (2454) ([#&#8203;50560](https://github.com/Microsoft/TypeScript/issues/50560))
-   [`3f28fa1`](3f28fa12df) Update package-lock.json
-   [`906ebe4`](906ebe4933) Revert structuredTypeRelatedTo change and fix isUnitLikeType ([#&#8203;51076](https://github.com/Microsoft/TypeScript/issues/51076))
-   [`8ac4652`](8ac465239f) change type ([#&#8203;51231](https://github.com/Microsoft/TypeScript/issues/51231))
-   [`245a02c`](245a02cbed) fix(51222): Go-to-definition on return statements should jump to the containing function declaration ([#&#8203;51227](https://github.com/Microsoft/TypeScript/issues/51227))
-   [`2dff34e`](2dff34e8c4) markAliasReferenced should include ExportValue as well ([#&#8203;51219](https://github.com/Microsoft/TypeScript/issues/51219))
-   [`5ef2634`](5ef2634f3d) Update package-lock.json
-   [`d0f0e35`](d0f0e35c88) Remove old tslint comments ([#&#8203;51220](https://github.com/Microsoft/TypeScript/issues/51220))
-   [`85d405a`](85d405a1d7) Fixed a false positive "await has no effect on the type" diagnostic with mixed generic union ([#&#8203;50833](https://github.com/Microsoft/TypeScript/issues/50833))
-   [`1f8959f`](1f8959f5dc) fix: avoid downleveled dynamic import closing over specifier expression ([#&#8203;49663](https://github.com/Microsoft/TypeScript/issues/49663))
-   [`11066b2`](11066b264f) Rename internal functions to `narrowTypeBySwitchOnTypeOf` and `narrowTypeByInKeyword` ([#&#8203;51215](https://github.com/Microsoft/TypeScript/issues/51215))
-   [`4c9afe8`](4c9afe8812) Update package-lock.json
-   [`f25bcb7`](f25bcb7c27) fix(49196): add jsdoc snippet for interface member functions ([#&#8203;51135](https://github.com/Microsoft/TypeScript/issues/51135))
-   [`7406ee9`](7406ee9c14) fix(51170): Completing an unimplemented property overwrites rest of line ([#&#8203;51175](https://github.com/Microsoft/TypeScript/issues/51175))
-   [`a1d82fc`](a1d82fc9dc) Remove some unnecessary code discovered by rollup ([#&#8203;51204](https://github.com/Microsoft/TypeScript/issues/51204))
-   [`0481773`](0481773a27) LEGO: Merge pull request 51200
-   [`98c19cb`](98c19cbbbe) LEGO: Merge pull request 51190
-   [`13c9b05`](13c9b05384) Update package-lock.json
-   [`673475e`](673475e1c5) Update package-lock.json
-   [`f6cf510`](f6cf51053e) Add more tracing to node16/nodenext resolution ([#&#8203;51168](https://github.com/Microsoft/TypeScript/issues/51168))
-   [`83c5581`](83c5581588) Update package-lock.json
-   [`be5f0fe`](be5f0fe5ac) Add an extra regression test for awaited unresolvable recursive union ([#&#8203;51167](https://github.com/Microsoft/TypeScript/issues/51167))
-   [`2cb7e77`](2cb7e779d7) fix(50416): correctly names disabled export refactors ([#&#8203;50663](https://github.com/Microsoft/TypeScript/issues/50663)) \[ [#&#8203;50416](https://github.com/Microsoft/TypeScript/issues/50416) ]
-   [`2bcfed0`](2bcfed01f3) feat(37440): Provide a quick-fix for non-exported types ([#&#8203;51038](https://github.com/Microsoft/TypeScript/issues/51038))
-   [`a24201c`](a24201c8ef) Remove VSDevMode.ps1 and createPlaygroundBuild ([#&#8203;51166](https://github.com/Microsoft/TypeScript/issues/51166))
-   [`2da62a7`](2da62a784b) fix(51112): omit parameter names that precede the type ([#&#8203;51142](https://github.com/Microsoft/TypeScript/issues/51142))
-   [`cf1b6b7`](cf1b6b7333) feat(51163): show QF to fill in the missing properties for the mapped type. ([#&#8203;51165](https://github.com/Microsoft/TypeScript/issues/51165))
-   [`bdcc240`](bdcc240d68) Remove bug-causing carve-out in conditional type instantiation that hopefully is no longer required ([#&#8203;51151](https://github.com/Microsoft/TypeScript/issues/51151))
-   [`37317a2`](37317a208f) Check nested weak types in intersections on target side of relation ([#&#8203;51140](https://github.com/Microsoft/TypeScript/issues/51140))
-   [`9f49f9c`](9f49f9ccb0) Update package-lock.json
-   [`4f54e7e`](4f54e7e947) Fix isExhaustiveSwitchStatement to better handle circularities ([#&#8203;51095](https://github.com/Microsoft/TypeScript/issues/51095))
-   [`503604c`](503604c884) Overloads shouldn't gain [@&#8203;deprecated](https://github.com/deprecated) tags of other overloads in quick info ([#&#8203;50904](https://github.com/Microsoft/TypeScript/issues/50904))
-   [`e14a229`](e14a2298c5) Update package-lock.json
-   [`67256e5`](67256e50c4) Remove unused declarations array in extractSymbol's TargetRange ([#&#8203;51091](https://github.com/Microsoft/TypeScript/issues/51091))
-   [`9c87ded`](9c87ded2b3) fix(51100): ensure tsserver shuts down when parent process is killed ([#&#8203;51107](https://github.com/Microsoft/TypeScript/issues/51107))
-   [`c01ae01`](c01ae01fac) Fix nightly publish oops in Gulpfile ([#&#8203;51131](https://github.com/Microsoft/TypeScript/issues/51131))
-   [`a7d10f1`](a7d10f15bb) Update package-lock.json
-   [`d0bfd8c`](d0bfd8caed) fix(51072): ts.preProcessFile finds import in template string after conditional expression with template strings ([#&#8203;51082](https://github.com/Microsoft/TypeScript/issues/51082))
-   [`ad56b5c`](ad56b5ca56) Convert scripts/Gulpfile to checked mjs/cjs so they can run without compilation ([#&#8203;50988](https://github.com/Microsoft/TypeScript/issues/50988))
-   [`dbeae5d`](dbeae5d943) fix(51017): Make lineText in the references response opt-out ([#&#8203;51081](https://github.com/Microsoft/TypeScript/issues/51081))
-   [`d06a592`](d06a592d02) Properly defer resolution of mapped types with generic `as` clauses ([#&#8203;51050](https://github.com/Microsoft/TypeScript/issues/51050))
-   [`42b1049`](42b1049aee) Update package-lock.json
-   [`5f3e6cc`](5f3e6cc498) Plugin probe location is higher priority than peer node_modules ([#&#8203;51079](https://github.com/Microsoft/TypeScript/issues/51079)) \[ [#&#8203;34616](https://github.com/Microsoft/TypeScript/issues/34616) ]
-   [`2648f6a`](2648f6ab09) Plugins in project were adding up after every config file reload ([#&#8203;51087](https://github.com/Microsoft/TypeScript/issues/51087))
-   [`c18791c`](c18791ccf1) Fix incorrect options type to WatchOptions ([#&#8203;51064](https://github.com/Microsoft/TypeScript/issues/51064))
-   [`b0795e9`](b0795e9c94) Update package-lock.json
-   [`43c6fd4`](43c6fd4c09) Covert some of the config testing to baselines for easy validation ([#&#8203;51063](https://github.com/Microsoft/TypeScript/issues/51063))
-   [`fc5e72b`](fc5e72b92c) Remove unused defaultWatchFileKind method since useFsEvents is default for tsserver and tsc ([#&#8203;51044](https://github.com/Microsoft/TypeScript/issues/51044))
-   [`8af9a93`](8af9a936b5) Use typescript.d.ts in APISample tests ([#&#8203;51061](https://github.com/Microsoft/TypeScript/issues/51061))
-   [`4953316`](49533168db) Remove configureLanguageServiceBuild, instrumenter ([#&#8203;51048](https://github.com/Microsoft/TypeScript/issues/51048))
-   [`9dfffd0`](9dfffd0fbb) Update GitHub Actions ([#&#8203;51045](https://github.com/Microsoft/TypeScript/issues/51045))
-   [`4635a5c`](4635a5cef9) Update package-lock.json
-   [`33a34e5`](33a34e5b96) Adding a JSDoc comment to the es5 type declarations to describe the functionality of Date.now() ([#&#8203;50630](https://github.com/Microsoft/TypeScript/issues/50630))
-   [`299745c`](299745cb21) Fix crash in goto-def on `@override` ([#&#8203;51016](https://github.com/Microsoft/TypeScript/issues/51016))
-   [`7dcf11f`](7dcf11f139) fix(50750): Object type literal with string literal property in contextual typing position causes language service error on all literal type references ([#&#8203;50757](https://github.com/Microsoft/TypeScript/issues/50757))
-   [`5cd49f6`](5cd49f6cbc) Update package-lock.json
-   [`8a1b858`](8a1b85880f) Update package-lock.json
-   [`96894db`](96894db6cb) Include type parameter defaults in contextual typing ([#&#8203;50994](https://github.com/Microsoft/TypeScript/issues/50994)) \[ [#&#8203;51002](https://github.com/Microsoft/TypeScript/issues/51002) ]
-   [`0d0a793`](0d0a793714) Allow Unicode extended escapes in ES5 and earlier ([#&#8203;50918](https://github.com/Microsoft/TypeScript/issues/50918))
-   [`58bae8d`](58bae8db69) Update package-lock.json
-   [`0ce72ef`](0ce72ef6c8) Add option to OrganizeImports for removal only ([#&#8203;50931](https://github.com/Microsoft/TypeScript/issues/50931))
-   [`42f9143`](42f9143e11) feat: codefix for `for await of` ([#&#8203;50623](https://github.com/Microsoft/TypeScript/issues/50623))
-   [`ecf50e8`](ecf50e81a7) Properly compute `SymbolFlags.Optional` for intersected properties ([#&#8203;50958](https://github.com/Microsoft/TypeScript/issues/50958))
-   [`d1586de`](d1586de043) Fully resolve aliases when checking symbol flags ([#&#8203;50853](https://github.com/Microsoft/TypeScript/issues/50853))
-   [`45148dd`](45148dd715) Update LKG to 4.8.4 ([#&#8203;50987](https://github.com/Microsoft/TypeScript/issues/50987))
-   [`9a83f25`](9a83f2551d) Update package-lock.json
-   [`865848f`](865848fcfb) Fix `<=` and `>` comparisons when compared against prerelease versions ([#&#8203;50915](https://github.com/Microsoft/TypeScript/issues/50915))
-   [`fbfe934`](fbfe9340a9) Fix comparability between type parameters related by a union constraint ([#&#8203;50978](https://github.com/Microsoft/TypeScript/issues/50978))
-   [`b09e93d`](b09e93d3f6) Merge pull request [#&#8203;50041](https://github.com/Microsoft/TypeScript/issues/50041) from microsoft/fix/47969
-   [`0ac12bb`](0ac12bbe7a) Update package-lock.json
-   [`8192d55`](8192d55049) Pick correct compilerOptions when checking if we can share emitSignatures ([#&#8203;50910](https://github.com/Microsoft/TypeScript/issues/50910)) \[ [#&#8203;50902](https://github.com/Microsoft/TypeScript/issues/50902) ]
-   [`16faef1`](16faef1d8d) During uptodate ness check with buildInfo, check if there are errors explicitly with noEmit ([#&#8203;50974](https://github.com/Microsoft/TypeScript/issues/50974)) \[ [#&#8203;50959](https://github.com/Microsoft/TypeScript/issues/50959) ]
-   [`63791f5`](63791f52d4) Update package-lock.json
-   [`09368bc`](09368bcbae) Handle if project for open file will get recollected because of pending cleanup from closed script info ([#&#8203;50908](https://github.com/Microsoft/TypeScript/issues/50908)) \[ [#&#8203;50868](https://github.com/Microsoft/TypeScript/issues/50868) ]
-   [`c81bf4d`](c81bf4d8b0) fix(49594): Typescript 4.7.3 bracketed class property compilation error strictPropertyInitialization:true ([#&#8203;49619](https://github.com/Microsoft/TypeScript/issues/49619))
-   [`bc9cbbe`](bc9cbbef42) Merge pull request [#&#8203;49912](https://github.com/Microsoft/TypeScript/issues/49912) from microsoft/fix/47508
-   [`5a10f46`](5a10f46c00) Update package-lock.json
-   [`8e71f42`](8e71f429c8) Fixing pr comments
-   [`c100c64`](c100c6488d) Update package-lock.json
-   [`2a91107`](2a91107f75) Update package-lock.json
-   [`4ab9e76`](4ab9e76fb7) Use paths in package.json 'files' array that work with npm 6 and later. ([#&#8203;50930](https://github.com/Microsoft/TypeScript/issues/50930))
-   [`549b542`](549b5429d4) Use paths in package.json 'files' array that work with npm 6 and later.
-   [`7f37d25`](7f37d251fc) Update version to 4.9.1-beta and LKG.
-   [`f16ca7d`](f16ca7dd36) Remove 'async' dependency, used only in errorCheck.ts, modernize file ([#&#8203;50667](https://github.com/Microsoft/TypeScript/issues/50667))
-   [`c6bef3f`](c6bef3f028) LEGO: Merge pull request 50921
-   [`6753027`](675302730b) Update package-lock.json
-   [`9740bcc`](9740bcc534) Pluralized `hasInvalidatedResolution` -> `hasInvalidatedResolutions` ([#&#8203;50912](https://github.com/Microsoft/TypeScript/issues/50912))
-   [`84c29cd`](84c29cd576) 🤖 Pick PR [#&#8203;50912](https://github.com/Microsoft/TypeScript/issues/50912) (Pluralized \`hasInvalidatedResolutio...) into release-4.9 ([#&#8203;50913](https://github.com/Microsoft/TypeScript/issues/50913))
-   [`a26f634`](a26f63424d) Merge remote-tracking branch 'origin/main' into release-4.9
-   [`a455955`](a455955aac) Make hasInvalidatedResolution non internal for program and add it watchApi ([#&#8203;50776](https://github.com/Microsoft/TypeScript/issues/50776)) \[ [#&#8203;48057](https://github.com/Microsoft/TypeScript/issues/48057) ]
-   [`645d1cd`](645d1cd7c1) Fix assert in addIndirectUser in FAR ([#&#8203;50905](https://github.com/Microsoft/TypeScript/issues/50905))
-   [`bbec17d`](bbec17d900) LEGO: Merge pull request 50900
-   [`a9ecc67`](a9ecc675d6) Update package-lock.json
-   [`221cf55`](221cf55a21) package.json `exports` should have priority over `typesVersions` ([#&#8203;50890](https://github.com/Microsoft/TypeScript/issues/50890))
-   [`acb8977`](acb8977190) Remove .github/tsc.json ([#&#8203;50664](https://github.com/Microsoft/TypeScript/issues/50664))
-   [`7a3de81`](7a3de819bf) fix(49993): skip the quick fix for an expression with an enum type ([#&#8203;50625](https://github.com/Microsoft/TypeScript/issues/50625))
-   [`2644f28`](2644f28677) fix(49200): skip duplicated method declarations ([#&#8203;50609](https://github.com/Microsoft/TypeScript/issues/50609))
-   [`98652a3`](98652a349a) Bump version to 4.9.0-beta and LKG.
-   [`4d91204`](4d91204c9d) fix(37030): Expand Selection in function and arrow function skips body block ([#&#8203;50711](https://github.com/Microsoft/TypeScript/issues/50711))
-   [`e2dd508`](e2dd5084f7) DOM update 2022/09/21 ([#&#8203;50884](https://github.com/Microsoft/TypeScript/issues/50884))
-   [`1d9ab83`](1d9ab83914) fix(50866): emit modifiers from export declarations ([#&#8203;50874](https://github.com/Microsoft/TypeScript/issues/50874))
-   [`92a1b12`](92a1b124c1) LEGO: Merge pull request 50877
-   [`e383db6`](e383db692e) Fix debug.ts \__debugKind check ([#&#8203;50871](https://github.com/Microsoft/TypeScript/issues/50871))
-   [`01054e0`](01054e05ab) Consistently add undefined/missing to optional tuple element types ([#&#8203;50831](https://github.com/Microsoft/TypeScript/issues/50831))
-   [`d90795e`](d90795e799) Improve escape sequence handling in private names ([#&#8203;50856](https://github.com/Microsoft/TypeScript/issues/50856))
-   [`938a69a`](938a69a526) Fix import statement completions followed by interface declaration ([#&#8203;50350](https://github.com/Microsoft/TypeScript/issues/50350))
-   [`e002159`](e002159ad1) feat(49962): Disallow comparison against NaN  ([#&#8203;50626](https://github.com/Microsoft/TypeScript/issues/50626))
-   [`80ae43d`](80ae43d239) Fixing spaces
-   [`abc58bd`](abc58bdabc) Fixing baseline errors
-   [`305f4bd`](305f4bd420) Merge branch 'main' into fix/47969
-   [`23746af`](23746af766) fix(50591): RangeError: Maximum call stack size exceeded ([#&#8203;50594](https://github.com/Microsoft/TypeScript/issues/50594))
-   [`168186f`](168186f93d) Allow a union property of a private/protected member and an intersection property including that same member ([#&#8203;50328](https://github.com/Microsoft/TypeScript/issues/50328))
-   [`812ebcf`](812ebcf6e3) Update package-lock.json
-   [`16156b1`](16156b1baf) Add rules from eslint's recommended set that triggered good lints ([#&#8203;50422](https://github.com/Microsoft/TypeScript/issues/50422))
-   [`a11c416`](a11c41621b) Improve checking of `in` operator ([#&#8203;50666](https://github.com/Microsoft/TypeScript/issues/50666))
-   [`67f2b62`](67f2b62ed2) Gabritto/jsemitfixsilly ([#&#8203;50849](https://github.com/Microsoft/TypeScript/issues/50849))
-   [`3014dec`](3014dec887) Don't elide imports when transforming JS files ([#&#8203;50404](https://github.com/Microsoft/TypeScript/issues/50404))
-   [`57c7aa7`](57c7aa755c) LEGO: Merge pull request 50842
-   [`48a8e89`](48a8e8953a) Improve check of whether type query node possibly contains reference to type parameter ([#&#8203;50070](https://github.com/Microsoft/TypeScript/issues/50070))
-   [`af9ced1`](af9ced11f5) LEGO: Merge pull request 50825
-   [`a8e13f7`](a8e13f7340) Fixed an issue with destructured bindings from a generic union constraint not being narrowed correctly ([#&#8203;50221](https://github.com/Microsoft/TypeScript/issues/50221))
-   [`08af0b6`](08af0b6bf0) Update package-lock.json
-   [`0df46e8`](0df46e8733) Fix test around RegExp match vs. exec results ([#&#8203;50813](https://github.com/Microsoft/TypeScript/issues/50813))
-   [`906510e`](906510e0f3) Fixes for pr
-   [`2970c5d`](2970c5d167) make `RegExpExecArray` always include index 0 ([#&#8203;50713](https://github.com/Microsoft/TypeScript/issues/50713))
-   [`0507192`](05071920a0) Accepting baselines
-   [`29e50b3`](29e50b3149) Rewording documentation
-   [`01cae69`](01cae69e34) fix(50796): omit questionToken in object literal method completions ([#&#8203;50802](https://github.com/Microsoft/TypeScript/issues/50802))
-   [`3b84f76`](3b84f76fb2) Fix crash caused by incorrect bounds check (regression in 4.8) ([#&#8203;50797](https://github.com/Microsoft/TypeScript/issues/50797))
-   [`7e51306`](7e51306d30) Update package-lock.json
-   [`8b35c13`](8b35c1300e) The error "Object is possibly null or undefined" is ambiguous. ([#&#8203;49797](https://github.com/Microsoft/TypeScript/issues/49797))
-   [`a3f51b3`](a3f51b3b82) Update user baselines +cc [@&#8203;sandersn](https://github.com/sandersn) ([#&#8203;43554](https://github.com/Microsoft/TypeScript/issues/43554))
-   [`ba10a0d`](ba10a0d7c0) Removing duplicated code
-   [`ec6ae1c`](ec6ae1c4d0) Partially revert [#&#8203;41044](https://github.com/Microsoft/TypeScript/issues/41044), restoring parameter destructurings in d.ts files ([#&#8203;50779](https://github.com/Microsoft/TypeScript/issues/50779))
-   [`28232ca`](28232ca4b8) LEGO: Merge pull request 50783
-   [`49cfa1d`](49cfa1db17) Update package-lock.json
-   [`4110b80`](4110b80fbb) Fix equality narrowing and comparable relation for intersections with {} ([#&#8203;50735](https://github.com/Microsoft/TypeScript/issues/50735))
-   [`b23f1d6`](b23f1d6b59) LEGO: Merge pull request 50771
-   [`618fb2d`](618fb2d8b9) Update package-lock.json
-   [`08b91f6`](08b91f6b82) fix(50717): tsc crashes when it sees a JSDoc tag inside an [@&#8203;override](https://github.com/override) annotation ([#&#8203;50724](https://github.com/Microsoft/TypeScript/issues/50724))
-   [`60963d7`](60963d7216) Discriminant of type `never` should never be matched ([#&#8203;50755](https://github.com/Microsoft/TypeScript/issues/50755))
-   [`e37ea53`](e37ea53715) Update package-lock.json
-   [`a88c366`](a88c36655b) Fix test baselining for tsserver host timeouts ([#&#8203;50748](https://github.com/Microsoft/TypeScript/issues/50748))
-   [`6d38487`](6d384876e5) Fix workflow typo ([#&#8203;50746](https://github.com/Microsoft/TypeScript/issues/50746))
-   [`6b890f9`](6b890f93c4) Handle more places where package direcroy is converted to canonical file path ([#&#8203;50740](https://github.com/Microsoft/TypeScript/issues/50740))
-   [`f5f2923`](f5f2923c7d) Revert removal of nonInferrableAnyType ([#&#8203;50691](https://github.com/Microsoft/TypeScript/issues/50691))
-   [`7120b52`](7120b520cf) Update twoslash workflow ([#&#8203;50738](https://github.com/Microsoft/TypeScript/issues/50738))
-   [`68d526c`](68d526c200) Don't run linter after tests runs ([#&#8203;50597](https://github.com/Microsoft/TypeScript/issues/50597))
-   [`8e5e2e0`](8e5e2e08ea) Fix backticks in our JSDoc comments ([#&#8203;50737](https://github.com/Microsoft/TypeScript/issues/50737))
-   [`a4cabe7`](a4cabe725b) Support for auto-accessor fields from the Stage 3 Decorators proposal ([#&#8203;49705](https://github.com/Microsoft/TypeScript/issues/49705))
-   [`7737473`](77374732df) Update package-lock.json
-   [`12ab0fe`](12ab0fea9f) Update package-lock.json
-   [`eb40134`](eb40134373) Don't leave space for property access on non-integer literals ([#&#8203;50703](https://github.com/Microsoft/TypeScript/issues/50703))
-   [`a70bb9d`](a70bb9d3ff) Preserve special intersections in mapped types ([#&#8203;50704](https://github.com/Microsoft/TypeScript/issues/50704))
-   [`1a1c271`](1a1c271675) Don't remove space before dot if in property access on numeric literal ([#&#8203;50695](https://github.com/Microsoft/TypeScript/issues/50695))
-   [`7c918fb`](7c918fb766) Baseline host state when baselining tsserver tests ([#&#8203;50678](https://github.com/Microsoft/TypeScript/issues/50678))
-   [`2f1ba45`](2f1ba45cba) Update LKG and devDep of typescript to v4.8.3 ([#&#8203;50689](https://github.com/Microsoft/TypeScript/issues/50689))
-   [`be4e9ba`](be4e9bac8f) Update package-lock.json
-   [`f46a680`](f46a680863) Remove error message in node16 ([#&#8203;50673](https://github.com/Microsoft/TypeScript/issues/50673))
-   [`ab831d0`](ab831d0180) Ignore `--help` and `-?` in `tsc init` generated `compilerOptions` ([#&#8203;50628](https://github.com/Microsoft/TypeScript/issues/50628))
-   [`bb6f36f`](bb6f36f7c8) Forward intersection state flag to conditional type target check ([#&#8203;50620](https://github.com/Microsoft/TypeScript/issues/50620))
-   [`b58721f`](b58721fe15) Update package-lock.json
-   [`3c3820b`](3c3820b1a4) Simplify CI detection ([#&#8203;50661](https://github.com/Microsoft/TypeScript/issues/50661))
-   [`9ac1fce`](9ac1fce117) Fix eslint not looking at certain scripts, fix lints ([#&#8203;50660](https://github.com/Microsoft/TypeScript/issues/50660))
-   [`fd05c0c`](fd05c0cc6d) Make useFsEvents as default strategy for the watching ([#&#8203;50366](https://github.com/Microsoft/TypeScript/issues/50366))
-   [`5c2f770`](5c2f770d97) Remove unused cancellation from build ([#&#8203;50658](https://github.com/Microsoft/TypeScript/issues/50658))
-   [`66fbf05`](66fbf058ec) Update package-lock.json
-   [`7910c50`](7910c509c4) Update package-lock.json
-   [`fd3a84c`](fd3a84c3f0) Report every instance of TS1208 ([#&#8203;50101](https://github.com/Microsoft/TypeScript/issues/50101))
-   [`62f980a`](62f980aff8) Check if its same buildinfo only for directly referenced projects and not recursively ([#&#8203;50617](https://github.com/Microsoft/TypeScript/issues/50617)) \[ [#&#8203;50545](https://github.com/Microsoft/TypeScript/issues/50545) ]
-   [`856c7c5`](856c7c5fdd) Allow `{}` to narrow in same special cases as `unknown` ([#&#8203;50601](https://github.com/Microsoft/TypeScript/issues/50601))
-   [`854d448`](854d448e5c) `in` operator shouldn't narrow `{}` originating in `unknown` ([#&#8203;50610](https://github.com/Microsoft/TypeScript/issues/50610))
-   [`549e61d`](549e61d0af) Update package-lock.json
-   [`bcf9949`](bcf994996e) fix(50079): show deprecated on JSX attributes ([#&#8203;50084](https://github.com/Microsoft/TypeScript/issues/50084))
-   [`5df09a5`](5df09a514c) Use bidirectional comparability in narrowing ([#&#8203;50592](https://github.com/Microsoft/TypeScript/issues/50592))
-   [`891cdc5`](891cdc58aa) Remove unused baselines ([#&#8203;50593](https://github.com/Microsoft/TypeScript/issues/50593))
-   [`6db2c88`](6db2c882f3) {} & null and {} & undefined should always be never  ([#&#8203;50553](https://github.com/Microsoft/TypeScript/issues/50553))
-   [`238c341`](238c341701) Defer distributing index over generic object types ([#&#8203;50540](https://github.com/Microsoft/TypeScript/issues/50540))
-   [`2983092`](298309271b) Do not canonicalize the file names when getting absolute paths during nodenext resolution ([#&#8203;50557](https://github.com/Microsoft/TypeScript/issues/50557)) \[ [#&#8203;50544](https://github.com/Microsoft/TypeScript/issues/50544) ]
-   [`dcade77`](dcade7732c) Update package-lock.json
-   [`a9797d2`](a9797d218d) fix(50340): typeof ... === "undefined" check on discriminated union of undefined and object type doesn't narrow correctly ([#&#8203;50344](https://github.com/Microsoft/TypeScript/issues/50344))
-   [`43f8ae6`](43f8ae6df4) Only normalize intersections that include {} ([#&#8203;50535](https://github.com/Microsoft/TypeScript/issues/50535))
-   [`d293e72`](d293e723a2) Rename API to importPlugin ([#&#8203;50554](https://github.com/Microsoft/TypeScript/issues/50554))
-   [`cd312d3`](cd312d3076) Managing control flow
-   [`19defbf`](19defbfe57) Update package-lock.json
-   [`f071d30`](f071d303c1) Move contributing related info out of README to CONTRIBUTING ([#&#8203;50543](https://github.com/Microsoft/TypeScript/issues/50543))
-   [`488d0ee`](488d0eebd0) Retain name and propertyName in declaration emit copies of binding patterns if property name is a keyword ([#&#8203;50537](https://github.com/Microsoft/TypeScript/issues/50537))
-   [`8b482b5`](8b482b513d) Update package-lock.json
-   [`c89f355`](c89f355a41) Remove redundant pretest script ([#&#8203;50518](https://github.com/Microsoft/TypeScript/issues/50518))
-   [`6d170b4`](6d170b490d) Handle intersections in isGenericTypeWithoutNullableConstraint ([#&#8203;50497](https://github.com/Microsoft/TypeScript/issues/50497))
-   [`ed6889c`](ed6889cd5b) LEGO: Merge pull request 50506
-   [`29cbfe9`](29cbfe9a25) LEGO: Merge pull request 50493
-   [`6faa291`](6faa291b45) LEGO: Merge pull request 50484
-   [`71b2ba6`](71b2ba6111) Reuse computed type of condition expressions ([#&#8203;49881](https://github.com/Microsoft/TypeScript/issues/49881))
-   [`8778c1d`](8778c1ded3) Update package-lock.json
-   [`4579245`](4579245f36) fix(50427): allow convert function expressions ([#&#8203;50430](https://github.com/Microsoft/TypeScript/issues/50430))
-   [`cbc0b17`](cbc0b17eac) Push package-lock.json updates via typescript-bot token ([#&#8203;50476](https://github.com/Microsoft/TypeScript/issues/50476))
-   [`bb3a7ae`](bb3a7aec11) fix(50415): Language server debug failure - Did not expect GetAccessor to have an Identifier in its trivia ([#&#8203;50470](https://github.com/Microsoft/TypeScript/issues/50470))
-   [`3557092`](3557092b14) Rephrase error message to be 100% technically correct ([#&#8203;50471](https://github.com/Microsoft/TypeScript/issues/50471))
-   [`71d1911`](71d1911503) add unknown to DateTimeFormatTypes ([#&#8203;50402](https://github.com/Microsoft/TypeScript/issues/50402))
-   [`8f89599`](8f895997d2) Don't include .gitattributes in package ([#&#8203;50475](https://github.com/Microsoft/TypeScript/issues/50475))
-   [`6e8337e`](6e8337ef70) Optimize substitution types ([#&#8203;50397](https://github.com/Microsoft/TypeScript/issues/50397))
-   [`226dd0b`](226dd0b7bf) Fix typechecking related lints that changed post 4.8, update LKG to 4.8.2 ([#&#8203;50472](https://github.com/Microsoft/TypeScript/issues/50472))
-   [`164dddc`](164dddc48e) feat(7481): Operator to ensure an expression is contextually typed by, and satisfies, some type ([#&#8203;46827](https://github.com/Microsoft/TypeScript/issues/46827))
-   [`0715791`](07157914eb) Update package-lock.json
-   [`e675ea8`](e675ea8dd8) Remove AUTHORS.md, .mailmap, authors.ts script ([#&#8203;50410](https://github.com/Microsoft/TypeScript/issues/50410))
-   [`38076df`](38076df346) Fix auto import crash due to difference in `paths` handling ([#&#8203;50419](https://github.com/Microsoft/TypeScript/issues/50419))
-   [`12eb519`](12eb519b3f) fix(50435): Duplicate seeming Code Actions for convert const to let ([#&#8203;50442](https://github.com/Microsoft/TypeScript/issues/50442))
-   [`a08b045`](a08b045d2b) Jsdoc property description ([#&#8203;50269](https://github.com/Microsoft/TypeScript/issues/50269)) \[ [#&#8203;47933](https://github.com/Microsoft/TypeScript/issues/47933) ]
-   [`5ba22e0`](5ba22e05a9) Remove top level loc folder ([#&#8203;50421](https://github.com/Microsoft/TypeScript/issues/50421))
-   [`c4eb37c`](c4eb37c8a0) Update package-lock.json
-   [`8d7ad8c`](8d7ad8c3ae) fix(50375): Errors for missing enum-named properties should attempt to preserve names ([#&#8203;50382](https://github.com/Microsoft/TypeScript/issues/50382))
-   [`fb717df`](fb717df6bf) Discard union types before considering weak type checks on unit-like types ([#&#8203;50423](https://github.com/Microsoft/TypeScript/issues/50423))
-   [`b9a5bbc`](b9a5bbc9af) Syntax operations also need to ensure project is present for the open script infos since update could be pending to make sure open script info has project ([#&#8203;50418](https://github.com/Microsoft/TypeScript/issues/50418)) \[ [#&#8203;50131](https://github.com/Microsoft/TypeScript/issues/50131) ]
-   [`1d4fbbb`](1d4fbbb529) Update package-lock.json
-   [`44ce3cf`](44ce3cff70) fix(50224): Intellisense for strings within a type's Union doesn't work properly for JSX ([#&#8203;50231](https://github.com/Microsoft/TypeScript/issues/50231))
-   [`6ee5db9`](6ee5db95c2) Use package.json files array instead of .npmignore ([#&#8203;50408](https://github.com/Microsoft/TypeScript/issues/50408))

This list of changes was [auto generated](https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/\_release?releaseId=116&\_a=release-summary).</details>

### [`v4.8.4`](https://github.com/microsoft/TypeScript/releases/tag/v4.8.4): TypeScript 4.8.4

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.8.3...v4.8.4)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.8.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.0%22+).
-   [fixed issues query for Typescript 4.8.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.1%22+).
-   [fixed issues query for Typescript 4.8.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.2%22+).
-   [fixed issues query for Typescript 4.8.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.3%22+).
-   [fixed issues query for Typescript 4.8.4 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.4%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-484) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.8.3`](https://github.com/microsoft/TypeScript/releases/tag/v4.8.3): TypeScript 4.8.3

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.8.2...v4.8.3)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.8.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.0%22+).
-   [fixed issues query for Typescript 4.8.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.1%22+).
-   [fixed issues query for Typescript 4.8.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.2%22+).
-   [fixed issues query for Typescript 4.8.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.3%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-483) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.8.2`](https://github.com/microsoft/TypeScript/releases/tag/v4.8.2): TypeScript 4.8

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.7.4...v4.8.2)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.8.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.0%22+).
-   [fixed issues query for Typescript 4.8.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.1%22+).
-   [fixed issues query for Typescript 4.8.1 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.8.2%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-48) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.7.4`](https://github.com/microsoft/TypeScript/releases/tag/v4.7.4): TypeScript 4.7.4

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.7.3...v4.7.4)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.7.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.0%22+).
-   [fixed issues query for Typescript 4.7.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.1%22+).
-   [fixed issues query for Typescript 4.7.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.2%22+).
-   [fixed issues query for Typescript 4.7.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.3%22+).
-   [fixed issues query for Typescript 4.7.4 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.4%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-474) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.7.3`](https://github.com/microsoft/TypeScript/releases/tag/v4.7.3): TypeScript 4.7.3

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.7.2...v4.7.3)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.7.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.0%22+).
-   [fixed issues query for Typescript 4.7.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.1%22+).
-   [fixed issues query for Typescript 4.7.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.2%22+).
-   [fixed issues query for Typescript 4.7.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.3%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-473) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.7.2`](https://github.com/microsoft/TypeScript/releases/tag/v4.7.2): TypeScript 4.7.2

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.6.4...v4.7.2)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.7.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.0%22+).
-   [fixed issues query for Typescript 4.7.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.1%22+).
-   [fixed issues query for Typescript 4.7.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.7.2%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-472) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.6.4`](https://github.com/microsoft/TypeScript/releases/tag/v4.6.4): TypeScript 4.6.4

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.6.3...v4.6.4)

This release includes [a bug fix for text formatting on certain ranges](Studiohttps://github.com/microsoft/TypeScript/pull/48463), which was impacting Visual Studio users.

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.6.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.0%22+).
-   [fixed issues query for Typescript 4.6.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.1%22+).
-   [fixed issues query for Typescript 4.6.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.2%22+).
-   [fixed issues query for Typescript 4.6.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.3%22+).
-   [fixed issues query for Typescript 4.6.4 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.4%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-464) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.6.3`](https://github.com/microsoft/TypeScript/releases/tag/v4.6.3): TypeScript 4.6.3

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.6.2...v4.6.3)

This release includes fixes for

-   [an incremental parsing bug caused by faulty error recovery logic](https://github.com/microsoft/TypeScript/issues/47895)
-   [improved results from the TypeScript API's `preProcessFile` function](https://github.com/microsoft/TypeScript/pull/47657)

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.6.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.0%22+).
-   [fixed issues query for Typescript 4.6.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.1%22+).
-   [fixed issues query for Typescript 4.6.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.2%22+).
-   [fixed issues query for Typescript 4.6.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.3%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)

### [`v4.6.2`](https://github.com/microsoft/TypeScript/releases/tag/v4.6.2): TypeScript 4.6.2

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.5.5...v4.6.2)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-6/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.6.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.0%22+).
-   [fixed issues query for Typescript 4.6.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.1%22+).
-   [fixed issues query for Typescript 4.6.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.6.2%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [Visual Studio 2022/2019](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-46) ([Select new version in project options](https://github.com/Microsoft/TypeScript/wiki/Updating-TypeScript-in-Visual-Studio-2017))
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.5.5`](https://github.com/microsoft/TypeScript/releases/tag/v4.5.5): TypeScript 4.5.5

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.5.4...v4.5.5)

This [patch release](https://github.com/microsoft/TypeScript/issues?q=is%3Aissue+milestone%3A%22TypeScript+4.5.5%22+is%3Aclosed) includes a number of fixes to language service crashes and assertion violations, along with improvements to JSX attribute snippets.

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.5.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.0%22+).
-   [fixed issues query for Typescript 4.5.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.1%22+).
-   [fixed issues query for Typescript 4.5.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.2%22+).
-   [fixed issues query for Typescript 4.5.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.3%22+).
-   [fixed issues query for Typescript 4.5.4 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.4%22+).
-   [fixed issues query for Typescript 4.5.5 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.5%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)

<!-- * [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild) -->

### [`v4.5.4`](https://github.com/microsoft/TypeScript/releases/tag/v4.5.4): TypeScript 4.5.4

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.5.3...v4.5.4)

This patch release includes a fix for [incorrectly offering up JSX attribute snippet completions at the beginning of a tag name](https://github.com/microsoft/TypeScript/issues/47090).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.5.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.0%22+).
-   [fixed issues query for Typescript 4.5.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.1%22+).
-   [fixed issues query for Typescript 4.5.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.2%22+).
-   [fixed issues query for Typescript 4.5.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.3%22+).
-   -   [fixed issues query for Typescript 4.5.4 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.4%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

### [`v4.5.3`](https://github.com/microsoft/TypeScript/releases/tag/v4.5.3): TypeScript 4.5.3

[Compare Source](https://github.com/Microsoft/TypeScript/compare/v4.5.2...v4.5.3)

For release notes, check out the [release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-5/).

For the complete list of fixed issues, check out the

-   [fixed issues query for Typescript 4.5.0 (Beta)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.0%22+).
-   [fixed issues query for Typescript 4.5.1 (RC)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.1%22+).
-   [fixed issues query for Typescript 4.5.2 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.2%22+).
-   [fixed issues query for Typescript 4.5.3 (Stable)](https://github.com/microsoft/TypeScript/issues?q=milestone%3A%22TypeScript+4.5.3%22+).

Downloads are available on:

-   [npm](https://www.npmjs.com/package/typescript)
-   [NuGet package](https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/275
Reviewed-by: Vylpes <ethan@vylpes.com>
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-03-20 18:02:12 +00:00
fabbd84f2a Update dependency minimatch to v7 (#271)
Some checks failed
continuous-integration/drone/push Build is failing
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [minimatch](https://github.com/isaacs/minimatch) | dependencies | major | [`3.1.2` -> `7.4.2`](https://renovatebot.com/diffs/npm/minimatch/3.1.2/7.4.2) |

---

### Release Notes

<details>
<summary>isaacs/minimatch</summary>

### [`v7.4.2`](https://github.com/isaacs/minimatch/compare/v7.4.1...v7.4.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.4.1...v7.4.2)

### [`v7.4.1`](https://github.com/isaacs/minimatch/compare/v7.4.0...v7.4.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.4.0...v7.4.1)

### [`v7.4.0`](https://github.com/isaacs/minimatch/compare/v7.3.0...v7.4.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.3.0...v7.4.0)

### [`v7.3.0`](https://github.com/isaacs/minimatch/compare/v7.2.0...v7.3.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.2.0...v7.3.0)

### [`v7.2.0`](https://github.com/isaacs/minimatch/compare/v7.1.4...v7.2.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.1.4...v7.2.0)

### [`v7.1.4`](https://github.com/isaacs/minimatch/compare/v7.1.3...v7.1.4)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.1.3...v7.1.4)

### [`v7.1.3`](https://github.com/isaacs/minimatch/compare/v7.1.2...v7.1.3)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.1.2...v7.1.3)

### [`v7.1.2`](https://github.com/isaacs/minimatch/compare/v7.1.1...v7.1.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.1.1...v7.1.2)

### [`v7.1.1`](https://github.com/isaacs/minimatch/compare/v7.1.0...v7.1.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.1.0...v7.1.1)

### [`v7.1.0`](https://github.com/isaacs/minimatch/compare/v7.0.1...v7.1.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.0.1...v7.1.0)

### [`v7.0.1`](https://github.com/isaacs/minimatch/compare/v7.0.0...v7.0.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v7.0.0...v7.0.1)

### [`v7.0.0`](https://github.com/isaacs/minimatch/compare/v6.2.0...v7.0.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.2.0...v7.0.0)

### [`v6.2.0`](https://github.com/isaacs/minimatch/compare/v6.1.10...v6.2.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.10...v6.2.0)

### [`v6.1.10`](https://github.com/isaacs/minimatch/compare/v6.1.9...v6.1.10)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.9...v6.1.10)

### [`v6.1.9`](https://github.com/isaacs/minimatch/compare/v6.1.8...v6.1.9)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.8...v6.1.9)

### [`v6.1.8`](https://github.com/isaacs/minimatch/compare/v6.1.7...v6.1.8)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.7...v6.1.8)

### [`v6.1.7`](https://github.com/isaacs/minimatch/compare/v6.1.6...v6.1.7)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.6...v6.1.7)

### [`v6.1.6`](https://github.com/isaacs/minimatch/compare/v6.1.5...v6.1.6)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.5...v6.1.6)

### [`v6.1.5`](https://github.com/isaacs/minimatch/compare/v6.1.4...v6.1.5)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.4...v6.1.5)

### [`v6.1.4`](https://github.com/isaacs/minimatch/compare/v6.1.3...v6.1.4)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.3...v6.1.4)

### [`v6.1.3`](https://github.com/isaacs/minimatch/compare/v6.1.2...v6.1.3)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.2...v6.1.3)

### [`v6.1.2`](https://github.com/isaacs/minimatch/compare/v6.1.1...v6.1.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.1...v6.1.2)

### [`v6.1.1`](https://github.com/isaacs/minimatch/compare/v6.1.0...v6.1.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.1.0...v6.1.1)

### [`v6.1.0`](https://github.com/isaacs/minimatch/compare/v6.0.4...v6.1.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.0.4...v6.1.0)

### [`v6.0.4`](https://github.com/isaacs/minimatch/compare/v6.0.3...v6.0.4)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.0.3...v6.0.4)

### [`v6.0.3`](https://github.com/isaacs/minimatch/compare/v6.0.2...v6.0.3)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.0.2...v6.0.3)

### [`v6.0.2`](https://github.com/isaacs/minimatch/compare/v6.0.1...v6.0.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.0.1...v6.0.2)

### [`v6.0.1`](https://github.com/isaacs/minimatch/compare/v6.0.0...v6.0.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v6.0.0...v6.0.1)

### [`v6.0.0`](https://github.com/isaacs/minimatch/compare/v5.1.6...v6.0.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.1.6...v6.0.0)

### [`v5.1.6`](https://github.com/isaacs/minimatch/compare/v5.1.5...v5.1.6)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.1.5...v5.1.6)

### [`v5.1.5`](https://github.com/isaacs/minimatch/compare/v5.1.4...v5.1.5)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.1.4...v5.1.5)

### [`v5.1.4`](https://github.com/isaacs/minimatch/compare/v5.1.3...v5.1.4)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.1.3...v5.1.4)

### [`v5.1.3`](https://github.com/isaacs/minimatch/compare/v5.1.2...v5.1.3)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.1.2...v5.1.3)

### [`v5.1.2`](https://github.com/isaacs/minimatch/compare/v5.1.1...v5.1.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.1.1...v5.1.2)

### [`v5.1.1`](https://github.com/isaacs/minimatch/compare/v5.1.0...v5.1.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.1.0...v5.1.1)

### [`v5.1.0`](https://github.com/isaacs/minimatch/compare/v5.0.1...v5.1.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.0.1...v5.1.0)

### [`v5.0.1`](https://github.com/isaacs/minimatch/compare/v5.0.0...v5.0.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v5.0.0...v5.0.1)

### [`v5.0.0`](https://github.com/isaacs/minimatch/compare/v4.2.3...v5.0.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v4.2.3...v5.0.0)

### [`v4.2.3`](https://github.com/isaacs/minimatch/compare/v4.2.2...v4.2.3)

[Compare Source](https://github.com/isaacs/minimatch/compare/v4.2.2...v4.2.3)

### [`v4.2.2`](https://github.com/isaacs/minimatch/compare/v4.2.1...v4.2.2)

[Compare Source](https://github.com/isaacs/minimatch/compare/v4.2.1...v4.2.2)

### [`v4.2.1`](https://github.com/isaacs/minimatch/compare/v4.2.0...v4.2.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v4.2.0...v4.2.1)

### [`v4.2.0`](https://github.com/isaacs/minimatch/compare/v4.1.1...v4.2.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v4.1.1...v4.2.0)

### [`v4.1.1`](https://github.com/isaacs/minimatch/compare/v4.1.0...v4.1.1)

[Compare Source](https://github.com/isaacs/minimatch/compare/v4.1.0...v4.1.1)

### [`v4.1.0`](https://github.com/isaacs/minimatch/compare/v4.0.0...v4.1.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v4.0.0...v4.1.0)

### [`v4.0.0`](https://github.com/isaacs/minimatch/compare/v3.1.2...v4.0.0)

[Compare Source](https://github.com/isaacs/minimatch/compare/v3.1.2...v4.0.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC43NC4yIiwidXBkYXRlZEluVmVyIjoiMzQuNzQuMiJ9-->

Co-authored-by: Renovate Bot <renovate@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/271
Reviewed-by: Vylpes <ethan@vylpes.com>
Co-authored-by: RenovateBot <renovate@vylpes.com>
Co-committed-by: RenovateBot <renovate@vylpes.com>
2023-03-20 17:59:28 +00:00
fc7c9ef02c Merge pull request 'Update drone.yml' (#265) from feature/263-drone-stuck into develop
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/265
2023-02-03 18:02:52 +00:00
b9fdd51897 Update drone.yml
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build was killed
2023-02-03 18:02:40 +00:00
1908f7505c Update drone.yml
Some checks reported errors
continuous-integration/drone/push Build encountered an error
continuous-integration/drone/pr Build encountered an error
2023-02-03 18:01:06 +00:00
b64ddacb41 Merge pull request 'Update drone.yml' (#264) from feature/263-drone-stuck into develop
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/264
2023-02-03 17:55:37 +00:00
f5f87a52dc Update drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-02-03 17:55:10 +00:00
1c3ba40b56 Merge pull request 'Add .drone.yml' (#260) from feature/218-drone-ci into develop
Some checks reported errors
continuous-integration/drone/push Build was killed
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/260
Reviewed-by: VylpesTester <tester@vylpes.com>
2023-02-03 17:35:27 +00:00
45a92a7b48 Merge branch 'develop' into feature/218-drone-ci
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-02-03 17:35:22 +00:00
19b6cb033c Merge pull request 'Fix not having a link set in the config crashing the bot' (#261) from feature/176-about-command into develop
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/261
Reviewed-by: VylpesTester <tester@vylpes.com>
2023-02-03 17:35:15 +00:00
ae5f9df366 Merge branch 'develop' into feature/176-about-command 2023-02-03 17:35:11 +00:00
3f09a2367c Merge pull request 'Update random-bunny to v2.0.5' (#262) from feature/179-update-random-bunny into develop
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/262
Reviewed-by: VylpesTester <tester@vylpes.com>
2023-02-03 17:35:01 +00:00
8e5ac6d01e Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-02-02 08:38:14 +00:00
6d7dc26e09 Update random-bunny to v2.0.5 2023-02-01 17:33:13 +00:00
4c1726f5c8 Update '.drone.yml'
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-01-30 11:11:04 +00:00
27afb3dd22 Fix not having a link set in the config crashing the bot 2023-01-28 14:53:23 +00:00
c3d65b5579 Add .drone.yml
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2023-01-27 17:41:05 +00:00
4bc6280a1c Merge pull request 'Update dependency uuid to v9' (#245) from renovate/uuid-9.x into develop
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/245
Reviewed-by: Vylpes <ethan@vylpes.com>
2023-01-23 19:56:57 +00:00
de5d0bab79 Merge branch 'develop' into renovate/uuid-9.x 2023-01-23 19:54:59 +00:00
fdcedbd640 Merge pull request 'Update dependency jest-mock-extended to v3' (#242) from renovate/jest-mock-extended-3.x into develop
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/242
Reviewed-by: Vylpes <ethan@vylpes.com>
2023-01-23 19:49:08 +00:00
568fa315bb Merge branch 'develop' into renovate/jest-mock-extended-3.x 2023-01-23 19:47:29 +00:00
feaf17ff81 Merge pull request 'Update dependency dotenv to v16' (#239) from renovate/dotenv-16.x into develop
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/239
Reviewed-by: Vylpes <ethan@vylpes.com>
2023-01-23 19:45:13 +00:00
2b63400684 Merge branch 'develop' into renovate/dotenv-16.x 2023-01-23 19:43:23 +00:00
b3b2186a61 Merge pull request 'Update dependency @types/node to v18' (#238) from renovate/node-18.x into develop
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/238
Reviewed-by: Vylpes <ethan@vylpes.com>
2023-01-23 19:43:01 +00:00
17ae4079e5 Update dependency uuid to v9 2022-12-28 18:52:33 +00:00
e808bbe6a5 Update dependency jest-mock-extended to v3 2022-12-28 18:52:23 +00:00
e02e58e280 Update dependency dotenv to v16 2022-12-28 18:52:11 +00:00
2164b9603d Update dependency @types/node to v18 2022-12-28 18:52:08 +00:00
31a600c74e Merge remote-tracking branch 'origin/renovate/configure' into develop 2022-12-28 18:48:09 +00:00
a001e63857 Update scripts 2022-12-27 15:22:11 +00:00
a3e062a1ff defect/214-application-did-not-respond (#233)
Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/233
2022-12-27 15:18:55 +00:00
29a4a596f7 Merge branch 'main' into develop 2022-12-02 17:41:26 +00:00
39967068bb Feature/155 lobby command list (#215)
- Add lobby list command to list all channels setup as a lobby for the current server as well as their last time used

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/215
Reviewed-by: VylpesTester <tester@vylpes.com>
2022-11-12 22:06:41 +00:00
d688d292c3
Fix the event embeds not having the user's avatar on the thumbnail (#213) 2022-11-04 18:07:00 +00:00
0b498eb023
Update MankBot rules to mention me (#203) 2022-10-16 15:10:48 +01:00
0d63bd120d
Feature/103 improve events (#201)
* Improve event handler to only run events that have been registered

* Tidy up events into their own function files
2022-10-09 15:23:25 +01:00
ed8f5927c8
Feature/81 slash command support (#192)
* Update discord.js

* Migrate to slash commands

* Clean up imports

* Update permissions

* Fix guild-specific commands not showing up

* Fix changes requested
2022-09-18 11:57:22 +01:00
0465697b87
Update stage script to use stage key 2022-09-06 19:37:06 +01:00
d4716e69d5
Change how bot token is gotten in script 2022-09-06 19:34:47 +01:00
e2988d39b5
Remove testing from deployment scripts 2022-09-06 19:29:16 +01:00
6238a20002
Update dates 2022-09-06 19:26:57 +01:00
7decd28dc9
Feature/182 setup actions (#186)
* Create scripts

* Create github workflows

* Create initial DB migration script

* Make default bot prefix configurable

* Add bot token fetcher
2022-09-06 19:24:40 +01:00
cd666d24fd
Feature/vba 77 (#178)
* Add say command (#174)

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/174

* Add repo and funding link to about message (#176)

Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/176

* Add other subreddits to bunny command (#177)

Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/177

* Add database table

* Save moderation actions to database

* Add audit command to see a user's audits

* Add audit view subcommand

* Add audit clear subcommand

* Create add audit subcommand

* Fix bot crashing when viewing an audit with no reason

* Fix changes requested
2022-09-05 18:10:04 +01:00
2da5e1aa75
Feature/vba 52 (#175)
* Add say command (#174)

Co-authored-by: Ethan Lane <ethan@vylpes.com>
Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/174

* Add repo and funding link to about message (#176)

Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/176

* Add other subreddits to bunny command

* Fix changes requested
2022-09-05 18:07:32 +01:00
4d01f0b34a
Pending changes exported from your codespace (#173) 2022-08-09 12:33:03 +01:00
a01a43788e
Feature/49 ignore channel lists when logging (#168)
* Add entity

* Create ignore command

* Update message events to ignore channels set
2022-07-16 16:12:55 +01:00
4621cec9d5 Merge branch 'main' into develop 2022-07-06 12:14:36 +01:00
ac6be6bf5a Merge branch 'main' into develop 2022-06-05 14:17:31 +01:00
d7576f1863
Update .env template to current date 2022-05-16 18:40:34 +01:00
514be692fd
150 assignable roles should be its own table to prevent limitations on length (#158)
* Add entity

* Update role config command to use new entity

* Update role command to use new entity

* Remove legacy code from config command
2022-05-14 14:59:32 +01:00
6522bee37b
Add bunny command back (#157) 2022-05-12 18:04:34 +01:00
861676ed0b
Update lobby command to give proper errors if role or channel id cannot be found (#156) 2022-05-11 17:26:50 +01:00
b4cec6778d
Change lobby command to error upon making a duplicate lobby channel (#154) 2022-05-10 18:15:40 +01:00
112 changed files with 4421 additions and 3222 deletions

View file

@ -7,7 +7,19 @@
# any secret values. # any secret values.
BOT_TOKEN= BOT_TOKEN=
BOT_VER=3.0.7 BOT_VER=3.1
BOT_AUTHOR=Vylpes BOT_AUTHOR=Vylpes
BOT_DATE=08 May 2023 BOT_DATE=08 May 2023
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_CLIENTID=682942374040961060
ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app
DB_HOST=127.0.0.1
DB_PORT=3101
DB_NAME=vylbot
DB_AUTH_USER=dev
DB_AUTH_PASS=dev
DB_SYNC=true
DB_LOGGING=true

72
.drone.yml Normal file
View file

@ -0,0 +1,72 @@
---
kind: pipeline
name: deployment
steps:
- name: deploy
image: appleboy/drone-ssh
settings:
host: 192.168.68.121
username: vylpes
password:
from_secret: ssh_password
port: 22
script:
- sh /home/vylpes/scripts/vylbot/deploy_prod.sh
trigger:
event:
- tag
---
kind: pipeline
name: staging
steps:
- name: stage
image: appleboy/drone-ssh
settings:
host: 192.168.68.121
username: vylpes
password:
from_secret: ssh_password
port: 22
script:
- sh /home/vylpes/scripts/vylbot/deploy_stage.sh
trigger:
branch:
- develop
event:
- push
---
kind: pipeline
name: integration
steps:
- name: build
image: node
commands:
- yarn install --frozen-lockfile
- yarn build
- name: test
image: node
commands:
- yarn install --frozen-lockfile
- yarn test
trigger:
branch:
- main
- develop
- hotfix/*
- feature/*
- renovate/*
event:
- push
- pull_request

18
.gitea/ISSUE_TEMPLATE.md Normal file
View file

@ -0,0 +1,18 @@
Epic: \
Story Points:
---
*No description*
## Acceptance Criteria
*No acceptance criteria*
## Subtasks
*No subtasks*
## Notes
*No notes*

View file

@ -0,0 +1,29 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify the changes. Provide instructions so we can reproduce. Please also list any relevant details to your test configuration.
# Checklist
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that provde my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

16
.github/ISSUE_TEMPLATE/epic.md vendored Normal file
View file

@ -0,0 +1,16 @@
---
name: Epic
about: Agile Epic
title: ''
labels: epic
assignees: ''
---
*Description here*
## Stories
*Stories linked to this epic*
## Bugs
*Bugs linked to this epic*

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

24
.github/ISSUE_TEMPLATE/user-story.md vendored Normal file
View file

@ -0,0 +1,24 @@
---
name: User Story
about: Agile User Story
title: ''
labels: needs criteria, story
assignees: ''
---
Epic Link: N/A
Story Points: N/A
---
*Description here*
## Acceptance Criteria
*Add your ACs here*
## Notes
*Any extra information*
## Subtasks
*Add technical subtasks here*

30
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,30 @@
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
# Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules

View file

@ -1,27 +0,0 @@
name: Testing
on:
pull_request:
branches:
- main
- develop
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: yarn install
- run: yarn build
- run: yarn test

24
.prod.env Normal file
View file

@ -0,0 +1,24 @@
# Security Warning! Do not commit this file to any VCS!
# This is a local file to speed up development process,
# so you don't have to change your environment variables.
#
# This is not applied to `.env.template`!
# Template files must be committed to the VCS, but must not contain
# any secret values.
BOT_TOKEN=
BOT_VER=3.1
BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816
BOT_CLIENTID=680083120896081954
ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app
DB_HOST=127.0.0.1
DB_PORT=3121
DB_NAME=vylbot
DB_AUTH_USER=prod
DB_AUTH_PASS=prod
DB_SYNC=false
DB_LOGGING=false

24
.stage.env Normal file
View file

@ -0,0 +1,24 @@
# Security Warning! Do not commit this file to any VCS!
# This is a local file to speed up development process,
# so you don't have to change your environment variables.
#
# This is not applied to `.env.template`!
# Template files must be committed to the VCS, but must not contain
# any secret values.
BOT_TOKEN=
BOT_VER=3.1
BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816
BOT_CLIENTID=1016767908740857949
ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://gitea.vylpes.xyz/RabbitLabs/vylbot-app
DB_HOST=127.0.0.1
DB_PORT=3111
DB_NAME=vylbot
DB_AUTH_USER=stage
DB_AUTH_PASS=stage
DB_SYNC=false
DB_LOGGING=false

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Vylpes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -6,7 +6,7 @@
"title": "Vylpes' Den", "title": "Vylpes' Den",
"description": [ "description": [
"Welcome to Vylpes' Den! Make sure to say hi!", "Welcome to Vylpes' Den! Make sure to say hi!",
"Invite link: https://discord.gg/UyAhAVp" "Invite link: https://go.vylpes.xyz/A6HcA"
] ]
}, },
{ {
@ -70,19 +70,17 @@
"description": [ "description": [
"This server uses a bot made by me, VylBot, to help moderate the server.", "This server uses a bot made by me, VylBot, to help moderate the server.",
"For more information on it, see the GitHub repositories:", "For more information on it, see the GitHub repositories:",
"https://github.com/Vylpes/vylbot-core", "https://gitea.vylpes.xyz/rabbitlabs/vylbot-app"
"https://github.com/Vylpes/vylbot-app"
] ]
}, },
{ {
"title": "Links", "title": "Links",
"description": [ "description": [
"YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA", "YouTube: https://www.youtube.com/@vylpes",
"Patreon: https://www.patreon.com/vylpes", "Patreon: https://www.patreon.com/vylpes",
"Twitch: https://www.twitch.tv/vylpes_", "Twitch: https://www.twitch.tv/vylpes_",
"Twitter: https://twitter.com/vylpes", "Twitter: https://twitter.com/vylpes"
"Blog: https://vylpes.xyz"
], ],
"footer": "Last updated 01/02/2022" "footer": "Last updated 20/06/2023"
} }
] ]

View file

@ -1,88 +1,12 @@
[ [
{ {
"image": "https://i.imgur.com/bjH1gza.png" "title": "Welcome to Mankalor's Discord Server!",
},
{
"title": "Bot Testing Ground",
"description": [ "description": [
"Welcome to Vylpes' Den! Make sure to say hi!", "*You must follow Discord's TOS, including the rule where", "you must be 13 years or older.",
"Invite link: https://discord.gg/UyAhAVp" "If moderators know you're under 13, we will have to ban you!*",
]
},
{
"title": "Discord TOS",
"description": [
"All servers are required to follow the Discord Terms of Service. This includes minimum age requirements (13+). If the moderation team discover a breach of TOS we are required by discord to ban. Make sure you know them!",
"https://discord.com/terms"
]
},
{
"title": "Rules",
"description": [
"**English Only**",
"In order for everyone to understand each other we would like to ask everyone to speak in English only.",
"", "",
"**No NSFW or Obscene Content**", "You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.",
"This includes text, images, or links featuring nudity, sex, hard violence, or other graphically disturbing content.", "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message <@147392775707426816>."
"",
"**Treat Everyone with Respect**",
"Absolutely no harassment, witch hunting, sexism, racism, or hate speech will be tolerated.",
"",
"**No spam or self promotion**",
"Outside of #self-promo. This includes DMing fellow members.",
"",
"**Keep Politics to #general**",
"And make sure it doesn't become too heated. Debate don't argue.",
"",
"**Drama From Other Servers**",
"Please don't bring up drama from other servers, keep that to DMs",
"",
"**Bot Abuse**",
"Don't abuse the bots or you will be blocked from using them",
"",
"**Event Spoilers**",
"Contents of events and keynotes, such as the Nintendo Direct, must be spoken about in events, this rule applies for up to 24 hours after the event ends. Even though we will only enforce talking there for a set time, please be considerate of those who haven't watched the event yet."
] ]
},
{
"title": "Moderators Discretion",
"description": [
"Don't argue with a mod's decision. A moderator's choice is final. If you have an issue with a member of the mod team DM me (Vylpes#0001)."
]
},
{
"title": "Supporters",
"description": [
"If you are a Twitch Subscriber or a Patreon Member and have linked your profiles to your discord account you will get exclusive access to the Vylpes Plus channels, including early access to videos!"
]
},
{
"title": "Self-Assignable Roles",
"description": [
"If you want to assign yourself roles, go to #bot-stuff and type v!role <role>. The current roles you can get are:",
"Notify: Get pinged when a new stream or video releases.",
"VotePings: Get pinged when I start a new poll",
"ProjectUpdates: Get pinged when I update my projects as well as new for them"
]
},
{
"title": "VylBot",
"description": [
"This server uses a bot made by me, VylBot, to help moderate the server.",
"For more information on it, see the GitHub repositories:",
"https://github.com/Vylpes/vylbot-core",
"https://github.com/Vylpes/vylbot-app"
]
},
{
"title": "Links",
"description": [
"YouTube: https://www.youtube.com/channel/UCwPlzKwCmP5Q9bCX3fHk2BA",
"Patreon: https://www.patreon.com/vylpes",
"Twitch: https://www.twitch.tv/vylpes_",
"Twitter: https://twitter.com/vylpes",
"Blog: https://vylpes.xyz"
],
"footer": "Last updated 01/02/2022"
} }
] ]

View file

@ -6,7 +6,7 @@
"If moderators know you're under 13, we will have to ban you!*", "If moderators know you're under 13, we will have to ban you!*",
"", "",
"You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.", "You need to input a code in *#entry* which is somewhere in this message before you can start chatting, so read the server rules and info below.",
"If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message Vylpes#5725." "If you still don't see the other channels after writing this code, message a moderator. For any issues with this bot, message <@147392775707426816>."
] ]
}, },
{ {

View file

@ -0,0 +1,11 @@
CREATE TABLE `audit` (
`Id` varchar(255) NOT NULL,
`WhenCreated` datetime NOT NULL,
`WhenUpdated` datetime NOT NULL,
`AuditId` varchar(255) NOT NULL,
`UserId` varchar(255) NOT NULL,
`AuditType` int NOT NULL,
`Reason` varchar(255) NOT NULL,
`ModeratorId` varchar(255) NOT NULL,
`ServerId` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

View file

@ -0,0 +1,5 @@
CREATE TABLE `ignored_channel` (
`Id` varchar(255) NOT NULL,
`WhenCreated` datetime NOT NULL,
`WhenUpdated` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

View file

@ -0,0 +1,10 @@
CREATE TABLE `lobby` (
`Id` varchar(255) NOT NULL,
`WhenCreated` datetime NOT NULL,
`WhenUpdated` datetime NOT NULL,
`ChannelId` varchar(255) NOT NULL,
`RoleId` varchar(255) NOT NULL,
`Cooldown` int NOT NULL,
`LastUsed` datetime NOT NULL,
`Name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

View file

@ -0,0 +1,7 @@
CREATE TABLE `role` (
`Id` varchar(255) NOT NULL,
`WhenCreated` datetime NOT NULL,
`WhenUpdated` datetime NOT NULL,
`RoleId` varchar(255) NOT NULL,
`serverId` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

View file

@ -0,0 +1,5 @@
CREATE TABLE `server` (
`Id` varchar(255) NOT NULL,
`WhenCreated` datetime NOT NULL,
`WhenUpdated` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

View file

@ -0,0 +1,8 @@
CREATE TABLE `setting` (
`Id` varchar(255) NOT NULL,
`WhenCreated` datetime NOT NULL,
`WhenUpdated` datetime NOT NULL,
`Key` varchar(255) NOT NULL,
`Value` varchar(255) NOT NULL,
`serverId` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

View file

@ -0,0 +1,2 @@
ALTER TABLE `audit`
ADD PRIMARY KEY (`Id`);

View file

@ -0,0 +1,2 @@
ALTER TABLE `ignored_channel`
ADD PRIMARY KEY (`Id`);

View file

@ -0,0 +1,2 @@
ALTER TABLE `lobby`
ADD PRIMARY KEY (`Id`);

View file

@ -0,0 +1,3 @@
ALTER TABLE `role`
ADD PRIMARY KEY (`Id`),
ADD KEY `FK_d9e438d88cfb64f7f8e1ae593c3` (`serverId`);

View file

@ -0,0 +1,2 @@
ALTER TABLE `server`
ADD PRIMARY KEY (`Id`);

View file

@ -0,0 +1,3 @@
ALTER TABLE `setting`
ADD PRIMARY KEY (`Id`),
ADD KEY `FK_a3623ec541bdb12fa0f58bdfde7` (`serverId`);

View file

@ -0,0 +1,2 @@
ALTER TABLE `role`
ADD CONSTRAINT `FK_d9e438d88cfb64f7f8e1ae593c3` FOREIGN KEY (`serverId`) REFERENCES `server` (`Id`);

View file

@ -0,0 +1,2 @@
ALTER TABLE `setting`
ADD CONSTRAINT `FK_a3623ec541bdb12fa0f58bdfde7` FOREIGN KEY (`serverId`) REFERENCES `server` (`Id`);

31
docker-compose.prod.yml Normal file
View file

@ -0,0 +1,31 @@
version: "3.9"
volumes:
prod_database_data:
services:
# discord:
# build: .
database:
image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_DATABASE=vylbot
- MYSQL_USER=prod
- MYSQL_PASSWORD=prod
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=0.0.0.0
ports:
- "3121:3306"
volumes:
- prod_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3122:80"
environment:
- PMA_ARBITRARY=1

31
docker-compose.stage.yml Normal file
View file

@ -0,0 +1,31 @@
version: "3.9"
volumes:
stage_database_data:
services:
# discord:
# build: .
database:
image: mysql/mysql-server
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
- MYSQL_DATABASE=vylbot
- MYSQL_USER=stage
- MYSQL_PASSWORD=stage
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=0.0.0.0
ports:
- "3111:3306"
volumes:
- stage_database_data:/var/lib/mysql
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- "3112:80"
environment:
- PMA_ARBITRARY=1

View file

@ -1,4 +1,8 @@
version: "3.9" version: "3.9"
volumes:
dev_database_data:
services: services:
# discord: # discord:
# build: . # build: .
@ -12,13 +16,16 @@ services:
- MYSQL_USER=dev - MYSQL_USER=dev
- MYSQL_PASSWORD=dev - MYSQL_PASSWORD=dev
- MYSQL_ROOT_PASSWORD=root - MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_HOST=0.0.0.0
ports: ports:
- 3306:3306 - "3101:3306"
volumes:
- dev_database_data:/var/lib/mysql
phpmyadmin: phpmyadmin:
image: phpmyadmin image: phpmyadmin
restart: always restart: always
ports: ports:
- 8080:80 - "3102:80"
environment: environment:
- PMA_ARBITRARY=1 - PMA_ARBITRARY=1

View file

@ -1,41 +1,46 @@
{ {
"name": "vylbot-app", "name": "vylbot-app",
"version": "3.0.7", "version": "3.1.0",
"description": "A discord bot made for Vylpes' Den", "description": "A discord bot made for Vylpes' Den",
"main": "./dist/vylbot", "main": "./dist/vylbot",
"typings": "./dist", "typings": "./dist",
"scripts": { "scripts": {
"clean": "rm -rf node_modules/ dist/",
"build": "tsc", "build": "tsc",
"start": "node ./dist/vylbot", "start": "node ./dist/vylbot",
"test": "jest", "test": "jest",
"db:up": "typeorm migration:run", "db:up": "typeorm migration:run -d dist/database/dataSources/appDataSource.js",
"db:down": "typeorm migration:revert" "db:down": "typeorm migration:revert -d dist/database/dataSources/appDataSource.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/Vylpes/vylbot-app.git" "url": "https://github.com/Vylpes/vylbot-app"
}, },
"author": "Vylpes", "author": "Vylpes",
"license": "MIT", "license": "MIT",
"bugs": "https://github.com/Vylpes/vylbot-app/issues", "bugs": {
"url": "https://github.com/Vylpes/vylbot-app/issues",
"email": "helpdesk@vylpes.com"
},
"homepage": "https://github.com/Vylpes/vylbot-app", "homepage": "https://github.com/Vylpes/vylbot-app",
"funding": "https://ko-fi.com/vylpes",
"dependencies": { "dependencies": {
"@types/jest": "^27.0.3", "@discordjs/rest": "^1.1.0",
"@types/uuid": "^8.3.4", "@types/jest": "^29.0.0",
"discord.js": "^13.6.0", "@types/uuid": "^9.0.0",
"dotenv": "^10.0.0", "discord.js": "^14.3.0",
"emoji-regex": "^9.2.0", "dotenv": "^16.0.0",
"jest": "^27.4.5", "emoji-regex": "^10.0.0",
"jest-mock-extended": "^2.0.4", "jest": "^29.0.0",
"minimatch": "3.1.2", "jest-mock-extended": "^3.0.0",
"minimatch": "9.0.2",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"random-bunny": "^2.0.0", "random-bunny": "^2.0.5",
"ts-jest": "^27.1.2", "ts-jest": "^29.0.0",
"typeorm": "0.3.14", "typeorm": "0.3.17"
"uuid": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.11.10", "@types/node": "^20.0.0",
"typescript": "^4.5.2" "typescript": "^5.0.0"
} }
} }

23
scripts/deploy_prod.sh Normal file
View file

@ -0,0 +1,23 @@
#! /bin/bash
export PATH="$HOME/.yarn/bin:$PATH"
export PATH="$HOME/.nodeuse/bin:$PATH"
export BOT_TOKEN=$(cat $HOME/scripts/vylbot/prod_key.txt)
cd ~/apps/vylbot/vylbot_prod \
&& git checkout main \
&& git fetch \
&& git pull \
&& docker compose --file docker-compose.prod.yml down \
&& (pm2 stop vylbot_prod || true) \
&& (pm2 delete vylbot_prod || true) \
&& cp .prod.env .env \
&& yarn clean \
&& yarn install --frozen-lockfile \
&& yarn build \
&& docker compose --file docker-compose.prod.yml up -d \
&& echo "Sleeping for 10 seconds to let database load..." \
&& sleep 10 \
&& yarn run db:up \
&& NODE_ENV=production pm2 start --name vylbot_prod dist/vylbot.js

23
scripts/deploy_stage.sh Normal file
View file

@ -0,0 +1,23 @@
#! /bin/bash
export PATH="$HOME/.yarn/bin:$PATH"
export PATH="$HOME/.nodeuse/bin:$PATH"
export BOT_TOKEN=$(cat $HOME/scripts/vylbot/stage_key.txt)
cd ~/apps/vylbot/vylbot_stage \
&& git checkout develop \
&& git fetch \
&& git pull \
&& docker compose --file docker-compose.stage.yml down \
&& (pm2 stop vylbot_stage || true) \
&& (pm2 delete vylbot_stage || true) \
&& cp .stage.env .env \
&& yarn clean \
&& yarn install --frozen-lockfile \
&& yarn build \
&& docker compose --file docker-compose.stage.yml up -d \
&& echo "Sleeping for 10 seconds to let database load..." \
&& sleep 10 \
&& yarn run db:up \
&& NODE_ENV=production pm2 start --name vylbot_stage dist/vylbot.js

View file

@ -1,19 +1,19 @@
import { Client } from "discord.js"; import { Client } from "discord.js";
import * as dotenv from "dotenv"; import * as dotenv from "dotenv";
import { createConnection } from "typeorm"; import { createConnection } from "typeorm";
import DefaultValues from "../constants/DefaultValues"; import { EventType } from "../constants/EventType";
import ICommandItem from "../contracts/ICommandItem"; import ICommandItem from "../contracts/ICommandItem";
import IEventItem from "../contracts/IEventItem"; import IEventItem from "../contracts/IEventItem";
import { Command } from "../type/command"; import { Command } from "../type/command";
import { Event } from "../type/event";
import { Events } from "./events"; import { Events } from "./events";
import { Util } from "./util"; import { Util } from "./util";
import AppDataSource from "../database/dataSources/appDataSource";
export class CoreClient extends Client { export class CoreClient extends Client {
private static _commandItems: ICommandItem[]; private static _commandItems: ICommandItem[];
private static _eventItems: IEventItem[]; private static _eventItems: IEventItem[];
private _events: Events; private _events: Events;
private _util: Util; private _util: Util;
@ -25,12 +25,10 @@ export class CoreClient extends Client {
return this._eventItems; return this._eventItems;
} }
constructor(intents: number[], devmode: boolean = false) { constructor(intents: number[]) {
super({ intents: intents }); super({ intents: intents });
dotenv.config(); dotenv.config();
DefaultValues.useDevPrefix = devmode;
CoreClient._commandItems = []; CoreClient._commandItems = [];
CoreClient._eventItems = []; CoreClient._eventItems = [];
@ -44,19 +42,17 @@ export class CoreClient extends Client {
return; return;
} }
await createConnection().catch(e => { await AppDataSource.initialize()
console.error(e); .then(() => console.log("Data Source Initialized"))
return; .catch((err) => console.error("Error Initialising Data Source", err));
});
super.on("messageCreate", (message) => { super.on("interactionCreate", this._events.onInteractionCreate);
this._events.onMessageCreate(message, CoreClient._commandItems)
});
super.on("ready", this._events.onReady); super.on("ready", this._events.onReady);
super.login(process.env.BOT_TOKEN); await super.login(process.env.BOT_TOKEN);
this._util.loadEvents(this, CoreClient._eventItems); this._util.loadEvents(this, CoreClient._eventItems);
this._util.loadSlashCommands(this);
} }
public static RegisterCommand(name: string, command: Command, serverId?: string) { public static RegisterCommand(name: string, command: Command, serverId?: string) {
@ -69,9 +65,10 @@ export class CoreClient extends Client {
CoreClient._commandItems.push(item); CoreClient._commandItems.push(item);
} }
public static RegisterEvent(event: Event) { public static RegisterEvent(eventType: EventType, func: Function) {
const item: IEventItem = { const item: IEventItem = {
Event: event, EventType: eventType,
ExecutionFunction: func,
}; };
CoreClient._eventItems.push(item); CoreClient._eventItems.push(item);

View file

@ -1,33 +1,40 @@
import { Message } from "discord.js"; import { Interaction } from "discord.js";
import ICommandItem from "../contracts/ICommandItem"; import ICommandItem from "../contracts/ICommandItem";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import { Util } from "./util"; import { CoreClient } from "./client";
export class Events { export class Events {
private _util: Util; public async onInteractionCreate(interaction: Interaction) {
if (!interaction.isChatInputCommand()) return;
if (!interaction.guildId) return;
constructor() { const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
this._util = new Util(); const disabledCommands = disabledCommandsString?.split(",");
}
// Emit when a message is sent const disabledCommandsMessage = await SettingsHelper.GetSetting("commands.disabled.message", interaction.guildId);
// Used to check for commands
public async onMessageCreate(message: Message, commands: ICommandItem[]) {
if (!message.guild) return;
if (message.author.bot) return;
const prefix = await SettingsHelper.GetSetting("bot.prefix", message.guild.id); if (disabledCommands?.find(x => x == interaction.commandName)) {
await interaction.reply(disabledCommandsMessage || "This command is disabled.");
if (!prefix) return; return;
if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) {
const args = message.content.substring(prefix.length).split(" ");
const name = args.shift();
if (!name) return;
await this._util.loadCommand(name, args, message, commands);
} }
const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId);
const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId);
let itemToUse: ICommandItem;
if (!itemForServer) {
if (!item) {
await interaction.reply('Command not found');
return;
}
itemToUse = item;
} else {
itemToUse = itemForServer;
}
itemToUse.Command.execute(interaction);
} }
// Emit when bot is logged in and ready to use // Emit when bot is logged in and ready to use

View file

@ -1,101 +1,95 @@
// Required Components import { Client, REST, Routes, SlashCommandBuilder } from "discord.js";
import { Client, Message } from "discord.js"; import { EventType } from "../constants/EventType";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandItem from "../contracts/ICommandItem";
import IEventItem from "../contracts/IEventItem"; import IEventItem from "../contracts/IEventItem";
import SettingsHelper from "../helpers/SettingsHelper"; import { CoreClient } from "./client";
import StringTools from "../helpers/StringTools";
import { CommandResponse } from "../constants/CommandResponse";
import ErrorMessages from "../constants/ErrorMessages";
// Util Class
export class Util { export class Util {
public async loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]) { public loadSlashCommands(client: Client) {
if (!message.member) return; const registeredCommands = CoreClient.commandItems;
if (!message.guild) return;
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", message.guild?.id); const globalCommands = registeredCommands.filter(x => !x.ServerId);
const disabledCommands = disabledCommandsString?.split(","); const guildCommands = registeredCommands.filter(x => x.ServerId);
if (disabledCommands?.find(x => x == name)) { const globalCommandData: SlashCommandBuilder[] = globalCommands
message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); .filter(x => x.Command.CommandBuilder)
return; .flatMap(x => x.Command.CommandBuilder);
}
const item = commands.find(x => x.Name == name && !x.ServerId); const guildIds: string[] = [];
const itemForServer = commands.find(x => x.Name == name && x.ServerId == message.guild?.id);
let itemToUse: ICommandItem; for (let command of guildCommands) {
if (!guildIds.find(x => x == command.ServerId)) {
if (!itemForServer) { guildIds.push(command.ServerId!);
if (!item) {
message.reply('Command not found');
return;
} }
itemToUse = item;
} else {
itemToUse = itemForServer;
} }
const requiredRoles = itemToUse.Command.Roles; const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN!);
if (message.author.id != process.env.BOT_OWNERID && message.author.id != message.guild.ownerId) { rest.put(
for (const i in requiredRoles) { Routes.applicationCommands(process.env.BOT_CLIENTID!),
if (message.guild) { {
const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, message.guild?.id); body: globalCommandData
}
if (!setting) { );
message.reply("Unable to verify if you have this role, please contact your bot administrator");
return; for (let guild of guildIds) {
} const guildCommandData = guildCommands.filter(x => x.ServerId == guild)
.filter(x => x.Command.CommandBuilder)
if (!message.member.roles.cache.find(role => role.name == setting)) { .flatMap(x => x.Command.CommandBuilder);
message.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`);
return; if (!client.guilds.cache.has(guild)) continue;
}
rest.put(
Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild),
{
body: guildCommandData
} }
} )
} }
const context: ICommandContext = {
name: name,
args: args,
message: message
};
const precheckResponse = itemToUse.Command.precheck(context);
const precheckAsyncResponse = await itemToUse.Command.precheckAsync(context);
if (precheckResponse != CommandResponse.Ok) {
message.reply(ErrorMessages.GetErrorMessage(precheckResponse));
return;
}
if (precheckAsyncResponse != CommandResponse.Ok) {
message.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse));
return;
}
itemToUse.Command.execute(context);
} }
// Load the events // Load the events
loadEvents(client: Client, events: IEventItem[]) { loadEvents(client: Client, events: IEventItem[]) {
events.forEach((e) => { events.forEach((e) => {
client.on('channelCreate', e.Event.channelCreate); switch(e.EventType) {
client.on('channelDelete', e.Event.channelDelete); case EventType.ChannelCreate:
client.on('channelUpdate', e.Event.channelUpdate); client.on('channelCreate', (channel) => e.ExecutionFunction(channel));
client.on('guildBanAdd', e.Event.guildBanAdd); break;
client.on('guildBanRemove', e.Event.guildBanRemove); case EventType.ChannelDelete:
client.on('guildCreate', e.Event.guildCreate); client.on('channelDelete', (channel) => e.ExecutionFunction(channel));
client.on('guildMemberAdd', e.Event.guildMemberAdd); break;
client.on('guildMemberRemove', e.Event.guildMemberRemove); case EventType.ChannelUpdate:
client.on('guildMemberUpdate', e.Event.guildMemberUpdate); client.on('channelUpdate', (channel) => e.ExecutionFunction(channel));
client.on('messageCreate', e.Event.messageCreate); break;
client.on('messageDelete', e.Event.messageDelete); case EventType.GuildBanAdd:
client.on('messageUpdate', e.Event.messageUpdate); client.on('guildBanAdd', (ban) => e.ExecutionFunction(ban));
client.on('ready', e.Event.ready); break;
case EventType.GuildBanRemove:
client.on('guildBanRemove', (ban) => e.ExecutionFunction(ban));
break;
case EventType.GuildCreate:
client.on('guildCreate', (guild) => e.ExecutionFunction(guild));
break;
case EventType.GuildMemberAdd:
client.on('guildMemberAdd', (member) => e.ExecutionFunction(member));
break;
case EventType.GuildMemberRemove:
client.on('guildMemberRemove', (member) => e.ExecutionFunction(member));
break;
case EventType.GuildMemberUpdate:
client.on('guildMemberUpdate', (oldMember, newMember) => e.ExecutionFunction(oldMember, newMember));
break;
case EventType.MessageCreate:
client.on('messageCreate', (message) => e.ExecutionFunction(message));
break;
case EventType.MessageDelete:
client.on('messageDelete', (message) => e.ExecutionFunction(message));
break;
case EventType.MessageUpdate:
client.on('messageUpdate', (oldMessage, newMessage) => e.ExecutionFunction(oldMessage, newMessage));
break;
default:
console.error('Event not implemented.');
}
}); });
} }
} }

View file

@ -0,0 +1,58 @@
import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { Command } from "../../../type/command";
import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby";
export default class AddRole extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('addlobby')
.setDescription('Add lobby channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addChannelOption(option =>
option
.setName('channel')
.setDescription('The channel')
.setRequired(true))
.addRoleOption(option =>
option
.setName('role')
.setDescription('The role to ping on request')
.setRequired(true))
.addNumberOption(option =>
option
.setName('cooldown')
.setDescription('The cooldown in minutes')
.setRequired(true))
.addStringOption(option =>
option
.setName('name')
.setDescription('The game name')
.setRequired(true));
}
public override async execute(interaction: CommandInteraction) {
const channel = interaction.options.get('channel');
const role = interaction.options.get('role');
const cooldown = interaction.options.get('cooldown');
const gameName = interaction.options.get('name');
if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) {
await interaction.reply('Fields are required.');
return;
}
const lobby = await eLobby.FetchOneByChannelId(channel.channel.id);
if (lobby) {
await interaction.reply('This channel has already been setup.');
return;
}
const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string);
await entity.Save(eLobby, entity);
await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown.value} minutes \` and will ping \`${role.name}\` on use`);
}
}

View file

@ -0,0 +1,48 @@
import { CacheType, CommandInteraction, EmbedBuilder, GuildBasedChannel, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { Command } from "../../../type/command";
import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby";
import EmbedColours from "../../../constants/EmbedColours";
export default class ListLobby extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('listlobby')
.setDescription('Lists all channels set up as lobbies')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers);
}
public override async execute(interaction: CommandInteraction<CacheType>) {
if (!interaction.guild) {
await interaction.reply('Guild not found.');
return;
}
const channels: eLobby[] = [];
for (let channel of interaction.guild.channels.cache.map(x => x)) {
const lobby = await eLobby.FetchOneByChannelId(channel.id);
if (lobby) {
channels.push(lobby);
}
}
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Lobbies")
.setDescription(`Channels: ${channels.length}`);
for (let lobby of channels) {
embed.addFields([
{
name: `# ${lobby.Name}`,
value: `Last Used: ${lobby.LastUsed}`
}
]);
}
await interaction.reply({ embeds: [ embed ]});
}
}

View file

@ -0,0 +1,41 @@
import { CommandInteraction, SlashCommandBuilder } from "discord.js";
import { Command } from "../../../type/command";
import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby";
export default class Lobby extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('lobby')
.setDescription('Attempt to organise a lobby');
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.channelId) return;
const lobby = await eLobby.FetchOneByChannelId(interaction.channelId);
if (!lobby) {
await interaction.reply('This channel is disabled from using the lobby command.');
return;
}
const timeNow = Date.now();
const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms
const timeAgo = timeNow - timeLength;
// If it was less than x minutes ago
if (lobby.LastUsed.getTime() > timeAgo) {
const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60);
await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`);
return;
}
lobby.MarkAsUsed();
await lobby.Save(eLobby, lobby);
await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`);
}
}

View file

@ -0,0 +1,40 @@
import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { Command } from "../../../type/command";
import { default as eLobby } from "../../../database/entities/501231711271780357/Lobby";
import BaseEntity from "../../../contracts/BaseEntity";
export default class RemoveLobby extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('removelobby')
.setDescription('Remove a lobby channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addChannelOption(option =>
option
.setName('channel')
.setDescription('The channel')
.setRequired(true));
}
public override async execute(interaction: CommandInteraction) {
const channel = interaction.options.get('channel');
if (!channel || !channel.channel) {
await interaction.reply('Channel is required.');
return;
}
const entity = await eLobby.FetchOneByChannelId(channel.channel.id);
if (!entity) {
await interaction.reply('Channel not found.');
return;
}
await BaseEntity.Remove<eLobby>(eLobby, entity);
await interaction.reply(`Removed <#${channel.channel.id}> from the list of lobby channels`);
}
}

View file

@ -1,5 +1,5 @@
import { ICommandContext } from "../../contracts/ICommandContext"; import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import PublicEmbed from "../../helpers/embeds/PublicEmbed"; import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
import { Command } from "../../type/command"; import { Command } from "../../type/command";
@ -7,19 +7,23 @@ export default class Entry extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName('entry')
"moderator" .setDescription('Sends the entry embed')
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers);
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) return; if (!interaction.guildId) return;
if (!interaction.channel) return;
const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", context.message.guild.id) || "rules"; const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", interaction.guildId) || "rules";
const embedInfo = new PublicEmbed(context, "", `Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Welcome")
.setDescription(`Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`);
await embedInfo.SendToCurrentChannel(); await interaction.channel.send({ embeds: [ embed ]});
} }
} }

View file

@ -1,159 +0,0 @@
import { TextChannel } from "discord.js";
import { ICommandContext } from "../../contracts/ICommandContext";
import { Command } from "../../type/command";
import { default as eLobby } from "../../entity/501231711271780357/Lobby";
import SettingsHelper from "../../helpers/SettingsHelper";
import PublicEmbed from "../../helpers/embeds/PublicEmbed";
import { readFileSync } from "fs";
import ErrorEmbed from "../../helpers/embeds/ErrorEmbed";
import BaseEntity from "../../contracts/BaseEntity";
export default class Lobby extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) return;
switch (context.args[0]) {
case "config":
await this.UseConfig(context);
break;
default:
await this.UseDefault(context);
}
}
// =======
// Default
// =======
private async UseDefault(context: ICommandContext) {
const channel = context.message.channel as TextChannel;
const channelId = channel.id;
const lobby = await eLobby.FetchOneByChannelId(channelId);
if (!lobby) {
this.SendDisabled(context);
return;
}
const timeNow = Date.now();
const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms
const timeAgo = timeNow - timeLength;
// If it was less than x minutes ago
if (lobby.LastUsed.getTime() > timeAgo) {
this.SendOnCooldown(context, timeLength, new Date(timeNow), lobby.LastUsed);
return;
}
await this.RequestLobby(context, lobby);
}
private async RequestLobby(context: ICommandContext, lobby: eLobby) {
lobby.MarkAsUsed();
await lobby.Save(eLobby, lobby);
context.message.channel.send(`${context.message.author} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`);
}
private SendOnCooldown(context: ICommandContext, timeLength: number, timeNow: Date, timeUsed: Date) {
const timeLeft = Math.ceil((timeLength - (timeNow.getTime() - timeUsed.getTime())) / 1000 / 60);
context.message.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`);
}
private SendDisabled(context: ICommandContext) {
context.message.reply("This channel hasn't been setup for lobbies.");
}
// ======
// Config
// ======
private async UseConfig(context: ICommandContext) {
const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id);
if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) {
const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command");
await errorEmbed.SendToCurrentChannel();
return;
}
switch (context.args[1]) {
case "add":
await this.AddLobbyConfig(context);
break;
case "remove":
await this.RemoveLobbyConfig(context);
break;
case "help":
default:
await this.SendConfigHelp(context);
}
}
private async SendConfigHelp(context: ICommandContext) {
const helpText = readFileSync(`${process.cwd()}/data/usage/lobby.txt`).toString();
const embed = new PublicEmbed(context, "Configure Lobby Command", helpText);
await embed.SendToCurrentChannel();
}
private async AddLobbyConfig(context: ICommandContext) {
const channel = context.message.guild!.channels.cache.find(x => x.id == context.args[2]);
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[3]);
const cooldown = Number(context.args[4]) || 30;
const gameName = context.args.splice(5).join(" ");
if (!channel) {
const errorEmbed = new ErrorEmbed(context, "The channel id you provided is invalid or channel does not exist.");
errorEmbed.SendToCurrentChannel();
return;
}
if (!role) {
const errorEmbed = new ErrorEmbed(context, "The role id you provided is invalid or role does not exist.");
errorEmbed.SendToCurrentChannel();
return;
}
const lobby = await eLobby.FetchOneByChannelId(channel.id);
if (lobby) {
const errorEmbed = new ErrorEmbed(context, "This channel has already been setup.");
errorEmbed.SendToCurrentChannel();
return;
}
const entity = new eLobby(channel.id, role.id, cooldown, gameName);
await entity.Save(eLobby, entity);
const embed = new PublicEmbed(context, "", `Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes\` and will ping \`${role.name}\` on use`);
await embed.SendToCurrentChannel();
}
private async RemoveLobbyConfig(context: ICommandContext) {
const entity = await eLobby.FetchOneByChannelId(context.args[2]);
if (!entity) {
const errorEmbed = new ErrorEmbed(context, "The channel id you provided has not been setup as a lobby, unable to remove.");
await errorEmbed.SendToCurrentChannel();
return;
}
await BaseEntity.Remove<eLobby>(eLobby, entity);
const embed = new PublicEmbed(context, "", `Removed <#${context.args[2]}> from the list of lobby channels`);
await embed.SendToCurrentChannel();
}
}

View file

@ -0,0 +1,54 @@
import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { Command } from "../../type/command";
import { default as eRole } from "../../database/entities/Role";
import Server from "../../database/entities/Server";
export default class ConfigRole extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('configrole')
.setDescription('Toggle your roles')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles)
.addRoleOption(option =>
option
.setName('role')
.setDescription('The role name')
.setRequired(true));
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.guildId || !interaction.guild) return;
if (!interaction.member) return;
const role = interaction.options.get('role');
if (!role || !role.role) {
await interaction.reply('Fields are required.');
return;
}
const existingRole = await eRole.FetchOneByRoleId(role.role.id);
if (existingRole) {
await eRole.Remove(eRole, existingRole);
await interaction.reply('Removed role from configuration.');
} else {
const server = await Server.FetchOneById(Server, interaction.guildId);
if (!server) {
await interaction.reply('This server has not been setup.');
return;
}
const newRole = new eRole(role.role.id);
newRole.SetServer(server);
await newRole.Save(eRole, newRole);
await interaction.reply('Added role to configuration.');
}
}
}

109
src/commands/Role/role.ts Normal file
View file

@ -0,0 +1,109 @@
import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js";
import { Command } from "../../type/command";
import { default as eRole } from "../../database/entities/Role";
import EmbedColours from "../../constants/EmbedColours";
export default class Role extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('role')
.setDescription('Toggle your roles')
.addSubcommand(subcommand =>
subcommand
.setName('toggle')
.setDescription('Toggle your role')
.addRoleOption(option =>
option
.setName('role')
.setDescription('The role name')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('list')
.setDescription('List togglable roles'));
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.isChatInputCommand()) return;
switch (interaction.options.getSubcommand()) {
case 'toggle':
await this.ToggleRole(interaction);
break;
case 'list':
await this.SendRolesList(interaction);
break;
default:
await interaction.reply('Subcommand not found.');
}
}
private async SendRolesList(interaction: CommandInteraction) {
const roles = await this.GetRolesList(interaction);
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Roles")
.setDescription(`Roles: ${roles.length}\n\n${roles.join("\n")}`);
await interaction.reply({ embeds: [ embed ]});
}
private async ToggleRole(interaction: CommandInteraction) {
if (!interaction.guild) return;
if (!interaction.member) return;
const roles = await this.GetRolesList(interaction);
const requestedRole = interaction.options.get('role');
if (!requestedRole || !requestedRole.role) {
await interaction.reply('Fields are required.');
return;
}
if (!roles.includes(requestedRole.role.name)) {
await interaction.reply('This role isn\'t marked as assignable.');
return;
}
const roleManager = interaction.member.roles as GuildMemberRoleManager;
const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name);
const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id);
if (!assignRole) return;
if (!assignRole.editable) {
await interaction.reply('Insufficient permissions. Please contact a moderator.');
return;
}
if (!userRole) {
await roleManager.add(assignRole);
await interaction.reply(`Gave role: \`${assignRole.name}\``);
} else {
await roleManager.remove(assignRole);
await interaction.reply(`Removed role: \`${assignRole.name}\``);
}
}
private async GetRolesList(interaction: CommandInteraction): Promise<string[]> {
if (!interaction.guildId || !interaction.guild) return [];
const rolesArray = await eRole.FetchAllByServerId(interaction.guildId);
const roles: string[] = [];
for (let i = 0; i < rolesArray.length; i++) {
const serverRole = interaction.guild.roles.cache.find(x => x.id == rolesArray[i].RoleId);
if (serverRole) {
roles.push(serverRole.name);
}
}
return roles;
}
}

View file

@ -1,19 +1,56 @@
import { ICommandContext } from "../contracts/ICommandContext"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
import PublicEmbed from "../helpers/embeds/PublicEmbed"; import EmbedColours from "../constants/EmbedColours";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class About extends Command { export default class About extends Command {
constructor() { constructor() {
super(); super();
super.Category = "General";
super.CommandBuilder = new SlashCommandBuilder()
.setName('about')
.setDescription('About VylBot');
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
const embed = new PublicEmbed(context, "About", "") const fundingLink = process.env.ABOUT_FUNDING;
.addField("Version", process.env.BOT_VER!) const repoLink = process.env.ABOUT_REPO;
.addField("Author", process.env.BOT_AUTHOR!)
.addField("Date", process.env.BOT_DATE!); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("About")
.setDescription("Discord Bot made by Vylpes");
embed.addFields([
{
name: "Version",
value: process.env.BOT_VER!,
inline: true,
},
{
name: "Author",
value: process.env.BOT_AUTHOR!,
inline: true,
},
]);
const row = new ActionRowBuilder<ButtonBuilder>();
if (repoLink) {
row.addComponents(
new ButtonBuilder()
.setURL(repoLink)
.setLabel("Repo")
.setStyle(ButtonStyle.Link));
}
if (fundingLink) {
row.addComponents(
new ButtonBuilder()
.setURL(fundingLink)
.setLabel("Funding")
.setStyle(ButtonStyle.Link));
}
await interaction.reply({ embeds: [ embed ], components: row.components.length > 0 ? [ row ] : [] });
} }
} }

212
src/commands/audits.ts Normal file
View file

@ -0,0 +1,212 @@
import Audit from "../database/entities/Audit";
import AuditTools from "../helpers/AuditTools";
import { Command } from "../type/command";
import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { AuditType } from "../constants/AuditType";
import EmbedColours from "../constants/EmbedColours";
export default class Audits extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName("audits")
.setDescription("View audits of a particular user in the server")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addSubcommand(subcommand =>
subcommand
.setName('user')
.setDescription('View all audits done against a user')
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('view')
.setDescription('View a particular audit')
.addStringOption(option =>
option
.setName('auditid')
.setDescription('The audit id in caps')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('clear')
.setDescription('Clears an audit from a user')
.addStringOption(option =>
option
.setName('auditid')
.setDescription('The audit id in caps')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('add')
.setDescription('Manually add an audit')
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('type')
.setDescription('The type of audit')
.setRequired(true)
.addChoices(
{ name: 'General', value: AuditType.General.toString() },
{ name: 'Warn', value: AuditType.Warn.toString() },
{ name: 'Mute', value: AuditType.Mute.toString() },
{ name: 'Kick', value: AuditType.Kick.toString() },
{ name: 'Ban', value: AuditType.Ban.toString() },
)
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason')));
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.isChatInputCommand()) return;
switch (interaction.options.getSubcommand()) {
case "user":
await this.SendAuditForUser(interaction);
break;
case "view":
await this.SendAudit(interaction);
break;
case "clear":
await this.ClearAudit(interaction);
break;
case "add":
await this.AddAudit(interaction);
break;
default:
await interaction.reply("Subcommand doesn't exist.");
}
}
private async SendAuditForUser(interaction: CommandInteraction) {
if (!interaction.guildId) return;
const user = interaction.options.getUser('target');
if (!user) {
await interaction.reply("User not found.");
return;
}
const audits = await Audit.FetchAuditsByUserId(user.id, interaction.guildId);
if (!audits || audits.length == 0) {
await interaction.reply("There are no audits for this user.");
return;
}
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Audits")
.setDescription(`Audits: ${audits.length}`);
for (let audit of audits) {
embed.addFields([
{
name: `${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`,
value: audit.WhenCreated.toString(),
}
]);
}
await interaction.reply({ embeds: [ embed ]});
}
private async SendAudit(interaction: CommandInteraction) {
if (!interaction.guildId) return;
const auditId = interaction.options.get('auditid');
if (!auditId || !auditId.value) {
await interaction.reply("AuditId not found.");
return;
}
const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId);
if (!audit) {
await interaction.reply("Audit not found.");
return;
}
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Audit")
.setDescription(audit.AuditId.toUpperCase())
.addFields([
{
name: "Reason",
value: audit.Reason || "*none*",
inline: true,
},
{
name: "Type",
value: AuditTools.TypeToFriendlyText(audit.AuditType),
inline: true,
},
{
name: "Moderator",
value: `<@${audit.ModeratorId}>`,
inline: true,
},
]);
await interaction.reply({ embeds: [ embed ]});
}
private async ClearAudit(interaction: CommandInteraction) {
if (!interaction.guildId) return;
const auditId = interaction.options.get('auditid');
if (!auditId || !auditId.value) {
await interaction.reply("AuditId not found.");
return;
}
const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId);
if (!audit) {
await interaction.reply("Audit not found.");
return;
}
await Audit.Remove(Audit, audit);
await interaction.reply("Audit cleared.");
}
private async AddAudit(interaction: CommandInteraction) {
if (!interaction.guildId) return;
const user = interaction.options.getUser('target');
const auditType = interaction.options.get('type');
const reasonInput = interaction.options.get('reason');
if (!user || !auditType || !auditType.value) {
await interaction.reply("Invalid input.");
return;
}
const type = auditType.value as AuditType;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "";
const audit = new Audit(user.id, type, reason, interaction.user.id, interaction.guildId);
await audit.Save(Audit, audit);
await interaction.reply(`Created new audit with ID \`${audit.AuditId}\``);
}
}

View file

@ -1,83 +1,79 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import ErrorMessages from "../constants/ErrorMessages";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../database/entities/Audit";
import ICommandReturnContext from "../contracts/ICommandReturnContext"; import { AuditType } from "../constants/AuditType";
import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import EmbedColours from "../constants/EmbedColours";
import SettingsHelper from "../helpers/SettingsHelper";
export default class Ban extends Command { export default class Ban extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("ban")
"moderator" .setDescription("Ban a member from the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.isChatInputCommand()) return;
if (!interaction.guildId) return;
if (!interaction.guild) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
return { if (!targetUser || !targetUser.user || !targetUser.member) {
commandContext: context, await interaction.reply("User not found.");
embeds: [embed], return;
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const member = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Banned")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.addFields([
{
name: "Moderator",
value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
return { if (!member.bannable) {
commandContext: context, await interaction.reply('Insufficient permissions. Please contact a moderator.');
embeds: [embed], return;
};
} }
const reasonArgs = context.args; await member.ban();
reasonArgs.splice(0, 1) await interaction.reply(`\`${targetUser.user.tag}\` has been banned.`);
const reason = reasonArgs.join(" "); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
if (!context.message.guild?.available) { if (!channelName) return;
return {
commandContext: context, const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
embeds: [],
}; if (channel) {
await channel.send({ embeds: [ logEmbed ]});
} }
if (!targetMember.bannable) { const audit = new Audit(targetUser.user.id, AuditType.Ban, reason, interaction.user.id, interaction.guildId);
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await audit.Save(Audit, audit);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed],
};
}
const logEmbed = new LogEmbed(context, "Member Banned");
logEmbed.AddUser("User", targetUser, true);
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been banned`);
await targetMember.ban({ reason: `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}` });
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [logEmbed, publicEmbed],
};
} }
} }

View file

@ -1,29 +1,45 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import randomBunny from "random-bunny"; import randomBunny from "random-bunny";
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
import EmbedColours from "../constants/EmbedColours";
export default class Bunny extends Command { export default class Bunny extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Fun"; super.CommandBuilder = new SlashCommandBuilder()
.setName("bunny")
.setDescription("Get a random picture of a rabbit.");
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
const result = await randomBunny('rabbits', 'hot'); if (!interaction.isChatInputCommand()) return;
const subreddits = [
'rabbits',
'bunnieswithhats',
'buncomfortable',
'bunnytongues',
'dutchbunnymafia',
];
const random = Math.floor(Math.random() * subreddits.length);
const selectedSubreddit = subreddits[random];
const result = await randomBunny(selectedSubreddit, 'hot');
if (result.IsSuccess) { if (result.IsSuccess) {
const embed = new PublicEmbed(context, result.Result!.Title, "") const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle(result.Result!.Title)
.setDescription(result.Result!.Permalink)
.setImage(result.Result!.Url) .setImage(result.Result!.Url)
.setURL(`https://reddit.com${result.Result!.Permalink}`) .setURL(`https://reddit.com${result.Result!.Permalink}`)
.setFooter({ text: `r/Rabbits · ${result.Result!.Ups} upvotes` }); .setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`});
await embed.SendToCurrentChannel(); await interaction.reply({ embeds: [ embed ]});
} else { } else {
const errorEmbed = new ErrorEmbed(context, "There was an error using this command."); await interaction.reply("There was an error running this command.");
await errorEmbed.SendToCurrentChannel();
} }
} }
} }

View file

@ -1,50 +1,43 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import { CommandInteraction, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { TextChannel } from "discord.js";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
export default class Clear extends Command { export default class Clear extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("clear")
"moderator" .setDescription("Clears the channel of messages")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages)
.addNumberOption(option =>
option
.setName('count')
.setDescription('The amount to delete')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
if (context.args.length == 0) { if (!interaction.isChatInputCommand()) return;
const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); if (!interaction.channel) return;
await errorEmbed.SendToCurrentChannel();
return { const totalToClear = interaction.options.getNumber('count');
commandContext: context,
embeds: [errorEmbed]
};
}
const totalToClear = Number.parseInt(context.args[0]);
if (!totalToClear || totalToClear <= 0 || totalToClear > 100) { if (!totalToClear || totalToClear <= 0 || totalToClear > 100) {
const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); await interaction.reply('Please specify an amount between 1 and 100.');
await errorEmbed.SendToCurrentChannel(); return;
return {
commandContext: context,
embeds: [errorEmbed]
};
} }
await (context.message.channel as TextChannel).bulkDelete(totalToClear); const channel = interaction.channel as TextChannel;
const embed = new PublicEmbed(context, "", `${totalToClear} message(s) were removed`); if (!channel.manageable) {
await embed.SendToCurrentChannel(); await interaction.reply('Insufficient permissions. Please contact a moderator.');
return;
}
return { await channel.bulkDelete(totalToClear);
commandContext: context,
embeds: [embed] await interaction.reply(`${totalToClear} message(s) were removed.`);
};
} }
} }

View file

@ -1,7 +1,4 @@
import { CommandResponse } from "../constants/CommandResponse"; import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import StringTools from "../helpers/StringTools"; import StringTools from "../helpers/StringTools";
import { Command } from "../type/command"; import { Command } from "../type/command";
@ -10,85 +7,58 @@ export default class Code extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName('code')
"moderator" .setDescription('Manage the verification code of the server')
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addSubcommand(subcommand =>
subcommand
.setName('randomise')
.setDescription('Regenerates the verification code for this server'))
.addSubcommand(subcommand =>
subcommand
.setName('embed')
.setDescription('Sends the embed with the current code to the current channel'));
} }
public override async precheckAsync(context: ICommandContext): Promise<CommandResponse> { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild){ if (!interaction.isChatInputCommand()) return;
return CommandResponse.NotInServer;
}
const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id); switch (interaction.options.getSubcommand()) {
if (!isEnabled) {
return CommandResponse.FeatureDisabled;
}
if (isEnabled.toLocaleLowerCase() != 'true') {
return CommandResponse.FeatureDisabled;
}
return CommandResponse.Ok;
}
public override async execute(context: ICommandContext) {
const action = context.args[0];
switch (action) {
case "randomise": case "randomise":
await this.Randomise(context); await this.Randomise(interaction);
break; break;
case "embed": case "embed":
await this.SendEmbed(context); await this.SendEmbed(interaction);
break; break;
default:
await this.SendUsage(context);
} }
} }
private async SendUsage(context: ICommandContext) { private async Randomise(interaction: CommandInteraction) {
const description = [ if (!interaction.guildId) return;
"USAGE: <randomise|embed>",
"",
"randomise: Sets the server's entry code to a random code",
"embed: Sends an embed with the server's entry code"
].join("\n");
const embed = new PublicEmbed(context, "", description);
await embed.SendToCurrentChannel();
}
private async Randomise(context: ICommandContext) {
if (!context.message.guild) {
return;
}
const randomCode = StringTools.RandomString(5); const randomCode = StringTools.RandomString(5);
await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode); await SettingsHelper.SetSetting("verification.code", interaction.guildId, randomCode);
const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``); await interaction.reply(`Entry code has been set to \`${randomCode}\``);
await embed.SendToCurrentChannel();
} }
private async SendEmbed(context: ICommandContext) { private async SendEmbed(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.guildId) return;
return; if (!interaction.channel) return;
}
const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id); const code = await SettingsHelper.GetSetting("verification.code", interaction.guildId);
if (!code || code == "") { if (!code || code == "") {
const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup."); await interaction.reply("There is no code for this server setup.");
errorEmbed.SendToCurrentChannel();
return; return;
} }
const embed = new PublicEmbed(context, "Entry Code", code!); const embed = new EmbedBuilder()
await embed.SendToCurrentChannel(); .setTitle("Entry Code")
.setDescription(code);
await interaction.channel.send({ embeds: [ embed ]});
} }
} }

View file

@ -1,126 +1,186 @@
import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { CommandResponse } from "../constants/CommandResponse";
import DefaultValues from "../constants/DefaultValues"; import DefaultValues from "../constants/DefaultValues";
import { ICommandContext } from "../contracts/ICommandContext"; import EmbedColours from "../constants/EmbedColours";
import Server from "../entity/Server"; import Server from "../database/entities/Server";
import Setting from "../entity/Setting"; import Setting from "../database/entities/Setting";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Config extends Command { export default class Config extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Administration";
super.Roles = [ super.CommandBuilder = new SlashCommandBuilder()
"administrator" .setName('config')
] .setDescription('Configure the current server')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
.addSubcommand(subcommand =>
subcommand
.setName('reset')
.setDescription('Reset a setting to the default')
.addStringOption(option =>
option
.setName('key')
.setDescription('The key')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('get')
.setDescription('Gets a setting for the server')
.addStringOption(option =>
option
.setName('key')
.setDescription('The key')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('set')
.setDescription('Sets a setting to a specified value')
.addStringOption(option =>
option
.setName('key')
.setDescription('The key')
.setRequired(true))
.addStringOption(option =>
option
.setName('value')
.setDescription('The value')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('list')
.setDescription('Lists all settings'))
} }
public override async precheckAsync(context: ICommandContext): Promise<CommandResponse> { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.isChatInputCommand()) return;
return CommandResponse.ServerNotSetup; if (!interaction.guildId) return;
}
const server = await Server.FetchOneById<Server>(Server, context.message.guild?.id, [ const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings", "Settings",
]); ]);
if (!server) { if (!server) {
return CommandResponse.ServerNotSetup; await interaction.reply('Server not setup. Please use the setup command,');
}
return CommandResponse.Ok;
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) {
return; return;
} }
const server = await Server.FetchOneById<Server>(Server, context.message.guild?.id, [ switch (interaction.options.getSubcommand()) {
"Settings", case 'list':
]); await this.SendHelpText(interaction);
break;
if (!server) { case 'reset':
return; await this.ResetValue(interaction);
} break;
case 'get':
const key = context.args[0]; await this.GetValue(interaction);
const action = context.args[1]; break;
const value = context.args.splice(2).join(" "); case 'set':
await this.SetValue(interaction);
if (!key) { break;
this.SendHelpText(context); default:
} else if (!action) { await interaction.reply('Subcommand not found.');
this.GetValue(context, server, key);
} else {
switch(action) {
case 'reset':
this.ResetValue(context, server, key);
break;
case 'set':
if (!value) {
const errorEmbed = new ErrorEmbed(context, "Value is required when setting");
errorEmbed.SendToCurrentChannel();
return;
}
this.SetValue(context, server, key, value);
break;
default:
const errorEmbed = new ErrorEmbed(context, "Action must be either set or reset");
errorEmbed.SendToCurrentChannel();
return;
}
} }
} }
private async SendHelpText(context: ICommandContext) { private async SendHelpText(interaction: CommandInteraction) {
const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString(); const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString();
const embed = new PublicEmbed(context, "Config", description); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Config")
.setDescription(description);
await embed.SendToCurrentChannel(); await interaction.reply({ embeds: [ embed ]});
} }
private async GetValue(context: ICommandContext, server: Server, key: string) { private async GetValue(interaction: CommandInteraction) {
const setting = server.Settings.filter(x => x.Key == key)[0]; if (!interaction.guildId) return;
const key = interaction.options.get('key');
if (!key || !key.value) {
await interaction.reply('Fields are required.');
return;
}
const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings",
]);
if (!server) {
await interaction.reply('Server not found.');
return;
}
const setting = server.Settings.filter(x => x.Key == key.value)[0];
if (setting) { if (setting) {
const embed = new PublicEmbed(context, "", `${key}: ${setting.Value}`); await interaction.reply(`\`${key}\`: \`${setting.Value}\``);
await embed.SendToCurrentChannel();
} else { } else {
const embed = new PublicEmbed(context, "", `${key}: ${DefaultValues.GetValue(key)} <DEFAULT>`); await interaction.reply(`\`${key}\`: \`${DefaultValues.GetValue(key.value.toString())}\` <DEFAULT>`);
await embed.SendToCurrentChannel();
} }
} }
private async ResetValue(context: ICommandContext, server: Server, key: string) { private async ResetValue(interaction: CommandInteraction) {
const setting = server.Settings.filter(x => x.Key == key)[0]; if (!interaction.guildId) return;
const key = interaction.options.get('key');
if (!key || !key.value) {
await interaction.reply('Fields are required.');
return;
}
const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings",
]);
if (!server) {
await interaction.reply('Server not found.');
return;
}
const setting = server.Settings.filter(x => x.Key == key.value)[0];
if (!setting) { if (!setting) {
const embed = new PublicEmbed(context, "", "Setting has been reset"); await interaction.reply('Setting not found.');
await embed.SendToCurrentChannel();
return; return;
} }
await Setting.Remove(Setting, setting); await Setting.Remove(Setting, setting);
const embed = new PublicEmbed(context, "", "Setting has been reset"); await interaction.reply('The setting has been reset to the default.');
await embed.SendToCurrentChannel();
} }
private async SetValue(context: ICommandContext, server: Server, key: string, value: string) { private async SetValue(interaction: CommandInteraction) {
const setting = server.Settings.filter(x => x.Key == key)[0]; if (!interaction.guildId) return;
const key = interaction.options.get('key');
const value = interaction.options.get('value');
if (!key || !key.value || !value || !value.value) {
await interaction.reply('Fields are required.');
return;
}
const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings",
]);
if (!server) {
await interaction.reply('Server not found.');
return;
}
const setting = server.Settings.filter(x => x.Key == key.value)[0];
if (setting) { if (setting) {
setting.UpdateBasicDetails(key, value); setting.UpdateBasicDetails(key.value.toString(), value.value.toString());
await setting.Save(Setting, setting); await setting.Save(Setting, setting);
} else { } else {
const newSetting = new Setting(key, value); const newSetting = new Setting(key.value.toString(), value.value.toString());
await newSetting.Save(Setting, newSetting); await newSetting.Save(Setting, newSetting);
@ -129,7 +189,6 @@ export default class Config extends Command {
await server.Save(Server, server); await server.Save(Server, server);
} }
const embed = new PublicEmbed(context, "", "Setting has been set"); await interaction.reply('Setting has been set.');
await embed.SendToCurrentChannel();
} }
} }

View file

@ -1,5 +1,4 @@
import { ICommandContext } from "../contracts/ICommandContext"; import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import { Command } from "../type/command"; import { Command } from "../type/command";
@ -7,87 +6,86 @@ export default class Disable extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName('disable')
"moderator" .setDescription('Disables a command')
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
.addSubcommand(subcommand =>
subcommand
.setName('add')
.setDescription('Disables a command for the server')
.addStringOption(option =>
option
.setName('name')
.setDescription('The name of the command')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('remove')
.setDescription('Enables a command for the server')
.addStringOption(option =>
option
.setName('name')
.setDescription('The name of the command')
.setRequired(true)));
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
const action = context.args[0]; if (!interaction.isChatInputCommand()) return;
switch (action) { switch (interaction.options.getSubcommand()) {
case "add": case "add":
await this.Add(context); await this.Add(interaction);
break; break;
case "remove": case "remove":
await this.Remove(context); await this.Remove(interaction);
break; break;
default: default:
await this.SendUsage(context); await interaction.reply('Subcommand not found.');
} }
} }
private async SendUsage(context: ICommandContext) { private async Add(interaction: CommandInteraction) {
const description = [ if (!interaction.guildId) return;
"USAGE: <add|remove> <name>",
"",
"add: Adds the command name to the server's disabled command string",
"remove: Removes the command name from the server's disabled command string",
"name: The name of the command to enable/disable"
].join("\n");
const embed = new PublicEmbed(context, "", description);
await embed.SendToCurrentChannel();
}
private async Add(context: ICommandContext) { const commandName = interaction.options.get('name');
if (!context.message.guild) {
if (!commandName || !commandName.value) {
await interaction.reply('Fields are required.');
return; return;
} }
const commandName = context.args[1]; const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
if (!commandName) {
this.SendUsage(context);
return;
}
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id);
const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : [];
disabledCommands?.push(commandName); disabledCommands?.push(commandName.value.toString());
await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(","));
const embed = new PublicEmbed(context, "", `Disabled command: ${commandName}`); await interaction.reply(`Disabled command ${commandName.value}`);
await embed.SendToCurrentChannel();
} }
private async Remove(context: ICommandContext) { private async Remove(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.guildId) return;
const commandName = interaction.options.get('name');
if (!commandName || !commandName.value) {
await interaction.reply('Fields are required.');
return; return;
} }
const commandName = context.args[1]; const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
if (!commandName) {
this.SendUsage(context);
return;
}
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id);
const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : [];
const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName); const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName.value!.toString());
if (disabledCommandsInstance! > -1) { if (disabledCommandsInstance! > -1) {
disabledCommands?.splice(disabledCommandsInstance!, 1); disabledCommands?.splice(disabledCommandsInstance!, 1);
} }
await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(","));
const embed = new PublicEmbed(context, "", `Enabled command: ${commandName}`); await interaction.reply(`Enabled command ${commandName.value}`);
await embed.SendToCurrentChannel();
} }
} }

View file

@ -1,68 +0,0 @@
import { existsSync, readdirSync } from "fs";
import { CoreClient } from "../client/client";
import { ICommandContext } from "../contracts/ICommandContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import StringTools from "../helpers/StringTools";
import { Command } from "../type/command";
export interface ICommandData {
Exists: boolean;
Name?: string;
Category?: string;
Roles?: string[];
}
export default class Help extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext) {
if (context.args.length == 0) {
await this.SendAll(context);
} else {
await this.SendSingle(context);
}
}
public async SendAll(context: ICommandContext) {
const allCommands = CoreClient.commandItems
.filter(x => !x.ServerId || x.ServerId == context.message.guild?.id);
const cateogries = [...new Set(allCommands.map(x => x.Command.Category))];
const embed = new PublicEmbed(context, "Commands", "");
cateogries.forEach(category => {
let filtered = allCommands.filter(x => x.Command.Category == category);
embed.addField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", "));
});
await embed.SendToCurrentChannel();
}
public async SendSingle(context: ICommandContext) {
const command = CoreClient.commandItems.find(x => x.Name == context.args[0] && !x.ServerId);
const exclusiveCommand = CoreClient.commandItems.find(x => x.Name == context.args[0] && x.ServerId == context.message.guild?.id);
if (exclusiveCommand) {
const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), "");
embed.addField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised"));
embed.addField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone");
await embed.SendToCurrentChannel();
} else if (command) {
const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), "");
embed.addField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised"));
embed.addField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone");
await embed.SendToCurrentChannel();
} else {
const errorEmbed = new ErrorEmbed(context, "Command does not exist");
await errorEmbed.SendToCurrentChannel();
}
}
}

39
src/commands/ignore.ts Normal file
View file

@ -0,0 +1,39 @@
import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import IgnoredChannel from "../database/entities/IgnoredChannel";
import { Command } from "../type/command";
export default class Ignore extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('ignore')
.setDescription('Ignore events in this channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.guildId) return;
const isChannelIgnored = await IgnoredChannel.IsChannelIgnored(interaction.guildId);
if (isChannelIgnored) {
const entity = await IgnoredChannel.FetchOneById(IgnoredChannel, interaction.guildId);
if (!entity) {
await interaction.reply('Unable to find channel.');
return;
}
await IgnoredChannel.Remove(IgnoredChannel, entity);
await interaction.reply('This channel will start being logged again.');
} else {
const entity = new IgnoredChannel(interaction.guildId);
await entity.Save(IgnoredChannel, entity);
await interaction.reply('This channel will now be ignored from logging.');
}
}
}

View file

@ -1,83 +1,79 @@
import ErrorMessages from "../constants/ErrorMessages";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import Audit from "../database/entities/Audit";
import { AuditType } from "../constants/AuditType";
import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import EmbedColours from "../constants/EmbedColours";
import SettingsHelper from "../helpers/SettingsHelper";
export default class Kick extends Command { export default class Kick extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("kick")
"moderator" .setDescription("Kick a member from the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.isChatInputCommand()) return;
if (!interaction.guildId) return;
if (!interaction.guild) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
return { if (!targetUser || !targetUser.user || !targetUser.member) {
commandContext: context, await interaction.reply("User not found.");
embeds: [embed] return;
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const member = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Kicked")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
return { .addFields([
commandContext: context, {
embeds: [embed] name: "Moderator",
}; value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
if (!member.kickable) {
await interaction.reply('Insufficient permissions. Please contact a moderator.');
return;
} }
const reasonArgs = context.args; await member.kick();
reasonArgs.splice(0, 1) await interaction.reply(`\`${targetUser.user.tag}\` has been kicked.`);
const reason = reasonArgs.join(" "); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
if (!context.message.guild?.available) { if (!channelName) return;
return {
commandContext: context, const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
embeds: []
}; if (channel) {
await channel.send({ embeds: [ logEmbed ]});
} }
if (!targetMember.kickable) { const audit = new Audit(targetUser.user.id, AuditType.Kick, reason, interaction.user.id, interaction.guildId);
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await audit.Save(Audit, audit);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
const logEmbed = new LogEmbed(context, "Member Kicked");
logEmbed.AddUser("User", targetUser, true);
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been kicked`);
await targetMember.kick(`Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`);
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -1,96 +1,85 @@
import ErrorMessages from "../constants/ErrorMessages"; import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext"; import { AuditType } from "../constants/AuditType";
import ICommandReturnContext from "../contracts/ICommandReturnContext"; import EmbedColours from "../constants/EmbedColours";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import Audit from "../database/entities/Audit";
import LogEmbed from "../helpers/embeds/LogEmbed"; import SettingsHelper from "../helpers/SettingsHelper";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Mute extends Command { export default class Mute extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("mute")
"moderator" .setDescription("(DEPRECATED) Mute a member in the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.guild || !interaction.guildId) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
if (!targetUser || !targetUser.user || !targetUser.member) {
return { await interaction.reply('Fields are required.');
commandContext: context, return;
embeds: [embed]
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const targetMember = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Muted")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
return { .addFields([
commandContext: context, {
embeds: [embed] name: "Moderator",
}; value: `<@${interaction.user.id}>`,
} },
{
name: "Reason",
value: reason,
},
]);
const reasonArgs = context.args; const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
reasonArgs.splice(0, 1);
const reason = reasonArgs.join(" "); if (!mutedRole) {
await interaction.reply('Muted role not found.');
if (!context.message.guild?.available) { return;
return {
commandContext: context,
embeds: []
};
} }
if (!targetMember.manageable) { if (!targetMember.manageable) {
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await interaction.reply('Insufficient permissions. Please contact a moderator.');
await embed.SendToCurrentChannel(); return;
return {
commandContext: context,
embeds: [embed]
};
} }
const logEmbed = new LogEmbed(context, "Member Muted"); await targetMember.roles.add(mutedRole);
logEmbed.AddUser("User", targetUser, true)
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been muted`); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
publicEmbed.AddReason(reason);
const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); if (!channelName) return;
if (!mutedRole) { const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound);
await embed.SendToCurrentChannel(); if (channel) {
await channel.send({ embeds: [ logEmbed ]});
return {
commandContext: context,
embeds: [embed]
};
} }
await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); const audit = new Audit(targetUser.user.id, AuditType.Mute, reason, interaction.user.id, interaction.guildId);
await audit.Save(Audit, audit);
await logEmbed.SendToModLogsChannel(); await interaction.reply("Please note the mute and unmute commands have been deprecated and will be removed in a future update. Please use timeout instead");
await publicEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -1,67 +0,0 @@
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command";
export default class Poll extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
const argsJoined = context.args.join(" ");
const argsSplit = argsJoined.split(";");
if (argsSplit.length < 3 || argsSplit.length > 10) {
const errorEmbed = new ErrorEmbed(context, "Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options");
await errorEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [errorEmbed]
};
}
const title = argsSplit[0];
const arrayOfNumbers = [
':one:',
':two:',
':three:',
':four:',
':five:',
':six:',
':seven:',
':eight:',
':nine:'
];
const reactionEmojis = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣"];
const description = arrayOfNumbers.splice(0, argsSplit.length - 1);
description.forEach((value, index) => {
description[index] = `${value} ${argsSplit[index + 1]}`;
});
const embed = new PublicEmbed(context, title, description.join("\n"));
const message = await context.message.channel.send({ embeds: [ embed ]});
description.forEach(async (value, index) => {
await message.react(reactionEmojis[index]);
});
if (context.message.deletable) {
await context.message.delete();
}
return {
commandContext: context,
embeds: [embed]
};
}
}

View file

@ -1,232 +0,0 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Role as DiscordRole } from "discord.js";
import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import SettingsHelper from "../helpers/SettingsHelper";
import { readFileSync } from "fs";
import { default as eRole } from "../entity/Role";
import Server from "../entity/Server";
export default class Role extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) return;
switch (context.args[0]) {
case "config":
await this.UseConfig(context);
break;
default:
await this.UseDefault(context);
}
}
// =======
// Default
// =======
private async UseDefault(context: ICommandContext) {
if (context.args.length == 0) {
await this.SendRolesList(context, context.message.guild!.id);
} else {
await this.ToggleRole(context);
}
}
public async GetRolesList(context: ICommandContext): Promise<string[]> {
const rolesArray = await eRole.FetchAllByServerId(context.message.guild!.id);
const stringArray: string[] = [];
for (let i = 0; i < rolesArray.length; i++) {
const serverRole = context.message.guild!.roles.cache.find(x => x.id == rolesArray[i].RoleId);
if (serverRole) {
stringArray.push(serverRole.name);
}
}
return stringArray;
}
public async SendRolesList(context: ICommandContext, serverId: string): Promise<ICommandReturnContext> {
const roles = await this.GetRolesList(context);
const botPrefix = await SettingsHelper.GetServerPrefix(serverId);
const description = roles.length == 0 ? "*no roles*" : `Do ${botPrefix}role <role> to get the role!\n\n${roles.join('\n')}`;
const embed = new PublicEmbed(context, "Roles", description);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
public async ToggleRole(context: ICommandContext): Promise<ICommandReturnContext> {
const roles = await this.GetRolesList(context);
const requestedRole = context.args.join(" ");
if (!roles.includes(requestedRole)) {
const errorEmbed = new ErrorEmbed(context, "This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters");
await errorEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [errorEmbed]
};
}
const assignRole = context.message.guild?.roles.cache.find(x => x.name == requestedRole);
if (!assignRole) {
const errorEmbed = new ErrorEmbed(context, "The current server doesn't have this role. Please contact the server's moderators");
await errorEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [errorEmbed]
};
}
const role = context.message.member?.roles.cache.find(x => x.name == requestedRole)
if (!role) {
await this.AddRole(context, assignRole);
} else {
await this.RemoveRole(context, assignRole);
}
return {
commandContext: context,
embeds: []
};
}
public async AddRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> {
await context.message.member?.roles.add(role, "Toggled with role command");
const embed = new PublicEmbed(context, "", `Gave role: \`${role.name}\``);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
public async RemoveRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> {
await context.message.member?.roles.remove(role, "Toggled with role command");
const embed = new PublicEmbed(context, "", `Removed role: \`${role.name}\``);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
// ======
// Config
// ======
private async UseConfig(context: ICommandContext) {
const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id);
if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) {
const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command");
await errorEmbed.SendToCurrentChannel();
return;
}
switch (context.args[1]) {
case "add":
await this.AddRoleConfig(context);
break;
case "remove":
await this.RemoveRoleConfig(context);
break;
default:
await this.SendConfigHelp(context);
}
}
private async SendConfigHelp(context: ICommandContext) {
const helpText = readFileSync(`${process.cwd()}/data/usage/role.txt`).toString();
const embed = new PublicEmbed(context, "Configure Role Command", helpText);
await embed.SendToCurrentChannel();
}
private async AddRoleConfig(context: ICommandContext) {
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]);
if (!role) {
this.SendConfigHelp(context);
return;
}
const existingRole = await eRole.FetchOneByRoleId(role.id);
if (existingRole) {
const errorEmbed = new ErrorEmbed(context, "This role has already been setup");
await errorEmbed.SendToCurrentChannel();
return;
}
const server = await Server.FetchOneById(Server, context.message.guild!.id, [
"Roles",
]);
if (!server) {
const errorEmbed = new ErrorEmbed(context, "Server not setup, please request the server owner runs the setup command.");
await errorEmbed.SendToCurrentChannel();
return;
}
const roleSetting = new eRole(role.id);
await roleSetting.Save(eRole, roleSetting);
server.AddRoleToServer(roleSetting);
await server.Save(Server, server);
const embed = new PublicEmbed(context, "", `Added \`${role.name}\` as a new assignable role`);
await embed.SendToCurrentChannel();
}
private async RemoveRoleConfig(context: ICommandContext) {
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]);
if (!role) {
this.SendConfigHelp(context);
return;
}
const existingRole = await eRole.FetchOneByRoleId(role.id);
if (!existingRole) {
const errorEmbed = new ErrorEmbed(context, "Unable to find this role");
errorEmbed.SendToCurrentChannel();
return;
}
await eRole.Remove(eRole, existingRole);
const embed = new PublicEmbed(context, "", `Removed \`${role.name}\` from the list of assignable roles`);
await embed.SendToCurrentChannel();
}
}

View file

@ -1,7 +1,6 @@
import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { existsSync, readFileSync } from "fs"; import { existsSync, readFileSync } from "fs";
import { ICommandContext } from "../contracts/ICommandContext"; import EmbedColours from "../constants/EmbedColours";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
interface IRules { interface IRules {
@ -15,38 +14,61 @@ export default class Rules extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Admin"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("rules")
"administrator" .setDescription("Send the rules embeds for this server")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!existsSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`)) { if (!interaction.guildId) return;
const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist");
await errorEmbed.SendToCurrentChannel();
if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) {
await interaction.reply('Rules file doesn\'t exist.');
return; return;
} }
const rulesFile = readFileSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`).toString(); const rulesFile = readFileSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`).toString();
const rules = JSON.parse(rulesFile) as IRules[]; const rules = JSON.parse(rulesFile) as IRules[];
const embeds: PublicEmbed[] = []; const embeds: EmbedBuilder[] = [];
rules.forEach(rule => {
const embed = new PublicEmbed(context, rule.title || "", rule.description?.join("\n") || "");
embed.setImage(rule.image || ""); if (rules.length == 0) {
embed.setFooter({ text: rule.footer || "" }); await interaction.reply({ content: "No rules have been supplied within code base for this server.", ephemeral: true });
return;
}
rules.forEach(rule => {
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle(rule.title || "Rules")
.setDescription(rule.description ? rule.description.join("\n") : "*none*");
if (rule.image) {
embed.setImage(rule.image);
}
if (rule.footer) {
embed.setFooter({ text: rule.footer });
}
embeds.push(embed); embeds.push(embed);
}); });
for (let i = 0; i < embeds.length; i++) { const channel = interaction.channel;
const embed = embeds[i];
await embed.SendToCurrentChannel(); if (!channel) {
await interaction.reply({ content: "Channel not found.", ephemeral: true });
return;
} }
await channel.send({ embeds: embeds });
const successEmbed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Success")
.setDescription("The rules have sent to this channel successfully");
await interaction.reply({ embeds: [ successEmbed ], ephemeral: true });
} }
} }

View file

@ -1,37 +1,31 @@
import { ICommandContext } from "../contracts/ICommandContext"; import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import Server from "../entity/Server"; import Server from "../database/entities/Server";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Setup extends Command { export default class Setup extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Administration";
super.Roles = [ super.CommandBuilder = new SlashCommandBuilder()
"moderator" .setName('setup')
] .setDescription('Makes the server ready to be configured')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.guildId) return;
return;
}
const server = await Server.FetchOneById(Server, context.message.guild?.id); const server = await Server.FetchOneById(Server, interaction.guildId);
if (server) { if (server) {
const embed = new ErrorEmbed(context, "This server has already been setup, please configure using the config command"); await interaction.reply('This server has already been setup, please configure using the config command.');
await embed.SendToCurrentChannel();
return; return;
} }
const newServer = new Server(context.message.guild?.id); const newServer = new Server(interaction.guildId);
await newServer.Save(Server, newServer); await newServer.Save(Server, newServer);
const embed = new PublicEmbed(context, "Success", "Please configure using the config command"); await interaction.reply('Success, please configure using the configure command.');
await embed.SendToCurrentChannel();
} }
} }

156
src/commands/timeout.ts Normal file
View file

@ -0,0 +1,156 @@
import { CacheType, CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { AuditType } from "../constants/AuditType";
import EmbedColours from "../constants/EmbedColours";
import Audit from "../database/entities/Audit";
import SettingsHelper from "../helpers/SettingsHelper";
import TimeLengthInput from "../helpers/TimeLengthInput";
import { Command } from "../type/command";
export default class Timeout extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName("timeout")
.setDescription("Timeouts a user out, sending them a DM with the reason if possible")
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName("length")
.setDescription("How long to timeout for? (Example: 24h, 60m)")
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
}
public override async execute(interaction: CommandInteraction<CacheType>) {
if (!interaction.guild || !interaction.guildId) return;
// Interaction Inputs
const targetUser = interaction.options.get('target');
const lengthInput = interaction.options.get('length');
const reasonInput = interaction.options.get('reason');
// Validation
if (!targetUser || !targetUser.user || !targetUser.member) {
await interaction.reply('Fields are required.');
return;
}
if (!lengthInput || !lengthInput.value) {
await interaction.reply('Fields are required.');
return;
}
if (targetUser.user.bot) {
await interaction.reply('Cannot timeout bots.');
return;
}
// General Variables
const targetMember = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : null;
const timeLength = new TimeLengthInput(lengthInput.value.toString());
const logEmbed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Member Timed Out")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.addFields([
{
name: "Moderator",
value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason || "*none*",
},
{
name: "Length",
value: timeLength.GetLengthShort(),
},
{
name: "Until",
value: timeLength.GetDateFromNow().toString(),
},
]);
// Bot Permissions Check
if (!targetMember.manageable) {
await interaction.reply('Insufficient bot permissions. Please contact a moderator.');
return;
}
// Execute Timeout
await targetMember.timeout(timeLength.GetMilliseconds(), reason || "");
// Log Embed To Channel
const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
if (!channelName) return;
const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
if (channel) {
await channel.send({ embeds: [ logEmbed ]});
}
// Create Audit
const audit = new Audit(targetUser.user.id, AuditType.Timeout, reason || "*none*", interaction.user.id, interaction.guildId);
await audit.Save(Audit, audit);
// DM User, if possible
const resultEmbed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setDescription(`<@${targetUser.user.id}> has been timed out`);
const dmEmbed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setDescription(`You have been timed out in ${interaction.guild.name}`)
.addFields([
{
name: "Reason",
value: reason || "*none*"
},
{
name: "Length",
value: timeLength.GetLengthShort(),
},
{
name: "Until",
value: timeLength.GetDateFromNow().toString(),
},
]);
try {
const dmChannel = await targetUser.user.createDM();
await dmChannel.send({ embeds: [ dmEmbed ]});
resultEmbed.addFields([
{
name: "DM Sent",
value: "true",
},
]);
} catch {
resultEmbed.addFields([
{
name: "DM Sent",
value: "false",
},
]);
}
// Success Reply
await interaction.reply({ embeds: [ resultEmbed ]});
}
}

View file

@ -1,96 +1,80 @@
import ErrorMessages from "../constants/ErrorMessages"; import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext"; import EmbedColours from "../constants/EmbedColours";
import ICommandReturnContext from "../contracts/ICommandReturnContext"; import SettingsHelper from "../helpers/SettingsHelper";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Unmute extends Command { export default class Unmute extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("unmute")
"moderator" .setDescription("(DEPRECATED) Unmute a member in the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.guild || !interaction.guildId) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
if (!targetUser || !targetUser.user || !targetUser.member) {
return { await interaction.reply('Fields are required.');
commandContext: context, return;
embeds: [embed]
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const targetMember = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Unmuted")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
return { .addFields([
commandContext: context, {
embeds: [embed] name: "Moderator",
}; value: `<@${interaction.user.id}>`,
} },
{
name: "Reason",
value: reason,
},
]);
const reasonArgs = context.args; const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
reasonArgs.splice(0, 1);
const reason = reasonArgs.join(" "); if (!mutedRole) {
await interaction.reply('Muted role not found.');
if (!context.message.guild?.available) { return;
return {
commandContext: context,
embeds: []
};
} }
if (!targetMember.manageable) { if (!targetMember.manageable) {
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await interaction.reply('Insufficient permissions. Please contact a moderator.');
await embed.SendToCurrentChannel(); return;
return {
commandContext: context,
embeds: [embed]
};
} }
const logEmbed = new LogEmbed(context, "Member Unmuted"); await targetMember.roles.remove(mutedRole);
logEmbed.AddUser("User", targetUser, true)
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been unmuted`); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
publicEmbed.AddReason(reason);
const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); if (!channelName) return;
if (!mutedRole) { const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound);
await embed.SendToCurrentChannel(); if (channel) {
await channel.send({ embeds: [ logEmbed ]});
return {
commandContext: context,
embeds: [embed]
};
} }
await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); await interaction.reply("Please note the mute and unmute commands have been deprecated and will be removed in a future update. Please use timeout instead");
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -1,71 +1,70 @@
import { ICommandContext } from "../contracts/ICommandContext"; import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import ICommandReturnContext from "../contracts/ICommandReturnContext"; import { AuditType } from "../constants/AuditType";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import EmbedColours from "../constants/EmbedColours";
import LogEmbed from "../helpers/embeds/LogEmbed"; import Audit from "../database/entities/Audit";
import PublicEmbed from "../helpers/embeds/PublicEmbed"; import SettingsHelper from "../helpers/SettingsHelper";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Warn extends Command { export default class Warn extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("warn")
"moderator" .setDescription("Warns a member in the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const user = context.message.mentions.users.first(); if (!interaction.guild || !interaction.guildId) return;
if (!user) { const targetUser = interaction.options.get('target');
const errorEmbed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await errorEmbed.SendToCurrentChannel();
if (!targetUser || !targetUser.user || !targetUser.member) {
return { await interaction.reply('Fields are required.');
commandContext: context, return;
embeds: [errorEmbed]
};
} }
const member = context.message.guild?.members.cache.find(x => x.user.id == user.id); const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!member) { const logEmbed = new EmbedBuilder()
const errorEmbed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await errorEmbed.SendToCurrentChannel(); .setTitle("Member Warned")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
return { .addFields([
commandContext: context, {
embeds: [errorEmbed] name: "Moderator",
}; value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
if (!channelName) return;
const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
if (channel) {
await channel.send({ embeds: [ logEmbed ]});
} }
const reasonArgs = context.args; const audit = new Audit(targetUser.user.id, AuditType.Warn, reason, interaction.user.id, interaction.guildId);
reasonArgs.splice(0, 1); await audit.Save(Audit, audit);
const reason = reasonArgs.join(" "); await interaction.reply('Successfully warned user.');
if (!context.message.guild?.available) {
return {
commandContext: context,
embeds: []
};
}
const logEmbed = new LogEmbed(context, "Member Warned");
logEmbed.AddUser("User", user, true);
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${user} has been warned`);
publicEmbed.AddReason(reason);
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -0,0 +1,8 @@
export enum AuditType {
General,
Warn,
Mute,
Kick,
Ban,
Timeout,
}

View file

@ -1,7 +0,0 @@
export enum CommandResponse {
Ok,
Unauthorised,
ServerNotSetup,
NotInServer,
FeatureDisabled,
}

View file

@ -17,11 +17,7 @@ export default class DefaultValues {
private static SetValues() { private static SetValues() {
if (this.values.length == 0) { if (this.values.length == 0) {
// Bot // Bot
if (this.useDevPrefix) { this.values.push({ Key: "bot.prefix", Value: process.env.BOT_PREFIX || "v!" })
this.values.push({ Key: "bot.prefix", Value: "d!" });
} else {
this.values.push({ Key: "bot.prefix", Value: "v!" });
}
// Commands // Commands
this.values.push({ Key: "commands.disabled", Value: "" }); this.values.push({ Key: "commands.disabled", Value: "" });

View file

@ -0,0 +1,3 @@
export default class EmbedColours {
public static readonly Ok = 0x3050ba;
}

View file

@ -1,27 +0,0 @@
import { CommandResponse } from "./CommandResponse";
export default class ErrorMessages {
public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?";
public static readonly ChannelNotFound = "Unable to find channel";
public static readonly RoleNotFound = "Unable to find role";
public static readonly UserUnauthorised = "You are not authorised to use this command";
public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command";
public static readonly NotInServer = "This command requires to be ran inside of a server";
public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator";
public static GetErrorMessage(response: CommandResponse): string {
switch (response) {
case CommandResponse.Unauthorised:
return this.UserUnauthorised;
case CommandResponse.ServerNotSetup:
return this.ServerNotSetup;
case CommandResponse.NotInServer:
return this.NotInServer;
case CommandResponse.FeatureDisabled:
return this.FeatureDisabled;
default:
return "";
}
}
}

View file

@ -0,0 +1,15 @@
export enum EventType {
ChannelCreate,
ChannelDelete,
ChannelUpdate,
GuildBanAdd,
GuildBanRemove,
GuildCreate,
GuildMemberAdd,
GuildMemberRemove,
GuildMemberUpdate,
MessageCreate,
MessageDelete,
MessageUpdate,
Ready,
}

View file

@ -1,5 +1,6 @@
import { Column, DeepPartial, EntityTarget, getConnection, PrimaryColumn, ObjectLiteral, FindOptionsWhere } from "typeorm"; import { Column, DeepPartial, EntityTarget, PrimaryColumn, ObjectLiteral, FindOptionsWhere } from "typeorm";
import { v4 } from "uuid"; import { v4 } from "uuid";
import AppDataSource from "../database/dataSources/appDataSource";
export default class BaseEntity { export default class BaseEntity {
constructor() { constructor() {
@ -21,25 +22,19 @@ export default class BaseEntity {
public async Save<T extends BaseEntity>(target: EntityTarget<T>, entity: DeepPartial<T>): Promise<void> { public async Save<T extends BaseEntity>(target: EntityTarget<T>, entity: DeepPartial<T>): Promise<void> {
this.WhenUpdated = new Date(); this.WhenUpdated = new Date();
const connection = getConnection(); const repository = AppDataSource.getRepository<T>(target);
const repository = connection.getRepository<T>(target);
await repository.save(entity); await repository.save(entity);
} }
public static async Remove<T extends BaseEntity>(target: EntityTarget<T>, entity: T): Promise<void> { public static async Remove<T extends BaseEntity>(target: EntityTarget<T>, entity: T): Promise<void> {
const connection = getConnection(); const repository = AppDataSource.getRepository<T>(target);
const repository = connection.getRepository<T>(target);
await repository.remove(entity); await repository.remove(entity);
} }
public static async FetchAll<T extends BaseEntity>(target: EntityTarget<T>, relations?: string[]): Promise<T[]> { public static async FetchAll<T extends BaseEntity>(target: EntityTarget<T>, relations?: string[]): Promise<T[]> {
const connection = getConnection(); const repository = AppDataSource.getRepository<T>(target);
const repository = connection.getRepository<T>(target);
const all = await repository.find({ relations: relations || [] }); const all = await repository.find({ relations: relations || [] });
@ -47,9 +42,7 @@ export default class BaseEntity {
} }
public static async FetchOneById<T extends BaseEntity>(target: EntityTarget<T>, id: string, relations?: string[]): Promise<T | null> { public static async FetchOneById<T extends BaseEntity>(target: EntityTarget<T>, id: string, relations?: string[]): Promise<T | null> {
const connection = getConnection(); const repository = AppDataSource.getRepository<T>(target);
const repository = connection.getRepository<T>(target);
const single = await repository.findOne({ where: ({ Id: id } as FindOptionsWhere<T>), relations: relations || {} }); const single = await repository.findOne({ where: ({ Id: id } as FindOptionsWhere<T>), relations: relations || {} });
@ -57,9 +50,7 @@ export default class BaseEntity {
} }
public static async Any<T extends ObjectLiteral>(target: EntityTarget<T>): Promise<boolean> { public static async Any<T extends ObjectLiteral>(target: EntityTarget<T>): Promise<boolean> {
const connection = getConnection(); const repository = AppDataSource.getRepository<T>(target);
const repository = connection.getRepository<T>(target);
const any = await repository.find(); const any = await repository.find();

View file

@ -1,7 +0,0 @@
import { Message } from "discord.js";
export interface ICommandContext {
name: string;
args: string[];
message: Message;
}

View file

@ -1,7 +0,0 @@
import { MessageEmbed } from "discord.js";
import { ICommandContext } from "./ICommandContext";
export default interface ICommandReturnContext {
commandContext: ICommandContext,
embeds: MessageEmbed[]
}

View file

@ -1,6 +1,7 @@
import { Event } from "../type/event"; import { EventType } from "../constants/EventType";
export default interface IEventItem { export default interface IEventItem {
Event: Event, EventType: EventType,
ExecutionFunction: Function,
} }

View file

@ -1,6 +0,0 @@
import { MessageEmbed } from "discord.js";
import { ICommandContext } from "./ICommandContext";
export default interface ICommandReturnContext {
embeds: MessageEmbed[]
}

View file

@ -0,0 +1,26 @@
import { DataSource } from "typeorm";
import * as dotenv from "dotenv";
dotenv.config();
const AppDataSource = new DataSource({
type: "mysql",
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
username: process.env.DB_AUTH_USER,
password: process.env.DB_AUTH_PASS,
database: process.env.DB_NAME,
synchronize: process.env.DB_SYNC == "true",
logging: process.env.DB_LOGGING == "true",
entities: [
"dist/database/entities/**/*.js",
],
migrations: [
"dist/database/migrations/**/*.js",
],
subscribers: [
"dist/database/subscribers/**/*.js",
],
});
export default AppDataSource;

View file

@ -1,5 +1,6 @@
import { Column, Entity, getConnection } from "typeorm"; import { Column, Entity } from "typeorm";
import BaseEntity from "../../contracts/BaseEntity"; import BaseEntity from "../../../contracts/BaseEntity";
import AppDataSource from "../../dataSources/appDataSource";
@Entity() @Entity()
export default class Lobby extends BaseEntity { export default class Lobby extends BaseEntity {
@ -34,9 +35,7 @@ export default class Lobby extends BaseEntity {
} }
public static async FetchOneByChannelId(channelId: string, relations?: string[]): Promise<Lobby | null> { public static async FetchOneByChannelId(channelId: string, relations?: string[]): Promise<Lobby | null> {
const connection = getConnection(); const repository = AppDataSource.getRepository(Lobby);
const repository = connection.getRepository(Lobby);
const single = await repository.findOne({ where: { ChannelId: channelId }, relations: relations || [] }); const single = await repository.findOne({ where: { ChannelId: channelId }, relations: relations || [] });

View file

@ -0,0 +1,53 @@
import { Column, Entity } from "typeorm";
import { AuditType } from "../../constants/AuditType";
import BaseEntity from "../../contracts/BaseEntity";
import StringTools from "../../helpers/StringTools";
import AppDataSource from "../dataSources/appDataSource";
@Entity()
export default class Audit extends BaseEntity {
constructor(userId: string, auditType: AuditType, reason: string, moderatorId: string, serverId: string) {
super();
this.AuditId = StringTools.RandomString(5).toUpperCase();
this.UserId = userId;
this.AuditType = auditType;
this.Reason = reason;
this.ModeratorId = moderatorId;
this.ServerId = serverId;
}
@Column()
AuditId: string;
@Column()
UserId: string;
@Column()
AuditType: AuditType;
@Column()
Reason: string;
@Column()
ModeratorId: string;
@Column()
ServerId: string;
public static async FetchAuditsByUserId(userId: string, serverId: string): Promise<Audit[] | null> {
const repository = AppDataSource.getRepository(Audit);
const all = await repository.find({ where: { UserId: userId, ServerId: serverId } });
return all;
}
public static async FetchAuditByAuditId(auditId: string, serverId: string): Promise<Audit | null> {
const repository = AppDataSource.getRepository(Audit);
const single = await repository.findOne({ where: { AuditId: auditId, ServerId: serverId } });
return single;
}
}

View file

@ -0,0 +1,20 @@
import { Entity } from "typeorm";
import BaseEntity from "../../contracts/BaseEntity";
import AppDataSource from "../dataSources/appDataSource";
@Entity()
export default class IgnoredChannel extends BaseEntity {
constructor(channelId: string) {
super();
this.Id = channelId;
}
public static async IsChannelIgnored(channelId: string): Promise<boolean> {
const repository = AppDataSource.getRepository(IgnoredChannel);
const single = await repository.findOne({ where: { Id: channelId } });
return single != undefined;
}
}

View file

@ -1,6 +1,7 @@
import { Column, Entity, EntityTarget, getConnection, ManyToOne } from "typeorm"; import { Column, Entity, ManyToOne } from "typeorm";
import BaseEntity from "../contracts/BaseEntity" import BaseEntity from "../../contracts/BaseEntity"
import Server from "./Server"; import Server from "./Server";
import AppDataSource from "../dataSources/appDataSource";
@Entity() @Entity()
export default class Role extends BaseEntity { export default class Role extends BaseEntity {
@ -16,10 +17,12 @@ export default class Role extends BaseEntity {
@ManyToOne(() => Server, x => x.Roles) @ManyToOne(() => Server, x => x.Roles)
Server: Server; Server: Server;
public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | null> { public SetServer(server: Server) {
const connection = getConnection(); this.Server = server;
}
const repository = connection.getRepository(Role); public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | null> {
const repository = AppDataSource.getRepository(Role);
const single = await repository.findOne({ where: { RoleId: roleId }, relations: relations || []}); const single = await repository.findOne({ where: { RoleId: roleId }, relations: relations || []});
@ -27,9 +30,7 @@ export default class Role extends BaseEntity {
} }
public static async FetchAllByServerId(serverId: string): Promise<Role[]> { public static async FetchAllByServerId(serverId: string): Promise<Role[]> {
const connection = getConnection(); const repository = AppDataSource.getRepository(Server);
const repository = connection.getRepository(Server);
const all = await repository.findOne({ where: { Id: serverId }, relations: [ const all = await repository.findOne({ where: { Id: serverId }, relations: [
"Roles", "Roles",

View file

@ -1,5 +1,5 @@
import { Entity, OneToMany } from "typeorm"; import { Entity, OneToMany } from "typeorm";
import BaseEntity from "../contracts/BaseEntity"; import BaseEntity from "../../contracts/BaseEntity";
import Role from "./Role"; import Role from "./Role";
import Setting from "./Setting"; import Setting from "./Setting";

View file

@ -1,6 +1,7 @@
import { Column, Entity, getConnection, ManyToOne } from "typeorm"; import { Column, Entity, ManyToOne } from "typeorm";
import BaseEntity from "../contracts/BaseEntity"; import BaseEntity from "../../contracts/BaseEntity";
import Server from "./Server"; import Server from "./Server";
import AppDataSource from "../dataSources/appDataSource";
@Entity() @Entity()
export default class Setting extends BaseEntity { export default class Setting extends BaseEntity {
@ -26,9 +27,7 @@ export default class Setting extends BaseEntity {
} }
public static async FetchOneByKey(key: string, relations?: string[]): Promise<Setting | null> { public static async FetchOneByKey(key: string, relations?: string[]): Promise<Setting | null> {
const connection = getConnection(); const repository = AppDataSource.getRepository(Setting);
const repository = connection.getRepository(Setting);
const single = await repository.findOne({ where: { Key: key }, relations: relations || {} }); const single = await repository.findOne({ where: { Key: key }, relations: relations || {} });

View file

@ -0,0 +1,30 @@
import { MigrationInterface, QueryRunner } from "typeorm"
import MigrationHelper from "../../../helpers/MigrationHelper"
export class vylbot1662399171315 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
MigrationHelper.Up('1662399171315-CreateBase', '3.1', [
"01-table/Audit",
"01-table/IgnoredChannel",
"01-table/Lobby",
"01-table/Role",
"01-table/Server",
"01-table/Setting",
"02-key/Audit",
"02-key/IgnoredChannel",
"02-key/Lobby",
"02-key/Role",
"02-key/Server",
"02-key/Setting",
"03-constraint/Role",
"03-constraint/Setting",
], queryRunner);
}
public async down(queryRunner: QueryRunner): Promise<void> {
}
}

View file

@ -1,53 +0,0 @@
import { Event } from "../type/event";
import { GuildMember } from "discord.js";
import EventEmbed from "../helpers/embeds/EventEmbed";
import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate";
import SettingsHelper from "../helpers/SettingsHelper";
export default class MemberEvents extends Event {
constructor() {
super();
}
public override async guildMemberAdd(member: GuildMember) {
if (!member.guild) return;
const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EventEmbed(member.client, member.guild, "Member Joined");
embed.AddUser("User", member.user, true);
embed.addField("Created", member.user.createdAt.toISOString());
embed.setFooter({ text: `Id: ${member.user.id}` });
const channel = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id);
if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel);
}
public override async guildMemberRemove(member: GuildMember) {
if (!member.guild) return;
const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EventEmbed(member.client, member.guild, "Member Left");
embed.AddUser("User", member.user, true);
embed.addField("Joined", member.joinedAt?.toISOString() || "n/a");
embed.setFooter({ text: `Id: ${member.user.id}` });
const channel = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id);
if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel);
}
public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) {
const handler = new GuildMemberUpdate(oldMember, newMember);
if (oldMember.nickname != newMember.nickname) { // Nickname change
await handler.NicknameChanged();
}
}
}

View file

@ -0,0 +1,35 @@
import { EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper";
export default async function GuildMemberAdd(member: GuildMember) {
if (!member.guild) return;
const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle('Member Joined')
.setDescription(`${member.user} \`${member.user.tag}\``)
.setFooter({ text: `Id: ${member.user.id}` })
.setThumbnail(member.avatarURL())
.addFields([
{
name: 'Created',
value: member.user.createdAt.toISOString(),
}
]);
const channelSetting = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id);
if (!channelSetting) return;
const channel = member.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [embed ]});
}

View file

@ -0,0 +1,35 @@
import { EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper";
export default async function GuildMemberRemove(member: GuildMember) {
if (!member.guild) return;
const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle('Member Left')
.setDescription(`${member.user} \`${member.user.tag}\``)
.setFooter({ text: `Id: ${member.user.id}` })
.setThumbnail(member.avatarURL())
.addFields([
{
name: 'Joined',
value: member.joinedAt ? member.joinedAt.toISOString() : "*none*",
}
]);
const channelSetting = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id);
if (!channelSetting) return;
const channel = member.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [embed ]});
}

View file

@ -1,32 +1,8 @@
import { GuildMember } from "discord.js"; import { GuildMember } from "discord.js";
import EventEmbed from "../../helpers/embeds/EventEmbed"; import NicknameChanged from "./GuildMemberUpdate/NicknameChanged";
import SettingsHelper from "../../helpers/SettingsHelper";
export default class GuildMemberUpdate { export default async function GuildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) {
public oldMember: GuildMember; if (oldMember.nickname != newMember.nickname) { // Nickname change
public newMember: GuildMember; await NicknameChanged(oldMember, newMember);
constructor(oldMember: GuildMember, newMember: GuildMember) {
this.oldMember = oldMember;
this.newMember = newMember;
}
public async NicknameChanged() {
const enabled = await SettingsHelper.GetSetting("event.member.update.enabled", this.newMember.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const oldNickname = this.oldMember.nickname || "*none*";
const newNickname = this.newMember.nickname || "*none*";
const embed = new EventEmbed(this.oldMember.client, this.newMember.guild, "Nickname Changed");
embed.AddUser("User", this.newMember.user, true);
embed.addField("Before", oldNickname, true);
embed.addField("After", newNickname, true);
embed.setFooter({ text: `Id: ${this.newMember.user.id}` });
const channel = await SettingsHelper.GetSetting("event.member.update.channel", this.newMember.guild.id);
if (!channel || channel.toLowerCase() != "true") return;
await embed.SendToChannel(channel);
} }
} }

View file

@ -0,0 +1,40 @@
import { EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import EmbedColours from "../../../constants/EmbedColours";
import SettingsHelper from "../../../helpers/SettingsHelper";
export default async function NicknameChanged(oldMember: GuildMember, newMember: GuildMember) {
const enabled = await SettingsHelper.GetSetting("event.member.update.enabled", newMember.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const oldNickname = oldMember.nickname || "*none*";
const newNickname = newMember.nickname || "*none*";
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle('Nickname Changed')
.setDescription(`${newMember.user} \`${newMember.user.tag}\``)
.setFooter({ text: `Id: ${newMember.user.id}` })
.setThumbnail(newMember.avatarURL())
.addFields([
{
name: 'Before',
value: oldNickname,
},
{
name: 'After',
value: newNickname,
},
]);
const channelSetting = await SettingsHelper.GetSetting("event.member.update.channel", newMember.guild.id);
if (!channelSetting) return;
const channel = newMember.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [embed ]});
}

View file

@ -1,64 +0,0 @@
import { Event } from "../type/event";
import { Message } from "discord.js";
import EventEmbed from "../helpers/embeds/EventEmbed";
import SettingsHelper from "../helpers/SettingsHelper";
import OnMessage from "./MessageEvents/OnMessage";
export default class MessageEvents extends Event {
constructor() {
super();
}
public override async messageDelete(message: Message) {
if (!message.guild) return;
if (message.author.bot) return;
const enabled = await SettingsHelper.GetSetting("event.message.delete.enabled", message.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EventEmbed(message.client, message.guild, "Message Deleted");
embed.AddUser("User", message.author, true);
embed.addField("Channel", message.channel.toString(), true);
embed.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``);
if (message.attachments.size > 0) {
embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``);
}
const channel = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id);
if (!channel || !message.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel);
}
public override async messageUpdate(oldMessage: Message, newMessage: Message) {
if (!newMessage.guild) return;
if (newMessage.author.bot) return;
if (oldMessage.content == newMessage.content) return;
const enabled = await SettingsHelper.GetSetting("event.message.update.enabled", newMessage.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EventEmbed(newMessage.client, newMessage.guild, "Message Edited");
embed.AddUser("User", newMessage.author, true);
embed.addField("Channel", newMessage.channel.toString(), true);
embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``);
embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``);
const channel = await SettingsHelper.GetSetting("event.message.update.channel", newMessage.guild.id);
if (!channel || !newMessage.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel);
}
public override async messageCreate(message: Message) {
if (!message.guild) return;
if (message.author.bot) return;
const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id);
if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") {
await OnMessage.VerificationCheck(message);
}
}
}

View file

@ -0,0 +1,14 @@
import { Message } from "discord.js";
import SettingsHelper from "../../helpers/SettingsHelper";
import VerificationCheck from "./MessageCreate/VerificationCheck";
export default async function MessageCreate(message: Message) {
if (!message.guild) return;
if (message.author.bot) return;
const isVerificationEnabled = await SettingsHelper.GetSetting("verification.enabled", message.guild.id);
if (isVerificationEnabled && isVerificationEnabled.toLocaleLowerCase() == "true") {
await VerificationCheck(message);
}
}

View file

@ -0,0 +1,57 @@
import { Message } from "discord.js";
import SettingsHelper from "../../../helpers/SettingsHelper";
export default async function VerificationCheck(message: Message) {
if (!message.guild) return;
const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id);
if (!verificationChannel) {
return;
}
const channel = message.guild.channels.cache.find(x => x.name == verificationChannel);
if (!channel) {
return;
}
const currentChannel = message.guild.channels.cache.find(x => x == message.channel);
if (!currentChannel || currentChannel.name != verificationChannel) {
return;
}
const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id);
if (!verificationCode || verificationCode == "") {
await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team.");
await message.delete();
return;
}
const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id);
if (!verificationRoleName) {
await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team.");
await message.delete();
return;
}
const role = message.guild.roles.cache.find(x => x.name == verificationRoleName);
if (!role) {
await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team.");
await message.delete();
return;
}
if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) {
await message.delete();
return;
}
await message.member?.roles.add(role);
await message.delete();
}

View file

@ -0,0 +1,53 @@
import { EmbedBuilder, Message, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours";
import IgnoredChannel from "../../database/entities/IgnoredChannel";
import SettingsHelper from "../../helpers/SettingsHelper";
export default async function MessageDelete(message: Message) {
if (!message.guild) return;
if (message.author.bot) return;
const enabled = await SettingsHelper.GetSetting("event.message.delete.enabled", message.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const ignored = await IgnoredChannel.IsChannelIgnored(message.channel.id);
if (ignored) return;
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Message Deleted")
.setDescription(`${message.author} \`${message.author.tag}\``)
.setThumbnail(message.author.avatarURL())
.addFields([
{
name: "Channel",
value: message.channel.toString(),
inline: true,
},
{
name: "Content",
value: `\`\`\`${message.content || "*none*"}\`\`\``,
}
]);
if (message.attachments.size > 0) {
embed.addFields([
{
name: "Attachments",
value: `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``
}
]);
}
const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id);
if (!channelSetting) return;
const channel = message.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [ embed ]});
}

View file

@ -0,0 +1,49 @@
import { EmbedBuilder, Message, TextChannel } from "discord.js";
import EmbedColours from "../../constants/EmbedColours";
import IgnoredChannel from "../../database/entities/IgnoredChannel";
import SettingsHelper from "../../helpers/SettingsHelper";
export default async function MessageUpdate(oldMessage: Message, newMessage: Message) {
if (!newMessage.guild) return;
if (newMessage.author.bot) return;
if (oldMessage.content == newMessage.content) return;
const enabled = await SettingsHelper.GetSetting("event.message.update.enabled", newMessage.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return;
const ignored = await IgnoredChannel.IsChannelIgnored(newMessage.channel.id);
if (ignored) return;
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Message Edited")
.setDescription(`${newMessage.author} \`${newMessage.author.tag}\``)
.setThumbnail(newMessage.author.avatarURL())
.addFields([
{
name: "Channel",
value: newMessage.channel.toString(),
inline: true,
},
{
name: "Before",
value: `\`\`\`${oldMessage.content || "*none*"}\`\`\``,
},
{
name: "After",
value: `\`\`\`${newMessage.content || "*none*"}\`\`\``,
}
]);
const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", newMessage.guild.id);
if (!channelSetting) return;
const channel = newMessage.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [ embed ]});
}

View file

@ -1,59 +0,0 @@
import { Message as Message } from "discord.js";
import SettingsHelper from "../../helpers/SettingsHelper";
export default class OnMessage {
public static async VerificationCheck(message: Message) {
if (!message.guild) return;
const verificationChannel = await SettingsHelper.GetSetting("verification.channel", message.guild.id);
if (!verificationChannel) {
return;
}
const channel = message.guild.channels.cache.find(x => x.name == verificationChannel);
if (!channel) {
return;
}
const currentChannel = message.guild.channels.cache.find(x => x == message.channel);
if (!currentChannel || currentChannel.name != verificationChannel) {
return;
}
const verificationCode = await SettingsHelper.GetSetting("verification.code", message.guild.id);
if (!verificationCode || verificationCode == "") {
await message.reply("`verification.code` is not set inside of the server's config. Please contact the server's mod team.");
await message.delete();
return;
}
const verificationRoleName = await SettingsHelper.GetSetting("verification.role", message.guild.id);
if (!verificationRoleName) {
await message.reply("`verification.role` is not set inside of the server's config. Please contact the server's mod team.");
await message.delete();
return;
}
const role = message.guild.roles.cache.find(x => x.name == verificationRoleName);
if (!role) {
await message.reply("The entry role configured for this server does not exist. Please contact the server's mod team.");
await message.delete();
return;
}
if (message.content.toLocaleLowerCase() != verificationCode.toLocaleLowerCase()) {
await message.delete();
return;
}
await message.member?.roles.add(role);
await message.delete();
}
}

41
src/helpers/AuditTools.ts Normal file
View file

@ -0,0 +1,41 @@
import { AuditType } from "../constants/AuditType";
export default class AuditTools {
public static TypeToFriendlyText(auditType: AuditType): string {
switch (auditType) {
case AuditType.General:
return "General";
case AuditType.Warn:
return "Warn";
case AuditType.Mute:
return "Mute";
case AuditType.Kick:
return "Kick";
case AuditType.Ban:
return "Ban";
case AuditType.Timeout:
return "Timeout";
default:
return "Other";
}
}
public static StringToType(str: string): AuditType {
switch (str.toLowerCase()) {
case "general":
return AuditType.General;
case "warn":
return AuditType.Warn;
case "mute":
return AuditType.Mute;
case "kick":
return AuditType.Kick;
case "ban":
return AuditType.Ban;
case "timeout":
return AuditType.Timeout;
default:
return AuditType.General;
}
}
}

View file

@ -0,0 +1,20 @@
import { readFileSync } from "fs";
import { QueryRunner } from "typeorm";
export default class MigrationHelper {
public static Up(migrationName: string, version: string, queryFiles: string[], queryRunner: QueryRunner) {
for (let path of queryFiles) {
const query = readFileSync(`${process.cwd()}/database/${version}/${migrationName}/Up/${path}.sql`).toString();
queryRunner.query(query);
}
}
public static Down(migrationName: string, version: string, queryFiles: string[], queryRunner: QueryRunner) {
for (let path of queryFiles) {
const query = readFileSync(`${process.cwd()}/database/${version}/${migrationName}/Down/${path}.sql`).toString();
queryRunner.query(query);
}
}
}

View file

@ -1,6 +1,6 @@
import DefaultValues from "../constants/DefaultValues"; import DefaultValues from "../constants/DefaultValues";
import Server from "../entity/Server"; import Server from "../database/entities/Server";
import Setting from "../entity/Setting"; import Setting from "../database/entities/Setting";
export default class SettingsHelper { export default class SettingsHelper {
public static async GetSetting(key: string, serverId: string): Promise<string | undefined> { public static async GetSetting(key: string, serverId: string): Promise<string | undefined> {

View file

@ -35,4 +35,8 @@ export default class StringTools {
return result; return result;
} }
public static ReplaceAll(str: string, find: string, replace: string) {
return str.replace(new RegExp(find, 'g'), replace);
}
} }

Some files were not shown because too many files have changed in this diff Show more