Skip to content

Commit 927f045

Browse files
authored
add /add-regular-member-role and /revoke-regular-member-role for moderators (#87)
Signed-off-by: GitHub <[email protected]>
1 parent b622d23 commit 927f045

File tree

7 files changed

+91
-28
lines changed

7 files changed

+91
-28
lines changed

apps/bot/commands/context/mark-answer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
} from '../../utils.js'
1717
import { markMessageAsSolution } from '../../db/actions/messages.js'
1818
import { env } from '../../env.js'
19-
import { tryToAssignRegularMemberRole } from '../../lib/points.js'
19+
import { tryToSetRegularMemberRole } from '../../lib/points.js'
2020

2121
export const command: ContextMenuCommand = {
2222
data: new ContextMenuCommandBuilder()
@@ -108,7 +108,7 @@ export const command: ContextMenuCommand = {
108108
interaction.targetMessage.author.id,
109109
)
110110
if (targetMember) {
111-
await tryToAssignRegularMemberRole(interactionMember)
111+
await tryToSetRegularMemberRole(interactionMember)
112112
}
113113

114114
const answeredTagId = mainChannel.availableTags.find((t) =>

apps/bot/commands/slash/give-points.ts renamed to apps/bot/commands/slash/add-regular-member-role.ts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
import { PermissionFlagsBits, SlashCommandBuilder } from 'discord.js'
22
import { SlashCommand } from '../types.js'
3-
import { addDirectPointsToUser, syncUser } from '../../db/actions/users.js'
4-
import { tryToAssignRegularMemberRole } from '../../lib/points.js'
3+
import { addFullPointsToUser, syncUser } from '../../db/actions/users.js'
4+
import { tryToSetRegularMemberRole } from '../../lib/points.js'
55

66
export const command: SlashCommand = {
77
data: new SlashCommandBuilder()
8-
.setName('give-points')
9-
.setDescription('Gives a specific amount of points to the target user')
8+
.setName('add-regular-member-role')
9+
.setDescription('Add the Regular Member role to the target user')
1010
.setDMPermission(false)
1111
.addUserOption((option) =>
1212
option
1313
.setName('user')
14-
.setDescription('The user to receive the points')
15-
.setRequired(true),
16-
)
17-
.addIntegerOption((option) =>
18-
option
19-
.setName('points')
20-
.setDescription('The amount of point to give')
14+
.setDescription('The user to receive the role')
2115
.setRequired(true),
2216
)
2317
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
2418

2519
async execute(interaction) {
2620
const user = interaction.options.getUser('user', true)
27-
const points = interaction.options.getInteger('points', true)
2821

2922
const guildMember = await interaction.guild?.members.fetch(user.id)
3023

@@ -39,8 +32,8 @@ export const command: SlashCommand = {
3932
await interaction.deferReply({ ephemeral: true })
4033

4134
await syncUser(user, guildMember)
42-
await addDirectPointsToUser(user.id, points)
43-
await tryToAssignRegularMemberRole(guildMember, true)
35+
await addFullPointsToUser(user.id)
36+
await tryToSetRegularMemberRole(guildMember, true)
4437

4538
await interaction.editReply({ content: 'Done!' })
4639
},

apps/bot/commands/slash/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import * as refreshAnswerCount from './refresh-answer-count.js'
22
import * as refreshLastActive from './refresh-last-active.js'
33
import * as lockLowEffortPost from './lock-low-effort-post.js'
44
import * as removePostAnswer from './remove-post-answer.js'
5-
import * as givePoints from './give-points.js'
5+
import * as addRegularMemberRole from './add-regular-member-role.js'
6+
import * as revokeRegularMemberRole from './revoke-regular-member-role.js'
67
import * as getAnswerCount from './get-answer-count.js'
78

89
export const slashCommands = [
910
refreshAnswerCount.command,
1011
refreshLastActive.command,
1112
lockLowEffortPost.command,
1213
removePostAnswer.command,
13-
givePoints.command,
14+
addRegularMemberRole.command,
15+
revokeRegularMemberRole.command,
1416
getAnswerCount.command,
1517
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { PermissionFlagsBits, SlashCommandBuilder } from 'discord.js'
2+
import { SlashCommand } from '../types.js'
3+
import { removeFullPointsFromUser, syncUser } from '../../db/actions/users.js'
4+
import { tryToSetRegularMemberRole } from '../../lib/points.js'
5+
6+
export const command: SlashCommand = {
7+
data: new SlashCommandBuilder()
8+
.setName('revoke-regular-member-role')
9+
.setDescription('Remove the Regular Member role from the target user')
10+
.setDMPermission(false)
11+
.addUserOption((option) =>
12+
option
13+
.setName('user')
14+
.setDescription('The user to be stripped of the role')
15+
.setRequired(true),
16+
)
17+
.setDefaultMemberPermissions(PermissionFlagsBits.ModerateMembers),
18+
19+
async execute(interaction) {
20+
const user = interaction.options.getUser('user', true)
21+
22+
const guildMember = await interaction.guild?.members.fetch(user.id)
23+
24+
if (!guildMember) {
25+
await interaction.reply({
26+
content: "I couldn't find the guild member from this user",
27+
ephemeral: true,
28+
})
29+
return
30+
}
31+
32+
await interaction.deferReply({ ephemeral: true })
33+
34+
await syncUser(user, guildMember)
35+
await removeFullPointsFromUser(user.id)
36+
await tryToSetRegularMemberRole(guildMember, true)
37+
38+
await interaction.editReply({ content: 'Done!' })
39+
},
40+
}

apps/bot/db/actions/messages.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { db, sql } from '@nextjs-forum/db/node'
33
import { addPointsToUser, removePointsFromUser, syncUser } from './users.js'
44
import { syncChannel, syncMessageChannel } from './channels.js'
55
import { updatePostLastActive } from './posts.js'
6-
import { tryToAssignRegularMemberRole } from '../../lib/points.js'
6+
import { tryToSetRegularMemberRole } from '../../lib/points.js'
77

88
export const syncMessage = async (message: Message) => {
99
const authorAsGuildMember = await message.guild?.members.fetch(
@@ -66,7 +66,7 @@ export const syncMessage = async (message: Message) => {
6666
})
6767

6868
if (authorAsGuildMember) {
69-
await tryToAssignRegularMemberRole(authorAsGuildMember)
69+
await tryToSetRegularMemberRole(authorAsGuildMember)
7070
}
7171
}
7272

apps/bot/db/actions/users.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { KyselyDB, TransactionDB, db, sql } from '@nextjs-forum/db/node'
44
import { AnimalModule, Faker, en } from '@faker-js/faker'
55
import { type CacheUser, usersCache } from '../../lib/cache.js'
66
import { env } from '../../env.js'
7-
import { POINTS_REWARDS } from '../../lib/points.js'
7+
import { POINTS_REWARDS, REQUIRED_POINTS_FOR_ROLE } from '../../lib/points.js'
88

99
const log = baseLog.extend('users')
1010

@@ -130,24 +130,40 @@ const updatePointsBySum = async (
130130
.execute()
131131
}
132132

133+
const updatePointsBySet = async (
134+
userId: string,
135+
value: number,
136+
trx: TransactionDB | KyselyDB = db,
137+
) => {
138+
await trx
139+
.updateTable('users')
140+
.where('snowflakeId', '=', userId)
141+
.set({ points: sql`LEAST(999999, ${value})` })
142+
.execute()
143+
}
144+
133145
export const addPointsToUser = async (
134146
userId: string,
135147
type: keyof typeof POINTS_REWARDS,
136148
trx: TransactionDB | KyselyDB = db,
137149
) => updatePointsBySum(userId, POINTS_REWARDS[type], trx)
138150

139-
export const addDirectPointsToUser = async (
151+
export const addFullPointsToUser = async (
140152
userId: string,
141-
value: number,
142153
trx: TransactionDB | KyselyDB = db,
143-
) => updatePointsBySum(userId, value, trx)
154+
) => updatePointsBySet(userId, REQUIRED_POINTS_FOR_ROLE, trx)
144155

145156
export const removePointsFromUser = async (
146157
userId: string,
147158
type: keyof typeof POINTS_REWARDS,
148159
trx: TransactionDB | KyselyDB = db,
149160
) => updatePointsBySum(userId, -POINTS_REWARDS[type], trx)
150161

162+
export const removeFullPointsFromUser = async (
163+
userId: string,
164+
trx: TransactionDB | KyselyDB = db,
165+
) => updatePointsBySet(userId, 0, trx)
166+
151167
export const getCorrectAnswersCount = (userId: string) => {
152168
return db
153169
.selectFrom('users')

apps/bot/lib/points.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ export const POINTS_REWARDS = {
88
question: 20,
99
answer: 50,
1010
} as const
11+
export const REQUIRED_POINTS_FOR_ROLE = 1000
1112

12-
const REQUIRED_POINTS_FOR_ROLE = 1000
1313
const USER_ROLE_SYNC_INTERVAL = 1000 * 60 * 60 // 1 hour
1414

1515
const lastUserSync = new LRUCache<string, number>({ max: 100 })
1616

17-
export const tryToAssignRegularMemberRole = async (
17+
export const tryToSetRegularMemberRole = async (
1818
member: GuildMember,
1919
skipCache: boolean = false,
2020
) => {
@@ -30,11 +30,23 @@ export const tryToAssignRegularMemberRole = async (
3030
}
3131

3232
lastUserSync.set(member.id, Date.now())
33-
if (member.roles.cache.has(env.REGULAR_MEMBER_ROLE_ID)) return
3433

3534
const user = await getUserById(member.id)
36-
if (!user || user.points < REQUIRED_POINTS_FOR_ROLE) return
35+
if (!user) return
3736

37+
const userPointsSatisfyRole = user.points >= REQUIRED_POINTS_FOR_ROLE
38+
const memberHasRole = member.roles.cache.has(env.REGULAR_MEMBER_ROLE_ID)
39+
40+
if (memberHasRole && userPointsSatisfyRole) return
41+
if (!memberHasRole && !userPointsSatisfyRole) return
42+
43+
if (memberHasRole && !userPointsSatisfyRole) {
44+
await member.roles.remove(env.REGULAR_MEMBER_ROLE_ID)
45+
// We don't need to alert the user about this.
46+
return
47+
}
48+
49+
// Now, it is !memberHasRole && userPointsSatisfyRole
3850
await member.roles.add(env.REGULAR_MEMBER_ROLE_ID)
3951
await member.send({
4052
embeds: [

0 commit comments

Comments
 (0)