-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Based off this discussion in #server-suggestions: https://discord.com/channels/782201995011817493/793224619012128779/819308969460891648 Which concerns adding channels hidden behind roles and a method through which to assign those roles This feature will allow any moderator to create a post (possibly in the welcome channel) which can be used to auto-assign roles based on which reactions a user clicks Example usage: Making a post like this: ``` !role message 📖: @ReadingRole - For people who want to view the reading channel ✍🏾: @WritingRole - For people who want to view the writing channel 👂: @ListeningRole - For people who want to view the listening channel ``` Would result in the bot auto-adding those 📖, ✍🏾 and 👂 reactions to the message. Any time a user clicks one of the reactions they are assigned to the corresponding role by the bot. They may also remove their reaction to be unnassigned from that role The usage rules currently: - The message must start with "!role message" - The reaction must be part of the standard set (no custom server emojis) - The sequence to form a role/reaction pair is: {Emoji}{colon}{Role mention} - Any line that contains the sequence is picked up and parsed - The line may include additional details after that sequence - Additional lines may be included that don't contain the sequence (these are ignored) - The message can be edited after creation to have none of these details and will still function as a role assignation message Tables added to the database: - RoleMessage - messageId - channelId - roleReactions (Array of objects:) - reactionName (emoji character code) - roleId (corresponding role for emoji) Dev dependencies added to package.json: - @babel/plugin-proposal-unicode-property-regex - Required to use Unicode property regexes - Allows us to use \p{Emoji} as a pattern to match any emoji New events added to index.js: - messageDelete - Fires whenever a message is deleted - Used to trigger the deletion of a role message from the database
- Loading branch information
Showing
6 changed files
with
184 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,9 @@ | ||
{ | ||
"plugins": ["@babel/plugin-proposal-throw-expressions"], | ||
"presets": ["@babel/preset-env"] | ||
"plugins": [ | ||
"@babel/plugin-proposal-throw-expressions", | ||
"@babel/plugin-proposal-unicode-property-regex" | ||
], | ||
"presets": [ | ||
"@babel/preset-env" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
const mongoose = require('mongoose'); | ||
|
||
const RoleMessageSchema = new mongoose.Schema({ | ||
messageId: { type: String, unique: true, required: true }, | ||
channelId: { type: String, unique: false, required: true }, | ||
roleReactions: [{ | ||
roleId: { type: String, unique: false, required: true }, | ||
reactionName: { type: String, unique: false, required: true }, | ||
}] | ||
}, { collection: 'RoleMessage' }).index({ | ||
messageId: 1, | ||
channelId: 1 | ||
}, { | ||
unique: true | ||
}); | ||
|
||
mongoose.model('RoleMessage', RoleMessageSchema); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
const StudySessionSchema = require('./StudySessionSchema'); | ||
const RoleMessageSchema = require('./RoleMessageSchema'); | ||
|
||
module.exports = { StudySessionSchema }; | ||
module.exports = { StudySessionSchema, RoleMessageSchema }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
const RoleMessage = require("mongoose").model("RoleMessage"); | ||
|
||
const COMMAND = "!role message"; | ||
|
||
function handleRoleMessage(message) { | ||
if (!isRoleMessage(message)) { | ||
return; | ||
} | ||
if (!messageAuthorIsMod(message)) { | ||
return; | ||
} | ||
const roleReactionStrings = getLinesWhichContainRoleReactionPairs(message); | ||
if (!roleReactionStrings) { | ||
return; | ||
} | ||
const roleReactions = createRoleReactionForDatabase(roleReactionStrings); | ||
if (!roleReactions || roleReactions.length === 0) { | ||
return; | ||
} | ||
|
||
createRoleReactionsForMessage(message, roleReactions); | ||
} | ||
|
||
function isRoleMessage(message) { | ||
return message.content.toLowerCase().startsWith(COMMAND); | ||
} | ||
|
||
function messageAuthorIsMod(message) { | ||
return Boolean(message.member.roles.cache.get(process.env.MODERATORS)); | ||
} | ||
|
||
function getLinesWhichContainRoleReactionPairs(message) { | ||
return message.content.split('\n').filter(function (line) { | ||
const validRoleReactionRegex = /[\p{Emoji}]{1}.*:.*<@&\d+>/u; | ||
return validRoleReactionRegex.test(line); | ||
}); | ||
} | ||
|
||
function createRoleReactionForDatabase(roleReactionStrings) { | ||
return roleReactionStrings.map(function (roleReactionString) { | ||
const reaction = roleReactionString.match(/[\p{Emoji}]{1}/u)[0]; | ||
const role = roleReactionString.split(':')[1].match(/\d+/g)[0]; | ||
return { | ||
reactionName: reaction, | ||
roleId: role | ||
} | ||
}); | ||
} | ||
|
||
function createRoleReactionsForMessage(message, roleReactions) { | ||
RoleMessage.create({ | ||
messageId: message.id, | ||
channelId: message.channel.id, | ||
roleReactions: roleReactions | ||
}).then(function () { | ||
addReactionsToMessage(message, roleReactions); | ||
}).catch(function (error) { | ||
console.log(error); | ||
}); | ||
} | ||
|
||
function addReactionsToMessage(message, roleReactions) { | ||
roleReactions.forEach(function (roleReaction) { | ||
message.react(roleReaction.reactionName).catch(function (error) { | ||
console.log(error); | ||
}); | ||
}); | ||
} | ||
|
||
function handleRolesOnReactionAdd(user, message, emoji) { | ||
RoleMessage.findOne({ | ||
messageId: message.id, | ||
channelId: message.channel.id, | ||
"roleReactions.reactionName": emoji.name | ||
}, null, {}, function (error, roleMessageModel) { | ||
if (error) { | ||
console.log(error); | ||
return; | ||
} | ||
if (!roleMessageModel) { | ||
return; | ||
} | ||
const roleReaction = getRoleReactionFromModel(emoji, roleMessageModel); | ||
addUserToRole(user, roleReaction); | ||
}).lean(); | ||
} | ||
|
||
function getRoleReactionFromModel(emoji, roleMessageModel) { | ||
return roleMessageModel.roleReactions.find(function (roleReaction) { | ||
return roleReaction.reactionName === emoji.name; | ||
}); | ||
} | ||
|
||
function addUserToRole(user, roleReaction) { | ||
user.client.guilds.cache | ||
.get(process.env.SERVER_ID).members.cache | ||
.get(user.id).roles | ||
.add(roleReaction.roleId) | ||
.catch(function (error) { | ||
console.log(error); | ||
}); | ||
} | ||
|
||
function handleRolesOnReactionRemove(user, message, emoji) { | ||
RoleMessage.findOne({ | ||
messageId: message.id, | ||
channelId: message.channel.id, | ||
"roleReactions.reactionName": emoji.name | ||
}, null, {}, function (error, roleMessageModel) { | ||
if (error) { | ||
console.log(error); | ||
return; | ||
} | ||
if (!roleMessageModel) { | ||
return; | ||
} | ||
const roleReaction = getRoleReactionFromModel(emoji, roleMessageModel); | ||
removeUserFromRole(user, roleReaction); | ||
}).lean(); | ||
} | ||
|
||
function removeUserFromRole(user, roleReaction) { | ||
user.client.guilds.cache | ||
.get(process.env.SERVER_ID).members.cache | ||
.get(user.id).roles | ||
.remove(roleReaction.roleId) | ||
.catch(function (error) { | ||
console.log(error); | ||
}); | ||
} | ||
|
||
function handleRoleMessageDelete(message) { | ||
RoleMessage.deleteOne({ | ||
messageId: message.id, | ||
channelId: message.channel.id | ||
}).catch(function (error) { | ||
console.log(error); | ||
}); | ||
} | ||
|
||
export { | ||
handleRolesOnReactionAdd, | ||
handleRolesOnReactionRemove, | ||
handleRoleMessage, | ||
handleRoleMessageDelete | ||
} |