mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-09-11 05:46:28 +02:00
Add Discord bot, reactivate team-assign
This commit is contained in:
@@ -10,7 +10,7 @@ import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
||||
// import { AskHosting, fetchFor as fetchForHosting } from "./AskHosting"
|
||||
// import { AskMeals, fetchFor as fetchForMeals } from "./AskMeals"
|
||||
// import { AskPersonalInfo, fetchFor as fetchForPersonalInfo } from "./AskPersonalInfo"
|
||||
// import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
||||
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
||||
// import {
|
||||
// AskParticipationDetails,
|
||||
// fetchFor as fetchForParticipationDetails,
|
||||
@@ -27,7 +27,7 @@ const Asks = (): JSX.Element | null => {
|
||||
AskDiscord(asks, 5)
|
||||
|
||||
AskDayWishes(asks, 10)
|
||||
// AskTeamWishes(asks, 11)
|
||||
AskTeamWishes(asks, 11)
|
||||
// AskParticipationDetails(asks, 12)
|
||||
// AskPersonalInfo(asks, 15)
|
||||
// AskHosting(asks, 20)
|
||||
@@ -72,7 +72,7 @@ export const fetchFor = [
|
||||
...fetchForDayWishes,
|
||||
// ...fetchForHosting,
|
||||
// ...fetchForMeals,
|
||||
// ...fetchForTeamWishes,
|
||||
...fetchForTeamWishes,
|
||||
// ...fetchForParticipationDetails,
|
||||
// ...fetchForPersonalInfo,
|
||||
]
|
||||
|
@@ -29,7 +29,7 @@ const LoginForm = (): JSX.Element => {
|
||||
return (
|
||||
<form>
|
||||
<div className={styles.loginIntro} key="login-intro">
|
||||
Si tu es bénévole, connecte-toi pour accéder à ton espace.
|
||||
Si tu es bénévole ou que tu l'as déjà été, connecte-toi pour accéder à ton espace.
|
||||
</div>
|
||||
<div className={styles.formLine} key="line-email">
|
||||
<label htmlFor="email">Email</label>
|
||||
|
@@ -2,10 +2,10 @@ import React, { memo } from "react"
|
||||
import { useSelector } from "react-redux"
|
||||
import styles from "./styles.module.scss"
|
||||
import TeamItem from "./TeamItem"
|
||||
import { selectSortedActiveTeams } from "../../store/teamList"
|
||||
import { selectSortedTeams } from "../../store/teamList"
|
||||
|
||||
const TeamList: React.FC = (): JSX.Element | null => {
|
||||
const teams = useSelector(selectSortedActiveTeams)
|
||||
const teams = useSelector(selectSortedTeams)
|
||||
if (!teams || teams.length === 0) return null
|
||||
|
||||
return (
|
||||
|
@@ -7,8 +7,8 @@ import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
||||
// import MealsFormModal from "./MealsForm/MealsFormModal"
|
||||
// import ParticipationDetails from "./ParticipationDetails/ParticipationDetails"
|
||||
// import ParticipationDetailsFormModal from "./ParticipationDetailsForm/ParticipationDetailsFormModal"
|
||||
// import TeamWishes from "./TeamWishes/TeamWishes"
|
||||
// import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
|
||||
import TeamWishes from "./TeamWishes/TeamWishes"
|
||||
import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
|
||||
// import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
|
||||
import withUserConnected from "../../utils/withUserConnected"
|
||||
import ContentTitle from "../ui/Content/ContentTitle"
|
||||
@@ -16,7 +16,7 @@ import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm
|
||||
// import { fetchFor as fetchForHostingForm } from "./HostingForm/HostingForm"
|
||||
// import { fetchFor as fetchForMealsForm } from "./MealsForm/MealsForm"
|
||||
// import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
|
||||
// import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
|
||||
import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
|
||||
import { fetchFor as fetchForPersonalInfoForm } from "./PersonalInfoForm/PersonalInfoForm"
|
||||
import PersonalInfo from "./PersonalInfo/PersonalInfo"
|
||||
import PersonalInfoFormModal from "./PersonalInfoForm/PersonalInfoFormModal"
|
||||
@@ -43,10 +43,10 @@ const Board: FC = (): JSX.Element => (
|
||||
<DayWishes />
|
||||
<DayWishesFormModal />
|
||||
{/* <ParticipationDetails />
|
||||
<ParticipationDetailsFormModal />
|
||||
<ParticipationDetailsFormModal /> */}
|
||||
<TeamWishes />
|
||||
<TeamWishesFormModal />
|
||||
<VolunteerTeam />
|
||||
{/* <VolunteerTeam />
|
||||
<Hosting />
|
||||
<HostingFormModal />
|
||||
<Meals />
|
||||
@@ -66,5 +66,5 @@ export const fetchFor = [
|
||||
// ...fetchForHostingForm,
|
||||
// ...fetchForMealsForm,
|
||||
// ...fetchForParticipationDetailsForm,
|
||||
// ...fetchForTeamWishesForm,
|
||||
...fetchForTeamWishesForm,
|
||||
]
|
||||
|
@@ -38,6 +38,12 @@ const DayWishes: FC = (): JSX.Element | null => {
|
||||
Je <b>ne sais pas encore</b> si je participerai à PeL 2023
|
||||
</div>
|
||||
)}
|
||||
{participation === "à distance" && (
|
||||
<div className={styles.participationLabel}>
|
||||
Je <b className={styles.yesParticipation}>participerai</b> à PeL 2023 ! Sans y
|
||||
être pendant le weekend.
|
||||
</div>
|
||||
)}
|
||||
{participation === "inconnu" && (
|
||||
<div className={styles.lineEmpty}>
|
||||
Participation à PeL 2023{" "}
|
||||
|
@@ -16,4 +16,5 @@ export default {
|
||||
],
|
||||
},
|
||||
DEV_JWT_SECRET: "fakeqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!",
|
||||
DEV_DISCORD_TOKEN: "fakeqA6uF#msq2312bebf2FLFn4XzWQ6dttXSJwBX#?gL2JWf!",
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ import AsyncTeamAssignment, { loadData as loadTeamAssignmentData } from "../page
|
||||
import AsyncRegisterPage, { loadData as loadRegisterPage } from "../pages/Register"
|
||||
import AsyncKnowledge, { loadData as loadKnowledgeData } from "../pages/Knowledge"
|
||||
// import AsyncLoans, { loadData as loadLoansData } from "../pages/Loans"
|
||||
// import AsyncLoaning, { loadData as loadLoaningData } from "../pages/Loaning"
|
||||
import AsyncLoaning, { loadData as loadLoaningData } from "../pages/Loaning"
|
||||
import AsyncKnowledgeCards, { loadData as loadCardKnowledgeData } from "../pages/KnowledgeCards"
|
||||
import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams"
|
||||
import AsyncBoard, { loadData as loadBoardData } from "../pages/Board"
|
||||
@@ -51,11 +51,11 @@ export default [
|
||||
// component: AsyncLoans,
|
||||
// loadData: loadLoansData,
|
||||
// },
|
||||
// {
|
||||
// path: "/emprunter",
|
||||
// component: AsyncLoaning,
|
||||
// loadData: loadLoaningData,
|
||||
// },
|
||||
{
|
||||
path: "/emprunter",
|
||||
component: AsyncLoaning,
|
||||
loadData: loadLoaningData,
|
||||
},
|
||||
{
|
||||
path: "/fiches",
|
||||
component: AsyncKnowledgeCards,
|
||||
|
405
src/server/discordBot.ts
Normal file
405
src/server/discordBot.ts
Normal file
@@ -0,0 +1,405 @@
|
||||
import _ from "lodash"
|
||||
import {
|
||||
Client,
|
||||
GatewayIntentBits,
|
||||
Collection,
|
||||
Events,
|
||||
SlashCommandBuilder,
|
||||
CommandInteraction,
|
||||
/* REST, Routes, */ Partials,
|
||||
MessageReaction,
|
||||
PartialMessageReaction,
|
||||
Guild,
|
||||
User,
|
||||
PartialUser,
|
||||
} from "discord.js"
|
||||
import { promises as fs, constants } from "fs"
|
||||
import path from "path"
|
||||
|
||||
import { translationVolunteer, Volunteer, VolunteerWithoutId } from "../services/volunteers"
|
||||
import {
|
||||
translationDiscordRoles,
|
||||
DiscordRole,
|
||||
DiscordRoleWithoutId,
|
||||
} from "../services/discordRoles"
|
||||
import { getSheet } from "./gsheets/accessors"
|
||||
import config from "../config"
|
||||
|
||||
let cachedToken: string
|
||||
// let cachedClientId: string
|
||||
let cachedGuildId: string
|
||||
const CREDS_PATH = path.resolve(process.cwd(), "access/discordToken.json")
|
||||
getCreds() // Necessary until we can make async express middleware
|
||||
|
||||
type Command = {
|
||||
data: SlashCommandBuilder
|
||||
execute: (interaction: CommandInteraction) => Promise<void>
|
||||
}
|
||||
const commands: Collection<string, Command> = new Collection()
|
||||
|
||||
// const userCommand: Command = {
|
||||
// data: new SlashCommandBuilder()
|
||||
// .setName('user')
|
||||
// .setDescription('Provides information about the user.'),
|
||||
// async execute(interaction: CommandInteraction) {
|
||||
|
||||
// const { commandName } = interaction
|
||||
|
||||
// if (commandName === 'user') {
|
||||
// const message = await interaction.reply({ content: 'You can react with Unicode emojis!', fetchReply: true })
|
||||
// message.react('😄')
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
|
||||
let hasDiscordAccessReturn: boolean | undefined
|
||||
export async function hasDiscordAccess(): Promise<boolean> {
|
||||
if (hasDiscordAccessReturn !== undefined) {
|
||||
return hasDiscordAccessReturn
|
||||
}
|
||||
try {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
await fs.access(CREDS_PATH, constants.R_OK | constants.W_OK)
|
||||
hasDiscordAccessReturn = true
|
||||
} catch {
|
||||
hasDiscordAccessReturn = false
|
||||
}
|
||||
return hasDiscordAccessReturn
|
||||
}
|
||||
|
||||
// export async function discordRegisterCommands(): Promise<void> {
|
||||
// if (!__REGISTER_DISCORD_COMMANDS__) {
|
||||
// return
|
||||
// }
|
||||
|
||||
// if (!(await hasDiscordAccess())) {
|
||||
// console.error(`Discord bot: no creds found, not running bot`)
|
||||
// return
|
||||
// }
|
||||
|
||||
// await getCreds()
|
||||
|
||||
// const commandsToRegister = []
|
||||
// commandsToRegister.push(userCommand.data.toJSON())
|
||||
|
||||
// const rest = new REST({ version: '10' }).setToken(cachedToken)
|
||||
|
||||
// try {
|
||||
// await rest.put(
|
||||
// Routes.applicationGuildCommands(cachedClientId, cachedGuildId),
|
||||
// { body: commandsToRegister },
|
||||
// )
|
||||
// } catch (error) {
|
||||
// console.error(error)
|
||||
// }
|
||||
|
||||
// process.exit()
|
||||
// }
|
||||
|
||||
export async function discordBot(): Promise<void> {
|
||||
try {
|
||||
if (__REGISTER_DISCORD_COMMANDS__) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!(await hasDiscordAccess())) {
|
||||
console.error(`Discord bot: no creds found, not running bot`)
|
||||
return
|
||||
}
|
||||
await getCreds()
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.GuildMessageReactions,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.MessageContent,
|
||||
],
|
||||
partials: [Partials.Message, Partials.Channel, Partials.Reaction],
|
||||
})
|
||||
|
||||
// commands.set(userCommand.data.name, userCommand)
|
||||
|
||||
client.once(Events.ClientReady, (localClient) => {
|
||||
setInterval(() => setBotReactions(localClient), 5 * 60 * 1000)
|
||||
setTimeout(() => setBotReactions(localClient), 20 * 1000)
|
||||
|
||||
setInterval(() => setAllRoles(localClient), 5 * 60 * 1000)
|
||||
setTimeout(() => setAllRoles(localClient), 5 * 1000)
|
||||
})
|
||||
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return
|
||||
|
||||
const command = commands.get(interaction.commandName)
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
})
|
||||
|
||||
client.on(Events.MessageReactionAdd, async (reaction, user) => {
|
||||
await fetchPartial(reaction)
|
||||
|
||||
await setRolesFromEmoji(client, user, reaction, "add")
|
||||
})
|
||||
|
||||
client.on(Events.MessageReactionRemove, async (reaction, user) => {
|
||||
await fetchPartial(reaction)
|
||||
|
||||
await setRolesFromEmoji(client, user, reaction, "remove")
|
||||
})
|
||||
|
||||
client.login(cachedToken)
|
||||
} catch (error) {
|
||||
console.error("Discord error", error)
|
||||
}
|
||||
}
|
||||
|
||||
async function setBotReactions(client: Client) {
|
||||
try {
|
||||
const discordRolesSheet = await getSheet<DiscordRoleWithoutId, DiscordRole>(
|
||||
"DiscordRoles",
|
||||
new DiscordRole(),
|
||||
translationDiscordRoles
|
||||
)
|
||||
const discordRolesList = await discordRolesSheet.getList()
|
||||
if (!discordRolesList) {
|
||||
return
|
||||
}
|
||||
|
||||
client.channels.cache.each(async (channel) => {
|
||||
if (!channel.isTextBased()) {
|
||||
return
|
||||
}
|
||||
|
||||
discordRolesList.forEach(async (discordRole: DiscordRole) => {
|
||||
let message
|
||||
try {
|
||||
message = await channel.messages.fetch(discordRole.messageId)
|
||||
} catch (error) {
|
||||
return
|
||||
}
|
||||
|
||||
const reaction = message.reactions.cache.find(
|
||||
(r) => r.emoji.name === discordRole.emoji
|
||||
)
|
||||
if (reaction) {
|
||||
return
|
||||
}
|
||||
await message.react(discordRole.emoji)
|
||||
})
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error in setBotReactions", error)
|
||||
}
|
||||
}
|
||||
|
||||
async function setAllRoles(client: Client) {
|
||||
try {
|
||||
const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>(
|
||||
"Volunteers",
|
||||
new Volunteer(),
|
||||
translationVolunteer
|
||||
)
|
||||
const volunteerList = await volunteerSheet.getList()
|
||||
if (!volunteerList) {
|
||||
return
|
||||
}
|
||||
|
||||
const volunteerByDiscordId = _.mapKeys(volunteerList, (v) => v.discordId.toString())
|
||||
|
||||
const guild = await client.guilds.fetch(cachedGuildId)
|
||||
|
||||
if (!guild || !guild.members.cache) {
|
||||
return
|
||||
}
|
||||
|
||||
// Set (maybe) volunteer roles
|
||||
|
||||
const volunteerRoleIds: { [key: string]: string } = _.mapValues(
|
||||
{
|
||||
oui: "Bénévole",
|
||||
"peut-etre": "Bénévole incertain",
|
||||
"à distance": "Bénévole à distance",
|
||||
non: "",
|
||||
},
|
||||
(v) => (_.isEmpty(v) ? "" : guild.roles.cache.find((role) => role.name === v)?.id || "")
|
||||
)
|
||||
|
||||
await setVolunteersRoles(
|
||||
guild,
|
||||
volunteerByDiscordId,
|
||||
volunteerRoleIds,
|
||||
(volunteer: Volunteer) => volunteer.active
|
||||
)
|
||||
|
||||
// Set Team- and Référent- roles
|
||||
|
||||
const teamIds = {
|
||||
"1": "accueil",
|
||||
"5": "paillante",
|
||||
"21": "photo",
|
||||
"4": "tournois",
|
||||
// "19": "exposants-asso", ignored because it's mixed with volunteers of last edition
|
||||
"2": "jav",
|
||||
"18": "jeux-xl",
|
||||
"27": "ring",
|
||||
"8": "coindespetitsjoueurs",
|
||||
"23": "jeuxdelires",
|
||||
"3": "jdd",
|
||||
"9": "figurines",
|
||||
"10": "jdr",
|
||||
"28": "jeuxhistoire",
|
||||
"6": "évènements",
|
||||
"29": "presse",
|
||||
"30": "initiation-18xx",
|
||||
"0": "",
|
||||
}
|
||||
|
||||
const teamRoleIds: { [key: string]: string } = _.mapValues(teamIds, (v) =>
|
||||
_.isEmpty(v)
|
||||
? ""
|
||||
: guild.roles.cache.find((role) => role.name === `Team-${v}`)?.id || ""
|
||||
)
|
||||
|
||||
await setVolunteersRoles(guild, volunteerByDiscordId, teamRoleIds, (volunteer: Volunteer) =>
|
||||
_.includes(["oui", "peut-etre", "à distance"], volunteer.active)
|
||||
? `${volunteer.team}`
|
||||
: ""
|
||||
)
|
||||
|
||||
const referentRoleIds: { [key: string]: string } = _.mapValues(teamIds, (v) =>
|
||||
_.isEmpty(v)
|
||||
? ""
|
||||
: guild.roles.cache.find((role) => role.name === `Référent-${v}`)?.id || ""
|
||||
)
|
||||
|
||||
await setVolunteersRoles(
|
||||
guild,
|
||||
volunteerByDiscordId,
|
||||
referentRoleIds,
|
||||
(volunteer: Volunteer) =>
|
||||
_.includes(["oui", "peut-etre", "à distance"], volunteer.active) &&
|
||||
volunteer.roles.includes("référent")
|
||||
? `${volunteer.team}`
|
||||
: "0"
|
||||
)
|
||||
} catch (error) {
|
||||
console.error("Error in setAllRoles", error)
|
||||
}
|
||||
}
|
||||
|
||||
async function setVolunteersRoles(
|
||||
guild: Guild,
|
||||
volunteerByDiscordId: { [key: string]: Volunteer },
|
||||
volunteerRoleIds: { [key: string]: string },
|
||||
funcKey: (volunteer: Volunteer) => string
|
||||
) {
|
||||
const members = await guild.members.fetch()
|
||||
members.each(async (member) => {
|
||||
const volunteer = volunteerByDiscordId[member.id]
|
||||
if (!volunteer) {
|
||||
return
|
||||
}
|
||||
|
||||
_.forOwn(volunteerRoleIds, (roleId, active) => {
|
||||
if (!roleId) {
|
||||
return
|
||||
}
|
||||
|
||||
const shouldHaveRole = active === funcKey(volunteer)
|
||||
const hasRole = member.roles.cache.has(roleId)
|
||||
|
||||
if (hasRole && !shouldHaveRole) {
|
||||
member.roles.remove(roleId)
|
||||
} else if (!hasRole && shouldHaveRole) {
|
||||
member.roles.add(roleId)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function setRolesFromEmoji(
|
||||
client: Client,
|
||||
user: User | PartialUser,
|
||||
reaction: MessageReaction | PartialMessageReaction,
|
||||
action: "add" | "remove"
|
||||
) {
|
||||
const discordRolesSheet = await getSheet<DiscordRoleWithoutId, DiscordRole>(
|
||||
"DiscordRoles",
|
||||
new DiscordRole(),
|
||||
translationDiscordRoles
|
||||
)
|
||||
const discordRolesList = await discordRolesSheet.getList()
|
||||
if (!discordRolesList) {
|
||||
return
|
||||
}
|
||||
|
||||
await client.guilds.fetch()
|
||||
const guild = client.guilds.resolve(cachedGuildId)
|
||||
|
||||
if (!guild || !guild.members.cache) {
|
||||
return
|
||||
}
|
||||
|
||||
discordRolesList.forEach(async (discordRole: DiscordRole) => {
|
||||
if (
|
||||
reaction.message.id === discordRole.messageId &&
|
||||
reaction.emoji.name === discordRole.emoji
|
||||
) {
|
||||
const roleId = guild.roles.cache.find((role) => role.name === discordRole.role)
|
||||
if (!roleId) {
|
||||
return
|
||||
}
|
||||
|
||||
const member = guild.members.cache.find((m) => m.id === user.id)
|
||||
if (!member) {
|
||||
return
|
||||
}
|
||||
await member.fetch()
|
||||
if (action === "add") {
|
||||
member.roles.add(roleId)
|
||||
} else if (action === "remove") {
|
||||
member.roles.remove(roleId)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function fetchPartial(reaction: MessageReaction | PartialMessageReaction): Promise<boolean> {
|
||||
if (reaction.partial) {
|
||||
try {
|
||||
await reaction.fetch()
|
||||
} catch (error) {
|
||||
console.error("Something went wrong when fetching the message", error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function getCreds(): Promise<void> {
|
||||
if (!cachedToken) {
|
||||
try {
|
||||
const credsContent = await fs.readFile(CREDS_PATH)
|
||||
const parsedCreds = credsContent && JSON.parse(credsContent.toString())
|
||||
if (!parsedCreds) {
|
||||
return
|
||||
}
|
||||
cachedToken = parsedCreds.token
|
||||
// cachedClientId = parsedCreds.clientId
|
||||
cachedGuildId = parsedCreds.guildId
|
||||
} catch (e: any) {
|
||||
cachedToken = config.DEV_DISCORD_TOKEN
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,8 +11,8 @@ export { SheetNames } from "./localDb"
|
||||
|
||||
const CRED_PATH = path.resolve(process.cwd(), "access/gsheets.json")
|
||||
|
||||
const REMOTE_UPDATE_DELAY = 40000
|
||||
const DELAY_BETWEEN_ATTEMPTS = 10000
|
||||
const REMOTE_UPDATE_DELAY = 120000
|
||||
const DELAY_BETWEEN_ATTEMPTS = 30000
|
||||
const DELAY_BETWEEN_FIRST_LOAD = 1500
|
||||
|
||||
let creds: string | undefined | null
|
||||
@@ -59,7 +59,7 @@ export async function getSheet<
|
||||
sheet = new Sheet<ElementNoId, Element>(sheetName, specimen, translation)
|
||||
await sheet.waitForFirstLoad()
|
||||
sheetList[sheetName] = sheet
|
||||
setInterval(() => sheet.dbUpdate(), REMOTE_UPDATE_DELAY)
|
||||
setInterval(() => sheet.dbUpdate(), REMOTE_UPDATE_DELAY * (1 + Math.random() / 10))
|
||||
} else {
|
||||
sheet = sheetList[sheetName] as Sheet<ElementNoId, Element>
|
||||
}
|
||||
@@ -265,6 +265,7 @@ export class Sheet<
|
||||
}
|
||||
|
||||
await tryNTimesVoidReturn(async () => {
|
||||
console.log(`dbSaveAsync on ${this.name} at ${new Date()}`)
|
||||
// Load sheet into an array of objects
|
||||
const rows = await sheet.getRows()
|
||||
if (!rows[0]) {
|
||||
@@ -317,6 +318,7 @@ export class Sheet<
|
||||
await rows[rowToDelete].delete()
|
||||
}
|
||||
}
|
||||
console.log(`dbSaveAsync successful on ${this.name} at ${new Date()}`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -330,6 +332,7 @@ export class Sheet<
|
||||
|
||||
await tryNTimesVoidReturn(async () => {
|
||||
// Load sheet into an array of objects
|
||||
console.log(`dbLoadAsync on ${this.name} at ${new Date()}`)
|
||||
const rows = (await sheet.getRows()) as StringifiedElement[]
|
||||
const elements: Element[] = []
|
||||
if (!rows[0]) {
|
||||
@@ -357,6 +360,7 @@ export class Sheet<
|
||||
})
|
||||
|
||||
this._state = elements
|
||||
console.log(`dbLoadAsync successful on ${this.name} at ${new Date()}`)
|
||||
})
|
||||
}
|
||||
|
||||
|
14
src/server/gsheets/discordRoles.ts
Normal file
14
src/server/gsheets/discordRoles.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import ExpressAccessors from "./expressAccessors"
|
||||
import {
|
||||
DiscordRole,
|
||||
DiscordRoleWithoutId,
|
||||
translationDiscordRoles,
|
||||
} from "../../services/discordRoles"
|
||||
|
||||
const expressAccessor = new ExpressAccessors<DiscordRoleWithoutId, DiscordRole>(
|
||||
"DiscordRoles",
|
||||
new DiscordRole(),
|
||||
translationDiscordRoles
|
||||
)
|
||||
|
||||
export const discordRolesListGet = expressAccessor.listGet()
|
@@ -16,6 +16,8 @@ export class SheetNames {
|
||||
|
||||
Boxes = "Boîtes"
|
||||
|
||||
DiscordRoles = "Rôles Discord"
|
||||
|
||||
Games = "Jeux"
|
||||
|
||||
Miscs = "Divers"
|
||||
|
@@ -50,6 +50,7 @@ import {
|
||||
import { wishListGet, wishAdd } from "./gsheets/wishes"
|
||||
import config from "../config"
|
||||
import { notificationsSubscribe, notificationMain } from "./notifications"
|
||||
import { /* discordRegisterCommands, */ discordBot, hasDiscordAccess } from "./discordBot"
|
||||
import checkAccess from "./checkAccess"
|
||||
import { hasGSheetsAccess } from "./gsheets/accessors"
|
||||
import { addStatus, showStatusAt } from "./status"
|
||||
@@ -64,6 +65,9 @@ checkAccess()
|
||||
|
||||
notificationMain()
|
||||
|
||||
// discordRegisterCommands()
|
||||
discordBot()
|
||||
|
||||
const app = express()
|
||||
|
||||
// Allow receiving big images
|
||||
@@ -243,6 +247,14 @@ if (hasPushNotifAccess) {
|
||||
addStatus("Push notif:", chalk.blue(`🚧 offline, simulated`))
|
||||
}
|
||||
|
||||
hasDiscordAccess().then((hasApiAccess: boolean) => {
|
||||
if (hasApiAccess) {
|
||||
addStatus("Discord bot:", chalk.green(`✅ online through discord.js`))
|
||||
} else {
|
||||
addStatus("Discord bot:", chalk.blue(`🚧 no creds, disabled`))
|
||||
}
|
||||
})
|
||||
|
||||
hasSecret().then((has: boolean) => {
|
||||
if (has) {
|
||||
addStatus("JWT secret:", chalk.green(`✅ prod private one from file`))
|
||||
|
@@ -108,7 +108,9 @@ async function notifyAboutAnnouncement(): Promise<void> {
|
||||
}
|
||||
|
||||
const audience = volunteerList.filter(
|
||||
(v) => v.acceptsNotifs === "oui" && (v.active === "oui" || v.active === "peut-etre")
|
||||
(v) =>
|
||||
v.acceptsNotifs === "oui" &&
|
||||
(v.active === "oui" || v.active === "peut-etre" || v.active === "à distance")
|
||||
)
|
||||
|
||||
console.error(
|
||||
|
28
src/services/discordRoles.ts
Normal file
28
src/services/discordRoles.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
export class DiscordRole {
|
||||
id = 0
|
||||
|
||||
messageId = ""
|
||||
|
||||
emoji = ""
|
||||
|
||||
role = ""
|
||||
}
|
||||
|
||||
export const translationDiscordRoles: { [k in keyof DiscordRole]: string } = {
|
||||
id: "id",
|
||||
messageId: "messageId",
|
||||
emoji: "emoji",
|
||||
role: "rôle",
|
||||
}
|
||||
|
||||
export const elementName = "DiscordRoles"
|
||||
|
||||
export type DiscordRoleWithoutId = Omit<DiscordRole, "id">
|
||||
|
||||
export interface DiscordRole {
|
||||
id: DiscordRole["id"]
|
||||
messageId: DiscordRole["messageId"]
|
||||
emoji: DiscordRole["emoji"]
|
||||
role: DiscordRole["role"]
|
||||
}
|
@@ -59,6 +59,10 @@ export const selectTeamList = createSelector(
|
||||
}
|
||||
)
|
||||
|
||||
export const selectSortedTeams = createSelector(selectTeamList, (teams) =>
|
||||
[...teams].sort((a, b) => get(a, "order", 0) - get(b, "order", 0))
|
||||
)
|
||||
|
||||
export const selectSortedActiveTeams = createSelector(selectTeamList, (teams) =>
|
||||
[...teams.filter((team) => get(team, "status") === "active")].sort(
|
||||
(a, b) => get(a, "order", 0) - get(b, "order", 0)
|
||||
|
2
src/types/index.d.ts
vendored
2
src/types/index.d.ts
vendored
@@ -2,6 +2,7 @@ declare const __CLIENT__: boolean
|
||||
declare const __SERVER__: boolean
|
||||
declare const __DEV__: boolean
|
||||
declare const __LOCAL__: boolean
|
||||
declare const __REGISTER_DISCORD_COMMANDS__: boolean
|
||||
declare const __TEST__: boolean
|
||||
|
||||
declare module "*.svg"
|
||||
@@ -19,6 +20,7 @@ declare namespace NodeJS {
|
||||
__SERVER__: boolean
|
||||
__DEV__: boolean
|
||||
__LOCAL__: boolean
|
||||
__REGISTER_DISCORD_COMMANDS__: boolean
|
||||
__TEST__: boolean
|
||||
$RefreshReg$: () => void
|
||||
$RefreshSig$$: () => void
|
||||
|
Reference in New Issue
Block a user