Compare commits

...
Author SHA1 Message Date
bc497c7dea
Fix sending links if they didn't get changed
Some checks failed
CI / CI (push) Has been cancelled
2026-02-27 19:55:37 +01:00
d7b8e706b8
Add ig link replacing command
Some checks are pending
CI / CI (push) Waiting to run
2026-02-27 19:28:54 +01:00
7791fa5654
Improve error message when message.delete() fails
Some checks are pending
CI / CI (push) Waiting to run
2026-02-27 19:25:40 +01:00
9796e9245a
Don't send an unchanged link in x command 2026-02-27 19:24:01 +01:00
62b8ebb4cf
Use lookbehind to improve x command regex 2026-02-27 19:23:36 +01:00
62fbb445e2
Send a message if no X/Twitter links are provided
Some checks are pending
CI / CI (push) Waiting to run
2026-02-26 18:30:02 +01:00
1f48a896a5
Add X command that replaces X/Twitter links with fxtwitter
Some checks failed
CI / CI (push) Has been cancelled
2026-02-26 10:58:18 +01:00
94d1b0f297
Make the say command admin only
Some checks are pending
CI / CI (push) Waiting to run
2026-02-26 10:52:40 +01:00
cafcc007c1
Don't specify global pfp when there's only one
All checks were successful
CI / CI (push) Successful in 22s
2026-01-29 22:37:59 +01:00
af4c42009c
Add channel count to serverinfo
All checks were successful
CI / CI (push) Successful in 22s
2025-10-22 09:45:52 +02:00
f1f9e3aaa3
Use correct variables
All checks were successful
CI / CI (push) Successful in 25s
2025-09-07 08:01:09 +02:00
4aaf72e4b4
Limit iterations to listens if less than 10
All checks were successful
CI / CI (push) Successful in 22s
2025-09-07 07:57:20 +02:00
69872ba0d1
Add artist count check to getTopArtists
All checks were successful
CI / CI (push) Successful in 22s
2025-09-07 07:49:10 +02:00
7dab688298
Slice off alpha from imagemagick output
All checks were successful
CI / CI (push) Successful in 22s
2025-09-06 03:47:45 +02:00
SileNce5k
3ee2a80201
Set embed to the average color of the album cover on fm command (#168)
And do not set a color in misc/fm.js when there already is one set.
2025-09-06 03:32:36 +02:00
8655f279f9
Use correct whitelist property
All checks were successful
CI / CI (push) Successful in 1m36s
2025-07-27 14:59:32 +02:00
aa10d6bc27
Fix mc command by converting all info to strings
Some checks are pending
CI / CI (push) Waiting to run
2025-07-27 14:55:15 +02:00
3d674f12e0
Fix mc command
Some checks are pending
CI / CI (push) Waiting to run
2025-07-27 14:45:23 +02:00
0a8df28a94
Add minecraft command to get server info
Some checks are pending
CI / CI (push) Waiting to run
2025-07-27 13:52:53 +02:00
dependabot[bot]
f78f4648d0
Bump dotenv from 17.2.0 to 17.2.1 (#161)
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.2.0 to 17.2.1.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v17.2.0...v17.2.1)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 17.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-25 20:05:41 +02:00
dependabot[bot]
7f0029d9f9
Bump jest from 30.0.4 to 30.0.5 (#160)
Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 30.0.4 to 30.0.5.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.0.5/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-23 19:59:22 +02:00
dependabot[bot]
681eca87e0
Bump dotenv from 17.1.0 to 17.2.0 (#159)
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.1.0 to 17.2.0.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v17.1.0...v17.2.0)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 17.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-10 06:52:44 +02:00
dependabot[bot]
914aceeb44
Bump dotenv from 17.0.1 to 17.1.0 (#158)
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.0.1 to 17.1.0.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v17.0.1...v17.1.0)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 17.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 04:27:47 +02:00
dependabot[bot]
dc7b49d255
Bump jest from 30.0.3 to 30.0.4 (#157)
Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 30.0.3 to 30.0.4.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.0.4/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.0.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-04 03:30:50 +02:00
dependabot[bot]
c1d942e204
Bump dotenv from 17.0.0 to 17.0.1 (#156)
Bumps [dotenv](https://github.com/motdotla/dotenv) from 17.0.0 to 17.0.1.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v17.0.0...v17.0.1)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 17.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-02 02:47:58 +02:00
dependabot[bot]
c53b707163
Bump dotenv from 16.6.1 to 17.0.0 (#155)
Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.6.1 to 17.0.0.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v16.6.1...v17.0.0)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 17.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-01 02:07:59 +02:00
dependabot[bot]
9dcc7adab4
Bump dotenv from 16.5.0 to 16.6.1 (#154)
Bumps [dotenv](https://github.com/motdotla/dotenv) from 16.5.0 to 16.6.1.
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v16.5.0...v16.6.1)

---
updated-dependencies:
- dependency-name: dotenv
  dependency-version: 16.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-27 23:52:30 +02:00
1de446aede
Use npm ci to install dependencies
All checks were successful
CI / CI (push) Successful in 23s
2025-06-26 23:30:55 +02:00
dependabot[bot]
94467bd57e
Bump discord.js from 14.20.0 to 14.21.0 (#153)
All checks were successful
CI / CI (push) Successful in 23s
Bumps [discord.js](https://github.com/discordjs/discord.js/tree/HEAD/packages/discord.js) from 14.20.0 to 14.21.0.
- [Release notes](https://github.com/discordjs/discord.js/releases)
- [Changelog](https://github.com/discordjs/discord.js/blob/14.21.0/packages/discord.js/CHANGELOG.md)
- [Commits](https://github.com/discordjs/discord.js/commits/14.21.0/packages/discord.js)

---
updated-dependencies:
- dependency-name: discord.js
  dependency-version: 14.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-26 23:25:57 +02:00
dependabot[bot]
be0091546f
Bump jest from 30.0.2 to 30.0.3 (#152)
All checks were successful
CI / CI (push) Successful in 21s
Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 30.0.2 to 30.0.3.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.0.3/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-25 20:22:06 +02:00
6ce4ca6fb8
Add unique member count to botinfo
All checks were successful
CI / CI (push) Successful in 22s
2025-06-24 03:47:30 +02:00
a891b37a31
Update githash properly
All checks were successful
CI / CI (push) Successful in 22s
2025-06-24 03:46:17 +02:00
e79d2a7a7e
Add output to executeCommand return
All checks were successful
CI / CI (push) Successful in 22s
2025-06-24 03:40:44 +02:00
24e019e34f
Add latest git hash to botinfo command
All checks were successful
CI / CI (push) Successful in 21s
2025-06-24 03:38:24 +02:00
9c4a57a098
Assign sliced string 2025-06-24 03:35:54 +02:00
723f14405a
Rename uniqueMemberCount to uniqueMembers
All checks were successful
CI / CI (push) Successful in 22s
2025-06-24 03:29:41 +02:00
b9e0b6abd3
Use has instead of get
All checks were successful
CI / CI (push) Successful in 22s
2025-06-24 02:49:52 +02:00
28cc7d6f15
Use const 2025-06-24 02:48:41 +02:00
4f0e948a84
Remove getGuildCount test
This function has been renamed and stuff so I have to rethink how I'll
implement tests for it.
2025-06-24 02:47:01 +02:00
9611046393
Add uniqueMemberCount as a variable in setPresence
Some checks failed
CI / CI (push) Failing after 22s
2025-06-24 02:32:32 +02:00
bbaf2205ad
Add unique members to getGuildInfo 2025-06-24 02:32:10 +02:00
08b52216a9
Compare with strict equality
All checks were successful
CI / CI (push) Successful in 22s
2025-06-21 17:51:56 +02:00
SileNce5k
4d42609f89
Separate executeCommand into its own util function 2025-06-21 17:50:07 +02:00
f55b54eb49
Update package-lock.json
All checks were successful
CI / CI (push) Successful in 22s
2025-06-21 16:51:17 +02:00
SileNce5k
18c2d9dc18
Add dl command for downloading videos
All checks were successful
CI / CI (push) Successful in 1m25s
2025-06-21 16:47:36 +02:00
dependabot[bot]
600173771e
Bump discord.js from 14.19.3 to 14.20.0 (#145)
All checks were successful
CI / CI (push) Successful in 1m25s
Bumps [discord.js](https://github.com/discordjs/discord.js/tree/HEAD/packages/discord.js) from 14.19.3 to 14.20.0.
- [Release notes](https://github.com/discordjs/discord.js/releases)
- [Changelog](https://github.com/discordjs/discord.js/blob/14.20.0/packages/discord.js/CHANGELOG.md)
- [Commits](https://github.com/discordjs/discord.js/commits/14.20.0/packages/discord.js)

---
updated-dependencies:
- dependency-name: discord.js
  dependency-version: 14.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 22:04:02 +02:00
dependabot[bot]
6c514b7396
Bump @types/jest from 29.5.14 to 30.0.0 (#146)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.5.14 to 30.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-version: 30.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 22:03:35 +02:00
dependabot[bot]
ee21f4d75f
Bump jest from 29.7.0 to 30.0.0 (#144)
All checks were successful
CI / CI (push) Successful in 1m33s
Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 29.7.0 to 30.0.0.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.0.0/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-14 21:20:10 +02:00
dependabot[bot]
561925a18e
Bump tar-fs from 2.1.2 to 2.1.3 (#143)
Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 2.1.2 to 2.1.3.
- [Commits](https://github.com/mafintosh/tar-fs/commits)

---
updated-dependencies:
- dependency-name: tar-fs
  dependency-version: 2.1.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-14 21:19:35 +02:00
47dba48c76
Setpresence: Add a force subcommand to update presence immediately
All checks were successful
CI / CI (push) Successful in 1m23s
2025-06-09 22:03:37 +02:00
e92210e856
Save globalPrefix in the settings map
All checks were successful
CI / CI (push) Successful in 1m23s
2025-06-09 21:50:27 +02:00
ba99719c20
Update setPresence custom variable logic
All checks were successful
CI / CI (push) Successful in 1m25s
2025-06-09 20:38:34 +02:00
69c7fd8d53
Sort guilds by amount of members
All checks were successful
CI / CI (push) Successful in 1m22s
2025-06-01 13:24:18 +02:00
8b2314ef1c
Use discord time format
All checks were successful
CI / CI (push) Successful in 1m23s
2025-06-01 13:20:31 +02:00
e5f4295281
Add info on when presence updates
All checks were successful
CI / CI (push) Successful in 1m23s
2025-06-01 13:10:43 +02:00
15749b1a9f
Don't check for variables when updating presence 2025-06-01 13:08:06 +02:00
cf41d58b47
Only save the presence in the setPresence command
All checks were successful
CI / CI (push) Successful in 1m24s
2025-06-01 12:36:41 +02:00
ddb7cb128f
Require outside the function 2025-06-01 12:35:24 +02:00
e06c6640b7
Use setInterval on updatePresence instead of setTimeout
All checks were successful
CI / CI (push) Successful in 1m24s
2025-06-01 11:18:24 +02:00
ac03b9d136
Add totalMember variable to presence
All checks were successful
CI / CI (push) Successful in 1m24s
2025-05-18 14:17:23 +02:00
15fb1d544f
Add colon to source info
All checks were successful
CI / CI (push) Successful in 1m24s
2025-05-18 13:52:43 +02:00
f14eff3f9c
Fix check if valid first arg on timer command
Some checks failed
CI / CI (push) Has been cancelled
2025-05-17 11:25:30 +02:00
ed77c2aaf1
Allow dates to be used in timer command
All checks were successful
CI / CI (push) Successful in 1m24s
And some general cleanup of a couple of functions.
2025-05-17 11:15:05 +02:00
c74f323c1b
Log command executed before executing
All checks were successful
CI / CI (push) Successful in 1m22s
Sometimes I use shift() on the args to handle them which causes the
logging to not log everything 100% accurately
2025-05-13 00:19:35 +02:00
be8d9660ba
Fix permissions checking not working if false
All checks were successful
CI / CI (push) Successful in 1m22s
2025-05-12 20:11:34 +02:00
9ac900bc4d
fm: Add top artists and albums to moreHelp
All checks were successful
CI / CI (push) Successful in 1m23s
2025-05-12 18:00:23 +02:00
6592f25882
Shift arguments so time option works
All checks were successful
CI / CI (push) Successful in 1m23s
2025-05-12 17:56:38 +02:00
4f57499cca
Add top artists to fm command
All checks were successful
CI / CI (push) Successful in 1m23s
2025-05-12 17:52:03 +02:00
9cb5ea22cb
Add top albums to fm command
All checks were successful
CI / CI (push) Successful in 1m23s
2025-05-12 03:08:25 +02:00
aa2ab653b9
tdoss: Improve error handling of downloadImage + add the tdoss template
All checks were successful
CI / CI (push) Successful in 1m22s
Also use tdoss directory in the data/ dir so I won't have a bunch of
random directories in the data dir.
2025-05-10 20:39:51 +02:00
2f371d00ed
Add message latency to ping command
All checks were successful
CI / CI (push) Successful in 1m23s
2025-05-09 04:03:32 +02:00
28d1f6d0a2
Add total members to botinfo command
All checks were successful
CI / CI (push) Successful in 1m22s
2025-05-08 17:37:05 +02:00
7053495f40
Guilds: Add members count to each server
All checks were successful
CI / CI (push) Successful in 1m23s
2025-05-08 16:23:03 +02:00
0d10a15f5b
Remove unused variable
All checks were successful
CI / CI (push) Successful in 1m22s
2025-05-04 21:58:01 +02:00
0a7f8b9ba3
Add permissions to workflow
All checks were successful
CI / CI (push) Successful in 1m22s
Mainly to get rid of code scanning alert lol
2025-05-04 04:22:15 +02:00
dependabot[bot]
99e0f3a59d
Bump cross-spawn from 7.0.3 to 7.0.6 (#139)
Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6.
- [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6)

---
updated-dependencies:
- dependency-name: cross-spawn
  dependency-version: 7.0.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-04 04:20:13 +02:00
dependabot[bot]
8481f4563c
Bump @babel/helpers from 7.25.7 to 7.27.1 (#138)
Bumps [@babel/helpers](https://github.com/babel/babel/tree/HEAD/packages/babel-helpers) from 7.25.7 to 7.27.1.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.1/packages/babel-helpers)

---
updated-dependencies:
- dependency-name: "@babel/helpers"
  dependency-version: 7.27.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-04 04:17:19 +02:00
dependabot[bot]
d39a1a005a
Bump tar-fs from 2.1.1 to 2.1.2 (#137)
Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 2.1.1 to 2.1.2.
- [Commits](https://github.com/mafintosh/tar-fs/compare/v2.1.1...v2.1.2)

---
updated-dependencies:
- dependency-name: tar-fs
  dependency-version: 2.1.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-04 04:13:17 +02:00
007c6db3bc
Update discord.js to 14.19.3 and dotenv to 16.5.0
All checks were successful
CI / CI (push) Successful in 1m24s
2025-05-04 04:07:05 +02:00
b2c6c705c3
Add more types of whitelisting, plus support for subcommands
All checks were successful
CI / CI (push) Successful in 1m24s
2025-05-04 04:02:03 +02:00
99d6fc2401
Add whitelist for users as well
All checks were successful
CI / CI (push) Successful in 1m25s
2025-05-03 07:27:42 +02:00
30c7fd171f
Fix crash when command does not exist
All checks were successful
CI / CI (push) Successful in 1m24s
This regression appeared when I removed custom command support
2025-05-02 16:50:20 +02:00
6b45bc6799
Switch to gemini 2.0 LLM
All checks were successful
CI / CI (push) Successful in 1m26s
2025-05-02 16:42:25 +02:00
42 changed files with 3044 additions and 1607 deletions

View file

@ -2,6 +2,9 @@ name: CI
on: [push]
permissions:
contents: read
jobs:
CI:
runs-on: node
@ -13,6 +16,6 @@ jobs:
with:
node-version: 'latest'
- name: install dependencies
run: npm install
run: npm ci
- name: run tests
run: npm test

View file

@ -1,3 +1,4 @@
const savePresence = require("../../util/savePresence");
const setPresence = require("../../util/setPresence");
module.exports = {
@ -10,12 +11,20 @@ module.exports = {
,"Updates once a minute if custom variables are used."
,""
,"Custom Variables:"
,"${guilds},${prefix},${uptime}"],
,"${guilds},${prefix},${uptime},{members}"],
admin: true,
execute({message, client, args, globalPrefix}) {
const savePresence = require("../../util/savePresence");
execute({message, client, args, prefix}) {
let forceUpdate = false;
if(args.length > 1 && args[0] === "force") {
forceUpdate = true;
args.shift();
}
if(args.length < 1){
message.channel.send(`You need at least two arguments for this command, see \`${prefix}help setpresence\``)
return;
}
let presenceType = args[0].toLocaleUpperCase();
let sendText = "Updated presence";
let sendText = "Presence has been set.";
switch (presenceType) {
case "PLAY":
@ -46,8 +55,11 @@ module.exports = {
const firstArg = args[0].length + 1;
let temp = args.join(" ");
let presenceText = temp.slice(firstArg, temp.length)
setPresence({presenceText: presenceText,presenceType: presenceType, client: client, globalPrefix: globalPrefix});
savePresence(presenceType, presenceText, client);
if(forceUpdate)
setPresence({presenceText, presenceType, client})
else
sendText = `${sendText} It will update <t:${Math.floor((client.lastPresenceUpdate + 60000) / 1000)}:R>`
}
message.channel.send(sendText);

View file

@ -1,4 +1,5 @@
const calculateReloaded = require("../../util/calculateReloaded");
const executeCommand = require("../../util/executeCommand");
const reloadCommands = require("../../util/reloadCommands");
module.exports = {
@ -27,10 +28,12 @@ module.exports = {
let commitCount = stdout.split(/\r\n|\r|\n/).length - 1
sendText = `${sendText}\n\nLatest commits (${commitCount}):\n${stdout}`
if(sendText.length >= 2000){
sendText.slice(1955)
sendText = sendText.slice(1955)
sendText = `${sendText}\n... Message is too long to show everything`
}
message.channel.send(sendText)
const githash = executeCommand(`git`, ["rev-parse", "--short", "HEAD"]);
client.githash = githash.error ? "N/A" : githash.output;
if (err) console.log(stderr)
})
}

View file

@ -2,55 +2,106 @@ const sqlite3 = require('sqlite3').verbose();
module.exports = {
name: 'whitelist',
description: 'Whitelist a command in a specific server.',
description: 'Whitelist a command in a specific server, or for a user.',
admin: true,
async execute({message, args, prefix, client}) {
if(args.length < 2){
message.channel.send(`You need to supply the command and channel that you want to whitelist\n\`${prefix}whitelist <command> <server_id>\``);
if(args.length < 3){
message.channel.send(`You need to supply the command and guild that you want to whitelist\n\`${prefix}whitelist <user|guild> <command> <user_id|guild_id>\``);
return;
}
let command = args[0];
let guild = args[1];
if(guild === "this") guild = message.guild.id;
// TODO: Add ability to remove server from whitelist.
const whitelistedCommands = client.whitelist.get(guild);
if(whitelistedCommands && whitelistedCommands.includes(command)){
message.channel.send("Command is already whitelisted in that server.")
return;
}
// TODO: First check if the bot has access to that guild before whitelisting.
const databasePath = 'data/database.db'
const db = new sqlite3.Database(databasePath)
let err = false;
await new Promise((resolve, reject)=>{
db.run(`INSERT INTO whitelist (
serverId,
command,
dateAdded
) VALUES (?, ?, ?)`,
[
guild,
command,
new Date().getTime()
], function (error) {
if (error) {
console.error(error);
err = true;
resolve();
}else{
resolve();
}
});
})
if(!err){
if(whitelistedCommands){
whitelistedCommands.push(command);
client.whitelist.set(guild, whitelistedCommands);
}else {
client.whitelist.set(guild, command);
}
message.channel.send("Command has been whitelisted in that server.")
let option = args[0];
let command = args[1];
let guildOrUser = args[2];
} else message.channel.send("Could not whitelist the server with that command. Check the logs.")
if(option === "guild" || option === "server"){
if(guildOrUser === "this") guildOrUser = message.guild.id;
const whitelistedCommands = client.whitelist.guild.get(guildOrUser);
if(whitelistedCommands && whitelistedCommands.includes(command)){
message.channel.send("Command is already whitelisted in that server.")
return;
}
// TODO: First check if the bot has access to that guild before whitelisting.
const databasePath = 'data/database.db'
const db = new sqlite3.Database(databasePath)
let err = false;
await new Promise((resolve, reject)=>{
db.run(`INSERT INTO guild_whitelist (
guildId,
command,
dateAdded
) VALUES (?, ?, ?)`,
[
guildOrUser,
command,
new Date().getTime()
], function (error) {
if (error) {
console.error(error);
err = true;
resolve();
}else{
resolve();
}
});
})
db.close();
if(!err){
if(whitelistedCommands){
whitelistedCommands.push(command);
client.whitelist.guild.set(guildOrUser, whitelistedCommands);
}else {
client.whitelist.guild.set(guildOrUser, command);
}
message.channel.send(`"${command}" has been whitelisted in that server.`)
} else message.channel.send("Could not whitelist the server with that command. Check the logs.")
return;
}else if (option === "user"){
const whitelistedCommands = client.whitelist.user.get(guildOrUser);
if(whitelistedCommands && whitelistedCommands.includes(command)){
message.channel.send("Command is already whitelisted for that user.")
return;
}
const databasePath = 'data/database.db'
const db = new sqlite3.Database(databasePath)
let err = false;
await new Promise((resolve, reject)=>{
db.run(`INSERT INTO user_whitelist (
userId,
command,
dateAdded
) VALUES (?, ?, ?)`,
[
guildOrUser,
command,
new Date().getTime()
], function (error) {
if (error) {
console.error(error);
err = true;
resolve();
}else{
resolve();
}
});
})
db.close();
if(!err){
if(whitelistedCommands){
whitelistedCommands.push(command);
client.whitelist.user.set(guildOrUser, whitelistedCommands);
}else {
client.whitelist.user.set(guildOrUser, command);
}
message.channel.send(`"${command}" has been whitelisted for ${guildOrUser}.`)
} else message.channel.send("Could not whitelist the server with that command. Check the logs.")
return;
}
// TODO: Add ability to remove server from whitelist.
}
};

View file

@ -1,6 +1,6 @@
const {EmbedBuilder} = require('discord.js');
const getCreationDate = require('../../util/getCreationDate');
const getGuildCount = require('../../util/getGuildCount');
const getGuildInfo = require('../../util/getGuildInfo');
@ -8,15 +8,28 @@ module.exports = {
name: 'botinfo',
description: 'Shows information about the bot',
execute({message, client, prefix}) {
let guildCount = getGuildCount(client)
let guildInfo = getGuildInfo(client)
let descriptionArr = [`Name: ${client.user.username}`,
`Prefix: ${prefix}`,
`Total Servers: ${guildInfo.guildCount}`,
`Total Members: ${guildInfo.totalMembers} (${guildInfo.uniqueMemberCount} unique)`,
`Total Commands: ${client.commands.size}`,
`Creation Date: ${getCreationDate(client)}`,
`Source: [Click Here](https://github.com/SileNce5k/discord_bot)`,
`Current Version: ${client.githash}`
]
let description = "";
descriptionArr.forEach(desc => {
description += `${desc}\n`;
})
const embed = new EmbedBuilder()
.setColor(15780145)
.setTitle("Information about bot")
.setTimestamp()
.setAuthor({name: client.user.username, iconURL: client.user.avatarURL({ format: 'png', dynamic: true, size: 2048 })})
.addFields({
name: "General info", value: `Name: ${client.user.username}\nPrefix: ${prefix}\nTotal Servers: ${guildCount}\nTotal Commands: ${client.commands.size}\nCreation Date: ${getCreationDate(client)}\nSource: [Click Here](https://github.com/SileNce5k/discord_bot)`,
},)
.setDescription(description)
message.channel.send({embeds :[embed]})

View file

@ -3,10 +3,10 @@ module.exports = {
description: 'Returns guild names',
admin: true,
execute({message, client}) {
let guildNames = "";
client.guilds.cache.each(guild => {
guildNames = guildNames + guild.name + "\n";
});
let guildNames = client.guilds.cache
.sort((a, b) => b.memberCount - a.memberCount)
.map(guild => `${guild.name} (${guild.memberCount} members)`)
.join("\n");
message.channel.send(guildNames)
}
};

View file

@ -13,7 +13,6 @@ module.exports = {
execute({ message, args, prefix, client }) {
let commands = ""
let commandFiles = getSubdirFiles('commands/')
let x = false;
let fieldName = `Page [[page]]/${Math.round(client.commands.size / 10)}`;
let iteration = 0;
let num_in_args = false;
@ -32,7 +31,6 @@ module.exports = {
fieldName = fieldName.replace("[[page]]", page);
const max_commands = iteration + 10;
if (x) return;
const embed = new EmbedBuilder()
.setColor(15780145)

103
commands/info/mc.js Normal file
View file

@ -0,0 +1,103 @@
// Code is taken from https://github.com/stphnduvall/mcstatus/blob/master/src/index.ts
// and converted to pure js.
const net = require('net')
const { EmbedBuilder } = require('discord.js');
module.exports = {
name: 'mc',
description: 'get minecraft server information',
hidden: true,
needsWhitelist: true,
async execute({ message, args }) {
let host = "";
let port = 25565;
if (args[0]) host = args[0];
if (host.includes(":")) {
port = host.replace(/.+(?:\:)/g, "");
host = host.match(/.+(?:\:)/g, "")[0].replace(":", "");
}
if(host === "") return message.channel.send("No host provided")
let info = await getMinecraftServerInfo(host, port);
if (info) {
const embed = new EmbedBuilder()
embed.setColor("#ee7939")
embed.setTimestamp()
embed.addFields(
{ name: "ping", value: info.ping.toString(), inline: false },
{ name: "Player Count", value: info.playercount.toString(), inline: false },
{ name: "Max Players", value: info.maxPlayers.toString(), inline: false },
{ name: "MOTD", value: info.motd, inline: false },
)
message.channel.send({ embeds: [embed] });
} else {
message.channel.send("Something went wrong\nThe minecraft server is likely not reachable from the discord bot")
}
}
};
async function getMinecraftServerInfo(host, port = 25565) {
let serverInfo = {
ping: undefined,
maxPlayers: undefined,
version: undefined,
playercount: undefined,
motd: undefined
}
let startTime = new Date();
let data;
let ping;
serverInfo = await new Promise((resolve) => {
const client = net.connect({ host, port }, () => {
ping = Math.round(new Date().getMilliseconds() - startTime.getMilliseconds());
let buff = Buffer.from([0xFE, 0x01]);
client.write(buff);
})
let error = false;
client.on('data', (d) => {
data = d.toString()
client.destroy();
})
client.once('error', (error) => {
console.error(error)
error = true;
})
client.once('connectionAttemptFailed', (ip) => {
console.error("in attempt failed")
error = true;
})
client.once('connectionAttemptTimeout', (ip) => {
console.error("in attempt timeout")
error = true;
})
client.on('close', () => {
if (!error) {
let _serverInfo = data?.split('\x00\x00\x00');
if (!_serverInfo) {
console.log("Something went wrong.")
resolve(serverInfo)
}
serverInfo.version = _serverInfo[2].replace(/\u0000/g, '')
serverInfo.motd = _serverInfo[3].replace(/\u0000/g, '')
serverInfo.playercount = Number(_serverInfo[4].replace(/\u0000/g, ''))
serverInfo.maxPlayers = Number(_serverInfo[5].replace(/\u0000/g, ''))
serverInfo.ping = Number(ping)
resolve(serverInfo)
} else {
resolve(0)
}
})
})
return serverInfo
}

View file

@ -16,9 +16,9 @@ module.exports = {
let user = message.guild.members.cache.get(info);
let guildPfp = user.avatarURL({format: 'png', dynamic: true, size: 4096});
let globalPfp = user.user.avatarURL({format: 'png', dynamic: true, size: 4096});
let sendText = `Global pfp:\n${globalPfp}`;
let sendText = `${globalPfp}`;
if(guildPfp != null){
sendText = `${sendText}\nGuild pfp:\n${guildPfp}`;
sendText = `Global pfp${sendText}\nGuild pfp:\n${guildPfp}`;
}
message.channel.send(sendText)

View file

@ -15,6 +15,7 @@ module.exports = {
{name: "Server Name", value: message.guild.name, inline: false},
{name: "Created", value: convertDateToISOString(message.guild.createdAt), inline: false},
{name: "Members", value: message.guild.memberCount.toString(), inline: false},
{name: "Channels", value: message.guild.channels.channelCountWithoutThreads.toString(), inline: false},
])

71
commands/misc/dl.js Normal file
View file

@ -0,0 +1,71 @@
const path = require('path');
const fs = require('fs')
const executeCommand = require('../../util/executeCommand');
module.exports = {
name: 'dl',
description: 'Download a video',
moreHelp: [
"Usage: <prefix>dl <url>"
],
async execute({message, args}) {
const downloadsDir = path.resolve(process.cwd(), 'data', 'downloads', Date.now().toString());
const cookieFilepath = path.resolve(process.cwd(), 'data', 'cookies.txt')
if(!fs.existsSync(cookieFilepath)) {
message.channel.send("Some dependencies are needed for the command to work properly. Please let the bot's owner know.")
return;
}
fs.mkdirSync(downloadsDir, {recursive: true});
let url;
if(args.length > 0){
if(args[0].charAt(0) === '<' && args[0].charAt(args[0].length - 1) === '>'){
args[0] = args[0].slice(1, args[0].length - 1)
}
try {
url = new URL(args[0]);
url = url.href;
} catch (error) {
this.cleanUp(downloadsDir);
message.channel.send("Could not parse the provided argument as a URL.");
return;
}
} else {
this.cleanUp(downloadsDir);
return message.channel.send("You have to provide a URL in an argument.")
}
const originalMessage = await message.channel.send("Downloading video...")
if(executeCommand("yt-dlp", [url, "-P", downloadsDir, "--cookies", cookieFilepath]).error){
originalMessage.edit("An error occurred when downloading the video.");
this.cleanUp(downloadsDir);
return;
}
let files = fs.readdirSync(downloadsDir);
if(files.length < 1) {
this.cleanUp(downloadsDir);
originalMessage.edit("Something went wrong when downloading the video.")
return;
}
const filename = files[0];
await originalMessage.edit({
content: null,
files: [{
attachment: path.resolve(downloadsDir, filename)
}]})
this.cleanUp(downloadsDir);
},
cleanUp(downloadsDir){
fs.rmSync(downloadsDir, {force: true, recursive: true});
},
}

View file

@ -9,6 +9,7 @@ const getTopArtists = require("../../util/lastfm/getTopArtists");
const getTopAlbums = require("../../util/lastfm/getTopAlbums");
const {EmbedBuilder} = require('discord.js');
const getNickname = require('../../util/getNickname');
const isWhitelisted = require("../../util/isWhitelisted");
module.exports = {
name: 'fm',
description: 'Last fm commands. See `<prefix>help fm` for more info.',
@ -16,6 +17,8 @@ module.exports = {
"They behave the same (for example: `<prefix>fmtt` and `<prefix>fm tt`)",
"Set username: `<prefix>fmset <lastfm_username>`",
"Get current scrobble: `<prefix>fm`",
"Get top artists `<prefix>fmas`",
"Get top albums: `<prefix>fmabl`",
"Get top tracks: `<prefix>fmtt`",
"Get album cover for current scrobble: `<prefix>fmcover`",
"Get a roast from an LLM using your top artists and albums: `<prefix>fmroast`"
@ -34,12 +37,31 @@ module.exports = {
args.shift();
sendText = await getTopTracks(message.author.id, args, message.guild);
break;
case "topalbums":
case "topalbum":
case "abl":
args.shift();
sendText = await getTopAlbums(message.author.id, args, message.guild)
break;
case "topartists":
case "topartist":
case "ta":
case "as":
args.shift();
sendText = await getTopArtists(message.author.id, args, message.guild);
break;
case "cover":
sendText = await getCurrentCover(message.author.id, message.guild);
break;
case "roast":
const commandName = "fmroast"
if(!isWhitelisted.isWhitelisted(commandName, client.whitelist, message.guild.id, message.author.id, isWhitelisted.whitelistTypes.EITHER)) {
message.channel.send("fmroast has not been whitelisted for you or this server.")
return;
}
let topArtists = await getTopArtists(message.author.id, ["yearly"], message.guild, true);
let topAlbums = await getTopAlbums(message.author.id, ["yearly"], message.guild, true);
message.channel.sendTyping();
let result = await roast(topArtists, topAlbums);
let parse = parseMention(message.author.id, message.guild)
@ -65,11 +87,13 @@ module.exports = {
if(sendText.embed != null){
let parse = parseMention(message.author.id, message.guild)
let user = message.guild.members.cache.get(parse);
let roleColor = 15788778;
if (user.roles.color) {
roleColor = user.roles.color.color;
if(!sendText.embed.data.color){
let roleColor = 15788778;
if (user.roles.color) {
roleColor = user.roles.color.color;
}
sendText.embed.setColor(roleColor);
}
sendText.embed.setColor(roleColor);
message.channel.send({embeds :[sendText.embed]})
}else{
message.channel.send(sendText.text.replaceAll("<prefix>", prefix));

28
commands/misc/ig.js Normal file
View file

@ -0,0 +1,28 @@
module.exports = {
name: 'ig',
description: 'Replaces Instagram links with kkinstagram and deletes the original message',
execute({message, args}) {
const noUrlErr = "You need to provide an Instagram link to use this command."
if(args.length < 1) {
message.channel.send(noUrlErr);
return;
}
let replacedLink = "";
const regex = /(?<=\/)(instagram)\.com/g
if(args[0].startsWith("https://") || args[0].startsWith("http://")){
replacedLink = args[0].replace(regex, "kkinstagram.com");
}
if(replacedLink.length === 0 || args[0] === replacedLink){
message.channel.send(noUrlErr);
return;
}
message.channel.send(replacedLink);
try{
message.delete()
}catch(err){
console.error(`${this.name}: An error occurred while trying to delete the original message.`)
console.error(err);
}
}
};

View file

@ -1,7 +1,11 @@
module.exports = {
name: 'ping',
description: 'Just ping.',
execute({message, client}) {
message.channel.send(`Pong.\n${client.ws.ping}ms`)
async execute({message, client}) {
let apiLatency = client.ws.ping;
const time = process.hrtime.bigint();
let initialMessage = await message.channel.send(`Pong.`)
const diff = Number(process.hrtime.bigint() - time) * 1e-6;
initialMessage.edit(`Pong.\nAPI latency: ${apiLatency} ms\nMessage latency: ${diff.toFixed(0)} ms.`);
}
};

View file

@ -1,10 +1,11 @@
const setServerPrefix = require("../../util/setServerPrefix");
const { PermissionsBitField } = require('discord.js');
module.exports = {
name: 'prefix',
description: 'Change the prefix of the bot in this server.',
execute({ message, client, args, prefix }) {
if (!message.member.permissions.has('MANAGE_GUILD')) {
if (!message.member.permissions.has(PermissionsBitField.Flags.ManageGuild)) {
message.channel.send("You do not have sufficient permissions(MANAGE_GUILD) to change the prefix of this server.")
return;
}

View file

@ -1,6 +1,7 @@
module.exports = {
name: 'say',
description: 'Repeats arguments',
admin: true,
execute({message, args}) {
if(args.length == 0)

View file

@ -3,16 +3,18 @@ const fs = require('fs');
const path = require('path');
const { writeFile } = require('node:fs/promises')
const { Readable } = require('node:stream')
const executeCommand = require('../../util/executeCommand');
module.exports = {
name: 'tdoss',
description: 'Combine picture with tdoss album cover template',
async execute({ message, args }) {
let dataDir = path.resolve(__dirname, '..', '..', 'data');
const directory = path.resolve(dataDir, Math.floor(new Date).toString())
fs.mkdirSync(directory)
let tdossDir = path.resolve(process.cwd(), 'data', 'tdoss');
const tdossTemplate = path.resolve(process.cwd(), 'resources', 'tdoss_template.png');
const directory = path.resolve(tdossDir, Math.floor(new Date).toString())
fs.mkdirSync(directory, {recursive: true})
let url = "";
if(message.attachments.size > 0){
@ -44,15 +46,22 @@ module.exports = {
}
// TODO: Download with correct extension.
message.channel.sendTyping();
if(await this.downloadImage(url, path.resolve(directory, "input.png")) != 0){
message.channel.send("Something went wrong during the download.\nThe link might be unreachable for the bot or it's not an image.")
let downloadResult = await this.downloadImage(url, path.resolve(directory, "input.png"));
if(downloadResult.value != this.ERROR_CODES.SUCCESS){
if(downloadResult.value === this.ERROR_CODES.FETCH_ERROR){
message.channel.send(`Failed to download the provided image, got error '${downloadResult.errorMessage}'`);
}else if (downloadResult.value === this.ERROR_CODES.HTTP_ERROR){
message.channel.send(`Failed to download the provided image, got response status '${downloadResult.errorMessage}'`);
}else if(downloadResult.value === this.ERROR_CODES.NOT_IMAGE){
message.channel.send(`The provided url was not an image.`)
}
fs.rmSync(`${directory}`, {recursive: true})
return;
}
const command = `magick ${dataDir}/tdoss_template.png \\( ${directory}/input.png -resize 800x800^ -gravity center -extent 1000x1000 \\) -compose dst-over -composite ${directory}/tdoss_result.png`;
if (this.executeCommand(command).error === true) {
const commandArgs = [tdossTemplate, "(", `${directory}/input.png`, "-resize", "800x800^", "-gravity", "center", "-extent", "1000x1000", ")", "-compose", "dst-over", "-composite", `${directory}/tdoss_result.png`]
if (executeCommand("magick", commandArgs).error === true) {
message.channel.send("Something went wrong during image manipulation.\nTry again and if it keeps happening, contact the owner of the bot.")
fs.rmSync(`${directory}`, {recursive: true})
return
@ -66,30 +75,26 @@ module.exports = {
await message.channel.send({files: [final_image]})
fs.rmSync(`${directory}`, {recursive: true})
},
executeCommand(command) {
console.log("Executing:", command)
try {
const output = execSync(command, { encoding: 'utf-8' })
if (output.length != 0)
console.log(output)
} catch (error) {
console.error(`Error executing ${command.split(" ")[0]} command:`, error);
return { error: true };
}
return { error: false };
},
// https://stackoverflow.com/a/77210219
async downloadImage(url, path) {
const res = await fetch(url);
if(!res.ok) return 1;
if(!res.headers.get('content-type').startsWith("image")) return 2;
let res;
try {
res = await fetch(url);
} catch (error) {
return {value: this.ERROR_CODES.FETCH_ERROR, errorMessage: error.cause?.message || error.message};
}
if(!res.ok) return {value: this.ERROR_CODES.HTTP_ERROR, errorMessage: res.status.toString()};
const contentType = res.headers.get('content-type');
if(!contentType || !contentType.startsWith("image")) return {value: this.ERROR_CODES.NOT_IMAGE, errorMessage: contentType || "No content-type header"};
const stream = Readable.fromWeb(res.body)
await writeFile(path, stream);
return 0;
return {value: this.ERROR_CODES.SUCCESS, errorMessage: ""};
},
ERROR_CODES: {
SUCCESS: 0,
HTTP_ERROR: 1,
NOT_IMAGE: 2,
FETCH_ERROR: 3
}
}

View file

@ -4,11 +4,11 @@ const parseTime = require('../../util/timer/parseTime');
const showTimer = require('../../util/timer/showTimer');
module.exports = {
name: "timer",
description: "Set a timer for a time in minutes.",
description: "Set a timer for a date or time duration.",
moreHelp: ["Usage:"
,"`<prefix>timer [add|create] <time_in_minutes> <message_to_send>`"
,"`<prefix>timer <time>(d|h|m|s|t) <message_to_send>`"
,"`<prefix>timer <time_in_minutes> <message_to_send>`"
,"`<prefix>timer <future_date> <message_to_send>`"
,"`<prefix>timer edit <timer_id> <new_time_in_minutes> <new_message_to_send>` (not implemented)"
,"`<prefix>timer [delete|remove] <timer_id>`"
,"`<prefix>timer show <timer_id>`"
@ -18,7 +18,8 @@ module.exports = {
switch (args[0]) {
case "add":
case "create":
sendText = await createTimer(message, args, false);
args.shift()
sendText = await createTimer(message, args);
break;
case "edit":
sendText = "not implemented yet"
@ -37,8 +38,8 @@ module.exports = {
sendText = "Please specify a time, and a message to send after the timer has finished";
break;
}
if(!isNaN(parseTime(args[0], Math.floor(new Date() / 1000))))
sendText = await createTimer(message, args, true);
if(!isNaN(parseTime(args[0], Math.floor(new Date() / 1000))) || !isNaN(Date.parse(args[0])))
sendText = await createTimer(message, args);
break;
}
message.channel.send(sendText);

28
commands/misc/x.js Normal file
View file

@ -0,0 +1,28 @@
module.exports = {
name: 'x',
description: 'Replaces X/Twitter links with fxtwitter and deletes the original message',
execute({message, args}) {
const noUrlErr = "You need to provide an X or twitter link to use this command."
if(args.length < 1) {
message.channel.send(noUrlErr);
return;
}
let replacedLink = "";
const regex = /(?<=\/)(x|twitter)\.com/g
if(args[0].startsWith("https://") || args[0].startsWith("http://")){
replacedLink = args[0].replace(regex, "fxtwitter.com");
}
if(replacedLink.length === 0 || args[0] === replacedLink){
message.channel.send(noUrlErr);
return;
}
message.channel.send(replacedLink);
try{
message.delete()
}catch(err){
console.error(`${this.name}: An error occurred while trying to delete the original message.`)
console.error(err);
}
}
};

3589
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -5,8 +5,8 @@
"main": "server.js",
"dependencies": {
"@zuzak/owo": "^1.14.1",
"discord.js": "^14.16.3",
"dotenv": "^16.4.5",
"discord.js": "^14.21.0",
"dotenv": "^17.2.1",
"seedrandom": "^3.0.5",
"sqlite3": "^5.1.6"
},
@ -20,7 +20,7 @@
"author": "SileNce5k",
"license": "UNLICENSE",
"devDependencies": {
"@types/jest": "^29.5.14",
"jest": "^29.7.0"
"@types/jest": "^30.0.0",
"jest": "^30.0.5"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

View file

@ -1,6 +1,7 @@
const fs = require('fs');
const createInitialConfig = require("./util/createInitialConfig")
const convertJSONToSQL = require('./util/timer/convertJSONToSQL');
const executeCommand = require('./util/executeCommand.js');
const sqlite3 = require('sqlite3').verbose();
if(!fs.existsSync("./data/config.json")) {
createInitialConfig();
@ -44,13 +45,20 @@ const {
client.settings = new Collection();
client.commands = new Collection();
client.serverPrefixes = new Collection();
client.whitelist = new Collection();
client.whitelist = {
guild: new Collection(),
user: new Collection()
}
process.env.TZ = "UTC";
createAndLoadWhitelistTable(client.whitelist);
client.settings.set("presenceType", presenceType);
client.settings.set("presenceText", presenceText);
client.settings.set("globalPrefix", globalPrefix);
const githash = executeCommand(`git`, ["rev-parse", "--short", "HEAD"]);
client.githash = githash.error ? "N/A" : githash.output;
const reloadCommands = require("./util/reloadCommands.js");
const onMessage = require('./server/message');
@ -71,7 +79,7 @@ client.once('disconnect', () => {
});
client.on('messageCreate', async message => {
onMessage(client, owners, message, globalPrefix);
onMessage(client, owners, message);
});

View file

@ -1,34 +1,61 @@
const sqlite3 = require('sqlite3').verbose();
module.exports = async function (clientWhitelist) {
const db = new sqlite3.Database('data/database.db');
await createGuildWhitelist(db);
await createUserWhitelist(db);
db.close();
loadWhitelist(clientWhitelist);
}
async function createGuildWhitelist(db) {
await new Promise ((resolve, reject)=>{
db.run(
`CREATE TABLE IF NOT EXISTS whitelist (
`CREATE TABLE IF NOT EXISTS guild_whitelist (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
serverId TEXT,
guildId TEXT,
command TEXT,
dateAdded INTEGER)`,
(err) => {
if (err) {
console.error(`Error while creating table 'whitelist': ${err}`);
db.close();
console.error(`Error while creating table 'guild_whitelist': ${err}`);
reject(err);
} else {
console.log("Table 'whitelist' created successfully.");
console.log("Table 'guild_whitelist' created successfully.");
resolve();
}
db.close();
}
);
})
loadWhitelist(clientWhitelist);
});
}
async function createUserWhitelist(db) {
await new Promise ((resolve, reject)=>{
db.run(
`CREATE TABLE IF NOT EXISTS user_whitelist (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
userId TEXT,
command TEXT,
dateAdded INTEGER)`,
(err) => {
if (err) {
db.close();
console.error(`Error while creating table 'user_whitelist': ${err}`);
reject(err);
} else {
console.log("Table 'user_whitelist' created successfully.");
resolve();
}
}
);
});
}
async function loadWhitelist(clientWhitelist) {
const db = new sqlite3.Database('data/database.db');
let rows = await new Promise((resolve) => {
db.all(`SELECT * FROM whitelist`, function (error, rows){
let guildRows = await new Promise((resolve) => {
db.all(`SELECT * FROM guild_whitelist`, function (error, rows){
if(error){
console.error("Failed to read whitelist table")
console.error("Failed to read guild_whitelist table")
console.error(error);
resolve([]);
}else{
@ -36,13 +63,33 @@ async function loadWhitelist(clientWhitelist) {
}
});
});
rows.forEach(row => {
if(clientWhitelist.has(row.serverId)){
let oldEntry = clientWhitelist.get(row.serverId);
let userRows = await new Promise((resolve) => {
db.all(`SELECT * FROM user_whitelist`, function (error, rows){
if(error){
console.error("Failed to read user_whitelist table")
console.error(error);
resolve([]);
}
})
})
guildRows.forEach(row => {
if(clientWhitelist.guild.has(row.guildId)){
let oldEntry = clientWhitelist.guild.get(row.guildId);
oldEntry.push(row.command)
clientWhitelist.set(row.serverId, oldEntry)
clientWhitelist.guild.set(row.guildId, oldEntry)
}else {
clientWhitelist.set(row.serverId, [row.command])
clientWhitelist.guild.set(row.guildId, [row.command])
}
});
userRows.forEach(row => {
if(clientWhitelist.user.has(row.userId)){
let oldEntry = clientWhitelist.user.get(row.userId);
oldEntry.push(row.command)
clientWhitelist.user.set(row.userId, oldEntry)
}else {
clientWhitelist.user.set(row.userId, [row.command])
}
});
db.close();
}

View file

@ -1,5 +1,6 @@
module.exports = function(client, owners, message, globalPrefix){
let prefix = globalPrefix;
const isWhitelisted = require('../util/isWhitelisted')
module.exports = function(client, owners, message){
let prefix = client.settings.get("globalPrefix");
let serverPrefix = client.serverPrefixes.get(message.guild.id);
if (serverPrefix) {
prefix = serverPrefix;
@ -19,17 +20,15 @@ module.exports = function(client, owners, message, globalPrefix){
const commandName = args.shift().toLowerCase();
const command = client.commands.get(commandName);
if(command.needsWhitelist){
let isWhitelisted = client.whitelist.get(message.guild.id)?.includes(command.name);
if(!isWhitelisted){
message.channel.send(`\`${command.name}\` is not whitelisted in this server. The bot admin needs to whitelist the command in this server for it to work`)
return;
}
if(!command) return;
if(command.needsWhitelist && !isWhitelisted.isWhitelisted(command.name, client.whitelist, message.guild.id, message.author.id, isWhitelisted.whitelistTypes.EITHER)){
message.channel.send(`\`${command.name}\` is not whitelisted for you or this server. The bot admin needs to whitelist the command for you or this server.`)
return;
}
if (command.admin && owners.indexOf(message.author.id.toString()) == -1) return;
try {
command.execute({ message: message, args: args, client: client, prefix: prefix, owners: owners, globalPrefix: globalPrefix})
console.log(`${message.author.username}(id: ${message.author.id}) executed ${command.name} with '${args}' as arguments`)
command.execute({ message: message, args: args, client: client, prefix: prefix, owners: owners})
} catch (error) {
let divider = "------------------------"
console.log(divider)

View file

@ -3,7 +3,15 @@ const checkTimer = require('../util/timer/checkTimer');
const updatePresence = require('../util/updatePresence');
module.exports = function(client, enableLoginMessage, loginChannel, loginMessage) {
updatePresence(client)
client.lastPresenceUpdate = Date.now()
setInterval(() => {
updatePresence(client)
client.lastPresenceUpdate = Date.now()
}, 60 * 1000);
console.log('Ready!');
if (enableLoginMessage === true)
try{

View file

@ -1,17 +0,0 @@
const getGuildCount = require('../util/getGuildCount');
test("Testing getGuildCount", () => {
for(let i = 1; i < 200000; i = i+i*30 ){
let client = {guilds: {cache: new Map()}}
client.guilds.cache.each = client.guilds.cache.forEach;
for(let j = 0; j < i; j++){
client.guilds.cache.set(`num: ${j}`, j);
}
expect(getGuildCount(client)).toBe(i);
}
})

View file

@ -0,0 +1,30 @@
const isWhitelisted = require('../util/isWhitelisted');
const whitelist = {
guild: new Map(),
user: new Map()
}
whitelist.guild.set(20, ["test1", "test3"]);
whitelist.guild.set(21, ["test2", "test4"]);
whitelist.user.set(1001, ["test1", "test3"]);
whitelist.user.set(1002, ["test2", "test4"]);
test('Test isWhitelisted', () => {
expect(isWhitelisted.isWhitelisted("test1", whitelist, 20, 1001, isWhitelisted.whitelistTypes.BOTH)).toBe(true);
expect(isWhitelisted.isWhitelisted("test3", whitelist, 20, 1002, isWhitelisted.whitelistTypes.BOTH)).toBe(false);
expect(isWhitelisted.isWhitelisted("test3", whitelist, 21, 1001, isWhitelisted.whitelistTypes.BOTH)).toBe(false);
expect(isWhitelisted.isWhitelisted("test1", whitelist, 20, 1001, isWhitelisted.whitelistTypes.USER_ONLY)).toBe(true);
expect(isWhitelisted.isWhitelisted("test1", whitelist, 21, 1001, isWhitelisted.whitelistTypes.USER_ONLY)).toBe(true);
expect(isWhitelisted.isWhitelisted("test1", whitelist, 20, 1002, isWhitelisted.whitelistTypes.USER_ONLY)).toBe(false);
expect(isWhitelisted.isWhitelisted("test4", whitelist, 20, 1001, isWhitelisted.whitelistTypes.GUILD_ONLY)).toBe(false);
expect(isWhitelisted.isWhitelisted("test4", whitelist, 21, 1001, isWhitelisted.whitelistTypes.GUILD_ONLY)).toBe(true);
expect(isWhitelisted.isWhitelisted("test4", whitelist, 21, 1002, isWhitelisted.whitelistTypes.GUILD_ONLY)).toBe(true);
expect(isWhitelisted.isWhitelisted("test1", whitelist, 20, 1002, isWhitelisted.whitelistTypes.EITHER)).toBe(true);
expect(isWhitelisted.isWhitelisted("test2", whitelist, 20, 1002, isWhitelisted.whitelistTypes.EITHER)).toBe(true);
expect(isWhitelisted.isWhitelisted("test3", whitelist, 21, 1001, isWhitelisted.whitelistTypes.EITHER)).toBe(true);
expect(isWhitelisted.isWhitelisted("test4", whitelist, 20, 1001, isWhitelisted.whitelistTypes.EITHER)).toBe(false);
});

15
util/executeCommand.js Normal file
View file

@ -0,0 +1,15 @@
const { execFileSync } = require('child_process');
module.exports = function(command, commandArgs, verbose=false) {
if (typeof command !== 'string' || !Array.isArray(commandArgs)) return { error: true };
console.log("Executing:", command, commandArgs.join(" "));
let output;
try {
output = execFileSync(command, commandArgs, {encoding: 'utf8'})
if (output.length !== 0 && verbose)
console.log(output)
} catch (error) {
console.error(`Error executing ${command} command:`, error);
return { error: true };
}
return { error: false, output};
}

View file

@ -1,8 +0,0 @@
module.exports = function(client){
let guildCount = 0;
client.guilds.cache.each(() => {
guildCount++
});
return guildCount;
}

16
util/getGuildInfo.js Normal file
View file

@ -0,0 +1,16 @@
module.exports = function(client){
let guildCount = 0;
let totalMembers = 0;
const uniqueMembers = new Map();
client.guilds.cache.each(guild => {
guildCount++
totalMembers += guild.memberCount;
guild.members.cache.each(member => {
if(!uniqueMembers.has(member.id)){
uniqueMembers.set(member.id, true);
}
})
});
const uniqueMemberCount = uniqueMembers.size;
return {guildCount, totalMembers, uniqueMemberCount};
}

22
util/isWhitelisted.js Normal file
View file

@ -0,0 +1,22 @@
// type: 0 for both, 1 for guild, 2 for userId, 3 for either.
function isWhitelisted(command, whitelist, guildId, userId, type=0){
const isGuildWhitelisted = Boolean(whitelist.guild.get(guildId)?.includes(command))
if(type === whitelistTypes.GUILD_ONLY) return isGuildWhitelisted;
if(type === whitelistTypes.BOTH && !isGuildWhitelisted) return false;
if(type === whitelistTypes.EITHER && isGuildWhitelisted) return true;
const isUserWhitelisted = Boolean(whitelist.user.get(userId)?.includes(command))
if(type === whitelistTypes.USER_ONLY) return isUserWhitelisted;
if(type === whitelistTypes.BOTH && isGuildWhitelisted && isUserWhitelisted) return true;
if(type === whitelistTypes.EITHER && (isGuildWhitelisted || isUserWhitelisted)) return true;
return false;
}
const whitelistTypes = {
BOTH: 0,
GUILD_ONLY: 1,
USER_ONLY: 2,
EITHER: 3
}
module.exports = {isWhitelisted, whitelistTypes}

View file

@ -2,6 +2,11 @@ const getNickname = require("../getNickname");
const parseMention = require("../parseMention");
const getFmUsername = require("./getFmUsername");
const {EmbedBuilder} = require('discord.js');
const path = require('path');
const fs = require('fs');
const { writeFile } = require('node:fs/promises')
const executeCommand = require('../executeCommand');
const { Readable } = require('node:stream')
require("dotenv").config();
module.exports = async function (userID, guild) {
@ -58,21 +63,56 @@ module.exports = async function (userID, guild) {
sendText.text = tracks.errorMsg;
return sendText;
}
let coverDir = path.resolve(process.cwd(), 'data', 'covers');
let color = "#C27D0E"
const directory = path.resolve(coverDir, Math.floor(new Date).toString())
fs.mkdirSync(directory, {recursive: true})
const coverFile = path.resolve(directory, "cover")
let downloadResult = await downloadImage(tracks[0].cover, coverFile);
if(downloadResult.value === ERROR_CODES.SUCCESS){
const commandArgs = [`${coverFile}.${downloadResult.ext}`, "-resize", "1x1", "txt:-"]
let res = executeCommand("magick", commandArgs);
if(!res.error) color = res.output.split("\n")[1].split(" ")[3].slice(0,7);
}
const embed = new EmbedBuilder()
.setAuthor({name: `Now playing - ${nickname}`, iconURL: user.user.avatarURL({ dynamic: true, size: 4096 })})
.setThumbnail(tracks[0].cover)
.setColor(15780145)
.addFields({
name: `${isCurrentScrobble}:`, value: `${tracks[0].song}\n **${tracks[0].artist} • ** ${tracks[0].album}`
},)
.setAuthor({name: `Now playing - ${nickname}`, iconURL: user.user.avatarURL({ dynamic: true, size: 4096 })})
.setThumbnail(tracks[0].cover)
.setColor(color)
.addFields({
name: `${isCurrentScrobble}:`, value: `${tracks[0].song}\n **${tracks[0].artist} • ** ${tracks[0].album}`
},)
if (isCurrentScrobble === "Current") {
embed.addFields({
name: "Previous:", value: `${tracks[1].song}\n **${tracks[1].artist} • ** ${tracks[1].album}`
},)
}
sendText.embed = embed;
fs.rmSync(`${directory}`, {recursive: true})
} else {
sendText.text = "You haven't set your last.fm username yet. Use `<prefix>fm set <lastfm_username>` to set it.";
}
return sendText;
}
const ERROR_CODES = {
SUCCESS: 0,
HTTP_ERROR: 1,
NOT_IMAGE: 2,
FETCH_ERROR: 3
}
async function downloadImage(url, downloadPath) {
let res;
try {
res = await fetch(url);
} catch (error) {
return {value: ERROR_CODES.FETCH_ERROR, errorMessage: error.cause?.message || error.message};
}
if(!res.ok) return {value: ERROR_CODES.HTTP_ERROR, errorMessage: res.status.toString()};
const contentType = res.headers.get('content-type');
if(!contentType || !contentType.startsWith("image")) return {value: ERROR_CODES.NOT_IMAGE, errorMessage: contentType || "No content-type header"};
const fileExt = contentType.split("/")[1]
const stream = Readable.fromWeb(res.body)
await writeFile(`${downloadPath}.${fileExt}`, stream);
return {value: ERROR_CODES.SUCCESS, errorMessage: "", ext: fileExt};
}

View file

@ -77,7 +77,8 @@ module.exports = async function (userID, option, guild, compatibility=false) {
fetch(`https://ws.audioscrobbler.com/2.0/?method=user.gettopalbums&user=${lastfmUsername}&period=${option[0]}&api_key=${process.env.LAST_FM_API_KEY}&format=json`)
.then(response => response.json())
.then(data => {
for(let i = 0; i < 10; i++){
const maxIterations = data.topalbums.album.length >= 10 ? 10 : data.topalbums.album.length;
for(let i = 0; i < maxIterations; i++){
let album = {}
let currentAlbum = data.topalbums.album[i];
album.artist = currentAlbum.artist.name;
@ -93,27 +94,24 @@ module.exports = async function (userID, option, guild, compatibility=false) {
});
});
}
// const embed = new EmbedBuilder()
// .setAuthor({name: `Top ${duration} albums for ${nickname}`, iconURL: user.user.avatarURL({ dynamic: true, size: 4096 })})
// .setThumbnail(albums[0].cover)
// .setColor(15780145)
// let tracksInfo = "";
// for(let i = 0; i < albums.length; i++){
// let pluralCharacter = albums[i].playcount > 1 ? 's' : '';
// let track = `${i}. **${albums[i].artist}** - ${albums[i].song} - *${albums[i].playcount} play${pluralCharacter}*`;
// if(i < albums.length - 1){
// tracksInfo += `${track}\n`;
// }else{
// tracksInfo += `${track}`;
// }
// }
// embed.addFields({
// name: ` `, value: `${tracksInfo}`
// },)
// sendText.embed = embed;
// if(compatibility)
const embed = new EmbedBuilder()
.setAuthor({name: `Top ${duration} albums for ${nickname}`, iconURL: user.user.avatarURL({ dynamic: true, size: 4096 })})
.setColor(15780145)
let albumInfo = "";
for(let i = 0; i < albums.length; i++){
let pluralCharacter = albums[i].playcount > 1 ? 's' : '';
let album = `${i}. **${albums[i].artist}** - ${albums[i].name} - *${albums[i].playcount} play${pluralCharacter}*`;
if(i < albums.length - 1){
albumInfo += `${album}\n`;
}else{
albumInfo += `${album}`;
}
}
embed.setDescription(albumInfo);
sendText.embed = embed;
if(compatibility)
return albums;
// else
// return sendText;
else
return sendText;
}

View file

@ -77,7 +77,8 @@ module.exports = async function (userID, option, guild, compatibility=false) {
fetch(`https://ws.audioscrobbler.com/2.0/?method=user.gettopartists&user=${lastfmUsername}&period=${option[0]}&api_key=${process.env.LAST_FM_API_KEY}&format=json`)
.then(response => response.json())
.then(data => {
for(let i = 0; i < 10; i++){
const maxIterations = data.topartists.artist.length >= 10 ? 10 : data.topartists.artist.length;
for(let i = 0; i < maxIterations; i++){
let artist = {}
let currentArtist = data.topartists.artist[i];
artist.name = currentArtist.name;
@ -92,27 +93,24 @@ module.exports = async function (userID, option, guild, compatibility=false) {
});
});
}
// const embed = new EmbedBuilder()
// .setAuthor({name: `Top ${duration} tracks for ${nickname}`, iconURL: user.user.avatarURL({ dynamic: true, size: 4096 })})
// .setThumbnail(tracks[0].cover)
// .setColor(15780145)
// let tracksInfo = "";
// for(let i = 0; i < tracks.length; i++){
// let pluralCharacter = tracks[i].playcount > 1 ? 's' : '';
// let track = `${i}. **${tracks[i].artist}** - ${tracks[i].song} - *${tracks[i].playcount} play${pluralCharacter}*`;
// if(i < tracks.length - 1){
// tracksInfo += `${track}\n`;
// }else{
// tracksInfo += `${track}`;
// }
// }
// embed.addFields({
// name: ` `, value: `${tracksInfo}`
// },)
// sendText.embed = embed;
// if(compatibility)
const embed = new EmbedBuilder()
.setAuthor({name: `Top ${duration} artists for ${nickname}`, iconURL: user.user.avatarURL({ dynamic: true, size: 4096 })})
.setColor(15780145)
let artistsInfo = "";
for(let i = 0; i < artists.length; i++){
let pluralCharacter = artists[i].playcount > 1 ? 's' : '';
let track = `${i}. **${artists[i].name}** - *${artists[i].playcount} play${pluralCharacter}*`;
if(i < artists.length - 1){
artistsInfo += `${track}\n`;
}else{
artistsInfo += `${track}`;
}
}
embed.setDescription(artistsInfo);
sendText.embed = embed;
if(compatibility)
return artists;
// else
// return sendText;
else
return sendText;
}

View file

@ -78,7 +78,8 @@ module.exports = async function (userID, option, guild, compatibility=false) {
fetch(`https://ws.audioscrobbler.com/2.0/?method=user.gettoptracks&user=${lastfmUsername}&period=${option[0]}&api_key=${apiKey}&format=json`)
.then(response => response.json())
.then(data => {
for(let i = 0; i < 10; i++){
const maxIterations = data.toptracks.track.length >= 10 ? 10 : data.toptracks.track.length;
for(let i = 0; i < maxIterations; i++){
let track = {}
let currentTrack = data.toptracks.track[i];
track.artist = currentTrack.artist.name;

View file

@ -19,7 +19,7 @@ module.exports = async function(topArtists, topAlbums) {
"Content-Type": `application/json`
},
body: JSON.stringify({
"model": `deepseek/deepseek-chat-v3-0324:free`,
"model": `google/gemini-2.0-flash-001`,
"messages": [
{
"role": `system`,

View file

@ -1,9 +1,9 @@
const getGuildCount = require("./getGuildCount")
const getGuildInfo = require("./getGuildInfo")
const parseMS = require('./parseMS');
const convertDateToISOString = require('./convertDateToISOString')
module.exports = function ({presenceText, presenceType, client}) {
const {globalPrefix} = require ('../data/config.json')
let guildCount = getGuildCount(client)
const globalPrefix = client.settings.get("globalPrefix")
const guildInfo = getGuildInfo(client);
let uptime = parseMS(client.uptime);
let uptimeFormat = "";
let uptimeSingularOrPlural;
@ -18,10 +18,23 @@ module.exports = function ({presenceText, presenceType, client}) {
uptimeFormat = `less than a minute`
}
let regex = [/\${guilds}/g,/\${prefix}/g,/\${uptime}/g];
let replaceValue = [guildCount, globalPrefix, uptimeFormat];
for(let i = 0; i < regex.length; i++){
presenceText = presenceText.replace(regex[i], replaceValue[i]);
let presenceVariables = {
guilds: guildInfo.guildCount,
prefix: globalPrefix,
uptime: uptimeFormat,
members: guildInfo.totalMembers,
uniqueMembers: guildInfo.uniqueMemberCount
}
const regex = /(?<=\${)(.*?)(?=})/g;
const matches = presenceText.match(regex);
if(matches){
matches.forEach(match => {
if(presenceVariables[match]){
presenceText = presenceText.replace(`\${${match}}`, presenceVariables[match]);
}
});
}
try {

View file

@ -1,17 +1,26 @@
const fs = require('fs');
const parseTime = require('./parseTime');
const timeUntil = require('./timeUntil');
const sqlite3 = require('sqlite3').verbose();
module.exports = async function (message, args, compatibility) {
module.exports = async function (message, args) {
const databasePath = 'data/database.db'
if (args.length < 2)
return message.channel.send("Please specify a time, and a message to send after the timer has finished");
return "Please specify a time, and a message to send after the timer has finished";
let currentUnixTime = Math.floor(new Date() / 1000);
let timeInSeconds = compatibility ? parseTime(args[0], currentUnixTime) : parseTime(args[1], currentUnixTime);
if (isNaN(timeInSeconds)) {
return message.channel.send("Please specify a time, and a message to send after the timer has finished")
let timeInSeconds;
if(!isNaN(Date.parse(args[0])) && isNaN(parseTime(args[0], currentUnixTime))){
timeInSeconds = timeUntil(args[0]).totalInSeconds;
if(timeInSeconds < 0){
return "The date must not be in the past."
}
}else {
timeInSeconds = parseTime(args[0], currentUnixTime);
}
let customMessage = compatibility ? args.slice(1).join(" ") : args.slice(2).join(" ");
let reminderTime = currentUnixTime + timeInSeconds
if (isNaN(timeInSeconds)) {
return "Please specify a time, and a message to send after the timer has finished"
}
const customMessage = args.slice(1).join(" ")
const reminderTime = currentUnixTime + timeInSeconds
let newTimerID;
const db = new sqlite3.Database(databasePath)
let sendText = await new Promise((resolve, reject)=>{

View file

@ -1,7 +1,7 @@
module.exports = function(time, currentUnixTime){
let timeInSeconds = parseFloat(time.slice(0, time.length - 1))
let letter = time.slice(time.length - 1)
if(!isNaN(letter)) return parseFloat(time) * 60;
let timeInSeconds = parseFloat(time)
const letterCount = time.length - timeInSeconds.toString().length;
const letter = time.slice(time.length - letterCount);
switch (letter.toUpperCase()) {
case "H":
timeInSeconds = timeInSeconds * 3_600;
@ -14,7 +14,8 @@ module.exports = function(time, currentUnixTime){
case "D":
timeInSeconds = timeInSeconds * 86_400;
break;
case "T": // TODO: Make it so that I can have multiple letters per case, so that "TS" would work here.
case "TS": // Unix timestamp
case "T":
timeInSeconds = timeInSeconds - currentUnixTime;
break;
case "W":
@ -22,17 +23,6 @@ module.exports = function(time, currentUnixTime){
break;
default:
timeInSeconds = NaN;
if(time.includes(':'))
timeInSeconds = getTime(time, currentUnixTime);
}
return timeInSeconds;
}
function getTime(time, currentUnixTime) {
return timeInSeconds;
}

20
util/timer/timeUntil.js Normal file
View file

@ -0,0 +1,20 @@
module.exports = function(targetTime) {
const countDownDate = new Date(targetTime).getTime();
const now = new Date().getTime();
const distance = countDownDate - now;
let days = Math.floor(distance / (1000 * 60 * 60 * 24));
let hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
let minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
let seconds = Math.floor((distance % (1000 * 60)) / 1000);
if (seconds < 0) { // Due to how the math above works, if the input time is in the past, the time will be off by 1.
days = days + 1;
hours = hours + 1;
minutes = minutes + 1;
seconds = seconds + 1;
}
const totalInSeconds = (days * 24 * 60 * 60) + (hours * 60 * 60) + (minutes * 60) + seconds;
return { days, hours, minutes, seconds, totalInSeconds };
}

View file

@ -1,14 +1,8 @@
const setPresence = require('./setPresence')
module.exports = function (client) {
const updatePresence = require('./updatePresence')
let presenceText = client.settings.get("presenceText")
let presenceType = client.settings.get("presenceType")
if(presenceText.includes("${guilds}") || presenceText.includes("${prefix}") || presenceText.includes("${uptime}")) {
setPresence({presenceText: presenceText, presenceType: presenceType, client: client});
}
setTimeout(updatePresence, 60000, client)
setPresence({presenceText: presenceText, presenceType: presenceType, client: client});
}