├── .github
├── FUNDING.yml
└── workflows
│ └── docker-image.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── LICENSE.md
├── README.md
├── config.js
├── embeds.js
├── lang.json
├── package.json
└── tempVCs.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: larshagrid
2 |
3 |
--------------------------------------------------------------------------------
/.github/workflows/docker-image.yml:
--------------------------------------------------------------------------------
1 | name: Docker Image CI
2 |
3 | on:
4 | push:
5 | branches: [ "master" ]
6 | pull_request:
7 | branches: [ "master" ]
8 |
9 | jobs:
10 |
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout Code
16 | uses: actions/checkout@v2
17 |
18 | - name: Login to GitHub Container Registry
19 | uses: docker/login-action@v1
20 | with:
21 | registry: ghcr.io
22 | username: ${{ github.actor }}
23 | password: ${{ secrets.GITHUB_TOKEN }}
24 |
25 | - name: Build and Push Docker Image
26 | run: |
27 | docker build . --tag ghcr.io/interfacegui/discord-temp-vc-bot:latest
28 | docker push ghcr.io/interfacegui/discord-temp-vc-bot:latest
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 | .config.json
106 | index copy.js
107 | db/
108 | package-lock.json
109 | config.js
110 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 | # 貢獻者公約
3 |
4 | ## 我們的承諾
5 |
6 | 為了促進一個開放透明且受歡迎的環境,我們作為貢獻者和維護者保證,無論年齡、種族、民族、性別認同和表達、體型、殘疾、經驗水平、國籍、個人表現、宗教或性別取向,在我們的專案以及社群的參與者都有不被騷擾的體驗。
7 |
8 | ## 我們的準則
9 |
10 | 舉例來說有助於創造正面環境的行為包括:
11 |
12 | * 使用歡迎和包容性語言
13 | * 尊重不同的觀點和經驗
14 | * 優雅地接受建設性批評
15 | * 關注在對於社群最好的事情上
16 | * 對其他社群成員的表現友善
17 |
18 | 舉例來說身為參與者不能接受的行為包括:
19 |
20 | * 使用與性有關的言語或是圖像,以及不受歡迎的性騷擾
21 | * 酸民/反串/釣魚行為或進行侮辱/貶損的評論,人身攻擊及政治攻擊
22 | * 公開或私下的騷擾
23 | * 未經許可地發布他人的個人資料,例如住址或是電子地址
24 | * 其他可以被合理地認定為不恰當或者違反職業操守的行為
25 |
26 | ## 我們的責任
27 |
28 | 專案維護者有責任為"可接受的行為"準則做出詮釋,以及對已發生的不被接受的行為採取恰當且公平的糾正措施。
29 |
30 | 專案維護者有權力及責任去刪除、編輯、拒絕與本行為準則有所違背的評論 (comments)、提交 (commits)、程式碼、wiki 編輯、問題 (issues) 和其他貢獻,以及專案維護者可暫時或永久性的禁止任何他們認為有不適當、威脅、冒犯、有害行為的貢獻者。
31 |
32 | ## 使用範圍
33 |
34 | 當一個人代表該專案或是其社群時,本行為準則適用於其專案平台和公共平台。
35 |
36 | 代表專案或是社群的情況,舉例來說包括使用官方專案的電子郵件地址、通過官方的社群媒體帳號發布或線上或線下事件中擔任指定代表。
37 |
38 | 該專案的呈現方式可由其專案維護者進行進一步的定義及解釋。
39 |
40 | ## 強制執行
41 |
42 | 可以透過[interfacegui@gmail.com],來聯繫專案團隊來報告濫用、騷擾或其他不被接受的行為。
43 |
44 | 任何維護團隊認為有必要且適合的所有投訴都將進行審查及調查,並做出相對應的回應。專案小組有對事件回報者有保密的義務。具體執行的方針近一步細節可能會單獨公佈。
45 |
46 | 沒有真誠的遵守或是執行本行為準則的專案維護人員,可能會因專案領導人或是其他成員的決定,暫時或是永久的取消其身份。
47 |
48 | ## 來源
49 |
50 | 本行為準則改編自[貢獻者公約][首頁],版本 1.4
51 | 可在此觀看https://www.contributor-covenant.org/zh-tw/version/1/4/code-of-conduct.html
52 |
53 | [首頁]: https://www.contributor-covenant.org
54 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:18
2 |
3 | WORKDIR /app
4 |
5 | COPY package.json /app
6 |
7 | COPY . .
8 |
9 | RUN npm install
10 | CMD [ "node", "tempVCs.js" ]
11 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Larshagrid
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 中文說明
2 |
3 | # Discord 語音包廂 BOT
4 |
5 | ## 概述
6 | 想要在Discord裡面有個不被打擾或是不想給別人加入的語音頻道
7 | 但是又不想麻煩管理員為了你而特別開設頻道?
8 | 或是語音地區出問題? 想更換但管理員都不再線上
9 | 又或者有破壞規矩、故意騷擾的用戶在,想要將他踢出語音頻道?
10 |
11 | 這Bot剛好可以達成您的需求!
12 | 私人語音包廂可以讓一般使用者無需特別權限就能建立語音頻道
13 |
14 | 並且有獨立的控制專區,可以讓開房的房主做到:
15 | 1. 踢人
16 | 2. Ban人(對某人隱藏語音頻道)
17 | 3. 隱藏頻道
18 | 4. 頻道上鎖 (看的到 但無法加入)
19 | 5. 自訂頻道名稱
20 | 6. 更改語音地區
21 | 7. 白名單
22 | 8. 限制人數
23 | 9. 禁音其他人 (對後來加入的有效)
24 |
25 | 對伺服器主人有:
26 | 1. 限定預設身分組
27 | 2. 一鍵清除所有包廂
28 | 3. 房主離開超過3分鐘自動清除語音頻道
29 | 4. Log紀錄
30 |
31 |
32 | #### 完全無需輸入指令!
33 | 除了特定指令(管理員刪除、呼叫控制台)
34 | 其餘都是使用 Discord 互動系統 (按鈕、表單、選單)
35 |
36 | ## 如何安裝及啟動?
37 |
38 | 1. 下載原始碼
39 | 2. 完成 `config.json` 所需要的資料 ( Token、guild、HUBvcChannelID、DefaultRoleID、categoryID、owners )
40 | 2. 安裝必要元件 `npm install`
41 | 3. 啟動bot `node tempVCs.js`
42 |
43 |
44 |
45 | ## 大感謝
46 |
47 | 靈感來自Autocode 平台上的 MeltedButter77 所做的 [Temp VCs](https://autocode.com/MeltedButter77/apps/tempvoice/)
48 |
49 |
50 |
51 |
52 |
53 |
54 | [1]:https://github.com/InterfaceGUI/Discord-TempVoice-Bot/actions/workflows/docker-image.yml/badge.svg?branch=master
55 | [2]:https://img.shields.io/github/license/interfacegui/Discord-TempVoice-Bot
56 | [3]:https://img.shields.io/github/package-json/v/interfacegui/Discord-TempVoice-Bot
57 |
58 | # Discord Temp Voice Channel Bot
59 |
60 |
61 | [![Docker Image CI][1]](https://github.com/InterfaceGUI/Discord-TempVoice-Bot/actions/workflows/docker-image.yml) ![GitHub][2] ![GitHub package.json version][3]
62 |
63 | This BOT allows the average user to create channels, edit names, numbers, visibility,etc.
64 | without the need for administrator intervention!
65 |
66 | And with the independent control interface, users can do:
67 | 1. Kick the user out of the voice channel.
68 | 2. Block the user from the voice channel.
69 | 3. Set a maximum number of users.
70 | 4. Change the region of the voice channel.
71 | 5. Change the name of the voice channel.
72 | 6. Set a whitelist.
73 | 7. Mute new users who join after the mute has been applied.
74 | 8. Hide the channel.
75 | 9. Lock the channel.
76 |
77 | Server owners can:
78 | 1. Set the default role.
79 | 2. Clear all temp vcs
80 | 3. Log
81 |
82 | And all temp vc will automatically clear the voice channel when the user leaves for more than 3 minutes
83 |
84 |
85 | ## Install
86 |
87 | 1. Download Repository
88 | 2. Fill in the necessary information in `config.json`
( Token、guild、HUBvcChannelID、DefaultRoleID、categoryID、owners )
89 | 2. `npm install`
90 | 3. `node tempVCs.js`
91 |
92 |
93 | This program is inspired by [Temp VCs](https://autocode.com/MeltedButter77/apps/tempvoice/) made by MeltedButter77 on the Autocode platform
94 |
95 |
96 | ## Docker compose
97 | ```yml
98 | version: "3.9"
99 | services:
100 | DiscordWelcomeBot:
101 | image: "ghcr.io/interfacegui/discord-temp-vc-bot:latest"
102 | environment:
103 | TOKEN: "YOUR Discord TOKEN"
104 | SERVER_ID: "YOUR Discord ServerID"
105 | CATEGORY_ID: "Your category ID"
106 | HUB_ID: "Your HUB Voice Channel ID"
107 | ROLE_ID: "Defualt role | if you use @everyone pls put server id here"
108 | PREFIX: "["
109 | OWNERS: "123456,123456 "
110 | ```
111 |
112 | ### Environmental Variables
113 |
114 | * `TOKEN`
115 | Discord bot token
116 |
117 | * `SERVER_ID`
118 | Your server ID
119 |
120 | * `CATEGORY_ID`
121 | Category ID
122 |
123 | * `HUB_ID`
124 | Hub voice channel ID
125 |
126 | * `ROLE_ID`
127 | Defualt role
128 | VC Show/Lock will use this role
129 | If you want to use `@everyone` please place the ServerID here
130 |
131 | * `PREFIX`
132 | The command perfix to create Hub text.
133 |
134 | * `OWNERS`
135 | Administrator ID
136 | If there are more than one, please separate them with a comma(`,`)
137 | Note: There must not be any spaces!
138 |
139 | ## Demo
140 |
141 | 
142 |
143 | 
144 |
145 | 
146 |
147 | 
148 |
149 | 
150 |
151 | 
152 |
153 | 
154 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | var config = {
2 | "token": "TOKEN",
3 | "guild": "SERVER ID",
4 | "HUBtxtChannelID": "leaveBlank",
5 | "HUBvcChannelID": "",
6 | "DefaultRoleID": "",
7 | "categoryID": "",
8 | "status": "",
9 | "enable_slash": true,
10 | "prefix": "]",
11 | "owners": [
12 | ""
13 | ]
14 | }
15 | //use env
16 | config.token = process.env.TOKEN || ''
17 | config.guild = process.env.SERVER_ID || ''
18 | config.HUBvcChannelID = process.env.HUB_ID || ''
19 | config.DefaultRoleID = process.env.ROLE_ID || ''
20 | config.categoryID = process.env.CATEGORY_ID || ''
21 | config.prefix = process.env.PREFIX || ']'
22 | config.owners = process.env.OWNERS ? process.env.OWNERS.split(',') : null || ['']
23 |
24 | module.exports = { config }
25 |
--------------------------------------------------------------------------------
/embeds.js:
--------------------------------------------------------------------------------
1 | const { Client, PermissionsBitField, Permissions, TextInputComponent, MessageSelectMenu, UserSelectMenuBuilder, GatewayIntentBits, SlashCommandBuilder, PermissionFlagsBits, ChannelType, RoleSelectMenuBuilder, ButtonBuilder, ActionRowBuilder, EmbedBuilder } = require('discord.js')
2 | const { config } = require('./config')
3 |
4 |
5 | /*---------------------------------------------------------------------------------------------------------
6 | * The BOT was created by lars. I hope those who use this BOT will keep the flooter part.
7 | * The Author can be changed at will. If there are any improvements needed, please open a Pullrequest on Github.
8 | * Thank you.
9 | */
10 | const flooter = {
11 | "text": "TempVCs by Larshagrid | ver 2023.12.18",
12 | "iconURL": "https://cdn.discordapp.com/attachments/920732721981038712/986987686751506512/-1.jpg"
13 | }
14 | //----------------------------------------------------------------------------------------------------------
15 |
16 | const Author = {
17 | name: "拉斯的私人語音包廂助理",
18 | iconURL: "https://cdn.discordapp.com/attachments/920732721981038712/986987686751506512/-1.jpg"
19 | }
20 |
21 | const HubEmbed = new EmbedBuilder()
22 | .setTitle(`臨時私人語音聊天`)
23 | .setColor(0x00B9FF)
24 | .setDescription(
25 | `加入 <#${config.HUBvcChannelID}> 來創建您自己的Temp VC,您將成為房主。 預設為顯示,除非手動點選按鈕開啟權限!`,
26 | )
27 | .setFooter(flooter)
28 |
29 | const AdminForceDelete = new EmbedBuilder()
30 | .setTitle(`管理員以使用修復指令!`)
31 | .setDescription(`頻道將於 $timeLeft 刪除。`)
32 | .setColor(0xe80000)
33 | .setFooter(flooter)
34 |
35 | const consoleembed = new EmbedBuilder()
36 | .setTitle("臨時私人語音聊天控制")
37 | .setDescription("**使用下面的按鈕可以快速更改一些設置!**. \n以下是按鈕的說明:\n\n使用 `Lock/Unlock` 鎖定 VC,以便只有列入白名單的用戶才能連接.\n使用 `Hide/Unhide` 將 VC 設為私有,以便只有列入白名單的用戶才能看到 VC。\n使用 `Mute/Unmute` 只允許白名單用戶發言,只影響新加入的用戶.\n使用 `Ban/Unban` 禁止指定用戶加入VC. \n使用 `Whitelist/Remove` 白名單.\n使用 `Limit` 限制可以加入的用戶數量.\n使用 `Change Owner` 轉讓包廂房主.\n使用 `Change Name` 將 VC 的名稱更改為您想要的任何名稱。 (記得遵守規則)\n使用 `Get Mention` 獲取語音邀請網址,立即邀請他們加入到語音頻道!!\n\n 轉讓房主會將原先的限制權限清除!!。")
38 | .setColor(0x00B9FF)
39 | .setFooter(flooter)
40 |
41 | const Whitelisttembed = new EmbedBuilder()
42 | .setTitle('白名單清單')
43 | .setColor(0xaf40de)
44 | .setAuthor(Author)
45 |
46 | const banliste = new EmbedBuilder()
47 | .setTitle('封鎖清單')
48 | .setColor(0xaf40de)
49 | .setAuthor(Author)
50 |
51 | const predelete_Cancel = new EmbedBuilder()
52 | .setTitle("刪除已取消!")
53 | .setDescription(`排定的刪除程序已取消!`)
54 | .setColor(0x02cf21)
55 | .setAuthor(Author)
56 |
57 | const predelete = new EmbedBuilder()
58 | .setTitle("已排定刪除!")
59 | .setDescription(`房主決定將 $tempVcId 刪除。 \n頻道將於 $timeLeft 刪除。 \n如要取消,請再次按下DeleteVC按鈕。`)
60 | .setColor(0xff0000)
61 | .setAuthor(Author)
62 |
63 | const ownerLeave = new EmbedBuilder()
64 | .setTitle("房主已離開語音包廂")
65 | .setDescription(`房主已離開 $tempVcId。 \n頻道將於 $timeLeft 刪除。 \n如要取消,請重新加入語音頻道。`)
66 | .setColor(0x00B9FF)
67 | .setFooter(flooter)
68 | .setAuthor(Author)
69 | const ownerLeavegivenext = new EmbedBuilder()
70 | .setTitle("房主已離開語音包廂")
71 | .setDescription(`房主已離開 $tempVcId。 \n房主將於 $timeLeft 賦予下一位用戶,若無人將自動刪除。 \n如要取消,請重新加入語音頻道。`)
72 | .setColor(0x00B9FF)
73 | .setFooter(flooter)
74 | .setAuthor(Author)
75 |
76 | const NewOwner = new EmbedBuilder()
77 | .setTitle("新房主!")
78 | .setDescription(`前個房主已離開 3 分鐘。 \n此頻道以自動延順房主給: $user 。 \n\n如找不到控制台,請使用 $command `)
79 | .setColor(0x00B9FF)
80 | .setFooter(flooter)
81 | .setAuthor(Author)
82 |
83 |
84 | module.exports = {
85 | embedFlooter: flooter,
86 | template_createHubEmbed: HubEmbed,
87 | template_adminDeleteEmbed: AdminForceDelete,
88 | template_controlsEmbed: consoleembed,
89 | template_whitelist_Embed: Whitelisttembed,
90 | template_banlist_Embed: banliste,
91 | template_CancelPreDelete_Embed: predelete_Cancel,
92 | template_predelete_Embed: predelete,
93 | template_ownerLeave_Embed: ownerLeave,
94 | template_ownerLeaveNext_Embed: ownerLeavegivenext,
95 | template_ownerNew_Embed: NewOwner,
96 | }
--------------------------------------------------------------------------------
/lang.json:
--------------------------------------------------------------------------------
1 | {
2 | "commands": {
3 | "vc-fix": {
4 | "en-US": "Fix tempvc database(Will delete all tempvc)",
5 | "zh-TW": "修復語音包廂"
6 | },
7 | "vc-hubmsg":{
8 | "en-US": "Send HUB message to current channel.",
9 | "zh-TW": "傳送HUB訊息"
10 | },
11 | "vc-control":{
12 | "en-US": "Show tempVC control UI.",
13 | "zh-TW": "顯示包廂控制介面"
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "discord_temp_vcs",
3 | "version": "1.1.1",
4 | "description": "Discord Temp voice bot, that makes voice channel easy to create, manage, and change regions.",
5 | "main": "tempVCs.js",
6 | "scripts": {
7 | "test": "node tempVCs.js",
8 | "start": "node tempVCs.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/InterfaceGUI/DiscordTempVCs.git"
13 | },
14 | "keywords": [
15 | "Discord_Bot",
16 | "Discord-js-v14",
17 | "Discord_Buttons",
18 | "TempVCs",
19 | "24/7_Online",
20 | "Button_interactions"
21 | ],
22 | "author": "Larshagrid",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/InterfaceGUI/DiscordTempVCs/issues"
26 | },
27 | "homepage": "https://github.com/InterfaceGUI/DiscordTempVCs",
28 | "dependencies": {
29 | "@discordjs/rest": "^2.1.0",
30 | "discord-api-types": "^0.37.61",
31 | "discord.js": "^14.11.1",
32 | "level": "^8.0.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tempVCs.js:
--------------------------------------------------------------------------------
1 | //Discord Client
2 | const { Client, PermissionsBitField, Permissions, TextInputBuilder, TextInputStyle, UserSelectMenuBuilder, GatewayIntentBits, SlashCommandBuilder, PermissionFlagsBits, ChannelType, RoleSelectMenuBuilder, ButtonBuilder, ActionRowBuilder, EmbedBuilder, ModalBuilder, StringSelectMenuBuilder } = require('discord.js')
3 |
4 | const client = new Client({
5 | intents: [
6 | GatewayIntentBits.GuildMembers,
7 | GatewayIntentBits.GuildEmojisAndStickers,
8 | GatewayIntentBits.Guilds,
9 | GatewayIntentBits.GuildVoiceStates]
10 | })
11 | const lang = require('./lang.json')
12 | const { REST, RequestData } = require('@discordjs/rest');
13 | const { Routes, ButtonStyle } = require('discord-api-types/v10');
14 |
15 | const { config } = require('./config')
16 | const {
17 | template_banlist_Embed,
18 | template_createHubEmbed,
19 | template_adminDeleteEmbed,
20 | template_controlsEmbed,
21 | template_whitelist_Embed,
22 | template_ownerNew_Embed,
23 | template_CancelPreDelete_Embed,
24 | template_predelete_Embed,
25 | template_ownerLeave_Embed,
26 | template_ownerLeaveNext_Embed
27 | } = require('./embeds.js')
28 |
29 | console.log(`Config Loaded prefix: ${config.prefix}`)
30 | var owners = config.owners
31 |
32 | //import LevelUP levelDown
33 | const { Level } = require('level')
34 | const db = new Level('./db', { valueEncoding: 'json' })
35 |
36 | var guild;
37 | var RTCRegions;
38 | var VC_control_command
39 |
40 | //房主離開用
41 | var removeTimeouts = {};
42 | var removeTimeoutsMsg = {};
43 |
44 | var removeTimeouts2 = {};
45 | var removeTimeoutsMsg2 = {};
46 |
47 |
48 | //Ready Event
49 | client.on('ready', async () => {
50 | console.log(`${client.user.tag} is Ready!`)
51 |
52 | /* custom status
53 | client.user.setPresence({
54 | status: "online",
55 | activities: [{
56 | name: config.status,
57 | type: "LISTENING",
58 | }]
59 | })
60 | */
61 |
62 | RTCRegions = await client.fetchVoiceRegions()
63 | guild = await client.guilds.cache.get(config.guild);
64 | await guild.members.fetch()
65 | await guild.roles.fetch()
66 |
67 | //Registering Slash
68 | if (config.enable_slash) {
69 | const rest = new REST({ version: '10' }).setToken(config.token);
70 |
71 | //const rest = new REST({ version: '9' }).setToken(config.token)
72 | const cmd1 = new SlashCommandBuilder()
73 | .setName('vc-fix')
74 | .setDescription('修復語音包廂')
75 | .setDescriptionLocalizations(lang.commands['vc-fix'])
76 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
77 | const cmd2 = new SlashCommandBuilder()
78 | .setName('vc-hubmsg')
79 | .setDescription('建立HUB訊息')
80 | .setDescriptionLocalizations(lang.commands['vc-hubmsg'])
81 | .setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
82 | const cmd3 = new SlashCommandBuilder()
83 | .setName('vc-control')
84 | .setDescription('呼叫主控台')
85 | .setDescriptionLocalizations(lang.commands['vc-control'])
86 | const commands = [cmd1.toJSON(), cmd2.toJSON(), cmd3.toJSON()]
87 |
88 | try {
89 | console.log('[INFO] Started refreshing application (/) commands.')
90 |
91 | await rest.put(
92 | Routes.applicationCommands(client.user.id),
93 | { body: commands },
94 | );
95 | const commands11 = await rest.get(
96 | Routes.applicationCommands(client.user.id)
97 | );
98 |
99 | VC_control_command = commands11.find(cmd => cmd.name === cmd3.name);
100 | console.log('[INFO] Successfully reloaded application (/) commands.')
101 | }
102 | catch (error) {
103 | console.error(error)
104 | }
105 |
106 | }
107 | cleanHUB_Timeban()
108 |
109 | })
110 |
111 | client.on('voiceStateUpdate', async (oldMember, newMember) => {
112 |
113 | let newUserChannel = newMember.channelId;
114 | let userid = newMember.id;
115 |
116 | /*
117 | console.log(newMember)
118 | console.log('User:',userid)
119 | console.log('channel:',newUserChannel);
120 | console.log('=======')
121 | */
122 |
123 | let tempVcId = await db.get(`tempVcIdKey_${userid}`).catch(err => { }) //= await kvs1.get('tempVcIdKey_'+userid);
124 | let tempTimestamp = await db.get(`tempTimestampKey_${userid}`).catch(err => { }) //= await kvs1.get('tempTimestampKey_'+userid);
125 |
126 | if (newUserChannel == config.HUBvcChannelID) {
127 | if (typeof tempVcId === 'undefined') {
128 | setTimeout(MoveUser, 1000, userid, newMember)
129 | return
130 | }
131 | }
132 |
133 | if (typeof tempVcId === 'undefined') return
134 | /* /// 房主離開 自動刪除頻道
135 | if (!newUserChannel || newUserChannel != tempVcId) {
136 | if (typeof tempVcId !== 'undefined') {
137 | if (removeTimeouts2[userid]) return
138 | if (removeTimeouts[userid]) return
139 |
140 | //ownerLeave_Embed.setDescription(`房主已離開 <#${tempVcId}>。 \n頻道將於 刪除。 \n如要取消,請重新加入語音頻道。`)
141 | ownerLeave_Embed.data.description = ownerLeave_Embed.data.description
142 | .replace('$tempVcId',`<#${tempVcId}>`)
143 | ownerLeave_Embed.data.description = ownerLeave_Embed.data.description
144 | .replace('$timeLeft',``)
145 |
146 | let Vc = await guild.channels.cache.find(channel => channel.id == tempVcId)
147 | if (!Vc) { // 找查頻道是否存在,不存在自動清除資料庫記憶
148 | await db.del(`tempVcIdKey_${userid}`).catch(err => { console.log('kv del error!') })
149 | await db.del(`tempmsgIdKey_${userid}`).catch(err => { console.log('kv del error!') })
150 | await db.del(`tempTimestampKey_${userid}`).catch(err => { console.log('kv del error!') })
151 | return
152 | }
153 | //傳送訊息,並且設定Timeout排程刪除
154 | let msg = await Vc.send({ content: `<@${newMember.id}>`, embeds: [ownerLeave_Embed] })
155 | let tout = setTimeout(RemoveVTChannels, 180000, userid, tempVcId)
156 | removeTimeouts[userid] = tout
157 | removeTimeoutsMsg[userid] = msg.id
158 | }
159 | }*/
160 |
161 | /// 改為 房主離開 自動給下一個使用者房主
162 | if (!newUserChannel || newUserChannel != tempVcId) {
163 | if (typeof tempVcId !== 'undefined') {
164 | if (removeTimeouts2[userid]) return
165 | if (removeTimeouts[userid]) return
166 |
167 |
168 | let Vc = await guild.channels.cache.find(channel => channel.id == tempVcId)
169 | if (!Vc) { // 找查頻道是否存在,不存在自動清除資料庫記憶
170 | await db.del(`tempVcIdKey_${userid}`).catch(err => { console.log('kv del error!') })
171 | await db.del(`tempmsgIdKey_${userid}`).catch(err => { console.log('kv del error!') })
172 | await db.del(`tempTimestampKey_${userid}`).catch(err => { console.log('kv del error!') })
173 | return
174 | }
175 |
176 | //ownerLeave_Embed.setDescription(`房主已離開 <#${tempVcId}>。 \n頻道將於 刪除。 \n如要取消,請重新加入語音頻道。`)
177 | let ownerLeaveNext_Embed = template_ownerLeaveNext_Embed
178 | ownerLeaveNext_Embed.data.description = template_ownerLeaveNext_Embed.data.description
179 | .replace('$tempVcId', `<#${tempVcId}>`)
180 | ownerLeaveNext_Embed.data.description = template_ownerLeaveNext_Embed.data.description
181 | .replace('$timeLeft', ``)
182 |
183 | //傳送訊息,並且設定Timeout排程
184 | let msg = await Vc.send({ content: `<@${newMember.id}>`, embeds: [ownerLeaveNext_Embed] })
185 | //let tout = setTimeout(RemoveVTChannels2, 180000, userid, tempVcId)
186 | let tout = setTimeout(RemoveVTChannels2, 180000, userid, tempVcId)
187 | removeTimeouts[userid] = tout
188 | removeTimeoutsMsg[userid] = msg.id
189 | }
190 | }
191 | if (newUserChannel == tempVcId) {
192 | if (removeTimeouts[userid]) {
193 | clearTimeout(removeTimeouts[userid])
194 | delete removeTimeouts[userid]
195 | let Vc = await guild.channels.cache.find(channel => channel.id == tempVcId)
196 | let Controlmsg = await Vc.messages.fetch(`${removeTimeoutsMsg[userid]}`)
197 | await Controlmsg.delete().catch(() => { })
198 | }
199 | }
200 |
201 | })
202 |
203 |
204 | async function RemoveMsg(msg) {
205 |
206 | await msg.delete().catch(() => { })
207 |
208 | }
209 |
210 | async function MoveUser(userid, Member) {
211 | //下次更新需添加 冷卻時間 預防進進出出
212 | let HUBVC = await guild.channels.cache.find(channel => channel.id == config.HUBvcChannelID)
213 | await HUBVC.permissionOverwrites.create(userid, { Connect: false })
214 |
215 | //await db.put(`tempHUBBan_${userid}`, Date.now() / 1000).catch()
216 |
217 | let newVCid = await CreatVTChannels(userid);
218 | let VC = await guild.channels.cache.find(channel => channel.id == newVCid)
219 |
220 | Member.setChannel(newVCid).then(async () => {
221 |
222 | await HUBVC.permissionOverwrites.delete(userid)
223 |
224 | }).catch(async err => {
225 | await Member.member.send(`系統無法將您移動到新的 語音頻道! 已經將保留的VC刪除! 60秒後才可再次創建新的語音頻道。\n\nThe system cannot move you to a new voice channel! The reserved VC has been deleted! A new voice channel can be created again after 60 seconds.`).catch(() => { console.log })
226 | var UBBans = await db.get(`tempHUBBans`).catch(async () => {
227 | await db.put('tempHUBBans', {})
228 | return await db.get(`tempHUBBans`)
229 | })
230 | if (Object.keys(UBBans).length === 0) setTimeout(cleanHUB_Timeban, 1000 * 5)
231 | UBBans[`${userid}`] = Date.now()
232 | await db.put('tempHUBBans', UBBans)
233 |
234 | if (typeof VC !== 'undefined') VC.delete().catch(() => { console.log('VC delete error') })
235 | await db.del(`tempVcIdKey_${userid}`).catch(err => { console.log('kv del error!') })
236 | await db.del(`tempmsgIdKey_${userid}`).catch(err => { console.log('kv del error!') })
237 | await db.del(`tempTimestampKey_${userid}`).catch(err => { console.log('kv del error!') })
238 | return
239 | })
240 | }
241 |
242 | async function cleanHUB_Timeban() {
243 | var UBBans = await db.get(`tempHUBBans`).catch(async () => {
244 | await db.put('tempHUBBans', {})
245 | return await db.get(`tempHUBBans`)
246 | })
247 | Object.keys(UBBans).forEach(async key => {
248 | //console.log((Date.now() - UBBans[key]) / 1000)
249 | if (Date.now() - UBBans[key] > 60 * 1000) {
250 | delete UBBans[key];
251 | let HUBVC = await guild.channels.cache.find(channel => channel.id == config.HUBvcChannelID)
252 | await HUBVC.permissionOverwrites.delete(key)
253 | }
254 | });
255 | await db.put('tempHUBBans', UBBans)
256 | if (Object.keys(UBBans).length === 0) return
257 | setTimeout(cleanHUB_Timeban, 1000 * 5)
258 | }
259 |
260 | function sleep(ms) {
261 | return new Promise((resolve) => {
262 | setTimeout(resolve, ms);
263 | });
264 | }
265 |
266 | async function RemoveVTChannels2(UserID, VCID) {
267 |
268 | delete removeTimeouts2[UserID]
269 | delete removeTimeouts[UserID]
270 | //await sleep(5000);
271 | let VC = await guild.channels.fetch(VCID)
272 |
273 | if (VC.members.size == 0) {
274 | if (typeof VC !== 'undefined') VC.delete().catch(() => { console.log('VC delete error') })
275 | await db.del(`tempVcIdKey_${UserID}`).catch(err => { console.log('kv del error!') })
276 | await db.del(`tempmsgIdKey_${UserID}`).catch(err => { console.log('kv del error!') })
277 | await db.del(`tempTimestampKey_${UserID}`).catch(err => { console.log('kv del error!') })
278 |
279 | } else {
280 |
281 | let newUser = VC.members.find(member => !member.user.bot)
282 | let Controlmsg = await VC.messages.fetch(`${removeTimeoutsMsg[UserID]}`)
283 | let VCPermissions = await VC.permissionsFor(config.DefaultRoleID).serialize()
284 | Controlmsg.edit(await CreateControlMsg(newUser.id, newUser.user.username, VCPermissions))
285 |
286 | await db.del(`tempVcIdKey_${UserID}`).catch(err => { console.log('kv del error!') })
287 | await db.del(`tempmsgIdKey_${UserID}`).catch(err => { console.log('kv del error!') })
288 | await db.del(`tempTimestampKey_${UserID}`).catch(err => { console.log('kv del error!') })
289 |
290 | await db.put(`tempVcIdKey_${newUser.id}`, VCID).catch(err => { console.log('kv save error!') })
291 | await db.put(`tempmsgIdKey_${newUser.id}`, Controlmsg.id).catch(err => { console.log('kv save error!') })
292 | await db.put(`tempTimestampKey_${newUser.id}`, Date.now() / 1000).catch(err => { console.log('kv save error!') })
293 |
294 | let ownerNew_Embed = template_ownerNew_Embed
295 | ownerNew_Embed.data.description = template_ownerNew_Embed.data.description
296 | .replace('$user', `<@${newUser.id}>`)
297 | ownerNew_Embed.data.description = template_ownerNew_Embed.data.description
298 | .replace('$command', `${VC_control_command.name}:${VC_control_command.id}>`)
299 |
300 | await VC.send({ content: `<@${newUser.id}>`, embeds: [ownerNew_Embed] })
301 | //await interaction.update({ content: `OK. GoodBye ${user}.`, components: [] });
302 |
303 | /// 如果想要自動更改名子 請取下下方註解
304 | //await VC.setName(`${newUser.nickname ? newUser.nickname : newUser.user.username}'s VC`).catch(err => { console.log('kv save error!') })
305 | return
306 | }
307 |
308 | }
309 |
310 | async function RemoveVTChannels(UserID, VCID) {
311 |
312 | delete removeTimeouts2[UserID]
313 | delete removeTimeouts[UserID]
314 | let VC = guild.channels.cache.find(channel => channel.id == VCID)
315 | if (typeof VC !== 'undefined') VC.delete().catch(() => { console.log('VC delete error') })
316 | await db.del(`tempVcIdKey_${UserID}`).catch(err => { console.log('kv del error!') })
317 | await db.del(`tempmsgIdKey_${UserID}`).catch(err => { console.log('kv del error!') })
318 | await db.del(`tempTimestampKey_${UserID}`).catch(err => { console.log('kv del error!') })
319 |
320 | }
321 |
322 | async function CreatVTChannels(UserID) {
323 |
324 | let User = client.users.cache.find(user => user.id == UserID)
325 | const guild = await client.guilds.cache.get(config.guild)
326 |
327 | const newTempVoiceChannel = await guild.channels.create({
328 | name: `${User.username}'s VC`,
329 | type: ChannelType.GuildVoice,
330 | parent: config.categoryID,
331 | permissionOverwrites: [
332 | {
333 | id: config.DefaultRoleID,
334 | allow: [PermissionsBitField.Flags.ViewChannel],
335 | },
336 | {
337 | id: UserID,
338 | allow: [PermissionsBitField.Flags.Connect],
339 | },
340 | {
341 | id: UserID,
342 | allow: [PermissionsBitField.Flags.Speak],
343 | },
344 | ],
345 | });
346 |
347 | await newTempVoiceChannel.lockPermissions().catch(console.error);
348 | await newTempVoiceChannel.permissionOverwrites.edit(UserID, { Speak: true, Connect: true, ViewChannel: true })
349 | const cmsg = await CreateControlMsg(UserID, User.username)
350 | const msg = await newTempVoiceChannel.send(cmsg)
351 |
352 | //console.log("newVC",newTempVoiceChannel)
353 | await db.put(`tempVcIdKey_${UserID}`, newTempVoiceChannel.id).catch(err => { console.log('kv save error!') })
354 | await db.put(`tempmsgIdKey_${UserID}`, msg.id).catch(err => { console.log('kv save error!') })
355 | await db.put(`tempTimestampKey_${UserID}`, Date.now() / 1000).catch(err => { console.log('kv save error!') })
356 | return newTempVoiceChannel.id
357 | //await kvs1.set('tempVcIdKey_'+UserID,newTempVoiceChannel.id);
358 | //await kvs1.set('tempTcIdKey_'+UserID,newTempTxTChannel.id);
359 | //await kvs1.set('tempTimestampKey_'+UserID,Date.now());
360 |
361 | }
362 |
363 | client.on("interactionCreate", async (interaction) => {
364 | if (!interaction.isRoleSelectMenu() && !interaction.isRoleSelectMenu && !interaction.isModalSubmit() && !interaction.isButton() && !interaction.isSelectMenu() && !interaction.isCommand()) return
365 | const guild = client.guilds.cache.get(config.guild);
366 | let textchannel = interaction.channel
367 | let user = interaction.user
368 | let tempVcId = await db.get(`tempVcIdKey_${user.id}`).catch(err => { }) //= await kvs1.get('tempVcIdKey_'+userid);
369 | let tempmsgid = await db.get(`tempmsgIdKey_${user.id}`).catch(err => { }) //= await kvs1.get('tempVcIdKey_'+userid);
370 | let tempTimestamp = await db.get(`tempTimestampKey_${user.id}`).catch(err => { }) //= await kvs1.get('tempVcIdKey_'+userid);
371 | let VC = await guild.channels.fetch(tempVcId)
372 |
373 | if (interaction.isRoleSelectMenu() || interaction.isUserSelectMenu()) {
374 | if (interaction.customId == "menu_whitelist_member" || interaction.customId == "menu_whitelist_role") {
375 | //whitelist_Embed.setDescription(".....")
376 | let whitelist_Embed = template_whitelist_Embed
377 | await interaction.update({ ephemeral: true, components: [], content: "", embeds: [whitelist_Embed] })
378 |
379 |
380 | if (interaction.customId == "menu_whitelist_role") {
381 | let wrole = guild.roles.cache.find(role => role.id == interaction.values[0])
382 | //console.log(await VC.permissionsFor(wrole.id))
383 | let VCPermissions = await VC.permissionsFor(wrole.id).serialize()
384 | //console.log(typeof(VCPermissions.VIEW_CHANNEL))
385 | //console.log(VCPermissions)
386 | if (!VCPermissions.ViewChannel) {
387 | await VC.permissionOverwrites.create(wrole.id, { ViewChannel: true, Connect: true })
388 | } else {
389 | await VC.permissionOverwrites.delete(wrole.id)
390 | }
391 |
392 | } else {
393 |
394 | let wUserid = client.users.cache.find(user => user.id == interaction.values[0])
395 | let VCPermissions = await VC.permissionsFor(wUserid.id).serialize()
396 | //console.log(typeof(VCPermissions.VIEW_CHANNEL))
397 | //console.log(VCPermissions)
398 | if (!VCPermissions.ViewChannel) {
399 | await VC.permissionOverwrites.create(wUserid.id, { ViewChannel: true, Connect: true })
400 | } else {
401 | await VC.permissionOverwrites.delete(wUserid.id)
402 | }
403 | }
404 | var str1_role = "", str1_member = ""
405 | VC.permissionOverwrites.cache.forEach((value) => {
406 | let VCperm = value.allow.serialize()
407 | if (!VCperm.ViewChannel) return
408 | if (value.type == 0) {
409 | str1_role += `<@&${value.id}>\n`
410 | } else if (value.type == 1) {
411 | str1_member += `<@${value.id}>\n`
412 | }
413 | })
414 | whitelist_Embed.setFields([
415 | { name: 'Roles', value: `${str1_role != "" ? str1_role : 'none'}`, inline: true },
416 | { name: 'Members', value: `${str1_member != "" ? str1_member : 'none'}`, inline: true }
417 | ])
418 | const userselectmenu = new UserSelectMenuBuilder()
419 | .setMaxValues(1)
420 | .setCustomId('menu_whitelist_member')
421 | .setPlaceholder('Pick one or more users')
422 |
423 |
424 | const roleselectmenu = new RoleSelectMenuBuilder()
425 | .setMaxValues(1)
426 | .setCustomId('menu_whitelist_role')
427 | .setPlaceholder('Pick one or more roles')
428 |
429 | const row1 = new ActionRowBuilder()
430 | .addComponents(userselectmenu)
431 | const row2 = new ActionRowBuilder()
432 | .addComponents(roleselectmenu)
433 | return await interaction.editReply({ ephemeral: true, content: "", components: [row1, row2], embeds: [whitelist_Embed] });
434 |
435 |
436 | } else if (interaction.customId == "menu_banlist_member" || interaction.customId == "menu_banlist_role") {
437 | await interaction.update({ ephemeral: true, components: [], content: "" })
438 |
439 |
440 | if (interaction.customId == "menu_banlist_role") {
441 | let banrole = guild.roles.cache.find(role => role.id == interaction.values[0])
442 | let VCPermissions = VC.permissionOverwrites.cache.find(value => value.id == banrole.id)
443 |
444 | if (typeof VCPermissions === 'undefined' || !VCPermissions.deny.serialize().ViewChannel) {
445 | await VC.permissionOverwrites.create(banrole.id, { ViewChannel: false })
446 | } else {
447 | await VC.permissionOverwrites.delete(banrole.id)
448 | }
449 | } else {
450 | let banUserid = guild.members.cache.find(user => user.id == interaction.values[0])
451 | if (banUserid.voice.channelId == VC.id) await banUserid.voice.disconnect().catch()
452 |
453 | let VCPermissions = VC.permissionOverwrites.cache.find(value => value.id == banUserid.id)
454 | if (typeof VCPermissions === 'undefined' || !VCPermissions.deny.serialize().ViewChannel) {
455 | await VC.permissionOverwrites.create(banUserid.id, { ViewChannel: false })
456 | } else {
457 | await VC.permissionOverwrites.delete(banUserid.id)
458 | }
459 |
460 | }
461 | var str1_role = "", str1_member = ""
462 | VC.permissionOverwrites.cache.forEach((value) => {
463 | let VCperm = value.allow.serialize()
464 | if (VCperm.ViewChannel || typeof value === 'undefined') return
465 | if (value.type == 0) {
466 | str1_role += `<@&${value.id}>\n`
467 | } else if (value.type == 1) {
468 | str1_member += `<@${value.id}>\n`
469 | }
470 | })
471 | let banlist_Embed = template_banlist_Embed
472 | banlist_Embed.setFields([
473 | { name: 'Roles', value: `${str1_role != "" ? str1_role : 'none'}`, inline: true },
474 | { name: 'Members', value: `${str1_member != "" ? str1_member : 'none'}`, inline: true }
475 | ])
476 |
477 | const userselectmenu = new UserSelectMenuBuilder()
478 | .setMaxValues(1)
479 | .setCustomId('menu_banlist_member')
480 | .setPlaceholder('Pick one or more users')
481 |
482 | const roleselectmenu = new RoleSelectMenuBuilder()
483 | .setMaxValues(1)
484 | .setCustomId('menu_banlist_role')
485 | .setPlaceholder('Pick one or more roles')
486 |
487 | const row1 = new ActionRowBuilder()
488 | .addComponents(userselectmenu)
489 | const row2 = new ActionRowBuilder()
490 | .addComponents(roleselectmenu)
491 | return await interaction.editReply({ ephemeral: true, components: [row1, row2], embeds: [banlist_Embed] });
492 |
493 | }
494 | } else if (interaction.isStringSelectMenu()) {
495 |
496 | if (interaction.customId === 'select_kick') {
497 |
498 | let kickUser = guild.members.cache.find(user => user.id == interaction.values[0])
499 |
500 | await kickUser.voice.disconnect()
501 | return await interaction.update({ content: `OK. GoodBye ${kickUser}.`, components: [] });
502 |
503 | }
504 | else if (interaction.customId === 'select_changeowner') {
505 |
506 | let newUser = guild.members.cache.find(user => user.id == interaction.values[0])
507 | //console.log(`${newUser.nickname?newUser.nickname:newUser.user.username}'s VC`)
508 |
509 | //await VC.permissionOverwrites.edit(user.id, { VIEW_CHANNEL: null })
510 |
511 | let Controlmsg = await VC.messages.fetch(`${tempmsgid}`)
512 | let VCPermissions = await VC.permissionsFor(config.DefaultRoleID).serialize()
513 | Controlmsg.edit(await CreateControlMsg(newUser.id, newUser.user.username, VCPermissions))
514 | if (removeTimeouts[user.id]) {
515 | clearTimeout(removeTimeouts[user.id])
516 | delete removeTimeouts[userid]
517 | let Controlmsg = await VC.messages.fetch(`${removeTimeoutsMsg[userid]}`)
518 | await Controlmsg.delete().catch(() => { })
519 | }
520 |
521 | if (removeTimeouts2[user.id]) return await interaction.update({ content: `No. Vc is delete now.`, components: [] });
522 |
523 | await db.del(`tempVcIdKey_${user.id}`).catch(err => { console.log('kv del error!') })
524 | await db.del(`tempmsgIdKey_${user.id}`).catch(err => { console.log('kv del error!') })
525 | await db.del(`tempTimestampKey_${user.id}`).catch(err => { console.log('kv del error!') })
526 |
527 | await db.put(`tempVcIdKey_${newUser.id}`, tempVcId).catch(err => { console.log('kv save error!') })
528 | await db.put(`tempmsgIdKey_${newUser.id}`, tempmsgid).catch(err => { console.log('kv save error!') })
529 | await db.put(`tempTimestampKey_${newUser.id}`, tempTimestamp).catch(err => { console.log('kv save error!') })
530 |
531 | await interaction.update({ content: `OK. GoodBye ${user}.`, components: [] });
532 |
533 | await VC.setName(`${newUser.nickname ? newUser.nickname : newUser.user.username}'s VC`).catch(err => { console.log('kv save error!') })
534 | return
535 |
536 | }
537 | else if (interaction.customId === 'select_Region') {
538 |
539 | await VC.setRTCRegion(interaction.values[0] == "null" ? null : interaction.values[0])
540 |
541 | return await interaction.update({ content: `OK. VC RTCRegion now is ${VC.rtcRegion ? VC.rtcRegion : "Auto"}.`, components: [] });
542 |
543 | }
544 | } else if (interaction.isModalSubmit()) {
545 |
546 | if (interaction.customId === 'modal_limit') {
547 |
548 | let inputnum = interaction.fields.getTextInputValue('_value')
549 | if (isNaN(Number(inputnum)) || Number(inputnum) > 99 || Number(inputnum) < 0) return interaction.reply({ content: "Please enter the correct value by the owner before pressing the button.\n請輸入正確的數量後再按下按鈕。", ephemeral: true }).catch(err => { VC.send("Unknow Error. Pls try again.") })
550 |
551 | await VC.setUserLimit(Number(inputnum))
552 | return interaction.reply({ content: `OK. The UserLimit is set to ${Number(inputnum)}.\n好的,最大語音人數已設定為${Number(inputnum)}.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
553 |
554 | } else if (interaction.customId === 'modal_changename') {
555 | await interaction.deferReply({ ephemeral: true })
556 | let inputstr = interaction.fields.getTextInputValue('_value')
557 |
558 | await VC.setName(inputstr)
559 | return interaction.editReply({ content: `OK. VC name is change to ${inputstr}.\n好的,已將名子更改為${inputstr}.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
560 |
561 | }
562 |
563 | } else if (interaction.isButton()) {
564 | if (tempVcId != textchannel.id) {
565 | return await interaction.reply({ content: "Uhhhhh... This is not your VC.", ephemeral: true }).catch(err => { console.log(err) })
566 | }
567 |
568 | let VCPermissions = await VC.permissionsFor(config.DefaultRoleID).serialize()
569 | //console.log(VCPermissions)
570 |
571 | if (interaction.customId === "button_lock") {
572 |
573 | await interaction.deferReply({ ephemeral: true })
574 | let Controlmsg = await VC.messages.fetch(`${tempmsgid}`)
575 | var VcPermissions = VCPermissions
576 |
577 | //console.log(VCPermissions)
578 | VcPermissions.Connect = !VcPermissions.Connect
579 | await VC.permissionOverwrites.edit(user.id, { Connect: true })
580 | await VC.permissionOverwrites.edit(config.DefaultRoleID, { Connect: VcPermissions.Connect })
581 |
582 |
583 | await Controlmsg.edit(await CreateControlMsg(user.id, user.username, VcPermissions))
584 | await interaction.editReply({ content: `Now everyone **can${!VcPermissions.Connect ? "not** " : "** "}connect to this channel.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
585 | return
586 |
587 | }
588 | else if (interaction.customId === "button_hide") {
589 | await interaction.deferReply({ ephemeral: true })
590 | var VcPermissions = VCPermissions
591 | VcPermissions.ViewChannel = !VcPermissions.ViewChannel
592 | let Controlmsg = await VC.messages.fetch(`${tempmsgid}`)
593 | //console.log(VCPermissions)
594 | await VC.permissionOverwrites.edit(user.id, { ViewChannel: true })
595 | await VC.permissionOverwrites.edit(config.DefaultRoleID, { ViewChannel: VcPermissions.ViewChannel })
596 |
597 | await Controlmsg.edit(await CreateControlMsg(user.id, user.username, VcPermissions))
598 | await interaction.editReply({ content: `Now everyone **can${!VcPermissions.ViewChannel ? "not** " : "** "}see this channel.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
599 | return
600 | }
601 | else if (interaction.customId === "button_mute") {
602 | await interaction.deferReply({ ephemeral: true })
603 |
604 | var VcPermissions = VCPermissions
605 | VcPermissions.Speak = !VcPermissions.Speak
606 | let Controlmsg = await VC.messages.fetch(`${tempmsgid}`)
607 |
608 | //console.log(VCPermissions)
609 | await VC.permissionOverwrites.edit(user.id, { Speak: true })
610 | await VC.permissionOverwrites.edit(config.DefaultRoleID, { Speak: VcPermissions.Speak })
611 |
612 | await Controlmsg.edit(await CreateControlMsg(user.id, user.username, VcPermissions))
613 | await interaction.editReply({ content: `Now new join user **can${VcPermissions.Speak ? "**" : "not**"} Speak.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
614 | return
615 | }
616 | else if (interaction.customId === "button_limit") {
617 |
618 | const modal = new ModalBuilder()
619 | .setCustomId('modal_limit')
620 | .setTitle('Set User limit');
621 | const MessageInput = new TextInputBuilder()
622 | .setCustomId("_value")
623 | .setLabel("User limit")
624 | .setPlaceholder("Type number here")
625 | .setRequired(true)
626 | .setMinLength(1)
627 | .setMaxLength(2)
628 | .setStyle(TextInputStyle.Short)
629 | modal.addComponents(new ActionRowBuilder().addComponents(MessageInput));
630 | return await interaction.showModal(modal);
631 |
632 | }
633 | else if (interaction.customId === "button_ban") {
634 |
635 | var str1_role = "", str1_member = ""
636 | VC.permissionOverwrites.cache.forEach((value) => {
637 | let VCperm = value.allow.serialize()
638 | if (VCperm.ViewChannel || typeof value === 'undefined') return
639 | if (value.type == 0) {
640 | str1_role += `<@&${value.id}>\n`
641 | } else if (value.type == 1) {
642 | str1_member += `<@${value.id}>\n`
643 | }
644 | })
645 | let banlist_Embed = template_banlist_Embed
646 | banlist_Embed.setFields([
647 | { name: 'Roles', value: `${str1_role != "" ? str1_role : 'none'}`, inline: true },
648 | { name: 'Members', value: `${str1_member != "" ? str1_member : 'none'}`, inline: true }
649 | ])
650 |
651 | const userselectmenu = new UserSelectMenuBuilder()
652 | .setMaxValues(1)
653 | .setCustomId('menu_banlist_member')
654 | .setPlaceholder('Pick one or more users')
655 |
656 | const roleselectmenu = new RoleSelectMenuBuilder()
657 | .setMaxValues(1)
658 | .setCustomId('menu_banlist_role')
659 | .setPlaceholder('Pick one or more roles')
660 |
661 | const row1 = new ActionRowBuilder()
662 | .addComponents(userselectmenu)
663 | const row2 = new ActionRowBuilder()
664 | .addComponents(roleselectmenu)
665 | return await interaction.reply({ ephemeral: true, components: [row1, row2], embeds: [banlist_Embed] });
666 |
667 |
668 | }
669 | else if (interaction.customId === "button_kick") {
670 | await interaction.deferReply({ ephemeral: true })
671 | let kickUser = VC.members
672 |
673 | //console.log(banUser)
674 | if (kickUser.size <= 1) return interaction.editReply({ content: `Error cannot find user.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
675 | const row = new ActionRowBuilder()
676 |
677 | if (kickUser.size > 1) {
678 | var options = []
679 | var index = 0
680 | kickUser.forEach((user) => {
681 | if (user.id == interaction.user.id) return
682 | index += 1
683 | if (index <= 25) {
684 | options.push({
685 | label: user.user.username,
686 | description: user.nickname ? user.nickname : user.user.username,
687 | value: user.id,
688 | })
689 | }
690 | });
691 |
692 | let msmComponents = new StringSelectMenuBuilder()
693 | .setCustomId('select_kick')
694 | .setPlaceholder('Nothing selected')
695 | .addOptions(options)
696 | row.addComponents(msmComponents);
697 | }
698 |
699 | return await interaction.editReply({ content: 'Please select the one you want.', components: [row], ephemeral: true });
700 |
701 |
702 | }
703 | else if (interaction.customId === "button_changename") {
704 |
705 | const modal = new ModalBuilder()
706 | .setCustomId('modal_changename')
707 | .setTitle('Change VC Name');
708 | const MessageInput = new TextInputBuilder()
709 | .setCustomId("_value")
710 | .setLabel("Name")
711 | .setPlaceholder("Type name here")
712 | .setRequired(true)
713 | .setMinLength(1)
714 | .setMaxLength(4000)
715 | .setStyle(TextInputStyle.Short)
716 | modal.addComponents(new ActionRowBuilder().addComponents(MessageInput));
717 | return await interaction.showModal(modal);
718 |
719 | }
720 | else if (interaction.customId === "button_changeowner") {
721 | await interaction.deferReply({ ephemeral: true })
722 | let finedUser = VC.members
723 | //console.log(banUser)
724 | if (finedUser.size <= 1) return interaction.editReply({ content: `Error cannot find user.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
725 | const row = new ActionRowBuilder()
726 |
727 | if (finedUser.size > 1) {
728 | var options = []
729 | var index = 0
730 | finedUser.forEach((user) => {
731 |
732 | index += 1
733 | if (index <= 25) {
734 | options.push({
735 | label: user.user.username,
736 | description: user.nickname ? user.nickname : user.user.username,
737 | value: user.id,
738 | default: user.id == interaction.user.id,
739 | })
740 | }
741 | });
742 |
743 | let msmComponents = new StringSelectMenuBuilder()
744 | .setCustomId('select_changeowner')
745 | .setPlaceholder('Nothing selected')
746 | .addOptions(options)
747 | row.addComponents(msmComponents);
748 | }
749 |
750 | return await interaction.editReply({ content: 'Please select the one you want.', components: [row], ephemeral: true });
751 |
752 |
753 | }
754 | else if (interaction.customId === "button_delete") {
755 | let Vc = await guild.channels.cache.find(channel => channel.id == tempVcId)
756 | if (removeTimeouts[user.id]) return
757 | if (removeTimeouts2[user.id]) {
758 | let cmsg = await Vc.send({ embeds: [template_CancelPreDelete_Embed] })
759 | setTimeout(RemoveMsg, 5000, cmsg)
760 | let Controlmsg = await Vc.messages.fetch(`${removeTimeoutsMsg2[user.id]}`)
761 | clearTimeout(removeTimeouts2[user.id])
762 | delete removeTimeouts2[user.id]
763 | await Controlmsg.delete().catch(err => { console.log(err) })
764 |
765 |
766 | } else {
767 |
768 | //predelete_Embed.setDescription(`房主決定將<#${tempVcId}>刪除。 \n頻道將於 刪除。 \n如要取消,請再次按下DeleteVC。`)
769 | let predelete_Embed = template_predelete_Embed
770 | predelete_Embed.data.description = predelete_Embed.data.description
771 | .replace('$tempVcId', `<#${tempVcId}>`)
772 | predelete_Embed.data.description = predelete_Embed.data.description
773 | .replace('$timeLeft', ``)
774 |
775 | let msg = await Vc.send({ embeds: [predelete_Embed] })
776 | let tout = setTimeout(RemoveVTChannels, 10000, user.id, tempVcId)
777 | removeTimeouts2[user.id] = tout
778 | removeTimeoutsMsg2[user.id] = msg.id
779 |
780 | }
781 |
782 | return interaction.deferUpdate()
783 |
784 | }
785 | else if (interaction.customId === "button_Region") {
786 | await interaction.deferReply({ ephemeral: true })
787 |
788 | //console.log(RTCRegions)
789 | if (RTCRegions.size < 1) return interaction.editReply({ content: `Error cannot find RTCRegions.`, ephemeral: true }).catch(err => { VC.send(`Something is wrong. Please try again.\nError catch: \n\`\`\` ${err}\n\`\`\``) })
790 | const row = new ActionRowBuilder()
791 |
792 | var options = []
793 | options.push({
794 | label: "Auto",
795 | value: "null",
796 | default: !VC.rtcRegion,
797 | })
798 | RTCRegions.forEach((Region) => {
799 |
800 | options.push({
801 | label: Region.name,
802 | value: Region.id,
803 | default: VC.rtcRegion == Region.id,
804 | })
805 | });
806 |
807 | let msmComponents = new StringSelectMenuBuilder()
808 | .setCustomId('select_Region')
809 | .setPlaceholder('Nothing selected')
810 | .addOptions(options)
811 | row.addComponents(msmComponents);
812 |
813 | return await interaction.editReply({ content: 'Please select a Region you want.', components: [row], ephemeral: true });
814 |
815 |
816 | }
817 | else if (interaction.customId === "button_whitelist") {
818 |
819 | var str1_role = "", str1_member = ""
820 | VC.permissionOverwrites.cache.forEach((value) => {
821 | let VCperm = value.allow.serialize()
822 | if (!VCperm.ViewChannel) return
823 | if (value.type == 0) {
824 | str1_role += `<@&${value.id}>\n`
825 | } else if (value.type == 1) {
826 | str1_member += `<@${value.id}>\n`
827 | }
828 | })
829 | let whitelist_Embed = template_whitelist_Embed
830 | whitelist_Embed.setFields([
831 | { name: 'Roles', value: `${str1_role != "" ? str1_role : 'none'}`, inline: true },
832 | { name: 'Members', value: `${str1_member != "" ? str1_member : 'none'}`, inline: true }
833 | ])
834 |
835 | const userselectmenu = new UserSelectMenuBuilder()
836 | .setMaxValues(1)
837 | .setCustomId('menu_whitelist_member')
838 | .setPlaceholder('Pick one or more users')
839 |
840 | const roleselectmenu = new RoleSelectMenuBuilder()
841 | .setMaxValues(1)
842 | .setCustomId('menu_whitelist_role')
843 | .setPlaceholder('Pick one or more roles')
844 |
845 | const row1 = new ActionRowBuilder()
846 | .addComponents(userselectmenu)
847 | const row2 = new ActionRowBuilder()
848 | .addComponents(roleselectmenu)
849 | return await interaction.reply({ ephemeral: true, components: [row1, row2], embeds: [whitelist_Embed] });
850 |
851 | }
852 | return await interaction.deferUpdate().catch()
853 | } else if (interaction.isCommand()) {
854 | if (interaction.commandName == "vc-fix") {
855 | await interaction.deferReply()
856 | if (!config.owners.includes(user.id)) return
857 | await db.del('tempHUBBans').catch()
858 | let HUBVC = await guild.channels.cache.find(channel => channel.id == config.HUBvcChannelID)
859 | HUBVC.permissionOverwrites.set([])
860 | .then(updatedChannel => {
861 | console.log('權限覆蓋已成功清空');
862 | })
863 | .catch(error => {
864 | console.error('清空權限覆蓋時出錯:', error);
865 | });
866 | for await (const key of db.keys()) {
867 | if (key.startsWith('tempVcIdKey_')) {
868 | //console.log(key)
869 | let uid = key.replace('tempVcIdKey_', '')
870 | let tempVcId = await db.get(`tempVcIdKey_${uid}`).catch(err => { }) //= await kvs1.get('tempVcIdKey_'+userid);
871 | let Vc = await guild.channels.cache.find(channel => channel.id == tempVcId)
872 | //adminDeleteEmbed.setDescription(`頻道將於 $timeLeft 刪除。`)
873 | let adminDeleteEmbed = template_adminDeleteEmbed
874 | adminDeleteEmbed.data.description = adminDeleteEmbed.data.description
875 | .replace('$timeLeft', ``)
876 |
877 | Vc.send({ content: `<@${uid}>`, embeds: [adminDeleteEmbed] })
878 | setTimeout(RemoveVTChannels, 10000, uid, tempVcId)
879 | }
880 | }
881 | return await interaction.editReply({ content: 'OK. Clear database.', ephemeral: true })
882 | }
883 | else if (interaction.commandName == "vc-hubmsg") {
884 | await interaction.deferReply()
885 | template_createHubEmbed.setAuthor({
886 | name: (await guild.fetchOwner()).displayName,
887 | iconURL: (await guild.fetchOwner()).avatarURL()
888 | })
889 |
890 | return interaction.editReply({ embeds: [template_createHubEmbed] })
891 | }
892 | else if (interaction.commandName == "vc-control") {
893 | if (tempVcId != textchannel.id) {
894 | return await interaction.reply({ content: "Uhhhhh... This is not your VC.", ephemeral: true }).catch(err => { console.log(err) })
895 | }
896 |
897 | await interaction.deferReply({ ephemeral: true })
898 |
899 | return interaction.editReply(await CreateControlMsg(user.id, user.username, VcPermissions))
900 | }
901 |
902 | }
903 | })
904 |
905 |
906 | async function CreateControlMsg(UserID, userName, Permissinos = { "ViewChannel": false, "Connect": true, "Speak": true, }) {
907 |
908 | let button1 = new ButtonBuilder()
909 | .setStyle(ButtonStyle.Primary)
910 | .setEmoji(`${Permissinos.Connect ? "🔒" : "🔓"}`)
911 | .setLabel(`${Permissinos.Connect ? "Lock" : "Unlock"}`)
912 | .setCustomId("button_lock")
913 | let button2 = new ButtonBuilder()
914 | .setStyle(ButtonStyle.Primary)
915 | .setEmoji(`${Permissinos.ViewChannel ? "👤" : "👥"}`)
916 | .setLabel(`${Permissinos.ViewChannel ? "Hide" : "Show"}`)
917 | .setCustomId("button_hide")
918 | let button3 = new ButtonBuilder()
919 | .setStyle(ButtonStyle.Primary)
920 | .setEmoji(`${Permissinos.Speak ? "🔇" : "🔊"}`)
921 | .setLabel(`${Permissinos.Speak ? "Mute" : "Unmute"}`)
922 | .setCustomId("button_mute")
923 | let button4 = new ButtonBuilder()
924 |
925 | .setStyle(ButtonStyle.Danger)
926 | .setEmoji("🚫")
927 | .setLabel("ㅤBan/Unban")
928 | .setCustomId("button_ban")
929 | let button5 = new ButtonBuilder()
930 | .setStyle(ButtonStyle.Primary)
931 | .setEmoji("🗒️")
932 | .setLabel("ㅤWhitelist/Remove")
933 | .setCustomId("button_whitelist")
934 | let button6 = new ButtonBuilder()
935 | .setStyle(ButtonStyle.Primary)
936 | .setEmoji("⚠️")
937 | .setLabel(" Limit")
938 | .setCustomId("button_limit")
939 | let button7 = new ButtonBuilder()
940 | .setStyle(ButtonStyle.Danger)
941 | .setEmoji("📲")
942 | .setLabel("ㅤChange Owner")
943 | .setCustomId("button_changeowner")
944 | let button8 = new ButtonBuilder()
945 | .setStyle(ButtonStyle.Primary)
946 | .setEmoji("📝")
947 | .setLabel("ㅤChange Name")
948 | .setCustomId("button_changename")
949 | let button9 = new ButtonBuilder()
950 | .setStyle(ButtonStyle.Secondary)
951 | .setEmoji("👂")
952 | .setLabel("Get Mention")
953 | .setCustomId("button_getmention")
954 | let button10 = new ButtonBuilder()
955 | .setStyle(ButtonStyle.Secondary)
956 | .setEmoji("📃")
957 | .setLabel("ㅤW-list List")
958 | .setCustomId("button_w-list")
959 | let button11 = new ButtonBuilder()
960 | .setStyle(ButtonStyle.Secondary)
961 | .setEmoji("📜")
962 | .setLabel("ㅤBan List")
963 | .setCustomId("button_banlist")
964 | let button12 = new ButtonBuilder()
965 | .setStyle(ButtonStyle.Danger)
966 | .setEmoji("💢")
967 | .setLabel("ㅤKick")
968 | .setCustomId("button_kick")
969 | let button13 = new ButtonBuilder()
970 | .setStyle(ButtonStyle.Danger)
971 | .setEmoji("🗑️")
972 | .setLabel("ㅤDelete VC")
973 | .setCustomId("button_delete")
974 | let button14 = new ButtonBuilder()
975 | .setStyle(ButtonStyle.Primary)
976 | .setEmoji("🌎")
977 | .setLabel("ㅤChange Region")
978 | .setCustomId("button_Region")
979 | let button15 = new ButtonBuilder()
980 | .setStyle(ButtonStyle.Primary)
981 | .setEmoji("🔖")
982 | .setLabel("ㅤKeep VC")
983 | .setCustomId("button_vcKeep")
984 | let buttonRow1 = new ActionRowBuilder()
985 | .addComponents([button4, button12, button7, button13])
986 |
987 | let buttonRow2 = new ActionRowBuilder()
988 | .addComponents([button1, button2, button3, button6])
989 |
990 | let buttonRow3 = new ActionRowBuilder()
991 | .addComponents([button8, button5, button14])
992 | let controlsEmbed = template_controlsEmbed
993 | controlsEmbed.setAuthor({
994 | name: `${userName}'s VoiceChannel`,
995 | iconURL: `${client.users.cache.find(user => user.id == UserID).avatarURL()}`
996 | })
997 |
998 | return { content: `<@${UserID}>`, embeds: [controlsEmbed], components: [buttonRow1, buttonRow2, buttonRow3] }
999 | }
1000 |
1001 | const banner = `
1002 | ==========================================
1003 |
1004 | _____ _____ _
1005 | |_ _|___ _____ ___| | |___|_|___ ___
1006 | | | | -_| | . | | | . | | _| -_|
1007 | |_| |___|_|_|_| _|\\___/|___|_|___|___|
1008 | |_|
1009 |
1010 | _____ _
1011 | | __ |___| |_
1012 | | __ -| . | _|
1013 | |_____|___|_|
1014 |
1015 |
1016 | Ver. 2024.9.04 made by Lars.
1017 |
1018 | ==========================================
1019 | `
1020 | console.log(banner)
1021 | client.login(config.token).catch(() => console.error('Invalid Token.Make Sure To Fill config.js or set ENV'))
1022 |
1023 |
1024 |
--------------------------------------------------------------------------------