mirror of
https://github.com/warengroup/eximiabots-radiox.git
synced 2025-04-19 23:04:47 +00:00
Compare commits
No commits in common. "master" and "0.3.13" have entirely different histories.
@ -1,8 +1,7 @@
|
||||
.env_example
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
.vscode
|
||||
build
|
||||
datastore
|
||||
node_modules
|
||||
Dockerfile
|
||||
LICENSE
|
||||
README.md
|
||||
|
@ -1,5 +1,5 @@
|
||||
DISCORD_TOKEN=
|
||||
RADIOX_STATIONSLISTURL=https://eximiabots.waren.io/radiox/stations.json
|
||||
RADIOX_STATIONSLISTURL=https://git.cwinfo.net/cwchristerw/radio/raw/branch/master/playlist.json
|
||||
DEV_MODE=false
|
||||
DEBUG_MODE=false
|
||||
STREAMER_MODE=manual
|
||||
|
16
.github/labeler.yml
vendored
16
.github/labeler.yml
vendored
@ -1,13 +1,9 @@
|
||||
dependencies:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- package-lock.json
|
||||
- package-lock.json
|
||||
|
||||
documentation:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- README.md
|
||||
- SECURITY.md
|
||||
- CONTRIBUTING.md
|
||||
- LICENSE
|
||||
- .env_example
|
||||
- README.md
|
||||
- SECURITY.md
|
||||
- CONTRIBUTING.md
|
||||
- LICENSE
|
||||
- .env_example
|
||||
|
10
.github/workflows/codeql-analyze.yml
vendored
10
.github/workflows/codeql-analyze.yml
vendored
@ -13,7 +13,7 @@ name: "CodeQL Analyze"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
branches: [ master, stable ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
@ -39,11 +39,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@ -54,7 +54,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@ -68,4 +68,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
20
.github/workflows/dependabot_auto-merge.yml
vendored
20
.github/workflows/dependabot_auto-merge.yml
vendored
@ -1,20 +0,0 @@
|
||||
name: Dependabot Auto-Merge
|
||||
on:
|
||||
pull_request_target:
|
||||
branches: [ develop ]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
name: Dependabot Auto-Merge
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
steps:
|
||||
- name: Enable auto-merge for Dependabot PRs
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
7
.github/workflows/docker-build.yml
vendored
7
.github/workflows/docker-build.yml
vendored
@ -2,9 +2,6 @@ name: Docker Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, assigned, edited, ready_for_review]
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
buildx:
|
||||
@ -12,9 +9,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.9.0
|
||||
uses: docker/setup-buildx-action@v1.6.0
|
||||
id: buildx
|
||||
with:
|
||||
install: true
|
||||
|
2
.github/workflows/labeler.yml
vendored
2
.github/workflows/labeler.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@v3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
|
7
.github/workflows/typescript-build.yml
vendored
7
.github/workflows/typescript-build.yml
vendored
@ -1,19 +1,16 @@
|
||||
name: TypeScript Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, assigned, edited, ready_for_review]
|
||||
push:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
tsc:
|
||||
name: TypeScript Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v1
|
||||
- name: install node v16
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v2.5.0
|
||||
with:
|
||||
node-version: 16
|
||||
- name: npm install
|
||||
|
644
CHANGELOG.md
644
CHANGELOG.md
@ -1,644 +0,0 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 0.5.9 (23.2.2025)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Updating code, because Discord.js has deprecated few options previously used.
|
||||
|
||||
**Package**
|
||||
- Dependencies Update
|
||||
|
||||
**Contributors:**
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.8 (30.9.2024)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Dependencies Update
|
||||
|
||||
**Documentation**
|
||||
- Update radio stations list address and repo in README.md
|
||||
- Fix versions 0.5.5-0.5.7 release years in CHANGELOG.md
|
||||
|
||||
**Contributors:**
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.7 (19.6.2024)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Dependencies Update
|
||||
|
||||
**Contributors:**
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.6 (8.6.2024)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Dependencies Update
|
||||
|
||||
**Documentation**
|
||||
- Add CHANGELOG.md
|
||||
|
||||
**Contributors:**
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.5 (30.4.2024)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Avoid refreshing player too often to keep in Discord API quotas.
|
||||
|
||||
**Package**
|
||||
- Dependencies Update
|
||||
|
||||
**Miscellaneous:**
|
||||
- Dockerfile: Use "docker.io/library/node:20-alpine" as upstream to image.
|
||||
|
||||
**Documentation**
|
||||
- Use Podman in instructions.
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.4 (21.12.2023)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Update new stationlistUrl address
|
||||
- Change player interval to every 10 seconds in Play function
|
||||
- Handle application commands better in commands.ts
|
||||
- Handle DiscordAPIError: unknown interaction in uncaughtException event
|
||||
- Remove audioPlayer maxMissedFrames in Streamer class
|
||||
- Remove Bug command
|
||||
- Remove Invite command
|
||||
|
||||
**Package**
|
||||
- Dependencies Update
|
||||
|
||||
**Miscellaneous:**
|
||||
- Dockerfile
|
||||
- Github Workflow: Labeler (update)
|
||||
|
||||
**Docs**
|
||||
- .env_example Update
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.3 (29.11.2023)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Add duration to Play command
|
||||
- Add RadioPlay playlist support to track info
|
||||
- Remove Now Playing command
|
||||
|
||||
**Package**
|
||||
- Dependencies Update
|
||||
- Typescript Typings
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.2 (23.11.2023)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Display track info in play and nowplaying commands
|
||||
- Fix idling audioPlayer
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
- Typescript Typings
|
||||
|
||||
**Docs**
|
||||
- Update supported versions list in SECURITY.md
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.1 (13.7.2023)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.5.0 (9.6.2023)
|
||||
|
||||
Minor Release
|
||||
|
||||
- Create event listeners once in Streamer class.
|
||||
- Limit commands in maintenance mode.
|
||||
- Remove unnecessary await in Play command.
|
||||
- Replace multiple forEach loop to for...of loops.
|
||||
- Move events and funcs from RadioClient to events.ts and funcs.ts respectively.
|
||||
- Remove execute functions in events and commands.ts.
|
||||
- Move emojis into messages.ts.
|
||||
- Fallback missing version into version 0.0.0.
|
||||
- Change em dash to dash in Stations class.
|
||||
- Remove messageCreate event and deprecation messages.
|
||||
- Converted codebase to Typescript
|
||||
|
||||
**Package**
|
||||
- NodeJS 18
|
||||
- Use lockfileVersion 3
|
||||
- Remove node-fetch dependency
|
||||
- Update Dependencies
|
||||
|
||||
**Documentation**
|
||||
- Removed version 0.4.x support in Security Policy
|
||||
|
||||
**Miscellaneous:**
|
||||
- Dockerfile
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.4.3 (4.6.2023)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Docker Build & TypeScript Build (update)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.4.2 (24.5.2023)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Fix Status command
|
||||
- Replaced SelectMenuBuilder (deprecated) with StringSelectMenuBuilder (Discord.js)
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.4.1 (29.11.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.4.0 (19.7.2022)
|
||||
|
||||
Minor Release
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
**Docs**
|
||||
- Improviding Docker instructions in README.md
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.20 (7.4.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Added dashboard link to Statistics command. Preparations to [#24](<https://github.com/warengroup/eximiabots-radiox/issues/24>)
|
||||
- Minor changes in Ready event and Stations class.
|
||||
- Fixed multiple bugs [#286](<https://github.com/warengroup/eximiabots-radiox/issues/286>), [#284](<https://github.com/warengroup/eximiabots-radiox/issues/284>), [#283](<https://github.com/warengroup/eximiabots-radiox/issues/283>), [#227](<https://github.com/warengroup/eximiabots-radiox/issues/227>).
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Dependabot Auto-Merge (update)
|
||||
|
||||
**Docs**
|
||||
- Improviding Docker instructions in README.md
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.19 (26.2.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Docker Build & TypeScript Build (update)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.18 (26.2.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Dependabot Auto-Merge (update)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.17 (26.2.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Dependabot Auto-Merge (update)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.16 (24.2.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Update Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Dependabot Auto-Merge (update)
|
||||
|
||||
**Docs**
|
||||
- Update year in LICENSE
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.15 (21.2.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: CodeQL Analyze (update)
|
||||
- Github Workflow: Dependabot Auto-Merge (new)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.14 (1.2.2022)
|
||||
|
||||
Patch Release
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Typescript Build (updated)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.13 (21.12.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Listen function will use play to restart playing station when streamerMode is manual and audioPlayer has no subscribers in Streamer class
|
||||
- Prevent bot restarting when uncaughtException event is caused by "DiscordAPIError - Unknown interaction" in uncaughtException event.
|
||||
- Remove Discord.js voice audioResource event listeners in Streamer class
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Typescript Build (updated)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.12 (30.11.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Add removal feature when station isn't working in Stations class
|
||||
- Add direct type to search function in Stations class
|
||||
- Add validation to station at restore function in Radio class
|
||||
- Add manual mode at play function in Streamer class
|
||||
- Update audioPlayer idle event in Streamer class
|
||||
- Update fetch function in Stations class
|
||||
- Change stationsListURL
|
||||
- Move previous search function to text type at search function in Stations class
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.11 (18.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Catch errors inside loadEntry method in Datastore class
|
||||
- Fix memory leak bug in Streamer class
|
||||
- Dont delete first streamer when refreshing streamers in Streamer class
|
||||
- Fix maintenance command
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.10 (17.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Fix Stations class bug
|
||||
- Prevent loadState function updating datastore entries everytime
|
||||
- Streamlined restore method in Radio class with play command
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.9 (17.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Move Datastore class into classes folder.
|
||||
- Add loadEntry method to Datastore class.
|
||||
- Move calculateGlobal method from Datastore to Statistics class.
|
||||
- Create Radio, Stations, Streamer, Statistics class.
|
||||
- Commands are now set into map in commands.js.
|
||||
- Remove application command options in maintenance command.
|
||||
- Add Streamer Mode – Manual and Streamer Mode – Auto to selectMenu in maintenance command.
|
||||
- Small fixes to next, play and prev command.
|
||||
- Hide owner when its same as station name in nowplaying command.
|
||||
- Update fields in status command.
|
||||
- Delete message when using stop command in different textChannel.
|
||||
- Small fixes to SIGINT, interactionCreate and ready event.
|
||||
- Delete radio when no members in voiceChannel with excluding bot users in voiceStateUpdate event.
|
||||
- Small fixes to check, isDev, listStations and logger function.
|
||||
- Move checkFetchStatus function to Stations class.
|
||||
- Delete message and send new message when textChannel has changed in play function.
|
||||
- Hide owner when its same as station name in play function.
|
||||
- Move restoreRadios function to Radio class.
|
||||
- Move saveRadios function to Radio class.
|
||||
- Move searchStation function to Statistics class.
|
||||
- Move statisticsUpdate function to Statistics class.
|
||||
- Update statusFields in messages.
|
||||
- Rename maintenanceMode in config.
|
||||
- Add Streamer Mode in config.
|
||||
- Add Dev Mode in config.
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
**Docs**
|
||||
- Add new environment variables to .env_example file.
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.8 (10.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Add maintenance mode
|
||||
- Node-fetch update to 3.0.0 with temporary solution
|
||||
- Create exit event in Client.ts and added logger.
|
||||
- Remove logger from SIGINT event
|
||||
- Handle warnings in event instead of default warnings.
|
||||
- Add logger to uncaughtException event
|
||||
- Update login error catcher
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- VSCode settings
|
||||
|
||||
**Docs**
|
||||
- Contributing Guide – CONTRIBUTING.md (new)
|
||||
- Security Policy – SECURITY.md (new)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.7 (7.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Fixed messageCreate event
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: Labeler (updated)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.6 (6.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Fixed prev & next command
|
||||
- Changed forgotten interaction replies to ephemeral in commands.
|
||||
- Handle uncaughtException event
|
||||
- Tidied code
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Github Workflow: CodeQL Analyze (new), Labeler (new)
|
||||
|
||||
**Docs**
|
||||
- Updated README.md
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.5 (6.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Avoid answering interaction that came from channels that bot has no rights to view.
|
||||
- Hide decimals from global percent in statistics
|
||||
- Check if there members when restoring radio instead of returning to empty channel and staying alone.
|
||||
- Simplified listStations function and decided to hide one channel because it has maximum of 25 items in select menu options.
|
||||
- Show unknown errors more transparently by using console.error function when needed.
|
||||
- Moved restoreRadios function to funcs folder
|
||||
- Created saveRadios function
|
||||
- Updated SIGINT event: Removed code that was there before saveRadios function was separated into function script
|
||||
- Added more controls to maintenance command
|
||||
- Fixed play command
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>) & [Vekki000](<https://github.com/Vekki000>)
|
||||
|
||||
## 0.3.4 (5.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Created next & prev command
|
||||
- Fixed typo in bug command code
|
||||
- Added loggers to Slash Commands creation process
|
||||
- Tidied code and moved few functions to funcs folder
|
||||
- Disabled removing commands when bot is going offline
|
||||
- Removed deprecated code that may have caused bot to restart unintentionally
|
||||
|
||||
**Package**
|
||||
- Updated Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.3 (4.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Changed few replies to ephemeral in nowplaying command.
|
||||
- Fixed bug command
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.2 (3.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Added station logo to embed thumbnail
|
||||
- Added empty image to make embeds same size
|
||||
- Improved mobile user experience by removing unnecessary spaces in messages
|
||||
- restoreradio.js is now checking that there is stations before continuing.
|
||||
- Improved Dev bot to remove slash commands during process ending.
|
||||
|
||||
***Package:***
|
||||
- Updated Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.1 (3.9.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Added message command deprecation message
|
||||
- Updated Invite link
|
||||
- Added messageDelete event
|
||||
- Edited play message
|
||||
- Updated logger
|
||||
- Updated list command
|
||||
- Gracefully handling process ending when requested (SIGINT & SIGTERM)
|
||||
- Update startTime when changing stations
|
||||
- Remove play message when bot is disconnected from voice channel
|
||||
- Removed references to prefix
|
||||
- Removed unnecessary comments & messages
|
||||
- Removed maintenance message in maintenance command because we will automatically resume playing after restart by saving and loading state.
|
||||
|
||||
***Package:***
|
||||
- Updated Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.3.0 (31.8.2021)
|
||||
|
||||
Minor Release
|
||||
|
||||
- Slash Commands
|
||||
- Removed Message Commands
|
||||
- Improved logging with new logger function
|
||||
- Yle X is now searchable
|
||||
- Ephemeral replies
|
||||
- New invite link
|
||||
- Using play command now gives you dropdown menu when no station id or name is given to command.
|
||||
- Elapsed time is better shown because bot has improved msToTime function.
|
||||
- New Emojis
|
||||
- We may utilize new Discord features because bot can now handle new types of interactions.
|
||||
- Version number in console
|
||||
|
||||
***Package:***
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Dockerfile
|
||||
- Github Workflow: TypeScript Build
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.2.4 (31.8.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
Changed voiceAdapterCreator to Discord.js instead of custom adapter. Should fix #26 indefinitely until major changes coming to Discord.js or Discord.js Voice.
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.2.3 (21.8.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
- Fixed help command (#28)
|
||||
- Nulling connection after bot is disconnected
|
||||
|
||||
***Package:***
|
||||
- Updated Dependencies
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.2.2 (21.8.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
Fixed #26 in voiceStateUpdate.js
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.2.1 (18.8.2021)
|
||||
|
||||
Patch Release
|
||||
|
||||
***Package:***
|
||||
- Updated Dependencies
|
||||
|
||||
***Miscellaneous:***
|
||||
- Dockerfile
|
||||
- Github Workflow: Docker Build (new)
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>)
|
||||
|
||||
## 0.2.0 (8.8.2021)
|
||||
|
||||
Minor Release
|
||||
|
||||
***Miscellaneous:***
|
||||
- eslint
|
||||
- prettier
|
||||
- Dockerfile
|
||||
- TypeScript
|
||||
|
||||
__**Contributors:**__
|
||||
[cwchristerw](<https://github.com/cwchristerw>) & [MatteZ02](<https://github.com/MatteZ02>)
|
||||
|
||||
## 0.1.0 (15.6.2021)
|
||||
\-
|
@ -1,4 +1,4 @@
|
||||
FROM docker.io/library/node:20-alpine
|
||||
FROM node:16-alpine
|
||||
|
||||
#Dependencies
|
||||
RUN apk add --virtual .build-deps python3 make g++ gcc git
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2025 EximiaBots by Warén Group
|
||||
Copyright (c) 2020-2021 EximiaBots by Warén Group
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
44
README.md
44
README.md
@ -1,48 +1,12 @@
|
||||
# RadioX by EximiaBots
|
||||
Internet Radio to your Discord guild
|
||||
|
||||
## [Radio Stations List](https://eximiabots.waren.io/radiox/stations.json)
|
||||
This bot is getting radio stations from our servers.
|
||||
|
||||
https://eximiabots.waren.io/radiox/stations.json
|
||||
|
||||
List is generated with cwchristerw's [radio](https://git.waren.io/cwchristerw/radio) repo. This list is currently maintained by Christer Warén. You can use alternative list with same format when using RADIOX_STATIONSLISTURL environment variable.
|
||||
## [Radio Stations List](https://git.cwinfo.net/cwchristerw/radio)
|
||||
This bot is using Gitea repo to get radio stations from [playlist.json](https://git.cwinfo.net/cwchristerw/radio/raw/branch/master/playlist.json) file. List is currently maintained by Christer Warén. You can use alternative list with same format when using RADIOX_STATIONSLISTURL environment variable.
|
||||
|
||||
## Docker
|
||||
|
||||
### 1. Build Image
|
||||
|
||||
**Production**
|
||||
```
|
||||
podman build -t warengroup/eximiabots-radiox:latest . --pull
|
||||
```
|
||||
|
||||
**Beta**
|
||||
```
|
||||
podman build -t warengroup/eximiabots-radiox:latest-beta . --pull
|
||||
```
|
||||
|
||||
**Dev**
|
||||
```
|
||||
podman build -t warengroup/eximiabots-radiox:latest-dev . --pull
|
||||
```
|
||||
|
||||
### 2. Run Container
|
||||
|
||||
**Production**
|
||||
```
|
||||
podman run --name radiox -d -e DISCORD_TOKEN= -e STREAMER_MODE=auto -v "$PWD/datastore":/usr/src/app/datastore/ warengroup/eximiabots-radiox:latest
|
||||
```
|
||||
|
||||
**Beta**
|
||||
```
|
||||
podman run --name radiox -d -e DISCORD_TOKEN= -e STREAMER_MODE=auto -v "$PWD/datastore":/usr/src/app/datastore/ warengroup/eximiabots-radiox:latest-beta
|
||||
```
|
||||
|
||||
**Dev**
|
||||
```
|
||||
podman run --rm --name radiox-dev -e DISCORD_TOKEN= -e DEV_MODE=true -v "$PWD":/usr/src/app/ warengroup/eximiabots-radiox:latest-dev
|
||||
```
|
||||
1. `docker build -t warengroup/eximiabots-radiox . --pull`
|
||||
2. `docker run --name radiox-dev -d --net host -e DISCORD_TOKEN= -v "$PWD/datastore":/usr/src/app/datastore/ warengroup/eximiabots-radiox`
|
||||
|
||||
## Join our Discord Server
|
||||
https://discord.gg/rRA65Mn
|
||||
|
@ -4,9 +4,7 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.5.x | :white_check_mark: |
|
||||
| 0.4.x | :x: |
|
||||
| 0.3.x | :x: |
|
||||
| 0.3.x | :white_check_mark: |
|
||||
| 0.2.x | :x: |
|
||||
| 0.1.x | :x: |
|
||||
|
||||
|
6814
package-lock.json
generated
6814
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "eximiabots-radiox",
|
||||
"version": "0.5.9",
|
||||
"version": "0.3.13",
|
||||
"description": "Internet Radio to your Discord guild",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "rimraf ./build && tsc",
|
||||
"start": "node --no-warnings build/index.js",
|
||||
"start:dev": "rimraf ./build && tsc && node --no-warnings build/index.js"
|
||||
"start:dev": "npm run build && node --no-warnings build/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -18,18 +18,34 @@
|
||||
"url": "https://github.com/warengroup/eximiabots-radiox/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordjs/voice": "^0.18.0",
|
||||
"discord.js": "^14.18.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"libsodium-wrappers": "^0.7.15",
|
||||
"@discordjs/builders": "^0.9.0",
|
||||
"@discordjs/opus": "^0.7.0",
|
||||
"@discordjs/rest": "^0.2.0-canary.0",
|
||||
"@discordjs/voice": "^0.7.5",
|
||||
"discord-api-types": "^0.25.2",
|
||||
"discord.js": "^13.2.0",
|
||||
"dotenv": "^10.0.0",
|
||||
"libsodium-wrappers": "^0.7.9",
|
||||
"node-fetch": "^3.1.0",
|
||||
"path": "^0.12.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "^6.0.1",
|
||||
"typescript": "^5.7.3"
|
||||
"@types/node": "^17.0.2",
|
||||
"@types/ws": "^8.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.8.0",
|
||||
"@typescript-eslint/parser": "^5.8.0",
|
||||
"eslint": "^8.5.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"nodemon": "^2.0.15",
|
||||
"prettier": "^2.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-node": "^10.4.0",
|
||||
"tsc-watch": "^4.6.0",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0",
|
||||
"npm": ">=10.0.0"
|
||||
"node": ">=16.9.1",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,27 @@
|
||||
import { Client, Collection, IntentsBitField } from "discord.js";
|
||||
import Datastore from "./client/classes/Datastore";
|
||||
import Radio from "./client/classes/Radio";
|
||||
import Stations from "./client/classes/Stations";
|
||||
import Streamer from "./client/classes/Streamer";
|
||||
import Statistics from "./client/classes/Statistics";
|
||||
import { command } from "./client/commands";
|
||||
import config from "./config";
|
||||
import events from "./client/events"
|
||||
import { funcs } from "./client/funcs";
|
||||
import { messages } from "./client/messages";
|
||||
import Discord, { Client, Collection } from "discord.js";
|
||||
import Datastore from "./client/classes/Datastore.js";
|
||||
import Radio from "./client/classes/Radio.js";
|
||||
import Stations from "./client/classes/Stations.js";
|
||||
import Streamer from "./client/classes/Streamer.js";
|
||||
import Statistics from "./client/classes/Statistics.js";
|
||||
import fs from "fs";
|
||||
import { command, radio } from "./client/utils/typings.js";
|
||||
import config from "./config.js";
|
||||
import messages from "./client/messages.js";
|
||||
import path from "path";
|
||||
|
||||
const events = "./client/events/";
|
||||
|
||||
const GatewayIntents = new IntentsBitField();
|
||||
const GatewayIntents = new Discord.Intents();
|
||||
GatewayIntents.add(
|
||||
1 << 0, // GUILDS
|
||||
1 << 7, // GUILD_VOICE_STATES
|
||||
1 << 9 // GUILD_MESSAGES
|
||||
);
|
||||
|
||||
export default class RadioClient extends Client {
|
||||
class RadioClient extends Client {
|
||||
readonly commands: Collection<string, command>;
|
||||
readonly funcs = funcs;
|
||||
public funcs: any;
|
||||
readonly config = config;
|
||||
readonly messages = messages;
|
||||
public datastore: Datastore | null;
|
||||
@ -28,7 +29,6 @@ export default class RadioClient extends Client {
|
||||
public streamer: Streamer | null;
|
||||
public statistics: Statistics | null;
|
||||
public radio: Radio | null;
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
intents: GatewayIntents
|
||||
@ -40,9 +40,19 @@ export default class RadioClient extends Client {
|
||||
this.statistics = null;
|
||||
this.radio = null;
|
||||
|
||||
this.funcs = {};
|
||||
this.funcs.check = require("./client/funcs/check.js");
|
||||
this.funcs.isDev = require("./client/funcs/isDev.js");
|
||||
this.funcs.logger = require("./client/funcs/logger.js");
|
||||
this.funcs.msToTime = require("./client/funcs/msToTime.js");
|
||||
this.funcs.saveState = require("./client/funcs/saveState.js");
|
||||
this.funcs.loadState = require("./client/funcs/loadState.js");
|
||||
this.funcs.play = require("./client/funcs/play.js");
|
||||
this.funcs.listStations = require("./client/funcs/listStations.js");
|
||||
|
||||
console.log('RadioX ' + this.config.version);
|
||||
console.log('Internet Radio to your Discord guild');
|
||||
console.log('(c)2020-2024 EximiaBots by Warén Group');
|
||||
console.log('(c)2020-2021 EximiaBots by Warén Group');
|
||||
console.log('');
|
||||
|
||||
this.funcs.logger("Bot", "Starting");
|
||||
@ -50,12 +60,58 @@ export default class RadioClient extends Client {
|
||||
this.funcs.logger("Maintenance Mode", "Enabled");
|
||||
this.config.maintenanceMode = true;
|
||||
|
||||
events(this);
|
||||
this.on("ready", () => {
|
||||
require(`${events}ready`).execute(this);
|
||||
});
|
||||
|
||||
this.on("messageCreate", msg => {
|
||||
require(`${events}messageCreate`).execute(this, msg);
|
||||
});
|
||||
|
||||
this.on("messageDelete", msg => {
|
||||
require(`${events}messageDelete`).execute(this, msg);
|
||||
});
|
||||
|
||||
this.on("interactionCreate", interaction => {
|
||||
require(`${events}interactionCreate`).execute(this, interaction);
|
||||
});
|
||||
|
||||
this.on("voiceStateUpdate", (oldState, newState) => {
|
||||
require(`${events}voiceStateUpdate`).execute(this, oldState, newState);
|
||||
});
|
||||
|
||||
this.on("error", error => {
|
||||
this.funcs.logger("Discord Client / Error");
|
||||
console.error(error);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
require(`${events}SIGINT`).execute(this);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
require(`${events}SIGTERM`).execute(this);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (error) => {
|
||||
require(`${events}uncaughtException`).execute(this, error);
|
||||
});
|
||||
|
||||
process.on('exit', () => {
|
||||
this.funcs.logger("Bot", "Stopping");
|
||||
});
|
||||
|
||||
process.on('warning', (warning) => {
|
||||
require(`${events}warning`).execute(this, warning);
|
||||
});
|
||||
|
||||
this.login(this.config.token).catch((err) => {
|
||||
this.funcs.logger("Discord Client", "Login Error");
|
||||
this.funcs.logger("Discord Client / Error");
|
||||
console.log(err);
|
||||
console.log('');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default RadioClient
|
||||
|
@ -1,21 +1,7 @@
|
||||
import { Guild } from 'discord.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { state } from './Radio';
|
||||
import { statistics } from './Statistics';
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
export interface datastore {
|
||||
guild: {
|
||||
id: string,
|
||||
name?: string
|
||||
},
|
||||
statistics: statistics,
|
||||
state: state | null,
|
||||
updated?: string
|
||||
}
|
||||
|
||||
export default class Datastore {
|
||||
map: Map<string, datastore>;
|
||||
module.exports = class {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
this.loadData();
|
||||
@ -27,7 +13,7 @@ export default class Datastore {
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
//console.log("");
|
||||
const dataFiles = fs.readdirSync(path.join(path.dirname(__dirname), '../../datastore')).filter((f: string) => f.endsWith('.json'));
|
||||
const dataFiles = fs.readdirSync(path.join(path.dirname(__dirname), '../../datastore')).filter(f => f.endsWith('.json'));
|
||||
for (const file of dataFiles) {
|
||||
try {
|
||||
const json = require(`../../../datastore/${file}`);
|
||||
@ -41,8 +27,7 @@ export default class Datastore {
|
||||
//console.log("");
|
||||
}
|
||||
|
||||
checkEntry(id: string | undefined){
|
||||
if(!id) return;
|
||||
checkEntry(id){
|
||||
this.loadEntry(id);
|
||||
if(!this.map.has(id)){
|
||||
this.createEntry(id);
|
||||
@ -52,19 +37,17 @@ export default class Datastore {
|
||||
}
|
||||
}
|
||||
|
||||
createEntry(id: string){
|
||||
let newData: datastore = {
|
||||
guild: {
|
||||
id: id,
|
||||
},
|
||||
statistics: {},
|
||||
state: null
|
||||
};
|
||||
createEntry(id){
|
||||
let newData = {};
|
||||
newData.guild = {};
|
||||
newData.guild.id = id;
|
||||
newData.statistics = {};
|
||||
newData.state = {};
|
||||
this.map.set(id, newData);
|
||||
this.saveEntry(id, newData);
|
||||
}
|
||||
|
||||
loadEntry(id: string){
|
||||
loadEntry(id){
|
||||
try {
|
||||
const json = require(`../../../datastore/` + id + '.json');
|
||||
this.map.set(id, json);
|
||||
@ -72,11 +55,11 @@ export default class Datastore {
|
||||
}
|
||||
}
|
||||
|
||||
getEntry(id: string){
|
||||
getEntry(id){
|
||||
return this.map.get(id);
|
||||
}
|
||||
|
||||
updateEntry(guild: Guild | { id: string, name?: string }, newData: datastore) {
|
||||
updateEntry(guild, newData) {
|
||||
newData.guild.name = guild.name;
|
||||
|
||||
let date = new Date();
|
||||
@ -87,15 +70,37 @@ export default class Datastore {
|
||||
//this.showEntry(this.getEntry(guild.id));
|
||||
}
|
||||
|
||||
showEntry(data : datastore){
|
||||
showEntry(data){
|
||||
console.log(data);
|
||||
}
|
||||
|
||||
saveEntry(file: string, data: datastore) {
|
||||
fs.writeFile(path.join(path.dirname(__dirname), '../../datastore') + "/" + file + ".json", JSON.stringify(data, null, 4), 'utf8', function(err: NodeJS.ErrnoException | null) {
|
||||
if (err) {
|
||||
createTestFile () {
|
||||
let newData = {
|
||||
"guild": {
|
||||
"id": "test",
|
||||
"name": "Test"
|
||||
},
|
||||
"statistics": {
|
||||
"test": {
|
||||
"time": 0,
|
||||
"used": 0
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.updateEntry(newData.guild, newData);
|
||||
}
|
||||
|
||||
saveEntry(file, data) {
|
||||
data = JSON.stringify(data, null, 4);
|
||||
|
||||
fs.writeFile(path.join(path.dirname(__dirname), '../../datastore') + "/" + file + ".json", data, 'utf8', function(err) {
|
||||
if (err) {
|
||||
//console.log(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
78
src/client/classes/Radio.js
Normal file
78
src/client/classes/Radio.js
Normal file
@ -0,0 +1,78 @@
|
||||
const {
|
||||
getVoiceConnection,
|
||||
joinVoiceChannel
|
||||
} = require("@discordjs/voice");
|
||||
|
||||
module.exports = class Radio extends Map {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
save(client) {
|
||||
let currentRadios = this.keys();
|
||||
let radio = currentRadios.next();
|
||||
|
||||
while (!radio.done) {
|
||||
let currentRadio = this.get(radio.value);
|
||||
|
||||
if (currentRadio) {
|
||||
currentRadio.guild = client.datastore.getEntry(radio.value).guild;
|
||||
|
||||
client.statistics.update(client, currentRadio.guild, currentRadio);
|
||||
client.funcs.saveState(client, currentRadio.guild, currentRadio);
|
||||
currentRadio.connection?.destroy();
|
||||
currentRadio.message?.delete();
|
||||
this.delete(radio.value);
|
||||
}
|
||||
|
||||
radio = currentRadios.next();
|
||||
}
|
||||
}
|
||||
|
||||
restore(client, guilds) {
|
||||
if(!client.stations) return;
|
||||
|
||||
guilds.forEach(async guild => {
|
||||
let state = client.funcs.loadState(client, guild);
|
||||
if(!state) return;
|
||||
if(!state.station || !state.channels.voice || !state.channels.text) return;
|
||||
let voiceChannel = client.channels.cache.get(state.channels.voice);
|
||||
if(!voiceChannel) return;
|
||||
if(voiceChannel.members.filter(member => !member.user.bot).size === 0) return;
|
||||
|
||||
|
||||
const sstation = await client.stations.search(state.station.name, "direct");
|
||||
let station = sstation;
|
||||
|
||||
if(!station) return;
|
||||
|
||||
const construct = {
|
||||
textChannel: client.channels.cache.get(state.channels.text),
|
||||
voiceChannel: client.channels.cache.get(state.channels.voice),
|
||||
connection: null,
|
||||
message: null,
|
||||
station: station
|
||||
};
|
||||
this.set(guild.id, construct);
|
||||
|
||||
try {
|
||||
const connection =
|
||||
getVoiceConnection(guild.id) ??
|
||||
joinVoiceChannel({
|
||||
channelId: voiceChannel.id,
|
||||
guildId: voiceChannel.guild.id,
|
||||
adapterCreator: voiceChannel.guild.voiceAdapterCreator
|
||||
});
|
||||
|
||||
construct.connection = connection;
|
||||
let date = new Date();
|
||||
construct.startTime = date.getTime();
|
||||
client.datastore.checkEntry(guild.id);
|
||||
client.funcs.play(client, null, guild, station);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
@ -1,110 +0,0 @@
|
||||
import { Collection, GuildMember, Message, Guild, OAuth2Guild, TextBasedChannel, VoiceBasedChannel, VoiceChannel } from "discord.js";
|
||||
import { DiscordGatewayAdapterCreator, getVoiceConnection, joinVoiceChannel, VoiceConnection } from "@discordjs/voice";
|
||||
import RadioClient from "../../Client";
|
||||
import { station } from "./Stations";
|
||||
import { datastore } from "./Datastore";
|
||||
|
||||
export interface radio {
|
||||
textChannel: TextBasedChannel | null,
|
||||
voiceChannel: VoiceBasedChannel | null,
|
||||
connection: VoiceConnection | undefined,
|
||||
message: Message | null,
|
||||
station: station,
|
||||
datastore?: datastore,
|
||||
currentTime?: number,
|
||||
startTime: number,
|
||||
playTime?: number,
|
||||
guild?: Guild | { id: string, name?: string }
|
||||
}
|
||||
|
||||
export interface state {
|
||||
channels: {
|
||||
"text": string | undefined,
|
||||
"voice": string | undefined
|
||||
},
|
||||
date: string,
|
||||
station: {
|
||||
name: string,
|
||||
owner: string
|
||||
}
|
||||
}
|
||||
|
||||
export default class Radio extends Map<string, radio> {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
save(client: RadioClient): void {
|
||||
let currentRadios = this.keys();
|
||||
let radio = currentRadios.next();
|
||||
|
||||
while (!radio.done) {
|
||||
let currentRadio = this.get(radio.value);
|
||||
|
||||
if (currentRadio) {
|
||||
currentRadio.guild = client.datastore?.getEntry(radio.value)?.guild;
|
||||
|
||||
client.statistics?.update(client, currentRadio.guild, currentRadio);
|
||||
client.funcs.saveState(client, currentRadio.guild, currentRadio);
|
||||
currentRadio.connection?.destroy();
|
||||
currentRadio.message?.delete();
|
||||
this.delete(radio.value);
|
||||
}
|
||||
|
||||
radio = currentRadios.next();
|
||||
}
|
||||
}
|
||||
|
||||
restore(client: RadioClient, guilds: Collection<string, OAuth2Guild>): void {
|
||||
if(!client.stations) return;
|
||||
|
||||
guilds.forEach(async (guild: OAuth2Guild) => {
|
||||
let state = client.funcs.loadState(client, guild);
|
||||
|
||||
if(!state) return;
|
||||
if(state.channels?.text === undefined || state.channels?.voice === undefined) return;
|
||||
|
||||
let voiceChannel = client.channels.cache.get(state.channels.voice);
|
||||
if(!voiceChannel || !(voiceChannel instanceof VoiceChannel)) return;
|
||||
if(voiceChannel.members.filter((member: GuildMember) => !member.user.bot).size === 0) return;
|
||||
|
||||
const sstation = client.stations?.search(state.station.name, "direct");
|
||||
let station = sstation;
|
||||
|
||||
if(!station) return;
|
||||
|
||||
let date = new Date();
|
||||
const construct: radio = {
|
||||
textChannel: client.channels.cache.get(state.channels.text) as TextBasedChannel,
|
||||
voiceChannel: client.channels.cache.get(state.channels.voice) as VoiceBasedChannel,
|
||||
connection: undefined,
|
||||
message: null,
|
||||
station: station,
|
||||
startTime: date.getTime(),
|
||||
guild: guild
|
||||
};
|
||||
this.set(guild.id, construct);
|
||||
|
||||
try {
|
||||
const connection =
|
||||
getVoiceConnection(guild.id) ??
|
||||
joinVoiceChannel({
|
||||
channelId: voiceChannel.id,
|
||||
guildId: voiceChannel.guild.id,
|
||||
adapterCreator: voiceChannel.guild.voiceAdapterCreator as DiscordGatewayAdapterCreator
|
||||
});
|
||||
|
||||
construct.connection = connection;
|
||||
let date = new Date();
|
||||
construct.startTime = date.getTime();
|
||||
client.datastore?.checkEntry(guild.id);
|
||||
|
||||
client.funcs.play(client, null, guild, station);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
@ -1,64 +1,82 @@
|
||||
import logger from "../funcs/logger";
|
||||
|
||||
export interface station {
|
||||
name: string,
|
||||
owner: string,
|
||||
logo: string,
|
||||
stream: {
|
||||
[key: string]: string
|
||||
},
|
||||
playlist?: {
|
||||
type: "radioplay" | "supla" | "yle",
|
||||
address: string | string
|
||||
}
|
||||
track?: string;
|
||||
}
|
||||
|
||||
export default class Stations extends Array {
|
||||
const _importDynamic = new Function('modulePath', 'return import(modulePath)');
|
||||
const fetch = (...args) => _importDynamic('node-fetch').then(({default: fetch}) => fetch(...args));
|
||||
|
||||
module.exports = class Stations extends Array {
|
||||
constructor() {
|
||||
super();
|
||||
this.logger = require("../funcs/logger.js");
|
||||
}
|
||||
|
||||
async fetch(options: { url: string, show?: boolean}){
|
||||
async fetch(options){
|
||||
try {
|
||||
logger('Stations', 'Started fetching list - ' + options.url);
|
||||
let stations: station[] = await fetch(options.url)
|
||||
this.logger('Stations', 'Started fetching list – ' + options.url);
|
||||
let list = await fetch(options.url)
|
||||
.then(this.checkFetchStatus)
|
||||
.then((response: Response) => response.json() as Promise<station[]>);
|
||||
.then(response => response.json());
|
||||
|
||||
for(const station of stations){
|
||||
this.push(station);
|
||||
if(options.show) logger('Stations', station.name);
|
||||
if(list){
|
||||
this.length = 0;
|
||||
list.forEach(station => {
|
||||
try {
|
||||
this.push(station);
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
if(options.show){
|
||||
this.logger('Stations');
|
||||
list.forEach(station => {
|
||||
console.log("- " + station.name);
|
||||
});
|
||||
console.log("\n");
|
||||
}
|
||||
|
||||
list.forEach(async station => {
|
||||
try {
|
||||
let stationTest = await fetch(station.stream[station.stream.default]);
|
||||
if(stationTest.ok === true) return;
|
||||
this.splice(this.indexOf(station),1);
|
||||
} catch (error) {
|
||||
this.splice(this.indexOf(station),1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logger('Stations', 'Successfully fetched list');
|
||||
this.logger('Stations', 'Successfully fetched list');
|
||||
} catch (error) {
|
||||
logger('Stations', 'Fetching list failed');
|
||||
this.logger('Stations', 'Fetching list failed');
|
||||
console.error(error + "\n");
|
||||
|
||||
if(this.length == 0) setTimeout( () => {
|
||||
this.fetch(options)
|
||||
}, 150 );
|
||||
if(this.length == 0) this.fetch(options);
|
||||
}
|
||||
}
|
||||
|
||||
checkFetchStatus(response: Response) {
|
||||
if (response.ok) {
|
||||
checkFetchStatus(response) {
|
||||
if (response.ok) { // res.status >= 200 && res.status < 300
|
||||
return response;
|
||||
} else {
|
||||
throw new Error(response.status + " " + response.statusText);
|
||||
}
|
||||
}
|
||||
|
||||
search(key: string, type: string) {
|
||||
if (this === null || !key || !type) return null;
|
||||
search(key, type) {
|
||||
if (this === null) return false;
|
||||
if (!key) return false;
|
||||
if (!type) return false;
|
||||
|
||||
if(type == "direct"){
|
||||
return this.find(station => station.name === key);
|
||||
let foundStation;
|
||||
this.forEach(station => {
|
||||
if(station.name != key) return false;
|
||||
foundStation = station;
|
||||
});
|
||||
|
||||
return foundStation;
|
||||
} else {
|
||||
let foundStations : { station: string, name: string, probability: number }[] = [];
|
||||
if (key == "radio") return null;
|
||||
|
||||
let foundStations = [];
|
||||
if (key == "radio") return false;
|
||||
|
||||
this
|
||||
.filter(
|
||||
@ -79,7 +97,7 @@ export default class Stations extends Array {
|
||||
foundStations.push({ station: x, name: x.name, probability: probabilityIncrement })
|
||||
);
|
||||
}
|
||||
if (foundStations.length === 0) return null;
|
||||
if (foundStations.length === 0) return false;
|
||||
for (let i = 0; i < foundStations.length; i++) {
|
||||
for (let j = 0; j < foundStations.length; j++) {
|
||||
if (foundStations[i] === foundStations[j] && i !== j) foundStations.splice(i, 1);
|
||||
@ -100,9 +118,7 @@ export default class Stations extends Array {
|
||||
}
|
||||
}
|
||||
}
|
||||
let highestProbabilityStation : { station: string, name: string, probability: number } | undefined;
|
||||
let stationName = "";
|
||||
|
||||
let highestProbabilityStation;
|
||||
for (let i = 0; i < foundStations.length; i++) {
|
||||
if (
|
||||
!highestProbabilityStation ||
|
||||
@ -113,10 +129,10 @@ export default class Stations extends Array {
|
||||
highestProbabilityStation &&
|
||||
highestProbabilityStation.probability === foundStations[i].probability
|
||||
) {
|
||||
stationName = foundStations[i].station;
|
||||
highestProbabilityStation = foundStations[i].station;
|
||||
}
|
||||
}
|
||||
return stationName;
|
||||
return highestProbabilityStation;
|
||||
}
|
||||
}
|
||||
};
|
70
src/client/classes/Statistics.js
Normal file
70
src/client/classes/Statistics.js
Normal file
@ -0,0 +1,70 @@
|
||||
module.exports = class {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
|
||||
update(client, guild, radio) {
|
||||
|
||||
client.datastore.checkEntry(guild.id);
|
||||
|
||||
radio.datastore = client.datastore.getEntry(guild.id);
|
||||
|
||||
if(!radio.datastore.statistics[radio.station.name]){
|
||||
radio.datastore.statistics[radio.station.name] = {};
|
||||
radio.datastore.statistics[radio.station.name].time = 0;
|
||||
radio.datastore.statistics[radio.station.name].used = 0;
|
||||
client.datastore.updateEntry(guild, radio.datastore);
|
||||
}
|
||||
|
||||
let date = new Date();
|
||||
radio.currentTime = date.getTime();
|
||||
radio.playTime = parseInt(radio.currentTime)-parseInt(radio.startTime);
|
||||
radio.datastore.statistics[radio.station.name].time = parseInt(radio.datastore.statistics[radio.station.name].time)+parseInt(radio.playTime);
|
||||
|
||||
radio.datastore.statistics[radio.station.name].used = parseInt(radio.datastore.statistics[radio.station.name].used)+1;
|
||||
client.datastore.updateEntry(guild, radio.datastore);
|
||||
this.calculateGlobal(client);
|
||||
}
|
||||
|
||||
calculateGlobal(client){
|
||||
if(!client.stations) return;
|
||||
if(!client.datastore.map) return;
|
||||
|
||||
let guilds = client.datastore.map.keys();
|
||||
let stations = client.stations;
|
||||
let statistics = {};
|
||||
|
||||
if(!client.stations) return;
|
||||
|
||||
let calculation = guilds.next();
|
||||
|
||||
while (!calculation.done) {
|
||||
let currentGuild = client.datastore.getEntry(calculation.value);
|
||||
if(calculation.value != 'global'){
|
||||
if(stations){
|
||||
Object.keys(stations).forEach(function(station) {
|
||||
if(currentGuild.statistics[stations[station].name] && currentGuild.statistics[stations[station].name].time && parseInt(currentGuild.statistics[stations[station].name].time) != 0 && currentGuild.statistics[stations[station].name].used && parseInt(currentGuild.statistics[stations[station].name].used) != 0){
|
||||
if(!statistics[stations[station].name]){
|
||||
statistics[stations[station].name] = {};
|
||||
statistics[stations[station].name].time = 0;
|
||||
statistics[stations[station].name].used = 0;
|
||||
}
|
||||
|
||||
statistics[stations[station].name].time = parseInt(statistics[stations[station].name].time)+parseInt(currentGuild.statistics[stations[station].name].time);
|
||||
statistics[stations[station].name].used = parseInt(statistics[stations[station].name].used)+parseInt(currentGuild.statistics[stations[station].name].used);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
calculation = guilds.next();
|
||||
}
|
||||
|
||||
let newData = {};
|
||||
newData.guild = {};
|
||||
newData.guild.id = "global";
|
||||
newData.guild.name = "global";
|
||||
newData.statistics = statistics;
|
||||
client.datastore.updateEntry(newData.guild, newData);
|
||||
}
|
||||
|
||||
};
|
@ -1,95 +0,0 @@
|
||||
import { Guild, OAuth2Guild } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { radio } from "./Radio";
|
||||
|
||||
export interface statistics {
|
||||
[key: string]: statistic
|
||||
}
|
||||
|
||||
interface statistic {
|
||||
"time": number,
|
||||
"used": number
|
||||
}
|
||||
|
||||
export default class Statistics {
|
||||
map: Map<string, statistics>;
|
||||
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
}
|
||||
|
||||
update(client: RadioClient, guild: Guild | { id: string, name?: string } | undefined, radio: radio) {
|
||||
if(!guild) return;
|
||||
|
||||
client.datastore?.checkEntry(guild.id);
|
||||
|
||||
radio.datastore = client.datastore?.getEntry(guild.id);
|
||||
|
||||
if(radio.datastore === undefined) return;
|
||||
|
||||
if(!radio.datastore.statistics[radio.station.name]){
|
||||
radio.datastore.statistics[radio.station.name] = {
|
||||
time: 0,
|
||||
used: 0
|
||||
};
|
||||
client.datastore?.updateEntry(guild, radio.datastore);
|
||||
}
|
||||
|
||||
let date = new Date();
|
||||
radio.currentTime = date.getTime();
|
||||
radio.playTime = radio.currentTime - radio.startTime;
|
||||
radio.datastore.statistics[radio.station.name] = {
|
||||
time: radio.datastore.statistics[radio.station.name].time + radio.playTime,
|
||||
used: radio.datastore.statistics[radio.station.name].used + 1
|
||||
}
|
||||
client.datastore?.updateEntry(guild, radio.datastore);
|
||||
this.calculateGlobal(client);
|
||||
}
|
||||
|
||||
calculateGlobal(client: RadioClient){
|
||||
if(!client.datastore?.map) return;
|
||||
|
||||
let guilds = client.datastore.map.keys();
|
||||
let statistics : statistics = {};
|
||||
|
||||
if(!client.stations) return;
|
||||
|
||||
let calculation = guilds.next();
|
||||
|
||||
while (!calculation.done) {
|
||||
let currentGuild = client.datastore.getEntry(calculation.value);
|
||||
if(calculation.value != 'global'){
|
||||
if(client.stations){
|
||||
for(const station of client.stations) {
|
||||
if(!currentGuild) return;
|
||||
if(currentGuild.statistics[station.name] && currentGuild.statistics[station.name]?.time && currentGuild.statistics[station.name].time != 0 && currentGuild.statistics[station.name].used && currentGuild.statistics[station.name].used != 0){
|
||||
if(!statistics[station.name]){
|
||||
statistics[station.name] = {
|
||||
time: 0,
|
||||
used: 0
|
||||
};
|
||||
}
|
||||
|
||||
statistics[station.name] = {
|
||||
time: statistics[station.name].time + currentGuild.statistics[station.name].time,
|
||||
used: statistics[station.name].used + currentGuild.statistics[station.name].used
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
calculation = guilds.next();
|
||||
}
|
||||
|
||||
let newData = {
|
||||
guild: {
|
||||
id: "global",
|
||||
name: "global"
|
||||
},
|
||||
statistics: statistics,
|
||||
state: null
|
||||
};
|
||||
client.datastore.updateEntry(newData.guild, newData);
|
||||
}
|
||||
|
||||
};
|
120
src/client/classes/Streamer.js
Normal file
120
src/client/classes/Streamer.js
Normal file
@ -0,0 +1,120 @@
|
||||
const {
|
||||
createAudioPlayer,
|
||||
createAudioResource,
|
||||
AudioPlayerStatus,
|
||||
NoSubscriberBehavior
|
||||
} = require("@discordjs/voice");
|
||||
|
||||
module.exports = class {
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
this.mode = null;
|
||||
this.logger = require("../funcs/logger.js");
|
||||
}
|
||||
|
||||
init(client){
|
||||
if(!client.config.streamerMode) return;
|
||||
|
||||
switch(client.config.streamerMode){
|
||||
case "manual":
|
||||
this.mode = "manual";
|
||||
break;
|
||||
case "auto":
|
||||
this.mode = "auto";
|
||||
break;
|
||||
default:
|
||||
this.mode = "manual";
|
||||
}
|
||||
|
||||
if(this.mode == "auto"){
|
||||
if(!client.stations) return;
|
||||
|
||||
client.stations.forEach(station => {
|
||||
this.play(station);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
refresh(client){
|
||||
this.init(client);
|
||||
|
||||
let streamers = this.map.keys();
|
||||
streamers.forEach(streamer => {
|
||||
if(client.stations.findIndex(station => station.name == streamer) == -1){
|
||||
this.stop(streamer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
play(station) {
|
||||
let audioPlayer = this.map.get(station.name);
|
||||
if(!audioPlayer) {
|
||||
if(this.mode == "auto"){
|
||||
audioPlayer = createAudioPlayer({
|
||||
behaviors: {
|
||||
noSubscriber: NoSubscriberBehavior.Play,
|
||||
maxMissedFrames: Math.round(5000 / 20),
|
||||
},
|
||||
});
|
||||
}
|
||||
if(this.mode == "manual"){
|
||||
audioPlayer = createAudioPlayer({
|
||||
behaviors: {
|
||||
noSubscriber: NoSubscriberBehavior.Stop,
|
||||
maxMissedFrames: Math.round(5000 / 20),
|
||||
},
|
||||
});
|
||||
}
|
||||
this.map.set(station.name, audioPlayer);
|
||||
}
|
||||
const url = station.stream[station.stream.default];
|
||||
const resource = createAudioResource(url);
|
||||
audioPlayer.play(resource);
|
||||
audioPlayer
|
||||
.on('playing', () => {
|
||||
this.logger('Streamer', station.name + " / " + "Playing");
|
||||
})
|
||||
.on('idle', () => {
|
||||
this.logger('Streamer', station.name + " / " + "Idle");
|
||||
audioPlayer.removeAllListeners();
|
||||
if(this.mode == "manual" && audioPlayer.subscribers.length == 0) return;
|
||||
this.play(station);
|
||||
})
|
||||
.on('paused', () => {
|
||||
this.logger('Streamer', station.name + " / " + "Paused");
|
||||
})
|
||||
.on('buffering', () => {
|
||||
this.logger('Streamer', station.name + " / " + "Buffering");
|
||||
})
|
||||
.on('autopaused', () => {
|
||||
this.logger('Streamer', station.name + " / " + "AutoPaused");
|
||||
})
|
||||
.on('error', error => {
|
||||
this.logger('Streamer', station.name + " / " + "Error" + "\n" + error);
|
||||
});
|
||||
return audioPlayer;
|
||||
}
|
||||
|
||||
stop(station){
|
||||
let audioPlayer = this.map.get(station.name);
|
||||
if(audioPlayer){
|
||||
this.logger('Streamer', station.name + " / " + "Stop");
|
||||
audioPlayer.removeAllListeners();
|
||||
audioPlayer.stop();
|
||||
}
|
||||
this.map.delete(station.name);
|
||||
}
|
||||
|
||||
listen(station) {
|
||||
let audioPlayer = this.map.get(station.name);
|
||||
if(!audioPlayer || this.mode == "manual" && audioPlayer.subscribers.length == 0) audioPlayer = this.play(station);
|
||||
return audioPlayer;
|
||||
}
|
||||
|
||||
leave(client) {
|
||||
if(!client.stations) return;
|
||||
client.stations.forEach(station => {
|
||||
this.stop(station);
|
||||
});
|
||||
}
|
||||
};
|
@ -1,121 +0,0 @@
|
||||
import logger from "../funcs/logger";
|
||||
import { AudioPlayer, AudioPlayerStatus, createAudioPlayer, createAudioResource, NoSubscriberBehavior } from "@discordjs/voice";
|
||||
import RadioClient from "../../Client";
|
||||
import { station } from "./Stations";
|
||||
|
||||
export default class Streamer {
|
||||
map: Map<string, AudioPlayer>;
|
||||
mode: "auto" | "manual";
|
||||
|
||||
constructor() {
|
||||
this.map = new Map();
|
||||
this.mode = "manual";
|
||||
}
|
||||
|
||||
init(client: RadioClient){
|
||||
if(!client.config.streamerMode) return;
|
||||
|
||||
switch(client.config.streamerMode){
|
||||
case "manual":
|
||||
this.mode = "manual";
|
||||
break;
|
||||
case "auto":
|
||||
this.mode = "auto";
|
||||
break;
|
||||
default:
|
||||
this.mode = "manual";
|
||||
}
|
||||
|
||||
if(this.mode == "auto"){
|
||||
if(!client.stations) return;
|
||||
|
||||
for(const station of client.stations){
|
||||
this.play(station);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresh(client: RadioClient){
|
||||
this.init(client);
|
||||
|
||||
for (const streamer of this.map.keys()){
|
||||
if(client.stations?.findIndex((station: station) => station.name == streamer) == -1){
|
||||
this.stop(streamer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
play(station: station) {
|
||||
let audioPlayer = this.map.get(station.name);
|
||||
if(!audioPlayer) {
|
||||
if(this.mode == "auto"){
|
||||
audioPlayer = createAudioPlayer({
|
||||
behaviors: {
|
||||
noSubscriber: NoSubscriberBehavior.Play
|
||||
}
|
||||
});
|
||||
} else {
|
||||
audioPlayer = createAudioPlayer({
|
||||
behaviors: {
|
||||
noSubscriber: NoSubscriberBehavior.Stop
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
audioPlayer
|
||||
.on(AudioPlayerStatus.Playing, () => {
|
||||
logger('Streamer', station.name + " / " + "Playing");
|
||||
})
|
||||
.on(AudioPlayerStatus.Idle, () => {
|
||||
logger('Streamer', station.name + " / " + "Idle");
|
||||
if(this.mode == "auto"){
|
||||
const url = station.stream[station.stream.default];
|
||||
const resource = createAudioResource(url);
|
||||
audioPlayer?.play(resource);
|
||||
} else {
|
||||
this.stop(station.name);
|
||||
}
|
||||
})
|
||||
.on(AudioPlayerStatus.Paused, () => {
|
||||
logger('Streamer', station.name + " / " + "Paused");
|
||||
})
|
||||
.on(AudioPlayerStatus.Buffering, () => {
|
||||
logger('Streamer', station.name + " / " + "Buffering");
|
||||
})
|
||||
.on(AudioPlayerStatus.AutoPaused, () => {
|
||||
logger('Streamer', station.name + " / " + "AutoPaused");
|
||||
})
|
||||
|
||||
this.map.set(station.name, audioPlayer);
|
||||
}
|
||||
|
||||
const url = station.stream[station.stream.default];
|
||||
const resource = createAudioResource(url);
|
||||
audioPlayer.play(resource);
|
||||
|
||||
return audioPlayer;
|
||||
}
|
||||
|
||||
stop(streamer: string){
|
||||
let audioPlayer = this.map.get(streamer);
|
||||
if(audioPlayer){
|
||||
logger('Streamer', streamer + " / " + "Stop");
|
||||
audioPlayer.removeAllListeners();
|
||||
audioPlayer.stop();
|
||||
}
|
||||
this.map.delete(streamer);
|
||||
}
|
||||
|
||||
listen(station: station) {
|
||||
let audioPlayer = this.map.get(station.name);
|
||||
if(!audioPlayer) audioPlayer = this.play(station);
|
||||
return audioPlayer;
|
||||
}
|
||||
|
||||
leave(client: RadioClient) {
|
||||
if(!client.stations) return;
|
||||
for(const station of client.stations){
|
||||
this.stop(station.name);
|
||||
}
|
||||
}
|
||||
};
|
83
src/client/commands.js
Normal file
83
src/client/commands.js
Normal file
@ -0,0 +1,83 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { REST } = require('@discordjs/rest');
|
||||
const { Routes } = require('discord-api-types/v9');
|
||||
const fs = require('fs');
|
||||
const path = require ('path');
|
||||
|
||||
module.exports = {
|
||||
async execute(client) {
|
||||
|
||||
const commands = [];
|
||||
const commandFiles = fs.readdirSync(path.join("./src/client/commands")).filter(f => f.endsWith(".js"));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const command = require(`./commands/${file}`);
|
||||
client.commands.set(command.name, command);
|
||||
|
||||
command.data = new SlashCommandBuilder()
|
||||
.setName(command.name)
|
||||
.setDescription(command.description);
|
||||
|
||||
command.data = command.data.toJSON();
|
||||
if(command.options) {
|
||||
command.options.forEach(function(option) {
|
||||
if(option.type == "STRING") option.type = 3;
|
||||
if(option.type == "NUMBER") option.type = 10;
|
||||
command.data.options.push(option);
|
||||
});
|
||||
}
|
||||
|
||||
commands.push(command.data);
|
||||
}
|
||||
|
||||
const rest = new REST({ version: '9' }).setToken(client.config.token);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
client.funcs.logger('Slash Commands', 'Started refreshing application (/) commands.');
|
||||
|
||||
if(client.config.devMode){
|
||||
await rest.put(
|
||||
Routes.applicationCommands(client.user.id),
|
||||
{ body: [] },
|
||||
);
|
||||
|
||||
let guilds = await client.guilds.fetch();
|
||||
guilds.forEach(async guild => {
|
||||
try {
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(client.user.id, guild.id),
|
||||
{ body: commands }
|
||||
);
|
||||
client.funcs.logger('Slash Commands', 'Guild Applications – Successful' + "\n" + guild.id + " / " + guild.name);
|
||||
} catch (DiscordAPIError) {
|
||||
client.funcs.logger('Slash Commands', 'Guild Applications – Failed' + "\n" + guild.id + " / " + guild.name);
|
||||
if(DiscordAPIError.name != "DiscordAPIError[50001]") console.error(DiscordAPIError.message + "\n\n");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await rest.put(
|
||||
Routes.applicationCommands(client.user.id),
|
||||
{ body: commands }
|
||||
);
|
||||
|
||||
let guilds = await client.guilds.fetch();
|
||||
guilds.forEach(async guild => {
|
||||
try {
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(client.user.id, guild.id),
|
||||
{ body: [] }
|
||||
);
|
||||
} catch (DiscordAPIError) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
client.funcs.logger('Slash Commands', 'Successfully reloaded application (/) commands.' + "\n");
|
||||
} catch (error) {
|
||||
client.funcs.logger('Slash Commands', 'Reloading application (/) commands failed.' + "\n");
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
import { ApplicationCommand, ApplicationCommandManager, BaseGuild, Guild, GuildApplicationCommandManager, OAuth2Guild, Snowflake } from "discord.js";
|
||||
import RadioClient from "../Client";
|
||||
import help from "./commands/help";
|
||||
import list from "./commands/list";
|
||||
import maintenance from "./commands/maintenance";
|
||||
import next from "./commands/next";
|
||||
import play from "./commands/play";
|
||||
import prev from "./commands/prev";
|
||||
import statistics from "./commands/statistics";
|
||||
import status from "./commands/status";
|
||||
import stop from "./commands/stop";
|
||||
|
||||
export interface command {
|
||||
name: string,
|
||||
description: string,
|
||||
category: string,
|
||||
options?: [],
|
||||
execute: Function
|
||||
}
|
||||
|
||||
export default async function commands(client: RadioClient) {
|
||||
const commands1 : command[] = [ help, list, maintenance, next, play, prev, statistics, status, stop ];
|
||||
const commands2 = await client.application?.commands.fetch();
|
||||
|
||||
for(const command of commands1){
|
||||
client.commands.set(command.name, command);
|
||||
}
|
||||
|
||||
client.funcs.logger('Application Commands', 'Started refreshing application (/) commands.');
|
||||
if(commands1){
|
||||
for(const command of commands1){
|
||||
await client.application?.commands.create({
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
options: command.options || []
|
||||
});
|
||||
|
||||
client.funcs.logger('Application Commands', 'Command: ' + command.name);
|
||||
}
|
||||
}
|
||||
|
||||
if(commands2){
|
||||
commands2.forEach(async command2 => {
|
||||
if(commands1.findIndex((command1) => command1.name == command2.name) == -1){
|
||||
await client.application?.commands.delete(command2.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let guilds = await client.guilds.fetch();
|
||||
guilds.forEach(async (guild: Guild | OAuth2Guild) => {
|
||||
try {
|
||||
if(!client.application) return;
|
||||
await client.application.commands.set([], guild.id);
|
||||
} catch (DiscordAPIError){
|
||||
}
|
||||
});
|
||||
|
||||
client.funcs.logger('Application Commands', 'Successfully reloaded application (/) commands.' + "\n");
|
||||
}
|
26
src/client/commands/bug.js
Normal file
26
src/client/commands/bug.js
Normal file
@ -0,0 +1,26 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'bug',
|
||||
description: 'Report a bug',
|
||||
category: 'info',
|
||||
async execute(interaction, client) {
|
||||
let message = {};
|
||||
|
||||
message.bugTitle = client.messages.bugTitle.replace("%client.user.username%", client.user.username);
|
||||
message.bugDescription = client.messages.bugDescription.replace("%client.config.supportGuild%", client.config.supportGuild);
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(message.bugTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(message.bugDescription)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
};
|
35
src/client/commands/help.js
Normal file
35
src/client/commands/help.js
Normal file
@ -0,0 +1,35 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'help',
|
||||
description: 'Get help using bot',
|
||||
category: 'info',
|
||||
execute(interaction, client) {
|
||||
let message = {};
|
||||
|
||||
const categories = [];
|
||||
for (let i = 0; i < client.commands.size; i++) {
|
||||
if (!categories.includes([...client.commands.values()][i].category)) categories.push([...client.commands.values()][i].category);
|
||||
}
|
||||
let commands = '';
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
commands += `**» ${categories[i].toUpperCase()}**\n${client.commands.filter(x => x.category === categories[i] && !x.omitFromHelp).map(x => `\`${x.name}\``).join(', ')}\n`;
|
||||
}
|
||||
|
||||
message.helpTitle = client.messages.helpTitle.replace("%client.user.username%", client.user.username);
|
||||
message.helpDescription = client.messages.helpDescription.replace("%commands%", commands);
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(message.helpTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(message.helpDescription)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { command } from "../commands";
|
||||
|
||||
export default {
|
||||
name: 'help',
|
||||
description: 'Get help using bot',
|
||||
category: 'info',
|
||||
execute(interaction: ChatInputCommandInteraction, client: RadioClient) {
|
||||
|
||||
if(!client.user) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(client.messages.helpTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["logo"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(client.messages.replace(client.messages.helpDescription, {
|
||||
"%client.config.supportGuild%": client.config.supportGuild
|
||||
}))
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
};
|
22
src/client/commands/invite.js
Normal file
22
src/client/commands/invite.js
Normal file
@ -0,0 +1,22 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'invite',
|
||||
description: 'Invite Bot',
|
||||
category: 'info',
|
||||
execute(interaction, client) {
|
||||
let message = {};
|
||||
message.inviteTitle = client.messages.inviteTitle.replace("%client.user.username%", client.user.username);
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(message.inviteTitle)
|
||||
.setColor(client.config.embedColor)
|
||||
.setURL("https://discord.com/api/oauth2/authorize?client_id=" + client.user.id + "&permissions=2184465408&scope=applications.commands%20bot") //View Channels, Send Messages, Embed Links, Use External Emojis, Use Slash Commands, Connect, Speak, Use Voice Activity
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
};
|
43
src/client/commands/list.js
Normal file
43
src/client/commands/list.js
Normal file
@ -0,0 +1,43 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'list',
|
||||
description: 'List radio stations',
|
||||
category: 'radio',
|
||||
execute(interaction, client) {
|
||||
let message = {};
|
||||
|
||||
if(!client.stations) {
|
||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + message.errorToGetPlaylist,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
const radio = client.radio.get(interaction.guild.id);
|
||||
|
||||
if(radio && !client.config.maintenanceMode){
|
||||
client.funcs.listStations(client, interaction);
|
||||
} else {
|
||||
let stations = `${client.stations.map(s => `**#** ${s.name}`).join('\n')}`
|
||||
const hashs = stations.split('**#**').length;
|
||||
for (let i = 0; i < hashs; i++) {
|
||||
stations = stations.replace('**#**', `**${i + 1}.**`);
|
||||
}
|
||||
|
||||
let embed = new Discord.MessageEmbed()
|
||||
.setTitle(client.messages.listTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["list"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(stations)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,72 +0,0 @@
|
||||
import { ApplicationCommandOptionType, ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { station } from "../classes/Stations";
|
||||
|
||||
export default {
|
||||
name: 'list',
|
||||
description: 'List stations',
|
||||
options: [
|
||||
{ type: ApplicationCommandOptionType.String, name: "query", description: "Select list", choices: [{"name": "1", "value": "1"},{"name": "2", "value": "2"}], required: false}
|
||||
],
|
||||
category: 'radio',
|
||||
execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||
|
||||
if(client.config.maintenanceMode){
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
if(!interaction.guild) return;
|
||||
|
||||
let query: string | null = null;
|
||||
|
||||
if(interaction.isChatInputCommand()){
|
||||
query = interaction.options?.getString("query");
|
||||
}
|
||||
|
||||
if(interaction.isStringSelectMenu()){
|
||||
query = interaction.values?.[0];
|
||||
}
|
||||
|
||||
if(!query) query = "1";
|
||||
|
||||
if(!client.stations) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||
"%client.config.supportGuild%": client.config.supportGuild
|
||||
}),
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
const radio = client.radio?.get(interaction.guild.id);
|
||||
|
||||
if(radio && !client.config.maintenanceMode){
|
||||
client.funcs.listStations(client, interaction, query);
|
||||
} else {
|
||||
let stations = `${client.stations.map((s: station) => `**#** ${s.name}`).join('\n')}`
|
||||
const hashs = stations.split('**#**').length;
|
||||
for (let i = 0; i < hashs; i++) {
|
||||
stations = stations.replace('**#**', `**${i + 1}.**`);
|
||||
}
|
||||
|
||||
let embed = new EmbedBuilder()
|
||||
.setTitle(client.messages.listTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["list"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(stations)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
206
src/client/commands/maintenance.js
Normal file
206
src/client/commands/maintenance.js
Normal file
@ -0,0 +1,206 @@
|
||||
import Discord from "discord.js";
|
||||
import Streamer from "../classes/Streamer.js";
|
||||
const _importDynamic = new Function('modulePath', 'return import(modulePath)');
|
||||
const fetch = (...args) => _importDynamic('node-fetch').then(({default: fetch}) => fetch(...args));
|
||||
|
||||
module.exports = {
|
||||
name: 'maintenance',
|
||||
description: 'Bot Maintenance',
|
||||
category: 'info',
|
||||
async execute(interaction, client) {
|
||||
let message = {};
|
||||
|
||||
if(!client.funcs.isDev(client.config.devId, interaction.user.id)) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.notAllowed,
|
||||
ephemeral: true
|
||||
});
|
||||
let action = interaction.options?.getNumber("action") ?? interaction.values?.[0];
|
||||
const options = new Array(
|
||||
{
|
||||
emoji: "🌀",
|
||||
label: "Restart Bot",
|
||||
description: "",
|
||||
value: "0"
|
||||
},
|
||||
{
|
||||
emoji: "<:RadioXStop:688541155377414168>",
|
||||
label: "Save Radios",
|
||||
description: "",
|
||||
value: "4"
|
||||
},
|
||||
{
|
||||
emoji: "<:RadioXPlay:688541155712827458>",
|
||||
label: "Restore Radios",
|
||||
description: "",
|
||||
value: "5"
|
||||
},
|
||||
{
|
||||
emoji: "#️⃣",
|
||||
label: "Reload Commands",
|
||||
description: "",
|
||||
value: "6"
|
||||
},
|
||||
{
|
||||
emoji: "<:RadioXList:688541155519889482>",
|
||||
label: "Reload Stations",
|
||||
description: "",
|
||||
value: "7"
|
||||
},
|
||||
{
|
||||
emoji: "<:dnd:746069698139127831>",
|
||||
label: "Enable Maintenance Mode",
|
||||
description: "",
|
||||
value: "8"
|
||||
},
|
||||
{
|
||||
emoji: "<:online:746069731836035098>",
|
||||
label: "Disable Maintenance Mode",
|
||||
description: "",
|
||||
value: "9"
|
||||
},
|
||||
{
|
||||
emoji: "💤",
|
||||
label: "Streamer Mode – Manual",
|
||||
description: "",
|
||||
value: "10"
|
||||
},
|
||||
{
|
||||
emoji: "📡",
|
||||
label: "Streamer Mode – Auto",
|
||||
description: "",
|
||||
value: "11"
|
||||
}
|
||||
);
|
||||
|
||||
const menu = new Discord.MessageActionRow()
|
||||
.addComponents(
|
||||
new Discord.MessageSelectMenu()
|
||||
.setCustomId('maintenance')
|
||||
.setPlaceholder('Select action')
|
||||
.addOptions(options)
|
||||
);
|
||||
|
||||
if(!action){
|
||||
return interaction.reply({
|
||||
content: "**" + client.messages.maintenanceTitle + "**",
|
||||
components: [menu],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
client.funcs.logger('Maintenance', options.find(option => option.value == action).label);
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(client.messages.maintenanceTitle)
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(options.find(option => option.value == action).label)
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
let guilds = await client.guilds.fetch();
|
||||
|
||||
switch(action){
|
||||
case "0":
|
||||
client.config.maintenanceMode = true;
|
||||
process.emit('SIGINT');
|
||||
break;
|
||||
case "4":
|
||||
client.config.maintenanceMode = true;
|
||||
client.user.setStatus('idle');
|
||||
client.radio.save(client);
|
||||
client.user.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "5":
|
||||
client.config.maintenanceMode = true;
|
||||
client.user.setStatus('idle');
|
||||
client.radio.restore(client, guilds);
|
||||
client.user.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "6":
|
||||
client.config.maintenanceMode = true;
|
||||
client.user.setStatus('idle');
|
||||
require(`../commands.js`).execute(client);
|
||||
client.user.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "7":
|
||||
try {
|
||||
client.stations.fetch({
|
||||
url: client.config.stationslistUrl
|
||||
});
|
||||
client.streamer.refresh(client);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
break;
|
||||
case "8":
|
||||
client.user.setStatus('dnd');
|
||||
client.funcs.logger("Maintenance Mode", "Enabled");
|
||||
client.config.maintenanceMode = true;
|
||||
break;
|
||||
case "9":
|
||||
client.user.setStatus('online');
|
||||
client.funcs.logger("Maintenance Mode", "Disabled");
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "10":
|
||||
client.config.streamerMode = "manual";
|
||||
client.config.maintenanceMode = true;
|
||||
|
||||
client.user.setStatus('idle');
|
||||
client.radio.save(client);
|
||||
|
||||
setInterval(() => {
|
||||
if(client.radio.size == 0 && client.config.streamerMode == "manual" && client.config.maintenanceMode){
|
||||
client.streamer.leave(client);
|
||||
client.streamer = new Streamer();
|
||||
client.streamer.init(client);
|
||||
|
||||
client.radio.restore(client, guilds);
|
||||
client.user.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
}
|
||||
|
||||
if(!client.config.maintenanceMode){
|
||||
clearInterval();
|
||||
}
|
||||
}, 500);
|
||||
|
||||
break;
|
||||
case "11":
|
||||
client.config.streamerMode = "auto";
|
||||
client.config.maintenanceMode = true;
|
||||
|
||||
client.user.setStatus('idle');
|
||||
client.radio.save(client);
|
||||
|
||||
setInterval(() => {
|
||||
if(client.radio.size == 0 && client.config.streamerMode == "auto" && client.config.maintenanceMode){
|
||||
client.streamer.leave(client);
|
||||
client.streamer = new Streamer();
|
||||
client.streamer.init(client);
|
||||
|
||||
client.radio.restore(client, guilds);
|
||||
client.user.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
}
|
||||
|
||||
if(!client.config.maintenanceMode){
|
||||
clearInterval();
|
||||
}
|
||||
}, 500);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
@ -1,232 +0,0 @@
|
||||
import { ActionRowBuilder, APISelectMenuOption, ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import Streamer from "../classes/Streamer";
|
||||
import commands from "../commands";
|
||||
|
||||
export default {
|
||||
name: 'maintenance',
|
||||
description: 'Bot Maintenance',
|
||||
category: 'info',
|
||||
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||
|
||||
if(!client.funcs.isDev(client.config.devIDs, interaction.user.id)) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.notAllowed,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
let action : number | string | null = null;
|
||||
|
||||
if(interaction.isChatInputCommand()){
|
||||
action = interaction.options?.getNumber("action");
|
||||
}
|
||||
|
||||
if(interaction.isStringSelectMenu()){
|
||||
action = interaction.values?.[0];
|
||||
}
|
||||
|
||||
const options: APISelectMenuOption[] = new Array(
|
||||
{
|
||||
emoji: {
|
||||
"name": "🌀",
|
||||
},
|
||||
label: "Restart Bot",
|
||||
value: "0"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
id: "688541155377414168",
|
||||
name: "RadioXStop",
|
||||
},
|
||||
label: "Save Radios",
|
||||
value: "4"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
id: "688541155712827458",
|
||||
name: "RadioXPlay",
|
||||
},
|
||||
label: "Restore Radios",
|
||||
value: "5"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
name: "#️⃣",
|
||||
},
|
||||
label: "Reload Commands",
|
||||
value: "6"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
id: "688541155519889482",
|
||||
name: "RadioXList",
|
||||
},
|
||||
label: "Reload Stations",
|
||||
value: "7"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
id: "746069698139127831",
|
||||
name: "dnd",
|
||||
},
|
||||
label: "Enable Maintenance Mode",
|
||||
value: "8"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
id: "746069731836035098",
|
||||
name: "online",
|
||||
},
|
||||
label: "Disable Maintenance Mode",
|
||||
value: "9"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
name: "💤",
|
||||
},
|
||||
label: "Streamer Mode - Manual",
|
||||
value: "10"
|
||||
},
|
||||
{
|
||||
emoji: {
|
||||
name: "📡",
|
||||
},
|
||||
label: "Streamer Mode - Auto",
|
||||
value: "11"
|
||||
}
|
||||
);
|
||||
|
||||
const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
|
||||
.addComponents(
|
||||
new StringSelectMenuBuilder()
|
||||
.setCustomId('maintenance')
|
||||
.setPlaceholder('Select action')
|
||||
.addOptions(options)
|
||||
);
|
||||
|
||||
if(!action){
|
||||
return interaction.reply({
|
||||
content: "**" + client.messages.maintenanceTitle + "**",
|
||||
components: [menu],
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
client.funcs.logger('Maintenance', options.find((option: APISelectMenuOption) => option.value == action)?.label);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(client.messages.maintenanceTitle)
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(options.find((option: APISelectMenuOption) => option.value == action)?.label || "-")
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
let guilds = await client.guilds.fetch();
|
||||
|
||||
switch(action){
|
||||
case "0":
|
||||
client.config.maintenanceMode = true;
|
||||
process.emit('SIGINT');
|
||||
break;
|
||||
case "4":
|
||||
client.config.maintenanceMode = true;
|
||||
client.user?.setStatus('idle');
|
||||
client.radio?.save(client);
|
||||
client.user?.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "5":
|
||||
client.config.maintenanceMode = true;
|
||||
client.user?.setStatus('idle');
|
||||
client.radio?.restore(client, guilds);
|
||||
client.user?.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "6":
|
||||
client.config.maintenanceMode = true;
|
||||
client.user?.setStatus('idle');
|
||||
commands(client);
|
||||
client.user?.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "7":
|
||||
try {
|
||||
client.stations?.fetch({
|
||||
url: client.config.stationslistUrl
|
||||
});
|
||||
client.streamer?.refresh(client);
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
break;
|
||||
case "8":
|
||||
client.user?.setStatus('dnd');
|
||||
client.funcs.logger("Maintenance Mode", "Enabled");
|
||||
client.config.maintenanceMode = true;
|
||||
break;
|
||||
case "9":
|
||||
client.user?.setStatus('online');
|
||||
client.funcs.logger("Maintenance Mode", "Disabled");
|
||||
client.config.maintenanceMode = false;
|
||||
break;
|
||||
case "10":
|
||||
client.config.streamerMode = "manual";
|
||||
client.config.maintenanceMode = true;
|
||||
|
||||
client.user?.setStatus('idle');
|
||||
client.radio?.save(client);
|
||||
|
||||
let timer : NodeJS.Timeout = setInterval(() => {
|
||||
if(client.radio?.size == 0 && client.config.streamerMode == "manual" && client.config.maintenanceMode){
|
||||
client.streamer?.leave(client);
|
||||
client.streamer = new Streamer();
|
||||
client.streamer.init(client);
|
||||
|
||||
client.radio?.restore(client, guilds);
|
||||
client.user?.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
}
|
||||
|
||||
if(!client.config.maintenanceMode){
|
||||
clearInterval(timer);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
break;
|
||||
case "11":
|
||||
client.config.streamerMode = "auto";
|
||||
client.config.maintenanceMode = true;
|
||||
|
||||
client.user?.setStatus('idle');
|
||||
client.radio?.save(client);
|
||||
|
||||
let timer2 : NodeJS.Timeout = setInterval(() => {
|
||||
if(client.radio?.size == 0 && client.config.streamerMode == "auto" && client.config.maintenanceMode){
|
||||
client.streamer?.leave(client);
|
||||
client.streamer = new Streamer();
|
||||
client.streamer.init(client);
|
||||
|
||||
client.radio.restore(client, guilds);
|
||||
client.user?.setStatus('online');
|
||||
client.config.maintenanceMode = false;
|
||||
}
|
||||
|
||||
if(!client.config.maintenanceMode){
|
||||
clearInterval(timer2);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
35
src/client/commands/next.js
Normal file
35
src/client/commands/next.js
Normal file
@ -0,0 +1,35 @@
|
||||
module.exports = {
|
||||
name: 'next',
|
||||
description: 'Next Station',
|
||||
category: 'radio',
|
||||
async execute(interaction, client, command) {
|
||||
if (client.funcs.check(client, interaction, command)) {
|
||||
const radio = client.radio.get(interaction.guild.id);
|
||||
|
||||
let index = client.stations.findIndex(station => station.name == radio.station.name) + 1;
|
||||
if(index == client.stations.length) index = 0;
|
||||
|
||||
let station = client.stations[index];
|
||||
|
||||
if(!station) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.noSearchResults,
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
client.statistics.update(client, interaction.guild, radio);
|
||||
|
||||
let date = new Date();
|
||||
radio.station = station;
|
||||
radio.textChannel = interaction.channel;
|
||||
radio.startTime = date.getTime();
|
||||
|
||||
if(interaction.isCommand()) {
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
}
|
||||
if(interaction.isButton()) {
|
||||
interaction.deferUpdate();
|
||||
client.funcs.play(client, null, interaction.guild, station);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
import { ButtonInteraction, ChatInputCommandInteraction, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { station } from "../classes/Stations"
|
||||
import { command } from "../commands";
|
||||
|
||||
export default {
|
||||
name: 'next',
|
||||
description: 'Next Station',
|
||||
category: 'radio',
|
||||
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient, command: command) {
|
||||
if (client.funcs.check(client, interaction, command)) {
|
||||
if(!interaction.guild) return;
|
||||
const radio = client.radio?.get(interaction.guild?.id);
|
||||
if(!radio) return;
|
||||
|
||||
if(client.config.maintenanceMode){
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
if(!client.stations) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||
"%client.config.supportGuild%": client.config.supportGuild
|
||||
}),
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
let index: number = client.stations.findIndex((station: station) => station.name == radio.station.name) + 1;
|
||||
if(index == client.stations?.length) index = 0;
|
||||
|
||||
let station = client.stations[index];
|
||||
|
||||
if(!station) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.noSearchResults,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
client.statistics?.update(client, interaction.guild, radio);
|
||||
|
||||
let date = new Date();
|
||||
radio.station = station;
|
||||
radio.textChannel = interaction.channel;
|
||||
radio.startTime = date.getTime();
|
||||
|
||||
if(interaction.isChatInputCommand()) {
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
}
|
||||
if(interaction.isButton()) {
|
||||
interaction.deferUpdate();
|
||||
client.funcs.play(client, null, interaction.guild, station);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
src/client/commands/nowplaying.js
Normal file
35
src/client/commands/nowplaying.js
Normal file
@ -0,0 +1,35 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'nowplaying',
|
||||
description: 'Current Radio Station',
|
||||
category: 'radio',
|
||||
async execute(interaction, client, command) {
|
||||
if (client.funcs.check(client, interaction, command)) {
|
||||
let message = {};
|
||||
const radio = client.radio.get(interaction.guild.id);
|
||||
|
||||
let date = new Date();
|
||||
radio.currentTime = date.getTime();
|
||||
radio.playTime = parseInt(radio.currentTime)-parseInt(radio.startTime);
|
||||
const completed = (radio.playTime);
|
||||
|
||||
message.nowplayingDescription = client.messages.nowplayingDescription.replace("%radio.station.name%", radio.station.name);
|
||||
message.nowplayingDescription = message.nowplayingDescription.replace("%radio.station.owner%" + "\n", radio.station.name != radio.station.owner ? radio.station.owner + "\n" : "");
|
||||
message.nowplayingDescription = message.nowplayingDescription.replace("%client.funcs.msToTime(completed)%", client.funcs.msToTime(completed));
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(client.messages.nowplayingTitle)
|
||||
.setThumbnail((radio.station.logo || "https://cdn.discordapp.com/emojis/" + client.messageEmojis["play"].replace(/[^0-9]+/g, '')))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(message.nowplayingDescription)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
147
src/client/commands/play.js
Normal file
147
src/client/commands/play.js
Normal file
@ -0,0 +1,147 @@
|
||||
const {
|
||||
getVoiceConnection,
|
||||
joinVoiceChannel
|
||||
} = require("@discordjs/voice");
|
||||
|
||||
module.exports = {
|
||||
name: "play",
|
||||
usage: "<song name>",
|
||||
description: "Play radio",
|
||||
options: [
|
||||
{ type: "STRING", name: "query", description: "Select station", required: false}
|
||||
],
|
||||
category: "radio",
|
||||
async execute(interaction, client) {
|
||||
let message = {};
|
||||
|
||||
if(client.config.maintenanceMode){
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.maintenance,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
if(!client.stations) {
|
||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + message.errorToGetPlaylist,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
let query = interaction.options?.getString("query") ?? interaction.values?.[0];
|
||||
if(!query){
|
||||
return client.funcs.listStations(client, interaction);
|
||||
}
|
||||
let url = query ? query.replace(/<(.+)>/g, "$1") : "";
|
||||
const radio = client.radio.get(interaction.guild.id);
|
||||
const voiceChannel = interaction.member.voice.channel;
|
||||
if (!voiceChannel) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.noVoiceChannel,
|
||||
ephemeral: true
|
||||
});
|
||||
if (radio) {
|
||||
if (voiceChannel !== radio.voiceChannel) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.wrongVoiceChannel,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
if (!query) return interaction.reply({
|
||||
content: client.messages.noQuery,
|
||||
ephemeral: true
|
||||
});
|
||||
const permissions = voiceChannel.permissionsFor(interaction.client.user);
|
||||
if (!permissions.has("CONNECT")) {
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.noPermsConnect,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
if (!permissions.has("SPEAK")) {
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.noPermsSpeak,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
let station;
|
||||
const number = parseInt(query - 1);
|
||||
if (url.startsWith("http")) {
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.errorStationURL,
|
||||
ephemeral: true
|
||||
});
|
||||
} else if (!isNaN(number)) {
|
||||
if (number > client.stations.length - 1) {
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.wrongStationNumber,
|
||||
ephemeral: true
|
||||
});
|
||||
} else {
|
||||
station = client.stations[number];
|
||||
}
|
||||
} else {
|
||||
if (query.length < 3) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.tooShortSearch,
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
let type = "";
|
||||
|
||||
if(interaction.values?.[0]){
|
||||
type = "direct";
|
||||
} else {
|
||||
type = "text";
|
||||
}
|
||||
|
||||
const sstation = await client.stations.search(query, type);
|
||||
if (!sstation) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.noSearchResults,
|
||||
ephemeral: true
|
||||
});
|
||||
station = sstation;
|
||||
}
|
||||
|
||||
if (radio) {
|
||||
client.statistics.update(client, interaction.guild, radio);
|
||||
|
||||
let date = new Date();
|
||||
radio.station = station;
|
||||
radio.textChannel = interaction.channel;
|
||||
radio.startTime = date.getTime();
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const construct = {
|
||||
textChannel: interaction.channel,
|
||||
voiceChannel: voiceChannel,
|
||||
connection: null,
|
||||
message: null,
|
||||
station: station
|
||||
};
|
||||
client.radio.set(interaction.guild.id, construct);
|
||||
|
||||
try {
|
||||
const connection =
|
||||
getVoiceConnection(voiceChannel.guild.id) ??
|
||||
joinVoiceChannel({
|
||||
channelId: voiceChannel.id,
|
||||
guildId: voiceChannel.guild.id,
|
||||
adapterCreator: voiceChannel.guild.voiceAdapterCreator
|
||||
});
|
||||
construct.connection = connection;
|
||||
let date = new Date();
|
||||
construct.startTime = date.getTime();
|
||||
client.datastore.checkEntry(interaction.guild.id);
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
client.radio.delete(interaction.guild.id);
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + `An error occured: ${error}`,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,162 +0,0 @@
|
||||
import { ApplicationCommandOptionType, ChatInputCommandInteraction, GuildMember, PermissionFlagsBits, StringSelectMenuInteraction } from "discord.js";
|
||||
import { DiscordGatewayAdapterCreator, getVoiceConnection, joinVoiceChannel } from "@discordjs/voice";
|
||||
import RadioClient from "../../Client";
|
||||
import { radio } from "../classes/Radio"
|
||||
|
||||
export default {
|
||||
name: "play",
|
||||
usage: "<song name>",
|
||||
description: "Play radio",
|
||||
options: [
|
||||
{ type: ApplicationCommandOptionType.String, name: "query", description: "Select station", required: false}
|
||||
],
|
||||
category: "radio",
|
||||
async execute(interaction: ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||
|
||||
if(!interaction.guild) return;
|
||||
|
||||
if(client.config.maintenanceMode){
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
if(!client.stations) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||
"%client.config.supportGuild%": client.config.supportGuild
|
||||
}),
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
let query: string | null = null;
|
||||
|
||||
if(interaction.isChatInputCommand()){
|
||||
query = interaction.options?.getString("query");
|
||||
}
|
||||
|
||||
if(interaction.isStringSelectMenu()){
|
||||
query = interaction.values?.[0];
|
||||
}
|
||||
|
||||
if(!query){
|
||||
return client.funcs.listStations(client, interaction, "1");
|
||||
}
|
||||
|
||||
const radio = client.radio?.get(interaction.guild.id);
|
||||
|
||||
if(!(interaction.member instanceof GuildMember)) return;
|
||||
const voiceChannel = interaction.member?.voice.channel;
|
||||
|
||||
if (!voiceChannel) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.noVoiceChannel,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
if (radio) {
|
||||
if (voiceChannel !== radio.voiceChannel) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.wrongVoiceChannel,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
if (!query) return interaction.reply({
|
||||
content: client.messages.noQuery,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
const permissions = voiceChannel.permissionsFor(interaction.client.user);
|
||||
if (!permissions?.has(PermissionFlagsBits.Connect)) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.noPermsConnect,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
if (!permissions?.has(PermissionFlagsBits.Speak)) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.noPermsSpeak,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
let station;
|
||||
|
||||
if(!isNaN(parseInt(query) - 1)){
|
||||
let number = parseInt(query) - 1;
|
||||
if(number > client.stations.length - 1) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.wrongStationNumber,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
} else {
|
||||
station = client.stations[number];
|
||||
}
|
||||
} else {
|
||||
|
||||
if(query.length < 3) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.tooShortSearch,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
let type = "text";
|
||||
|
||||
if(interaction.isStringSelectMenu() && interaction.values?.[0]){
|
||||
type = "direct";
|
||||
}
|
||||
|
||||
const sstation = client.stations.search(query, type);
|
||||
if (!sstation) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.noSearchResults,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
station = sstation;
|
||||
}
|
||||
|
||||
if (radio) {
|
||||
client.statistics?.update(client, interaction.guild, radio);
|
||||
|
||||
let date = new Date();
|
||||
radio.station = station;
|
||||
radio.textChannel = interaction.channel;
|
||||
radio.startTime = date.getTime();
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let date = new Date();
|
||||
const construct: radio = {
|
||||
textChannel: interaction.channel,
|
||||
voiceChannel: voiceChannel,
|
||||
connection: undefined,
|
||||
message: null,
|
||||
station: station,
|
||||
startTime: date.getTime(),
|
||||
guild: interaction.guild
|
||||
};
|
||||
client.radio?.set(interaction.guild?.id, construct);
|
||||
|
||||
try {
|
||||
const connection =
|
||||
getVoiceConnection(voiceChannel.guild.id) ??
|
||||
joinVoiceChannel({
|
||||
channelId: voiceChannel.id,
|
||||
guildId: voiceChannel.guild.id,
|
||||
adapterCreator: voiceChannel.guild?.voiceAdapterCreator as DiscordGatewayAdapterCreator
|
||||
});
|
||||
construct.connection = connection;
|
||||
let date = new Date();
|
||||
construct.startTime = date.getTime();
|
||||
client.datastore?.checkEntry(interaction.guild?.id);
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
client.radio?.delete(interaction.guild?.id);
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + `An error occured: ${error}`,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
36
src/client/commands/prev.js
Normal file
36
src/client/commands/prev.js
Normal file
@ -0,0 +1,36 @@
|
||||
module.exports = {
|
||||
name: 'prev',
|
||||
description: 'Previous Station',
|
||||
category: 'radio',
|
||||
async execute(interaction, client, command) {
|
||||
if (client.funcs.check(client, interaction, command)) {
|
||||
const radio = client.radio.get(interaction.guild.id);
|
||||
|
||||
let index = client.stations.findIndex(station => station.name == radio.station.name) - 1;
|
||||
if(index == -1) index = client.stations.length - 1;
|
||||
|
||||
let station = client.stations[index];
|
||||
|
||||
if(!station) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.noSearchResults,
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
client.statistics.update(client, interaction.guild, radio);
|
||||
|
||||
let date = new Date();
|
||||
radio.station = station;
|
||||
radio.textChannel = interaction.channel;
|
||||
radio.startTime = date.getTime();
|
||||
|
||||
if(interaction.isCommand()) {
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
}
|
||||
if(interaction.isButton()) {
|
||||
interaction.deferUpdate();
|
||||
client.funcs.play(client, null, interaction.guild, station);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
import { ButtonInteraction, ChatInputCommandInteraction, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { command } from "../commands";
|
||||
import { station } from "../classes/Stations"
|
||||
|
||||
export default {
|
||||
name: 'prev',
|
||||
description: 'Previous Station',
|
||||
category: 'radio',
|
||||
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient, command: command) {
|
||||
if (client.funcs.check(client, interaction, command)) {
|
||||
if(!interaction.guild) return;
|
||||
const radio = client.radio?.get(interaction.guild?.id);
|
||||
if(!radio) return;
|
||||
|
||||
if(client.config.maintenanceMode){
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
if(!client.stations) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||
"%client.config.supportGuild%": client.config.supportGuild
|
||||
}),
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
let index = client.stations.findIndex((station: station) => station.name == radio.station.name) - 1;
|
||||
if(index == -1) index = client.stations.length - 1;
|
||||
|
||||
let station = client.stations[index];
|
||||
|
||||
if(!station) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.noSearchResults,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
client.statistics?.update(client, interaction.guild, radio);
|
||||
|
||||
let date = new Date();
|
||||
radio.station = station;
|
||||
radio.textChannel = interaction.channel;
|
||||
radio.startTime = date.getTime();
|
||||
|
||||
if(interaction.isChatInputCommand()) {
|
||||
client.funcs.play(client, interaction, interaction.guild, station);
|
||||
}
|
||||
if(interaction.isButton()) {
|
||||
interaction.deferUpdate();
|
||||
client.funcs.play(client, null, interaction.guild, station);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
52
src/client/commands/statistics.js
Normal file
52
src/client/commands/statistics.js
Normal file
@ -0,0 +1,52 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
|
||||
module.exports = {
|
||||
name: 'statistics',
|
||||
description: 'Show statistics',
|
||||
category: 'info',
|
||||
execute(interaction, client) {
|
||||
let message = {};
|
||||
let stations = client.stations;
|
||||
let currentGuild = client.datastore.getEntry(interaction.guild.id);
|
||||
let global = client.datastore.getEntry("global");
|
||||
let statistics = "";
|
||||
|
||||
if(!client.stations) {
|
||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
||||
return interaction.reply({
|
||||
content: client.messageEmojis["error"] + message.errorToGetPlaylist,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
|
||||
if(!currentGuild || currentGuild && !currentGuild.statistics){
|
||||
statistics = "You have not listened any radio stations";
|
||||
} else {
|
||||
Object.keys(stations).forEach(function(station) {
|
||||
if(currentGuild.statistics[stations[station].name] && currentGuild.statistics[stations[station].name].time && parseInt(currentGuild.statistics[stations[station].name].time) > 0 && currentGuild.statistics[stations[station].name].used && parseInt(currentGuild.statistics[stations[station].name].used) > 0){
|
||||
statistics += `**${parseInt(station) + 1}. ` + stations[station].name + "** \n";
|
||||
if(global && global.statistics[stations[station].name] && global.statistics[stations[station].name].time && parseInt(global.statistics[stations[station].name].time) > 0 && global.statistics[stations[station].name].used && parseInt(global.statistics[stations[station].name].used) > 0){
|
||||
statistics += "Guild – Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time) + " (" + ((currentGuild.statistics[stations[station].name].time / global.statistics[stations[station].name].time) * 100).toFixed(0) + "%" + ")" + " / " + "Used: " + currentGuild.statistics[stations[station].name].used + " (" + ((currentGuild.statistics[stations[station].name].used / global.statistics[stations[station].name].used) * 100).toFixed(0) + "%" + ")" + "\n";
|
||||
statistics += "Global – Time: " + client.funcs.msToTime(global.statistics[stations[station].name].time) + " / " + "Used: " + global.statistics[stations[station].name].used + "\n\n";
|
||||
} else {
|
||||
statistics += "Time: " + client.funcs.msToTime(currentGuild.statistics[stations[station].name].time) + " / " + "Used: " + currentGuild.statistics[stations[station].name].used + "\n\n";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(client.messages.statisticsTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["statistics"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(statistics)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
};
|
@ -1,51 +0,0 @@
|
||||
import { ButtonInteraction, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
|
||||
export default {
|
||||
name: 'statistics',
|
||||
description: 'Show statistics',
|
||||
category: 'info',
|
||||
execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient) {
|
||||
|
||||
if(!interaction.guild) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
let currentGuild = client.datastore?.getEntry(interaction.guild.id);
|
||||
let global = client.datastore?.getEntry("global");
|
||||
let statistics = "";
|
||||
|
||||
if(!client.stations) {
|
||||
return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||
"%client.config.supportGuild%": client.config.supportGuild
|
||||
}),
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
|
||||
if(!currentGuild || currentGuild && !currentGuild.statistics){
|
||||
statistics = "You have not listened any radio stations";
|
||||
} else {
|
||||
statistics = "[Open Dashboard](https://eximiabots.waren.io/radiox/" + interaction.guild.id + "/stats?info=" + Buffer.from(JSON.stringify(currentGuild), 'utf8').toString('base64') + "&globalInfo=" + Buffer.from(JSON.stringify(global), 'utf8').toString('base64') + ")" + "\n";
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(client.messages.statisticsTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["statistics"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(statistics)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
};
|
31
src/client/commands/status.js
Normal file
31
src/client/commands/status.js
Normal file
@ -0,0 +1,31 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'status',
|
||||
description: 'Bot Status',
|
||||
category: 'info',
|
||||
async execute(interaction, client) {
|
||||
let message = {};
|
||||
|
||||
message.statusTitle = client.messages.statusTitle.replace("%client.user.username%", client.user.username);
|
||||
let uptime = client.funcs.msToTime(client.uptime);
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(message.statusTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.addField(client.messages.statusField1, uptime, false)
|
||||
.addField(client.messages.statusField2, client.config.version, false)
|
||||
.addField(client.messages.statusField3, Date.now() - interaction.createdTimestamp + "ms", false)
|
||||
.addField(client.messages.statusField4, client.ws.ping + "ms", false)
|
||||
.addField(client.messages.statusField5, client.config.hostedBy, false)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
}
|
||||
};
|
@ -1,42 +0,0 @@
|
||||
import { ChatInputCommandInteraction, EmbedBuilder } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default {
|
||||
name: 'status',
|
||||
description: 'Bot Status',
|
||||
category: 'info',
|
||||
async execute(interaction: ChatInputCommandInteraction, client: RadioClient) {
|
||||
|
||||
if(!client.user) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.maintenance,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
let uptime = client.funcs.msToTime(client.uptime || 0);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(client.messages.replace(client.messages.statusTitle, {
|
||||
"%client.user.username%": client.user.username
|
||||
}))
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["logo"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.addFields([
|
||||
{ name: client.messages.statusField1, value: uptime },
|
||||
{ name: client.messages.statusField2, value: client.config.version },
|
||||
{ name: client.messages.statusField3, value: Date.now() - interaction.createdTimestamp + "ms" },
|
||||
{ name: client.messages.statusField4, value: client.ws.ping.toString() },
|
||||
{ name: client.messages.statusField5, value: client.config.hostedBy }
|
||||
])
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
interaction.reply({
|
||||
embeds: [embed],
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
}
|
||||
};
|
44
src/client/commands/stop.js
Normal file
44
src/client/commands/stop.js
Normal file
@ -0,0 +1,44 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'stop',
|
||||
description: 'Stop radio',
|
||||
category: 'radio',
|
||||
async execute(interaction, client, command) {
|
||||
if (client.funcs.check(client, interaction, command)) {
|
||||
const radio = client.radio.get(interaction.guild.id);
|
||||
client.statistics.update(client, interaction.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
client.funcs.logger('Radio', interaction.guild.id + " / " + 'Stop');
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(client.user.username)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["stop"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.addField(client.messages.nowplayingTitle, "-", true)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
if(!radio.message){
|
||||
radio.message = radio.textChannel.send({ embeds: [embed], components: [] });
|
||||
} else {
|
||||
if(radio.textChannel.id == radio.message.channel.id){
|
||||
radio.message.edit({ embeds: [embed], components: [] });
|
||||
} else {
|
||||
radio.message?.delete();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(async function() {
|
||||
await radio.message?.delete();
|
||||
}, 5000);
|
||||
|
||||
client.radio.delete(interaction.guild.id);
|
||||
|
||||
interaction.reply({
|
||||
content: client.messageEmojis["stop"] + client.messages.stop,
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
@ -1,55 +0,0 @@
|
||||
import { ButtonInteraction, ChannelType, ChatInputCommandInteraction, EmbedBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { command } from "../commands";
|
||||
|
||||
export default {
|
||||
name: 'stop',
|
||||
description: 'Stop radio',
|
||||
category: 'radio',
|
||||
async execute(interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, client: RadioClient, command: command) {
|
||||
if (client.funcs.check(client, interaction, command)) {
|
||||
if(!interaction.guild) return;
|
||||
const radio = client.radio?.get(interaction.guild?.id);
|
||||
if(!radio) return;
|
||||
if(radio.textChannel?.type == ChannelType.DM || radio.textChannel?.type == ChannelType.GroupDM) return;
|
||||
client.statistics?.update(client, interaction.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
client.funcs.logger('Radio', interaction.guild?.id + " / " + 'Stop');
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(client.user?.username || "-")
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messages.emojis["stop"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.addFields({
|
||||
name: client.messages.playTitle1,
|
||||
value: "-"
|
||||
})
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
if(!radio.message){
|
||||
radio.message = await radio.textChannel?.send({ embeds: [embed], components: [] }) ?? null;
|
||||
} else {
|
||||
if(radio.textChannel?.id == radio.message.channel.id){
|
||||
radio.message.edit({ embeds: [embed], components: [] });
|
||||
} else {
|
||||
radio.message?.delete();
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(async function() {
|
||||
await radio.message?.delete();
|
||||
}, 5000);
|
||||
|
||||
client.radio?.delete(interaction.guild.id);
|
||||
|
||||
interaction.reply({
|
||||
content: client.messages.emojis["stop"] + client.messages.stop,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
42
src/client/emojis.js
Normal file
42
src/client/emojis.js
Normal file
@ -0,0 +1,42 @@
|
||||
module.exports = {
|
||||
name: 'emojis',
|
||||
async execute(client) {
|
||||
let customEmojis = {
|
||||
logo: "<:RadioX:688765708808487072>",
|
||||
eximiabots: "<:EximiaBots:693277919929303132>",
|
||||
list: "<:RadioXList:688541155519889482>",
|
||||
play: "<:RadioXPlay:688541155712827458>",
|
||||
stop: "<:RadioXStop:688541155377414168>",
|
||||
statistics: "<:RadioXStatistics:694954485507686421>",
|
||||
maintenance: "<:RadioXMaintenance:695043843057254493>",
|
||||
error: "<:RadioXError:688541155792781320>",
|
||||
prev: "<:RadioXPrev:882153637370023957>",
|
||||
next: "<:RadioXNext:882153637474893834>"
|
||||
};
|
||||
|
||||
let fallbackEmojis = {
|
||||
logo: "RadioX",
|
||||
eximiabots: "EximiaBots",
|
||||
list: "📜",
|
||||
play: "▶️",
|
||||
stop: "⏹️",
|
||||
statistics: "📊",
|
||||
maintenance: "🛠️",
|
||||
error: "❌",
|
||||
prev: "⏪",
|
||||
next: "⏩"
|
||||
};
|
||||
|
||||
client.messageEmojis = {};
|
||||
|
||||
for (const customEmojiName in customEmojis) {
|
||||
const customEmojiID = customEmojis[customEmojiName].replace(/[^0-9]+/g, '');
|
||||
const customEmoji = client.emojis.cache.get(customEmojiID);
|
||||
if (customEmoji) {
|
||||
client.messageEmojis[customEmojiName] = customEmojis[customEmojiName];
|
||||
} else {
|
||||
client.messageEmojis[customEmojiName] = fallbackEmojis[customEmojiName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import RadioClient from "../Client"
|
||||
import interactionCreate from "./events/interactionCreate"
|
||||
import messageDelete from "./events/messageDelete"
|
||||
import ready from "./events/ready"
|
||||
import SIGINT from "./events/SIGINT"
|
||||
import SIGTERM from "./events/SIGTERM"
|
||||
import uncaughtException from "./events/uncaughtException"
|
||||
import voiceStateUpdate from "./events/voiceStateUpdate"
|
||||
import warning from "./events/warning"
|
||||
|
||||
export default function events(client: RadioClient) {
|
||||
client.on("ready", () => {
|
||||
ready(client);
|
||||
});
|
||||
|
||||
client.on("messageDelete", msg => {
|
||||
messageDelete(client, msg);
|
||||
});
|
||||
|
||||
client.on("interactionCreate", interaction => {
|
||||
interactionCreate(client, interaction);
|
||||
});
|
||||
|
||||
client.on("voiceStateUpdate", (oldState, newState) => {
|
||||
voiceStateUpdate(client, oldState, newState);
|
||||
});
|
||||
|
||||
client.on("error", error => {
|
||||
client.funcs.logger("Discord Client", "Error");
|
||||
console.error(error);
|
||||
console.log('');
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
SIGINT(client);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
SIGTERM(client);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (error) => {
|
||||
uncaughtException(client, error);
|
||||
});
|
||||
|
||||
process.on('exit', () => {
|
||||
client.funcs.logger("Bot", "Stopping");
|
||||
});
|
||||
|
||||
process.on('warning', (error) => {
|
||||
warning(client, error);
|
||||
});
|
||||
}
|
15
src/client/events/SIGINT.js
Normal file
15
src/client/events/SIGINT.js
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
name: 'SIGINT',
|
||||
execute(client) {
|
||||
client.user.setStatus('dnd');
|
||||
|
||||
client.streamer.leave(client);
|
||||
client.radio.save(client);
|
||||
|
||||
setInterval(() => {
|
||||
if(client.radio.size == 0){
|
||||
process.exit();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function SIGINT(client: RadioClient) {
|
||||
client.user?.setStatus('dnd');
|
||||
|
||||
client.streamer?.leave(client);
|
||||
client.radio?.save(client);
|
||||
|
||||
setInterval(() => {
|
||||
if(client.radio?.size == 0){
|
||||
process.exit();
|
||||
}
|
||||
}, 500);
|
||||
}
|
6
src/client/events/SIGTERM.js
Normal file
6
src/client/events/SIGTERM.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
name: 'SIGTERM',
|
||||
execute(client) {
|
||||
process.emit('SIGINT');
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function SIGTERM(client: RadioClient) {
|
||||
process.emit('SIGINT');
|
||||
}
|
43
src/client/events/interactionCreate.js
Normal file
43
src/client/events/interactionCreate.js
Normal file
@ -0,0 +1,43 @@
|
||||
module.exports = {
|
||||
name: 'interactionCreate',
|
||||
async execute(client, interaction) {
|
||||
|
||||
const permissions = interaction.channel.permissionsFor(interaction.client.user);
|
||||
if (!permissions.has('VIEW_CHANNEL')) return;
|
||||
|
||||
if (!permissions.has('EMBED_LINKS')) return interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.noPermsEmbed,
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
if(interaction.isCommand()){
|
||||
const commandName = interaction.commandName;
|
||||
const command = client.commands.get(commandName);
|
||||
if (!command) return;
|
||||
|
||||
try {
|
||||
command.execute(interaction, client);
|
||||
} catch (error) {
|
||||
interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.runningCommandFailed,
|
||||
ephemeral: true
|
||||
});
|
||||
console.error(error);
|
||||
}
|
||||
} else if (interaction.isSelectMenu() || interaction.isButton()){
|
||||
const commandName = interaction.customId;
|
||||
const command = client.commands.get(commandName);
|
||||
if (!command) return;
|
||||
|
||||
try {
|
||||
command.execute(interaction, client, command);
|
||||
} catch (error) {
|
||||
interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.runningCommandFailed,
|
||||
ephemeral: true
|
||||
});
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import { ChannelType, Interaction, PermissionFlagsBits } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function interactionCreate(client: RadioClient, interaction: Interaction) {
|
||||
if(!(interaction.isButton()) && !(interaction.isChatInputCommand()) && !(interaction.isStringSelectMenu())) return;
|
||||
if(interaction.channel?.type == ChannelType.DM || interaction.channel?.type == ChannelType.GroupDM) return;
|
||||
|
||||
const permissions = interaction.channel?.permissionsFor(interaction.client.user);
|
||||
if (!permissions?.has(PermissionFlagsBits.ViewChannel)) return;
|
||||
|
||||
if (!permissions?.has(PermissionFlagsBits.EmbedLinks)) return interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.noPermsEmbed,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
if(interaction.isChatInputCommand()){
|
||||
const commandName = interaction.commandName;
|
||||
const command = client.commands.get(commandName);
|
||||
if (!command) return;
|
||||
|
||||
try {
|
||||
command.execute(interaction, client);
|
||||
} catch (error) {
|
||||
interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.runningCommandFailed,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
console.error(error);
|
||||
}
|
||||
} else if (interaction.isStringSelectMenu() || interaction.isButton()){
|
||||
const commandName = interaction.customId;
|
||||
const command = client.commands.get(commandName);
|
||||
if (!command) return;
|
||||
|
||||
try {
|
||||
command.execute(interaction, client, command);
|
||||
} catch (error) {
|
||||
interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.runningCommandFailed,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
58
src/client/events/messageCreate.js
Normal file
58
src/client/events/messageCreate.js
Normal file
@ -0,0 +1,58 @@
|
||||
import Discord from "discord.js";
|
||||
module.exports = {
|
||||
name: 'messageCreate',
|
||||
async execute(client, message) {
|
||||
|
||||
if (message.author.bot || !message.guild) return;
|
||||
let prefix = "rx$";
|
||||
if(client.user.username == "RadioX"){
|
||||
prefix = "rx>";
|
||||
} else if (client.user.username == "RadioX Beta"){
|
||||
prefix = "rx-";
|
||||
} else if (client.user.username == "RadioX Dev"){
|
||||
prefix = "rx$";
|
||||
} else if(message.mentions.members.first() && message.mentions.members.first().user.id === client.user.id){
|
||||
prefix = "<@!" + client.user.id + "> ";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const args = message.content.slice(prefix.length).split(' ');
|
||||
if (!message.content.startsWith(prefix)) return;
|
||||
if (!args[0]) return;
|
||||
const commandName = args[0].toLowerCase();
|
||||
if (commandName === 'none') return;
|
||||
const command = client.commands.get(commandName) || client.commands.find(cmd => cmd.aliases && cmd.aliases.includes(commandName));
|
||||
if (!command && message.content !== `${prefix}`) return;
|
||||
const permissions = message.channel.permissionsFor(message.client.user);
|
||||
if (!permissions.has('EMBED_LINKS')) return message.channel.send(client.messages.noPermsEmbed);
|
||||
try {
|
||||
let newMessage = {};
|
||||
|
||||
newMessage.messageCommandsDeprecatedTitle = client.messages.messageCommandsDeprecatedTitle.replace("%client.user.username%", client.user.username);
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(newMessage.messageCommandsDeprecatedTitle)
|
||||
.setThumbnail("https://cdn.discordapp.com/emojis/" + client.messageEmojis["logo"].replace(/[^0-9]+/g, ''))
|
||||
.setColor(client.config.embedColor)
|
||||
.setDescription(client.messages.messageCommandsDeprecatedDescription)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
let msg = await message.channel.send({ embeds: [embed] });
|
||||
|
||||
setTimeout(async function() {
|
||||
try {
|
||||
await msg.delete();
|
||||
} catch (DiscordAPIError) {
|
||||
}
|
||||
}, 30000);
|
||||
} catch (error) {
|
||||
message.reply({
|
||||
content: client.messages.runningCommandFailed,
|
||||
ephemeral: true
|
||||
});
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
10
src/client/events/messageDelete.js
Normal file
10
src/client/events/messageDelete.js
Normal file
@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
name: 'messageDelete',
|
||||
async execute(client, msg) {
|
||||
if (!msg.author.bot || !msg.guild) return;
|
||||
const radio = client.radio.get(msg.guild.id);
|
||||
if (!radio) return;
|
||||
if(msg.id != radio.message.id) return;
|
||||
radio.message = null;
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { Message, PartialMessage } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function messageDelete(client: RadioClient, msg: Message | PartialMessage){
|
||||
if(!msg.author?.bot || !msg.guild) return;
|
||||
const radio = client.radio?.get(msg.guild.id);
|
||||
if(!radio) return;
|
||||
if(!radio.message) return;
|
||||
if(msg.id != radio.message.id) return;
|
||||
radio.message = null;
|
||||
}
|
102
src/client/events/ready.js
Normal file
102
src/client/events/ready.js
Normal file
@ -0,0 +1,102 @@
|
||||
import Datastore from "../classes/Datastore.js";
|
||||
import Radio from "../classes/Radio.js";
|
||||
import Stations from "../classes/Stations.js";
|
||||
import Streamer from "../classes/Streamer.js";
|
||||
import Statistics from "../classes/Statistics.js";
|
||||
|
||||
module.exports = {
|
||||
name: 'ready',
|
||||
async execute(client) {
|
||||
|
||||
client.funcs.logger("Bot", "Ready");
|
||||
|
||||
/*DATASTORE*/
|
||||
client.funcs.logger('Datastore', 'Initialize');
|
||||
client.datastore = new Datastore();
|
||||
|
||||
client.funcs.logger('Datastore');
|
||||
client.datastore.map.forEach(datastore => {
|
||||
console.log("- " + datastore.guild.id + " / " + datastore.guild.name);
|
||||
});
|
||||
console.log("\n");
|
||||
|
||||
client.funcs.logger('Datastore', 'Ready');
|
||||
|
||||
/*DEVELOPERS*/
|
||||
client.funcs.logger('Developers');
|
||||
|
||||
client.developers = "";
|
||||
let user = "";
|
||||
for (let i = 0; i < client.config.devId.length; i++) {
|
||||
user = await client.users.fetch(client.config.devId[i]);
|
||||
console.log("- " + user.tag);
|
||||
if (i == client.config.devId.length - 1) {
|
||||
client.developers += user.tag;
|
||||
} else {
|
||||
client.developers += user.tag + " & ";
|
||||
}
|
||||
}
|
||||
console.log("\n");
|
||||
|
||||
/*STATIONS*/
|
||||
client.stations = new Stations();
|
||||
|
||||
await client.stations.fetch({
|
||||
url: client.config.stationslistUrl,
|
||||
show: true
|
||||
});
|
||||
|
||||
setInterval(async () => {
|
||||
await client.stations.fetch({
|
||||
url: client.config.stationslistUrl,
|
||||
show: false
|
||||
});
|
||||
}, 3600000);
|
||||
|
||||
client.streamer = new Streamer();
|
||||
client.streamer.init(client);
|
||||
|
||||
if(!client.stations) {
|
||||
client.user.setStatus('dnd');
|
||||
}
|
||||
|
||||
/*GUILDS*/
|
||||
client.funcs.logger('Guilds', 'Started fetching list');
|
||||
|
||||
client.funcs.logger('Guilds');
|
||||
let guilds = await client.guilds.fetch();
|
||||
guilds.forEach(guild => {
|
||||
console.log("- " + guild.id + " / " + guild.name);
|
||||
});
|
||||
console.log("\n");
|
||||
|
||||
client.funcs.logger('Guilds', 'Successfully fetched list');
|
||||
|
||||
/*STATISTICS*/
|
||||
client.statistics = new Statistics();
|
||||
client.statistics.calculateGlobal(client);
|
||||
|
||||
/*EMOJIS*/
|
||||
require(`../emojis.js`).execute(client);
|
||||
|
||||
/*COMMANDS*/
|
||||
require(`../commands.js`).execute(client);
|
||||
|
||||
/*RADIO*/
|
||||
client.radio = new Radio();
|
||||
|
||||
setTimeout(function () {
|
||||
/*RESTORE RADIOS*/
|
||||
client.radio.restore(client, guilds);
|
||||
}, 5000);
|
||||
|
||||
setTimeout(function () {
|
||||
if(client.stations) {
|
||||
/*MAINTENANCE MODE*/
|
||||
client.funcs.logger("Maintenance Mode", "Disabled");
|
||||
client.config.maintenanceMode = false;
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
import RadioClient from "../../Client";
|
||||
import Datastore, { datastore } from "../classes/Datastore";
|
||||
import Radio from "../classes/Radio";
|
||||
import Stations from "../classes/Stations";
|
||||
import Streamer from "../classes/Streamer";
|
||||
import Statistics from "../classes/Statistics";
|
||||
import commands from "../commands";
|
||||
import { OAuth2Guild } from "discord.js";
|
||||
|
||||
export default async function ready(client: RadioClient) {
|
||||
client.funcs.logger("Bot", "Ready");
|
||||
|
||||
/*DATASTORE*/
|
||||
client.funcs.logger('Datastore', 'Initialize');
|
||||
client.datastore = new Datastore();
|
||||
|
||||
client.datastore.map.forEach((datastore: datastore) => {
|
||||
client.funcs.logger('Datastore', datastore.guild.id + " / " + datastore.guild.name);
|
||||
});
|
||||
|
||||
client.funcs.logger('Datastore', 'Ready');
|
||||
|
||||
/*DEVELOPERS*/
|
||||
let developers : string[] = [];
|
||||
for(let devID of client.config.devIDs){
|
||||
developers.push((await client.users.fetch(devID)).tag);
|
||||
}
|
||||
client.funcs.logger('Developers', developers.join(" & "));
|
||||
|
||||
/*STATIONS*/
|
||||
client.stations = new Stations();
|
||||
|
||||
await client.stations.fetch({
|
||||
url: client.config.stationslistUrl,
|
||||
show: true
|
||||
});
|
||||
|
||||
client.streamer = new Streamer();
|
||||
client.streamer.init(client);
|
||||
|
||||
if(!client.stations) {
|
||||
client.user?.setStatus('dnd');
|
||||
}
|
||||
|
||||
/*GUILDS*/
|
||||
client.funcs.logger('Guilds', 'Started fetching list');
|
||||
|
||||
let guilds = await client.guilds.fetch();
|
||||
guilds.forEach((guild: OAuth2Guild) => {
|
||||
client.funcs.logger('Guilds', guild.id + " / " + guild.name);
|
||||
});
|
||||
|
||||
client.funcs.logger('Guilds', 'Successfully fetched list');
|
||||
|
||||
/*STATISTICS*/
|
||||
client.statistics = new Statistics();
|
||||
client.statistics.calculateGlobal(client);
|
||||
|
||||
/*COMMANDS*/
|
||||
commands(client);
|
||||
|
||||
/*RADIO*/
|
||||
client.radio = new Radio();
|
||||
|
||||
setTimeout(function () {
|
||||
/*RESTORE RADIOS*/
|
||||
client.radio?.restore(client, guilds);
|
||||
}, 5000);
|
||||
|
||||
setTimeout(function () {
|
||||
if(client.stations) {
|
||||
/*MAINTENANCE MODE*/
|
||||
client.funcs.logger("Maintenance Mode", "Disabled");
|
||||
client.config.maintenanceMode = false;
|
||||
}
|
||||
}, 10000);
|
||||
}
|
11
src/client/events/uncaughtException.js
Normal file
11
src/client/events/uncaughtException.js
Normal file
@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
name: 'uncaughtException',
|
||||
execute(client, error) {
|
||||
client.funcs.logger("Error");
|
||||
console.log(error.stack);
|
||||
console.log('');
|
||||
|
||||
if(error.name == "DiscordAPIError" && error.message == "Unknown interaction") return;
|
||||
process.emit('SIGINT');
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function uncaughtException(client: RadioClient, error: Error) {
|
||||
client.funcs.logger("Error");
|
||||
console.log(error.stack);
|
||||
console.log('');
|
||||
|
||||
if(error.name == "DiscordAPIError[10062]" && error.message == "Unknown interaction") return;
|
||||
process.emit('SIGINT');
|
||||
}
|
67
src/client/events/voiceStateUpdate.js
Normal file
67
src/client/events/voiceStateUpdate.js
Normal file
@ -0,0 +1,67 @@
|
||||
const {
|
||||
getVoiceConnection,
|
||||
joinVoiceChannel
|
||||
} = require("@discordjs/voice");
|
||||
|
||||
module.exports = {
|
||||
name: "voiceStateUpdate",
|
||||
async execute(client, oldState, newState) {
|
||||
if (oldState.channel === null) return;
|
||||
let change = false;
|
||||
const radio = client.radio.get(newState.guild.id);
|
||||
if (!radio) return;
|
||||
|
||||
if (newState.member.id === client.user.id && oldState.member.id === client.user.id) {
|
||||
|
||||
if (newState.channel === null) {
|
||||
client.statistics.update(client, newState.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
radio.message?.delete();
|
||||
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||
return client.radio.delete(newState.guild.id);
|
||||
}
|
||||
|
||||
const newPermissions = newState.channel.permissionsFor(newState.client.user);
|
||||
if (!newPermissions.has("CONNECT") || !newPermissions.has("SPEAK") || !newPermissions.has("VIEW_CHANNEL")) {
|
||||
try {
|
||||
setTimeout(
|
||||
async () => (
|
||||
radio.connection = joinVoiceChannel({
|
||||
channelId: oldState.channel.id,
|
||||
guildId: oldState.channel.guild.id,
|
||||
adapterCreator: oldState.channel.guild.voiceAdapterCreator
|
||||
})
|
||||
//radio.connection = await oldState.channel.join()
|
||||
),
|
||||
1000
|
||||
);
|
||||
} catch (error) {
|
||||
client.statistics.update(client, newState.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
radio.message?.delete();
|
||||
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||
client.radio.delete(oldState.guild.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (newState.channel !== radio.voiceChannel) {
|
||||
change = true;
|
||||
radio.voiceChannel = newState.channel;
|
||||
radio.connection = getVoiceConnection(newState.channel.guild.id);
|
||||
//radio.connection = await newState.channel.join();
|
||||
}
|
||||
}
|
||||
if ((oldState.channel.members.filter(member => !member.user.bot).size === 0 && oldState.channel === radio.voiceChannel) || change) {
|
||||
setTimeout(() => {
|
||||
if (!radio || !radio.connection || !radio.connection === null) return;
|
||||
if (radio.voiceChannel.members.filter(member => !member.user.bot).size === 0) {
|
||||
client.statistics.update(client, newState.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
radio.message?.delete();
|
||||
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||
client.radio.delete(newState.guild.id);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
};
|
@ -1,62 +0,0 @@
|
||||
import { GuildMember, PermissionFlagsBits, VoiceState } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { DiscordGatewayAdapterCreator, getVoiceConnection, joinVoiceChannel } from "@discordjs/voice";
|
||||
|
||||
export default async function voiceStateUpdate(client: RadioClient, oldState: VoiceState, newState: VoiceState) {
|
||||
if (oldState.channel === null) return;
|
||||
let change = false;
|
||||
const radio = client.radio?.get(newState.guild.id);
|
||||
if (!radio) return;
|
||||
|
||||
if (newState.member?.id === client.user?.id && oldState.member?.id === client.user?.id) {
|
||||
|
||||
if (newState.channel === null) {
|
||||
client.statistics?.update(client, newState.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
radio.message?.delete();
|
||||
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||
return client.radio?.delete(newState.guild.id);
|
||||
}
|
||||
|
||||
const newPermissions = newState.channel.permissionsFor(newState.client.user);
|
||||
if (!newPermissions?.has(PermissionFlagsBits.Connect) || !newPermissions?.has(PermissionFlagsBits.Speak) || !newPermissions?.has(PermissionFlagsBits.ViewChannel)) {
|
||||
try {
|
||||
setTimeout(
|
||||
async () => (
|
||||
radio.connection = joinVoiceChannel({
|
||||
channelId: oldState.channel?.id as string,
|
||||
guildId: oldState.channel?.guild.id as string,
|
||||
adapterCreator: oldState.channel?.guild.voiceAdapterCreator as DiscordGatewayAdapterCreator
|
||||
})
|
||||
),
|
||||
1000
|
||||
);
|
||||
} catch (error) {
|
||||
client.statistics?.update(client, newState.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
radio.message?.delete();
|
||||
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||
client.radio?.delete(oldState.guild.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (newState.channel !== radio.voiceChannel) {
|
||||
change = true;
|
||||
radio.voiceChannel = newState.channel;
|
||||
radio.connection = getVoiceConnection(newState.channel.guild.id);
|
||||
|
||||
}
|
||||
}
|
||||
if ((oldState.channel.members.filter(member => !member.user.bot).size === 0 && oldState.channel === radio.voiceChannel) || change) {
|
||||
setTimeout(() => {
|
||||
if (!radio || !radio.connection || !radio.connection === null) return;
|
||||
if (radio.voiceChannel?.members.filter((member: GuildMember) => !member.user.bot).size === 0) {
|
||||
client.statistics?.update(client, newState.guild, radio);
|
||||
radio.connection?.destroy();
|
||||
radio.message?.delete();
|
||||
client.funcs.logger('Radio', newState.guild.id + " / " + 'Stop');
|
||||
client.radio?.delete(newState.guild.id);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
};
|
12
src/client/events/warning.js
Normal file
12
src/client/events/warning.js
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
name: 'warning',
|
||||
execute(client, warning) {
|
||||
if(warning.name == "ExperimentalWarning" && warning.message.startsWith("stream/web")) return;
|
||||
|
||||
client.funcs.logger("Warning");
|
||||
console.warn(warning.name);
|
||||
console.warn(warning.message);
|
||||
console.warn(warning.stack);
|
||||
console.log('');
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function warning(client: RadioClient, warning: Error) {
|
||||
if(warning.name == "ExperimentalWarning" && warning.message.startsWith("stream/web")) return;
|
||||
|
||||
client.funcs.logger("Warning");
|
||||
console.warn(warning.name);
|
||||
console.warn(warning.message);
|
||||
console.warn(warning.stack);
|
||||
console.log('');
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import check from "./funcs/check";
|
||||
import isDev from "./funcs/isDev";
|
||||
import listStations from "./funcs/listStations";
|
||||
import loadState from "./funcs/loadState";
|
||||
import logger from "./funcs/logger";
|
||||
import msToTime from "./funcs/msToTime";
|
||||
import play from "./funcs/play";
|
||||
import saveState from "./funcs/saveState";
|
||||
|
||||
export const funcs = {
|
||||
check, isDev, listStations, loadState, logger, msToTime, play, saveState
|
||||
}
|
36
src/client/funcs/check.js
Normal file
36
src/client/funcs/check.js
Normal file
@ -0,0 +1,36 @@
|
||||
module.exports = function check(client, interaction, command) {
|
||||
let message = {};
|
||||
const radio = client.radio.get(interaction.guild.id);
|
||||
const permissions = interaction.channel.permissionsFor(interaction.user);
|
||||
if(client.config.maintenanceMode){
|
||||
interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.maintenance,
|
||||
ephemeral: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if(!client.stations) {
|
||||
message.errorToGetPlaylist = client.messages.errorToGetPlaylist.replace("%client.config.supportGuild%", client.config.supportGuild);
|
||||
interaction.reply({
|
||||
content: client.messageEmojis["error"] + message.errorToGetPlaylist,
|
||||
ephemeral: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (!radio) {
|
||||
interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.notPlaying,
|
||||
ephemeral: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (interaction.member.voice.channel !== radio.voiceChannel) {
|
||||
interaction.reply({
|
||||
content: client.messageEmojis["error"] + client.messages.wrongVoiceChannel,
|
||||
ephemeral: true
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
@ -1,35 +0,0 @@
|
||||
import { ButtonInteraction, ChatInputCommandInteraction, GuildMember, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { command } from "../commands";
|
||||
|
||||
export default function check(client: RadioClient, interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, command: command) {
|
||||
|
||||
if(!interaction.guild) return;
|
||||
const radio = client.radio?.get(interaction.guild?.id);
|
||||
if(!client.stations) {
|
||||
interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.replace(client.messages.errorToGetPlaylist, {
|
||||
"%client.config.supportGuild%": client.config.supportGuild
|
||||
}),
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (!radio) {
|
||||
interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.notPlaying,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (interaction.member instanceof GuildMember && interaction.member?.voice.channel !== radio.voiceChannel) {
|
||||
interaction.reply({
|
||||
content: client.messages.emojis["error"] + client.messages.wrongVoiceChannel,
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
10
src/client/funcs/isDev.js
Normal file
10
src/client/funcs/isDev.js
Normal file
@ -0,0 +1,10 @@
|
||||
module.exports = function isDev(devList, authorID){
|
||||
let response = false;
|
||||
Object.keys(devList).forEach(function(oneDev) {
|
||||
let devID = devList[oneDev];
|
||||
if(authorID == devID){
|
||||
response = true;
|
||||
}
|
||||
});
|
||||
return response;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { Snowflake } from "discord.js";
|
||||
|
||||
export default function isDev(devIDs : string[], authorID : Snowflake){
|
||||
for (const devID of devIDs){
|
||||
if(authorID == devID){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
33
src/client/funcs/listStations.js
Normal file
33
src/client/funcs/listStations.js
Normal file
@ -0,0 +1,33 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = function listStations(client, interaction){
|
||||
let stations = new Array();
|
||||
let options = new Array();
|
||||
|
||||
stations = client.stations.forEach(station => {
|
||||
if(station.name == "GrooveFM") return;
|
||||
station = {
|
||||
label: station.name,
|
||||
description: station.owner,
|
||||
value: station.name
|
||||
};
|
||||
options.push(station);
|
||||
});
|
||||
|
||||
const menu = new Discord.MessageActionRow()
|
||||
.addComponents(
|
||||
new Discord.MessageSelectMenu()
|
||||
.setCustomId('play')
|
||||
.setPlaceholder('Nothing selected')
|
||||
.addOptions(options)
|
||||
);
|
||||
|
||||
stations = null;
|
||||
options = null;
|
||||
|
||||
return interaction.reply({
|
||||
content: '**Select station:**',
|
||||
components: [menu],
|
||||
ephemeral: true
|
||||
});
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import { ActionRowBuilder, ButtonInteraction, ChatInputCommandInteraction, SelectMenuComponentOptionData, StringSelectMenuBuilder, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function listStations(client: RadioClient, interaction: ButtonInteraction | ChatInputCommandInteraction | StringSelectMenuInteraction, offset: string){
|
||||
if(!client.stations) return;
|
||||
|
||||
let options : SelectMenuComponentOptionData[] = new Array();
|
||||
|
||||
|
||||
for (const station of client.stations){
|
||||
options.push({
|
||||
label: station.name,
|
||||
description: station.owner,
|
||||
value: station.name
|
||||
});
|
||||
}
|
||||
|
||||
switch(offset){
|
||||
case "1":
|
||||
options = options.slice(0,Math.round(options.length/2));
|
||||
break;
|
||||
case "2":
|
||||
options = options.slice(Math.round(options.length/2),options.length-1);
|
||||
break;
|
||||
default:
|
||||
options = options.slice(0,Math.round(options.length/2));
|
||||
}
|
||||
|
||||
const menu = new ActionRowBuilder<StringSelectMenuBuilder>()
|
||||
.addComponents(
|
||||
new StringSelectMenuBuilder()
|
||||
.setCustomId('play')
|
||||
.setPlaceholder('Nothing selected')
|
||||
.addOptions(options)
|
||||
);
|
||||
|
||||
return interaction.reply({
|
||||
content: '**Select station:**',
|
||||
components: [menu],
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
}
|
12
src/client/funcs/loadState.js
Normal file
12
src/client/funcs/loadState.js
Normal file
@ -0,0 +1,12 @@
|
||||
module.exports = function loadState(client, guild){
|
||||
let data = client.datastore.getEntry(guild.id);
|
||||
if(!data) return;
|
||||
let state;
|
||||
|
||||
state = data.state;
|
||||
if(!state) return;
|
||||
|
||||
data.state = {};
|
||||
client.datastore.updateEntry(guild, data);
|
||||
return state;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { OAuth2Guild } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
|
||||
export default function loadState(client: RadioClient, guild: OAuth2Guild) {
|
||||
if(!client.datastore) return;
|
||||
let data = client.datastore.getEntry(guild.id);
|
||||
if(!data) return;
|
||||
let state = data.state;
|
||||
if(!state) return;
|
||||
data.state = null;
|
||||
client.datastore.updateEntry(guild, data);
|
||||
return state;
|
||||
}
|
5
src/client/funcs/logger.js
Normal file
5
src/client/funcs/logger.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = function logger(area, text){
|
||||
let date = new Date();
|
||||
console.log('[' + area + '] – ' + date.toISOString());
|
||||
if(text) console.log(text + '\n');
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export default function logger(area: string, text?: string){
|
||||
let date = new Date();
|
||||
console.log('[' + area + '] - ' + date.toISOString());
|
||||
if(text) console.log(text + '\n');
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export default function msToTime(duration : number) {
|
||||
module.exports = function msToTime(duration) {
|
||||
let seconds = Math.floor((duration / 1000) % 60),
|
||||
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
||||
hours = Math.floor((duration / (1000 * 60 * 60)) % 24),
|
74
src/client/funcs/play.js
Normal file
74
src/client/funcs/play.js
Normal file
@ -0,0 +1,74 @@
|
||||
import Discord from "discord.js";
|
||||
|
||||
module.exports = async function play(client, interaction, guild, station) {
|
||||
let message = {};
|
||||
const radio = client.radio.get(guild.id);
|
||||
const audioPlayer = client.streamer.listen(station);
|
||||
radio.connection.subscribe(audioPlayer);
|
||||
client.funcs.logger('Radio', guild.id + " / " + "Play" + " / " + radio.station.name);
|
||||
|
||||
message.nowplayingDescription = client.messages.nowplayingDescription.replace("%radio.station.name%", radio.station.name);
|
||||
message.nowplayingDescription = message.nowplayingDescription.replace("%radio.station.owner%", radio.station.name != radio.station.owner ? radio.station.owner + "\n" : "");
|
||||
message.nowplayingDescription = message.nowplayingDescription.replace("%client.funcs.msToTime(completed)%", "");
|
||||
message.nowplayingDescription = message.nowplayingDescription.replace("**", "");
|
||||
message.nowplayingDescription = message.nowplayingDescription.replace("**", "");
|
||||
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setTitle(client.user.username)
|
||||
.setThumbnail((radio.station.logo || "https://cdn.discordapp.com/emojis/" + client.messageEmojis["play"].replace(/[^0-9]+/g, '')))
|
||||
.setColor(client.config.embedColor)
|
||||
.addField(client.messages.nowplayingTitle, message.nowplayingDescription, true)
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter(client.messages.footerText, "https://cdn.discordapp.com/emojis/" + client.messageEmojis["eximiabots"].replace(/[^0-9]+/g, ''));
|
||||
|
||||
const buttons = new Discord.MessageActionRow()
|
||||
.addComponents(
|
||||
new Discord.MessageButton()
|
||||
.setCustomId('list')
|
||||
.setEmoji(client.messageEmojis["list"])
|
||||
.setStyle('SECONDARY')
|
||||
)
|
||||
.addComponents(
|
||||
new Discord.MessageButton()
|
||||
.setCustomId('prev')
|
||||
.setEmoji(client.messageEmojis["prev"])
|
||||
.setStyle('SECONDARY')
|
||||
)
|
||||
.addComponents(
|
||||
new Discord.MessageButton()
|
||||
.setCustomId('stop')
|
||||
.setEmoji(client.messageEmojis["stop"])
|
||||
.setStyle('SECONDARY')
|
||||
)
|
||||
.addComponents(
|
||||
new Discord.MessageButton()
|
||||
.setCustomId('next')
|
||||
.setEmoji(client.messageEmojis["next"])
|
||||
.setStyle('SECONDARY')
|
||||
)
|
||||
.addComponents(
|
||||
new Discord.MessageButton()
|
||||
.setCustomId('statistics')
|
||||
.setEmoji(client.messageEmojis["statistics"])
|
||||
.setStyle('SECONDARY')
|
||||
);
|
||||
|
||||
if(!radio.message){
|
||||
radio.message = await radio.textChannel.send({ embeds: [embed], components: [buttons] });
|
||||
} else {
|
||||
if(radio.textChannel.id == radio.message.channel.id){
|
||||
radio.message.edit({ embeds: [embed], components: [buttons] });
|
||||
} else {
|
||||
radio.message?.delete();
|
||||
radio.message = await radio.textChannel.send({ embeds: [embed], components: [buttons] });
|
||||
}
|
||||
}
|
||||
|
||||
message.play = client.messages.play.replace("%radio.station.name%", radio.station.name);
|
||||
|
||||
interaction?.reply({
|
||||
content: client.messageEmojis["play"] + message.play,
|
||||
ephemeral: true
|
||||
});
|
||||
|
||||
}
|
@ -1,218 +0,0 @@
|
||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, EmbedBuilder, Guild, OAuth2Guild, StringSelectMenuInteraction } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { station } from "../classes/Stations";
|
||||
|
||||
export default async function play(client: RadioClient, interaction: ChatInputCommandInteraction | StringSelectMenuInteraction | null, guild: OAuth2Guild | Guild | null, station: station) {
|
||||
if(!guild) return;
|
||||
|
||||
const radio = client.radio?.get(guild.id);
|
||||
if(!radio) return;
|
||||
if(radio.textChannel?.type == ChannelType.DM || radio.textChannel?.type == ChannelType.GroupDM) return;
|
||||
const audioPlayer = client.streamer?.listen(station);
|
||||
if(!audioPlayer) return;
|
||||
radio.connection?.subscribe(audioPlayer);
|
||||
client.funcs.logger('Radio', guild.id + " / " + "Play" + " / " + radio.station.name);
|
||||
|
||||
if(radio.station.playlist){
|
||||
if(radio.station.playlist.type == "radioplay" || radio.station.playlist.type == "supla" || radio.station.playlist.type == "yle"){
|
||||
let playlist: any = await fetch(radio.station.playlist.address)
|
||||
.then((response: Response) => response.json())
|
||||
.catch(error => {
|
||||
});
|
||||
|
||||
radio.station.track = "-";
|
||||
|
||||
if(playlist){
|
||||
switch(radio.station.playlist.type){
|
||||
case "radioplay":
|
||||
if(playlist[0] && playlist[0].stationNowPlaying && playlist[0].stationNowPlaying.nowPlayingArtist && playlist[0].stationNowPlaying.nowPlayingTrack){
|
||||
radio.station.track = "__" + playlist[0].stationNowPlaying.nowPlayingArtist + "__" + "\n" + playlist[0].stationNowPlaying.nowPlayingTrack;
|
||||
}
|
||||
break;
|
||||
case "supla":
|
||||
if(playlist.items && playlist.items[0] && playlist.items[0].artist && playlist.items[0].song){
|
||||
radio.station.track = "__" + playlist.items[0].artist + "__" + "\n" + playlist.items[0].song;
|
||||
}
|
||||
break;
|
||||
case "yle":
|
||||
if(playlist.data && playlist.data.performer && playlist.data.title){
|
||||
radio.station.track = "__" + playlist.data.performer + "__" + "\n" + playlist.data.title;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
radio.station.track = "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(client.user?.username || "-")
|
||||
.setThumbnail((radio.station.logo || "https://cdn.discordapp.com/emojis/" + client.messages.emojis["play"].replace(/[^0-9]+/g, '')))
|
||||
.setColor(client.config.embedColor)
|
||||
.addFields({
|
||||
name: client.messages.playTitle1,
|
||||
value: client.messages.replace(client.messages.playDescription1, {
|
||||
"%radio.station.name%": radio.station.name,
|
||||
"%radio.station.owner%": radio.station.name != radio.station.owner ? radio.station.owner + "\n" : ""
|
||||
})
|
||||
},
|
||||
{
|
||||
name: client.messages.playTitle2,
|
||||
value: client.messages.replace(client.messages.playDescription2, {
|
||||
"%radio.station.track%": radio.station.track != undefined ? "\n\n" + radio.station.track : "-"
|
||||
})
|
||||
},
|
||||
{
|
||||
name: client.messages.playTitle3,
|
||||
value: client.messages.replace(client.messages.playDescription3, {
|
||||
"%client.funcs.msToTime(completed)%": "-"
|
||||
})
|
||||
})
|
||||
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
const buttons = new ActionRowBuilder<ButtonBuilder>()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('list')
|
||||
.setEmoji(client.messages.emojis["list"])
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
)
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('prev')
|
||||
.setEmoji(client.messages.emojis["prev"])
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
)
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('stop')
|
||||
.setEmoji(client.messages.emojis["stop"])
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
)
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('next')
|
||||
.setEmoji(client.messages.emojis["next"])
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
)
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('statistics')
|
||||
.setEmoji(client.messages.emojis["statistics"])
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
);
|
||||
|
||||
if(!radio.message){
|
||||
radio.message = await radio.textChannel?.send({ embeds: [embed], components: [buttons] }) ?? null;
|
||||
} else {
|
||||
if(radio.textChannel?.id == radio.message.channel.id){
|
||||
radio.message.edit({ embeds: [embed], components: [buttons] });
|
||||
} else {
|
||||
radio.message?.delete();
|
||||
radio.message = await radio.textChannel?.send({ embeds: [embed], components: [buttons] }) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
const oldRadio = {...radio};
|
||||
|
||||
let timer : NodeJS.Timeout = setInterval(async function(){
|
||||
const radio = client.radio?.get(guild.id);
|
||||
|
||||
if(!radio || !oldRadio || radio.station.name != oldRadio.station.name || radio.textChannel?.type == ChannelType.DM || radio.textChannel?.type == ChannelType.GroupDM) {
|
||||
return clearInterval(timer);
|
||||
}
|
||||
|
||||
if(radio.station.playlist){
|
||||
if(radio.station.playlist.type == "radioplay" || radio.station.playlist.type == "supla" || radio.station.playlist.type == "yle"){
|
||||
let playlist: any = await fetch(radio.station.playlist.address)
|
||||
.then((response: Response) => response.json())
|
||||
.catch(error => {
|
||||
});
|
||||
|
||||
radio.station.track = "-";
|
||||
|
||||
if(playlist){
|
||||
switch(radio.station.playlist?.type){
|
||||
case "radioplay":
|
||||
if(playlist[0] && playlist[0].stationNowPlaying && playlist[0].stationNowPlaying.nowPlayingArtist && playlist[0].stationNowPlaying.nowPlayingTrack){
|
||||
radio.station.track = "__" + playlist[0].stationNowPlaying.nowPlayingArtist + "__" + "\n" + playlist[0].stationNowPlaying.nowPlayingTrack;
|
||||
}
|
||||
break;
|
||||
case "supla":
|
||||
if(playlist.items && playlist.items[0] && playlist.items[0].artist && playlist.items[0].song){
|
||||
radio.station.track = "__" + playlist.items[0].artist + "__" + "\n" + playlist.items[0].song;
|
||||
}
|
||||
break;
|
||||
case "yle":
|
||||
if(playlist.data && playlist.data.performer && playlist.data.title){
|
||||
radio.station.track = "__" + playlist.data.performer + "__" + "\n" + playlist.data.title;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
radio.station.track = "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let date = new Date();
|
||||
radio.currentTime = date.getTime();
|
||||
radio.playTime = radio.currentTime - radio.startTime;
|
||||
const completed = (radio.playTime);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(client.user?.username || "-")
|
||||
.setThumbnail((radio.station.logo || "https://cdn.discordapp.com/emojis/" + client.messages.emojis["play"].replace(/[^0-9]+/g, '')))
|
||||
.setColor(client.config.embedColor)
|
||||
.addFields({
|
||||
name: client.messages.playTitle1,
|
||||
value: client.messages.replace(client.messages.playDescription1, {
|
||||
"%radio.station.name%": radio.station.name,
|
||||
"%radio.station.owner%": radio.station.name != radio.station.owner ? radio.station.owner + "\n" : ""
|
||||
})
|
||||
},
|
||||
{
|
||||
name: client.messages.playTitle2,
|
||||
value: client.messages.replace(client.messages.playDescription2, {
|
||||
"%radio.station.track%": radio.station.track != undefined ? "\n\n" + radio.station.track : "-"
|
||||
})
|
||||
},
|
||||
{
|
||||
name: client.messages.playTitle3,
|
||||
value: client.messages.replace(client.messages.playDescription3, {
|
||||
"%client.funcs.msToTime(completed)%": client.funcs.msToTime(completed)
|
||||
})
|
||||
})
|
||||
.setImage('https://waren.io/berriabot-temp-sa7a36a9xm6837br/images/empty-3.png')
|
||||
.setFooter({
|
||||
text: client.messages.footerText,
|
||||
iconURL: "https://cdn.discordapp.com/emojis/" + client.messages.emojis["eximiabots"].replace(/[^0-9]+/g, '')
|
||||
});
|
||||
|
||||
|
||||
if(!radio.message){
|
||||
radio.message = await radio.textChannel?.send({ embeds: [embed], components: [buttons] }) ?? null;
|
||||
} else {
|
||||
if(radio.textChannel?.id == radio.message.channel.id){
|
||||
radio.message?.edit({ embeds: [embed], components: [buttons] });
|
||||
} else {
|
||||
radio.message?.delete();
|
||||
radio.message = await radio.textChannel?.send({ embeds: [embed], components: [buttons] }) ?? null;
|
||||
}
|
||||
}
|
||||
},30000);
|
||||
|
||||
interaction?.reply({
|
||||
content: client.messages.emojis["play"] + client.messages.replace(client.messages.play, {
|
||||
"%radio.station.name%": radio.station.name
|
||||
}),
|
||||
flags: 'Ephemeral'
|
||||
});
|
||||
|
||||
}
|
18
src/client/funcs/saveState.js
Normal file
18
src/client/funcs/saveState.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = function saveState(client, guild, radio){
|
||||
client.datastore.checkEntry(guild.id);
|
||||
|
||||
let date = new Date();
|
||||
|
||||
let data = client.datastore.getEntry(guild.id);
|
||||
|
||||
data.state = {};
|
||||
data.state.channels = {};
|
||||
data.state.channels.text = radio.textChannel.id;
|
||||
data.state.channels.voice = radio.voiceChannel.id;
|
||||
data.state.date = date.toISOString();
|
||||
data.state.station = {};
|
||||
data.state.station.name = radio.station.name;
|
||||
data.state.station.owner = radio.station.owner;
|
||||
|
||||
client.datastore.updateEntry(guild, data);
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
import { Guild } from "discord.js";
|
||||
import RadioClient from "../../Client";
|
||||
import { radio } from "../classes/Radio";
|
||||
|
||||
export default function saveState(client: RadioClient, guild: Guild | { id: string, name?: string } | undefined, radio: radio){
|
||||
if(!client.datastore || !guild) return;
|
||||
client.datastore.checkEntry(guild.id);
|
||||
|
||||
let date = new Date();
|
||||
|
||||
let data = client.datastore.getEntry(guild.id);
|
||||
if(!data) return;
|
||||
data.state = {
|
||||
channels: {
|
||||
text: radio.textChannel?.id,
|
||||
voice: radio.voiceChannel?.id
|
||||
},
|
||||
date: date.toISOString(),
|
||||
station: {
|
||||
name: radio.station.name,
|
||||
owner: radio.station.owner
|
||||
}
|
||||
};
|
||||
|
||||
client.datastore.updateEntry(guild, data);
|
||||
}
|
@ -1,30 +1,17 @@
|
||||
export const messages = {
|
||||
replace(message: string, variables: { [key: string]: string }){
|
||||
for(let variable in variables){
|
||||
if(variable.includes('%')){
|
||||
message = message.replace(variable, variables[variable]);
|
||||
} else if(variable.includes(':')){
|
||||
message = message.replace(variable.split(':')[0], variables[variable]);
|
||||
} else {
|
||||
message = message.replace(variable, variables[variable]);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
},
|
||||
module.exports = {
|
||||
wrongVoiceChannel: "You need to be in the same voice channel as RadioX to use this command!",
|
||||
noPerms: "You need the %command.permission% permission to use this command!",
|
||||
notPlaying: "There is nothing playing!",
|
||||
runningCommandFailed: "Running this command failed!",
|
||||
noPermsEmbed: "I cannot send embeds (Embed links).",
|
||||
helpTitle: "Help",
|
||||
helpDescription: "Join to our support server" + "\n" + "%client.config.supportGuild%",
|
||||
bugTitle: "Found a bug with %client.user.username%?",
|
||||
bugDescription: "Join the support server" + "\n" + "%client.config.supportGuild%",
|
||||
helpTitle: "%client.user.username% help:",
|
||||
helpDescription: "%commands%",
|
||||
inviteTitle: "Invite %client.user.username% to your Discord server!",
|
||||
listTitle: "Radio Stations",
|
||||
playTitle1: ":radio: Channel",
|
||||
playDescription1: "__%radio.station.name%__" + "\n" + "%radio.station.owner%",
|
||||
playTitle2: ":musical_note: Track",
|
||||
playDescription2: "%radio.station.track%",
|
||||
playTitle3: ":stopwatch: Duration",
|
||||
playDescription3: "%client.funcs.msToTime(completed)%",
|
||||
nowplayingTitle: "Now Playing",
|
||||
nowplayingDescription: "**%radio.station.name%**" + "\n" + "%radio.station.owner%" + "\n" + "%client.funcs.msToTime(completed)%",
|
||||
noVoiceChannel: "You need to be in a voice channel to play radio!",
|
||||
noQuery: "You need to use a number or search for a supported station!",
|
||||
noPermsConnect: "I cannot connect to your voice channel.",
|
||||
@ -48,17 +35,7 @@ export const messages = {
|
||||
statusField4: ":hourglass: Latency",
|
||||
statusField5: ":globe_with_meridians: Hosted by",
|
||||
errorStationURL: "Station can't be URL",
|
||||
maintenance: "Shhhh... We are now sleeping and dreaming about new features to implement. Will be back soon.",
|
||||
emojis: {
|
||||
logo: "<:RadioX:688765708808487072>",
|
||||
eximiabots: "<:EximiaBots:693277919929303132>",
|
||||
list: "<:RadioXList:688541155519889482>",
|
||||
play: "<:RadioXPlay:688541155712827458>",
|
||||
stop: "<:RadioXStop:688541155377414168>",
|
||||
statistics: "<:RadioXStatistics:694954485507686421>",
|
||||
maintenance: "<:RadioXMaintenance:695043843057254493>",
|
||||
error: "<:RadioXError:688541155792781320>",
|
||||
prev: "<:RadioXPrev:882153637370023957>",
|
||||
next: "<:RadioXNext:882153637474893834>"
|
||||
}
|
||||
messageCommandsDeprecatedTitle: "%client.user.username%",
|
||||
messageCommandsDeprecatedDescription: "We recommend you to reauthorize our bot by clicking the invite link down below, because Discord is planning to remove message content from verified bots [Read More](https://support-dev.discord.com/hc/en-us/articles/4404772028055)" + "\n\n" + "**Invite Bot**" + "\n" + "https://wgi.fi/radiox_invite" + "\n\n" + "This bot now supports slash commands, you should start using them instead. Type / into the message box and select the bot you wish to use. Remember to be careful as there are a few bugs here and there on Discord." + "\n\n" + "We will remove this deprecation message in March of 2022 when RadioX 1.0.0 is released.",
|
||||
maintenance: "Shhhh... We are now sleeping and dreaming about new features to implement. Will be back soon."
|
||||
};
|
3
src/client/utils/typings.ts
Normal file
3
src/client/utils/typings.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface command { }
|
||||
|
||||
export interface radio {}
|
@ -1,26 +1,26 @@
|
||||
import { ColorResolvable } from "discord.js";
|
||||
require('dotenv/config');
|
||||
|
||||
export default {
|
||||
module.exports = {
|
||||
|
||||
//credentials
|
||||
token: process.env.DISCORD_TOKEN,
|
||||
|
||||
//radio stations
|
||||
stationslistUrl: process.env.RADIOX_STATIONSLISTURL || "https://eximiabots.waren.io/radiox/stations.json",
|
||||
stationslistUrl: process.env.RADIOX_STATIONSLISTURL || "https://git.cwinfo.net/cwchristerw/radio/raw/branch/master/playlist.json",
|
||||
|
||||
//support
|
||||
supportGuild: "https://discord.gg/rRA65Mn",
|
||||
devIDs: [
|
||||
devId: [
|
||||
"493174343484833802",
|
||||
"360363051792203779"
|
||||
],
|
||||
|
||||
//misc
|
||||
embedColor: "#88aa00" as ColorResolvable,
|
||||
embedColor: "#88aa00",
|
||||
hostedBy: "[Warén Group](https://waren.io)",
|
||||
|
||||
//Settings
|
||||
version: process.env.DEV_MODE ? (process.env.npm_package_version ?? "0.0.0") + "-dev" : process.env.npm_package_version ?? "-",
|
||||
version: process.env.DEV_MODE ? process.env.npm_package_version + "-dev" : process.env.npm_package_version,
|
||||
debug: process.env.DEBUG_MODE || false,
|
||||
devMode: process.env.DEV_MODE || false,
|
||||
maintenanceMode: false,
|
3
src/index.js
Normal file
3
src/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
const { default: RadioClient } = require("./Client");
|
||||
|
||||
const client = new RadioClient();
|
@ -1,3 +0,0 @@
|
||||
import RadioClient from "./Client";
|
||||
|
||||
new RadioClient();
|
Loading…
x
Reference in New Issue
Block a user