mirror of
https://github.com/arthur-pbty/LazyBot.git
synced 2026-06-03 15:07:29 +02:00
add auto role for new member and member in vocal
This commit is contained in:
+48
@@ -42,6 +42,18 @@ client.on(Events.GuildMemberAdd, member => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
db.get(
|
||||||
|
"SELECT enabled, role_id FROM autorole_newuser_config WHERE guild_id = ?",
|
||||||
|
[member.guild.id],
|
||||||
|
(err, row) => {
|
||||||
|
if (err || !row || !row.enabled) return;
|
||||||
|
|
||||||
|
const role = member.guild.roles.cache.get(row.role_id);
|
||||||
|
if (role) {
|
||||||
|
member.roles.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +79,42 @@ client.on(Events.GuildMemberRemove, member => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
client.on(Events.VoiceStateUpdate, (oldState, newState) => {
|
||||||
|
if (newState.member.user.bot) return;
|
||||||
|
|
||||||
|
const guildId = newState.guild.id;
|
||||||
|
|
||||||
|
db.get(
|
||||||
|
"SELECT enabled, role_id, exclude_channel_ids FROM autorole_vocal_config WHERE guild_id = ?",
|
||||||
|
[guildId],
|
||||||
|
(err, row) => {
|
||||||
|
if (err || !row || !row.enabled) return;
|
||||||
|
|
||||||
|
let excludeChannelIds = [];
|
||||||
|
try {
|
||||||
|
excludeChannelIds = row.exclude_channel_ids
|
||||||
|
? JSON.parse(row.exclude_channel_ids)
|
||||||
|
: [];
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Erreur parsing exclude_channel_ids", err);
|
||||||
|
excludeChannelIds = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = newState.guild.roles.cache.get(row.role_id);
|
||||||
|
if (!role) return;
|
||||||
|
|
||||||
|
// User joins a voice channel and it's not excluded et a pas déjà le rôle
|
||||||
|
if (newState.channelId && !excludeChannelIds.includes(newState.channelId) && !newState.member.roles.cache.has(role.id)) {
|
||||||
|
newState.member.roles.add(role);
|
||||||
|
}
|
||||||
|
// User leaves a voice channel or joins an excluded one
|
||||||
|
else if (!newState.channelId || excludeChannelIds.includes(newState.channelId)) {
|
||||||
|
newState.member.roles.remove(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
client.login(process.env.BOT_TOKEN);
|
client.login(process.env.BOT_TOKEN);
|
||||||
|
|
||||||
module.exports = client;
|
module.exports = client;
|
||||||
|
|||||||
@@ -24,6 +24,19 @@ db.exec(`
|
|||||||
enabled INTEGER NOT NULL,
|
enabled INTEGER NOT NULL,
|
||||||
message TEXT NOT NULL
|
message TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS autorole_newuser_config (
|
||||||
|
guild_id TEXT PRIMARY KEY,
|
||||||
|
role_id TEXT,
|
||||||
|
enabled INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS autorole_vocal_config (
|
||||||
|
guild_id TEXT PRIMARY KEY,
|
||||||
|
role_id TEXT,
|
||||||
|
exclude_channel_ids TEXT,
|
||||||
|
enabled INTEGER NOT NULL
|
||||||
|
);
|
||||||
`);
|
`);
|
||||||
|
|
||||||
module.exports = db;
|
module.exports = db;
|
||||||
|
|||||||
+58
-1
@@ -139,4 +139,61 @@ small code {
|
|||||||
form {
|
form {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#autorole-vocal-exclude-channel {
|
||||||
|
background-color: #0f1115;
|
||||||
|
color: #e5e7eb;
|
||||||
|
|
||||||
|
border: 1px solid #2a2f3a;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
min-width: 240px;
|
||||||
|
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* options */
|
||||||
|
#autorole-vocal-exclude-channel option {
|
||||||
|
background-color: #0f1115;
|
||||||
|
color: #e5e7eb;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hover */
|
||||||
|
#autorole-vocal-exclude-channel option:hover {
|
||||||
|
background-color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sélection */
|
||||||
|
#autorole-vocal-exclude-channel option:checked {
|
||||||
|
background-color: #2563eb; /* bleu */
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* focus */
|
||||||
|
#autorole-vocal-exclude-channel:focus {
|
||||||
|
border-color: #2563eb;
|
||||||
|
box-shadow: 0 0 0 1px #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* scrollbar (Chrome / Edge) */
|
||||||
|
#autorole-vocal-exclude-channel::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#autorole-vocal-exclude-channel::-webkit-scrollbar-track {
|
||||||
|
background: #0f1115;
|
||||||
|
}
|
||||||
|
|
||||||
|
#autorole-vocal-exclude-channel::-webkit-scrollbar-thumb {
|
||||||
|
background: #2a2f3a;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#autorole-vocal-exclude-channel::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #3b4252;
|
||||||
|
}
|
||||||
|
|||||||
+160
-12
@@ -88,6 +88,49 @@
|
|||||||
<button type="submit">Sauvegarder</button>
|
<button type="submit">Sauvegarder</button>
|
||||||
<div id="status-goodbye-form"></div>
|
<div id="status-goodbye-form"></div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<form id="autorole-newuser-form">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="autorole-enabled" />
|
||||||
|
Activer le rôle automatique pour les nouveaux membres
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Rôle à attribuer :
|
||||||
|
<br />
|
||||||
|
<select id="autorole-role">
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit">Sauvegarder</button>
|
||||||
|
<div id="status-autorole-form"></div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<form id="autorole-vocal-form">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="autorole-vocal-enabled" />
|
||||||
|
Activer le rôle automatique pour les membres en vocal
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Rôle à attribuer :
|
||||||
|
<br />
|
||||||
|
<select id="autorole-vocal-role">
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Salon à éviter :
|
||||||
|
<br />
|
||||||
|
<select id="autorole-vocal-exclude-channel" multiple size="5">
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit">Sauvegarder</button>
|
||||||
|
<div id="status-autorole-vocal-form"></div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -107,6 +150,68 @@
|
|||||||
"guild-name"
|
"guild-name"
|
||||||
).textContent = `Dashboard : ${guild.name}`;
|
).textContent = `Dashboard : ${guild.name}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
fetch(`/api/bot/get-text-channels/${guildId}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(channels => {
|
||||||
|
const selectWelcome = document.getElementById("welcome-channel");
|
||||||
|
const selectGoodbye = document.getElementById("goodbye-channel");
|
||||||
|
channels.forEach(channel => {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = channel.id;
|
||||||
|
option.textContent = `#${channel.name}`;
|
||||||
|
selectWelcome.appendChild(option);
|
||||||
|
selectGoodbye.appendChild(option.cloneNode(true));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const selectExclude = document.getElementById("autorole-vocal-exclude-channel");
|
||||||
|
selectExclude.innerHTML = ""; // reset
|
||||||
|
|
||||||
|
fetch(`/api/bot/get-voice-channels/${guildId}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(channels => {
|
||||||
|
channels.forEach(channel => {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = channel.id;
|
||||||
|
option.textContent = `#${channel.name}`;
|
||||||
|
selectExclude.appendChild(option);
|
||||||
|
});
|
||||||
|
return fetch(`/api/bot/get-autorole-vocal-config/${guildId}`);
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(cfg => {
|
||||||
|
document.getElementById("autorole-vocal-enabled").checked = !!cfg.enabled;
|
||||||
|
document.getElementById("autorole-vocal-role").value = cfg.roleId ?? "";
|
||||||
|
|
||||||
|
const excluded = Array.isArray(cfg.excludeChannelIds)
|
||||||
|
? cfg.excludeChannelIds.map(String) // sécurité
|
||||||
|
: [];
|
||||||
|
|
||||||
|
Array.from(selectExclude.options).forEach(option => {
|
||||||
|
option.selected = excluded.includes(option.value);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fetch(`/api/bot/get-roles/${guildId}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(roles => {
|
||||||
|
const selectAutorole = document.getElementById("autorole-role");
|
||||||
|
const selectAutoroleVocal = document.getElementById("autorole-vocal-role");
|
||||||
|
roles.forEach(role => {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = role.id;
|
||||||
|
option.textContent = role.name;
|
||||||
|
selectAutorole.appendChild(option);
|
||||||
|
selectAutoroleVocal.appendChild(option.cloneNode(true));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
const welcomeForm = document.getElementById("welcome-form");
|
const welcomeForm = document.getElementById("welcome-form");
|
||||||
@@ -159,20 +264,53 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
fetch(`/api/bot/get-text-channels/${guildId}`)
|
const autoroleNewUserForm = document.getElementById("autorole-newuser-form");
|
||||||
.then(res => res.json())
|
autoroleNewUserForm.addEventListener("submit", async (e) => {
|
||||||
.then(channels => {
|
e.preventDefault();
|
||||||
const selectWelcome = document.getElementById("welcome-channel");
|
const enabled = document.getElementById("autorole-enabled").checked;
|
||||||
const selectGoodbye = document.getElementById("goodbye-channel");
|
const roleId = document.getElementById("autorole-role").value;
|
||||||
channels.forEach(channel => {
|
|
||||||
const option = document.createElement("option");
|
const res = await fetch("/api/bot/save-autorole-newuser-config", {
|
||||||
option.value = channel.id;
|
method: "POST",
|
||||||
option.textContent = `#${channel.name}`;
|
headers: { "Content-Type": "application/json" },
|
||||||
selectWelcome.appendChild(option);
|
body: JSON.stringify({
|
||||||
selectGoodbye.appendChild(option.cloneNode(true));
|
guildId,
|
||||||
});
|
roleId,
|
||||||
|
enabled,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
document.getElementById("status-autorole-form").textContent = data.success
|
||||||
|
? "Config d'auto-rôle pour nouveaux membres sauvegardée ✅"
|
||||||
|
: "Erreur ❌";
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const autoroleVocalForm = document.getElementById("autorole-vocal-form");
|
||||||
|
autoroleVocalForm.addEventListener("submit", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const enabled = document.getElementById("autorole-vocal-enabled").checked;
|
||||||
|
const roleId = document.getElementById("autorole-vocal-role").value;
|
||||||
|
const excludeChannelSelect = document.getElementById("autorole-vocal-exclude-channel");
|
||||||
|
const excludeChannelId = Array.from(excludeChannelSelect.selectedOptions).map(option => option.value);
|
||||||
|
|
||||||
|
const res = await fetch("/api/bot/save-autorole-vocal-config", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
guildId,
|
||||||
|
roleId,
|
||||||
|
excludeChannelId,
|
||||||
|
enabled,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
document.getElementById("status-autorole-vocal-form").textContent = data.success
|
||||||
|
? "Config d'auto-rôle pour membres en vocal sauvegardée ✅"
|
||||||
|
: "Erreur ❌";
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
fetch(`/api/bot/get-welcome-config/${guildId}`)
|
fetch(`/api/bot/get-welcome-config/${guildId}`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
@@ -190,6 +328,16 @@
|
|||||||
document.getElementById("goodbye-channel").value = cfg.channelId;
|
document.getElementById("goodbye-channel").value = cfg.channelId;
|
||||||
document.getElementById("goodbye-message").value = cfg.message;
|
document.getElementById("goodbye-message").value = cfg.message;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
fetch(`/api/bot/get-autorole-newuser-config/${guildId}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(cfg => {
|
||||||
|
document.getElementById("autorole-enabled").checked = cfg.enabled;
|
||||||
|
document.getElementById("autorole-role").value = cfg.roleId;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<div id="profil">
|
<div id="profil">
|
||||||
<img id="avatar" src="" alt="Avatar">
|
<img id="avatar" src="" alt="Avatar">
|
||||||
<span id="username"></span>
|
<span id="username"></span>
|
||||||
<a id="logout" href="/logout">Se déconnecter</a>
|
<a id="logout" href="/auth/logout">Se déconnecter</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>LazyBot</h1>
|
<h1>LazyBot</h1>
|
||||||
|
|||||||
@@ -0,0 +1,339 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
module.exports = (app, db, client) => {
|
||||||
|
|
||||||
|
// --- User info ---
|
||||||
|
router.get("/user", (req, res) => {
|
||||||
|
if (req.session.user) res.json(req.session.user);
|
||||||
|
else res.status(401).json({ error: "Utilisateur non connecté" });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/guilds", (req, res) => {
|
||||||
|
const userGuilds = req.session.guilds;
|
||||||
|
if (!userGuilds) return res.status(401).json({ error: "Utilisateur non connecté" });
|
||||||
|
|
||||||
|
const botGuildIds = client.guilds.cache.map(g => g.id);
|
||||||
|
const validGuilds = userGuilds.filter(g => {
|
||||||
|
const hasAdmin = (g.permissions & 0x8) === 0x8;
|
||||||
|
return hasAdmin && botGuildIds.includes(g.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json(validGuilds);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// API pour sauvegarder la configuration de bienvenue
|
||||||
|
router.post("/bot/save-welcome-config", express.json(), (req, res) => {
|
||||||
|
const { guildId, channelId, welcomeEnabled, welcomeMessage } = req.body;
|
||||||
|
|
||||||
|
if (!req.session.guilds) {
|
||||||
|
return res.status(401).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAdmin = req.session.guilds.find(
|
||||||
|
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return res.status(403).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
`
|
||||||
|
INSERT INTO welcome_config (guild_id, channel_id, enabled, message)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
ON CONFLICT(guild_id)
|
||||||
|
DO UPDATE SET channel_id = ?, enabled = ?, message = ?
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
guildId,
|
||||||
|
channelId,
|
||||||
|
welcomeEnabled ? 1 : 0,
|
||||||
|
welcomeMessage,
|
||||||
|
channelId,
|
||||||
|
welcomeEnabled ? 1 : 0,
|
||||||
|
welcomeMessage
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).json({ success: false });
|
||||||
|
}
|
||||||
|
res.json({ success: true });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.get("/bot/get-welcome-config/:guildId", (req, res) => {
|
||||||
|
const { guildId } = req.params;
|
||||||
|
|
||||||
|
db.get(
|
||||||
|
"SELECT enabled, channel_id, message FROM welcome_config WHERE guild_id = ?",
|
||||||
|
[guildId],
|
||||||
|
(err, row) => {
|
||||||
|
if (err || !row) {
|
||||||
|
return res.json({ enabled: false, channelId: null, message: "" });
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
enabled: !!row.enabled,
|
||||||
|
channelId: row.channel_id,
|
||||||
|
message: row.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.post("/bot/save-goodbye-config", express.json(), (req, res) => {
|
||||||
|
const { guildId, channelId, goodbyeEnabled, goodbyeMessage } = req.body;
|
||||||
|
|
||||||
|
if (!req.session.guilds) {
|
||||||
|
return res.status(401).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAdmin = req.session.guilds.find(
|
||||||
|
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return res.status(403).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
`
|
||||||
|
INSERT INTO goodbye_config (guild_id, channel_id, enabled, message)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
ON CONFLICT(guild_id)
|
||||||
|
DO UPDATE SET channel_id = ?, enabled = ?, message = ?
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
guildId,
|
||||||
|
channelId,
|
||||||
|
goodbyeEnabled ? 1 : 0,
|
||||||
|
goodbyeMessage,
|
||||||
|
channelId,
|
||||||
|
goodbyeEnabled ? 1 : 0,
|
||||||
|
goodbyeMessage
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).json({ success: false });
|
||||||
|
}
|
||||||
|
res.json({ success: true });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.get("/bot/get-goodbye-config/:guildId", (req, res) => {
|
||||||
|
const { guildId } = req.params;
|
||||||
|
|
||||||
|
db.get(
|
||||||
|
"SELECT enabled, channel_id, message FROM goodbye_config WHERE guild_id = ?",
|
||||||
|
[guildId],
|
||||||
|
(err, row) => {
|
||||||
|
if (err || !row) {
|
||||||
|
return res.json({ enabled: false, channelId: null, message: "" });
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
enabled: !!row.enabled,
|
||||||
|
channelId: row.channel_id,
|
||||||
|
message: row.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.post("/bot/save-autorole-newuser-config", express.json(), (req, res) => {
|
||||||
|
const { guildId, roleId, enabled } = req.body;
|
||||||
|
console.log("Received autorole-newuser config:", { guildId, roleId, enabled });
|
||||||
|
if (!req.session.guilds) {
|
||||||
|
return res.status(401).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAdmin = req.session.guilds.find(
|
||||||
|
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return res.status(403).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
`
|
||||||
|
INSERT INTO autorole_newuser_config (guild_id, role_id, enabled)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON CONFLICT(guild_id)
|
||||||
|
DO UPDATE SET role_id = ?, enabled = ?
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
guildId,
|
||||||
|
roleId,
|
||||||
|
enabled ? 1 : 0,
|
||||||
|
roleId,
|
||||||
|
enabled ? 1 : 0
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).json({ success: false });
|
||||||
|
}
|
||||||
|
res.json({ success: true });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/bot/get-autorole-newuser-config/:guildId", (req, res) => {
|
||||||
|
const { guildId } = req.params;
|
||||||
|
|
||||||
|
db.get(
|
||||||
|
"SELECT enabled, role_id FROM autorole_newuser_config WHERE guild_id = ?",
|
||||||
|
[guildId],
|
||||||
|
(err, row) => {
|
||||||
|
if (err || !row) {
|
||||||
|
return res.json({ enabled: false, roleId: null });
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
enabled: !!row.enabled,
|
||||||
|
roleId: row.role_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.post("/bot/save-autorole-vocal-config", express.json(), (req, res) => {
|
||||||
|
const { guildId, roleId, excludeChannelId, enabled } = req.body;
|
||||||
|
|
||||||
|
if (!req.session.guilds) {
|
||||||
|
return res.status(401).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAdmin = req.session.guilds.find(
|
||||||
|
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAdmin) {
|
||||||
|
return res.status(403).json({ success: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
const excludeChannelIdArray = Array.isArray(excludeChannelId) ? excludeChannelId : [excludeChannelId];
|
||||||
|
|
||||||
|
db.run(
|
||||||
|
`
|
||||||
|
INSERT INTO autorole_vocal_config (guild_id, role_id, exclude_channel_ids, enabled)
|
||||||
|
VALUES (?, ?, ?, ?)
|
||||||
|
ON CONFLICT(guild_id)
|
||||||
|
DO UPDATE SET role_id = ?, exclude_channel_ids = ?, enabled = ?
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
guildId,
|
||||||
|
roleId,
|
||||||
|
JSON.stringify(excludeChannelIdArray),
|
||||||
|
enabled ? 1 : 0,
|
||||||
|
roleId,
|
||||||
|
JSON.stringify(excludeChannelIdArray),
|
||||||
|
enabled ? 1 : 0
|
||||||
|
],
|
||||||
|
err => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
return res.status(500).json({ success: false });
|
||||||
|
}
|
||||||
|
res.json({ success: true });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/bot/get-autorole-vocal-config/:guildId", (req, res) => {
|
||||||
|
const { guildId } = req.params;
|
||||||
|
|
||||||
|
db.get(
|
||||||
|
"SELECT enabled, role_id, exclude_channel_ids FROM autorole_vocal_config WHERE guild_id = ?",
|
||||||
|
[guildId],
|
||||||
|
(err, row) => {
|
||||||
|
if (err || !row) {
|
||||||
|
return res.json({ enabled: false, roleId: null });
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
enabled: !!row.enabled,
|
||||||
|
roleId: row.role_id,
|
||||||
|
excludeChannelIds: JSON.parse(row.exclude_channel_ids),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.get("/bot/get-text-channels/:guildId", (req, res) => {
|
||||||
|
const { guildId } = req.params;
|
||||||
|
const guild = client.guilds.cache.get(guildId);
|
||||||
|
if (!guild) {
|
||||||
|
return res.status(404).json({ error: "Serveur non trouvé" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const channels = guild.channels.cache
|
||||||
|
.filter(channel => channel.isTextBased())
|
||||||
|
.map(channel => ({
|
||||||
|
id: channel.id,
|
||||||
|
name: channel.name
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.json(channels);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.get("/bot/get-voice-channels/:guildId", (req, res) => {
|
||||||
|
const { guildId } = req.params;
|
||||||
|
const guild = client.guilds.cache.get(guildId);
|
||||||
|
if (!guild) {
|
||||||
|
return res.status(404).json({ error: "Serveur non trouvé" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const channels = guild.channels.cache
|
||||||
|
.filter(channel => channel.isVoiceBased())
|
||||||
|
.map(channel => ({
|
||||||
|
id: channel.id,
|
||||||
|
name: channel.name
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.json(channels);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.get("/bot/get-roles/:guildId", (req, res) => {
|
||||||
|
const { guildId } = req.params;
|
||||||
|
const guild = client.guilds.cache.get(guildId);
|
||||||
|
if (!guild) {
|
||||||
|
return res.status(404).json({ error: "Serveur non trouvé" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const botMember = guild.members.cache.get(client.user.id);
|
||||||
|
if (!botMember) return res.status(500).json({ error: "Bot non trouvé dans ce serveur" });
|
||||||
|
|
||||||
|
const botRolePos = botMember.roles.highest.position;
|
||||||
|
|
||||||
|
// On filtre :
|
||||||
|
// - rôle sous le plus haut rôle du bot
|
||||||
|
// - pas @everyone
|
||||||
|
// - pas managed (roles de bot/intégrations)
|
||||||
|
const roles = guild.roles.cache
|
||||||
|
.filter(role =>
|
||||||
|
role.position < botRolePos &&
|
||||||
|
role.name !== "@everyone" &&
|
||||||
|
role.managed === false
|
||||||
|
)
|
||||||
|
.map(role => ({
|
||||||
|
id: role.id,
|
||||||
|
name: role.name
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.json(roles);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use("/api", router);
|
||||||
|
};
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const fetch = require("cross-fetch");
|
||||||
|
const router = express.Router();
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const CLIENT_ID = process.env.CLIENT_ID;
|
||||||
|
const CLIENT_SECRET = process.env.CLIENT_SECRET;
|
||||||
|
const REDIRECT_URI = process.env.REDIRECT_URI;
|
||||||
|
|
||||||
|
module.exports = (app, db, client) => {
|
||||||
|
// --- Connexion Discord ---
|
||||||
|
router.get("/discord", (req, res) => {
|
||||||
|
const url = `https://discord.com/api/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=identify%20guilds`;
|
||||||
|
res.redirect(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/discord/callback", async (req, res) => {
|
||||||
|
const code = req.query.code;
|
||||||
|
if (!code) return res.send("Pas de code OAuth reçu !");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = new URLSearchParams();
|
||||||
|
data.append("client_id", CLIENT_ID);
|
||||||
|
data.append("client_secret", CLIENT_SECRET);
|
||||||
|
data.append("grant_type", "authorization_code");
|
||||||
|
data.append("code", code);
|
||||||
|
data.append("redirect_uri", REDIRECT_URI);
|
||||||
|
data.append("scope", "identify");
|
||||||
|
|
||||||
|
const tokenResponse = await fetch("https://discord.com/api/oauth2/token", {
|
||||||
|
method: "POST",
|
||||||
|
body: data,
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const tokenJson = await tokenResponse.json();
|
||||||
|
const accessToken = tokenJson.access_token;
|
||||||
|
|
||||||
|
const userResponse = await fetch("https://discord.com/api/users/@me", {
|
||||||
|
headers: { Authorization: `Bearer ${accessToken}` },
|
||||||
|
});
|
||||||
|
const user = await userResponse.json();
|
||||||
|
|
||||||
|
const guildsResponse = await fetch("https://discord.com/api/users/@me/guilds", {
|
||||||
|
headers: { Authorization: `Bearer ${accessToken}` },
|
||||||
|
});
|
||||||
|
const guilds = await guildsResponse.json();
|
||||||
|
|
||||||
|
req.session.user = user;
|
||||||
|
req.session.guilds = guilds;
|
||||||
|
|
||||||
|
res.redirect("/dashboard");
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.send("Erreur lors de la connexion Discord !");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/logout", (req, res) => {
|
||||||
|
req.session.destroy();
|
||||||
|
res.redirect("/");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use("/auth", router);
|
||||||
|
};
|
||||||
+4
-234
@@ -31,94 +31,9 @@ app.use(session({
|
|||||||
// --- Servir le dossier public ---
|
// --- Servir le dossier public ---
|
||||||
app.use(express.static(path.join(__dirname, "public")));
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
// --- Route pour démarrer la connexion Discord ---
|
// --- Routes ---
|
||||||
app.get("/auth/discord", (req, res) => {
|
require("./routes/auth")(app, db, client);
|
||||||
const url = `https://discord.com/api/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}&response_type=code&scope=identify%20guilds`;
|
require("./routes/api")(app, db, client);
|
||||||
res.redirect(url);
|
|
||||||
});
|
|
||||||
|
|
||||||
// --- Callback après connexion Discord ---
|
|
||||||
app.get("/auth/discord/callback", async (req, res) => {
|
|
||||||
const code = req.query.code;
|
|
||||||
if (!code) return res.send("Pas de code OAuth reçu !");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Échange du code contre access token
|
|
||||||
const data = new URLSearchParams();
|
|
||||||
data.append("client_id", CLIENT_ID);
|
|
||||||
data.append("client_secret", CLIENT_SECRET);
|
|
||||||
data.append("grant_type", "authorization_code");
|
|
||||||
data.append("code", code);
|
|
||||||
data.append("redirect_uri", REDIRECT_URI);
|
|
||||||
data.append("scope", "identify");
|
|
||||||
|
|
||||||
const tokenResponse = await fetch("https://discord.com/api/oauth2/token", {
|
|
||||||
method: "POST",
|
|
||||||
body: data,
|
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
||||||
});
|
|
||||||
|
|
||||||
const tokenJson = await tokenResponse.json();
|
|
||||||
const accessToken = tokenJson.access_token;
|
|
||||||
|
|
||||||
// Récupération des infos utilisateur
|
|
||||||
const userResponse = await fetch("https://discord.com/api/users/@me", {
|
|
||||||
headers: { Authorization: `Bearer ${accessToken}` },
|
|
||||||
});
|
|
||||||
const user = await userResponse.json();
|
|
||||||
|
|
||||||
// Récupérer la liste des guilds
|
|
||||||
const guildsResponse = await fetch("https://discord.com/api/users/@me/guilds", {
|
|
||||||
headers: { Authorization: `Bearer ${accessToken}` },
|
|
||||||
});
|
|
||||||
const guilds = await guildsResponse.json();
|
|
||||||
|
|
||||||
// Stocker l'utilisateur dans la session
|
|
||||||
req.session.user = user;
|
|
||||||
req.session.guilds = guilds;
|
|
||||||
|
|
||||||
// Rediriger vers la page HTML
|
|
||||||
res.redirect("/dashboard");
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
res.send("Erreur lors de la connexion Discord !");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/logout", (req, res) => {
|
|
||||||
req.session.destroy();
|
|
||||||
res.redirect("/");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// --- API pour récupérer l'objet user côté front ---
|
|
||||||
app.get("/api/user", (req, res) => {
|
|
||||||
if (req.session.user) {
|
|
||||||
res.json(req.session.user);
|
|
||||||
} else {
|
|
||||||
res.status(401).json({ error: "Utilisateur non connecté" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/api/guilds", (req, res) => {
|
|
||||||
const userGuilds = req.session.guilds; // toutes les guilds de l'utilisateur
|
|
||||||
if (!userGuilds) return res.status(401).json({ error: "Utilisateur non connecté" });
|
|
||||||
|
|
||||||
// Liste des guilds où le bot est présent
|
|
||||||
const botGuildIds = client.guilds.cache.map(g => g.id);
|
|
||||||
|
|
||||||
// Filtrer : bot présent + admin
|
|
||||||
const validGuilds = userGuilds.filter(g => {
|
|
||||||
const hasAdmin = (g.permissions & 0x8) === 0x8; // flag admin
|
|
||||||
const botHere = botGuildIds.includes(g.id);
|
|
||||||
return hasAdmin && botHere;
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json(validGuilds);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/invite-bot", (req, res) => {
|
app.get("/invite-bot", (req, res) => {
|
||||||
const permissions = 8; // Permissions administrateur
|
const permissions = 8; // Permissions administrateur
|
||||||
@@ -142,7 +57,6 @@ app.get("/invite-bot", (req, res) => {
|
|||||||
res.json({ url });
|
res.json({ url });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Servir le dashboard par serveur
|
// Servir le dashboard par serveur
|
||||||
app.get("/guild/:guildId", (req, res) => {
|
app.get("/guild/:guildId", (req, res) => {
|
||||||
const guildId = req.params.guildId;
|
const guildId = req.params.guildId;
|
||||||
@@ -159,150 +73,6 @@ app.get("/guild/:guildId", (req, res) => {
|
|||||||
res.sendFile(path.join(__dirname, "public", "guild.html"));
|
res.sendFile(path.join(__dirname, "public", "guild.html"));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// API pour sauvegarder la configuration de bienvenue
|
|
||||||
app.post("/api/bot/save-welcome-config", express.json(), (req, res) => {
|
|
||||||
const { guildId, channelId, welcomeEnabled, welcomeMessage } = req.body;
|
|
||||||
|
|
||||||
if (!req.session.guilds) {
|
|
||||||
return res.status(401).json({ success: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAdmin = req.session.guilds.find(
|
|
||||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isAdmin) {
|
|
||||||
return res.status(403).json({ success: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
db.run(
|
|
||||||
`
|
|
||||||
INSERT INTO welcome_config (guild_id, channel_id, enabled, message)
|
|
||||||
VALUES (?, ?, ?, ?)
|
|
||||||
ON CONFLICT(guild_id)
|
|
||||||
DO UPDATE SET channel_id = ?, enabled = ?, message = ?
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
guildId,
|
|
||||||
channelId,
|
|
||||||
welcomeEnabled ? 1 : 0,
|
|
||||||
welcomeMessage,
|
|
||||||
channelId,
|
|
||||||
welcomeEnabled ? 1 : 0,
|
|
||||||
welcomeMessage
|
|
||||||
],
|
|
||||||
err => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return res.status(500).json({ success: false });
|
|
||||||
}
|
|
||||||
res.json({ success: true });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/api/bot/get-welcome-config/:guildId", (req, res) => {
|
|
||||||
const { guildId } = req.params;
|
|
||||||
|
|
||||||
db.get(
|
|
||||||
"SELECT enabled, channel_id, message FROM welcome_config WHERE guild_id = ?",
|
|
||||||
[guildId],
|
|
||||||
(err, row) => {
|
|
||||||
if (err || !row) {
|
|
||||||
return res.json({ enabled: false, channelId: null, message: "" });
|
|
||||||
}
|
|
||||||
res.json({
|
|
||||||
enabled: !!row.enabled,
|
|
||||||
channelId: row.channel_id,
|
|
||||||
message: row.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.post("/api/bot/save-goodbye-config", express.json(), (req, res) => {
|
|
||||||
const { guildId, channelId, goodbyeEnabled, goodbyeMessage } = req.body;
|
|
||||||
|
|
||||||
if (!req.session.guilds) {
|
|
||||||
return res.status(401).json({ success: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
const isAdmin = req.session.guilds.find(
|
|
||||||
g => g.id === guildId && (BigInt(g.permissions) & 0x8n) === 0x8n
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!isAdmin) {
|
|
||||||
return res.status(403).json({ success: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
db.run(
|
|
||||||
`
|
|
||||||
INSERT INTO goodbye_config (guild_id, channel_id, enabled, message)
|
|
||||||
VALUES (?, ?, ?, ?)
|
|
||||||
ON CONFLICT(guild_id)
|
|
||||||
DO UPDATE SET channel_id = ?, enabled = ?, message = ?
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
guildId,
|
|
||||||
channelId,
|
|
||||||
goodbyeEnabled ? 1 : 0,
|
|
||||||
goodbyeMessage,
|
|
||||||
channelId,
|
|
||||||
goodbyeEnabled ? 1 : 0,
|
|
||||||
goodbyeMessage
|
|
||||||
],
|
|
||||||
err => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return res.status(500).json({ success: false });
|
|
||||||
}
|
|
||||||
res.json({ success: true });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/api/bot/get-goodbye-config/:guildId", (req, res) => {
|
|
||||||
const { guildId } = req.params;
|
|
||||||
|
|
||||||
db.get(
|
|
||||||
"SELECT enabled, channel_id, message FROM goodbye_config WHERE guild_id = ?",
|
|
||||||
[guildId],
|
|
||||||
(err, row) => {
|
|
||||||
if (err || !row) {
|
|
||||||
return res.json({ enabled: false, channelId: null, message: "" });
|
|
||||||
}
|
|
||||||
res.json({
|
|
||||||
enabled: !!row.enabled,
|
|
||||||
channelId: row.channel_id,
|
|
||||||
message: row.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/api/bot/get-text-channels/:guildId", (req, res) => {
|
|
||||||
const { guildId } = req.params;
|
|
||||||
const guild = client.guilds.cache.get(guildId);
|
|
||||||
if (!guild) {
|
|
||||||
return res.status(404).json({ error: "Serveur non trouvé" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const channels = guild.channels.cache
|
|
||||||
.filter(channel => channel.isTextBased())
|
|
||||||
.map(channel => ({
|
|
||||||
id: channel.id,
|
|
||||||
name: channel.name
|
|
||||||
}));
|
|
||||||
|
|
||||||
res.json(channels);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
app.get("/dashboard", (req, res) => {
|
app.get("/dashboard", (req, res) => {
|
||||||
if (!req.session.user) {
|
if (!req.session.user) {
|
||||||
return res.redirect("/auth/discord");
|
return res.redirect("/auth/discord");
|
||||||
@@ -313,4 +83,4 @@ app.get("/dashboard", (req, res) => {
|
|||||||
|
|
||||||
|
|
||||||
// --- Lancement du serveur ---
|
// --- Lancement du serveur ---
|
||||||
app.listen(PORT, () => console.log(`Serveur lancé sur http://localhost:${PORT}`));
|
app.listen(PORT, () => console.log(`Serveur lancé sur http://localhost:${PORT}`));
|
||||||
Reference in New Issue
Block a user