mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-24 07:25:10 +00:00
plugin implementation
This commit is contained in:
parent
6cccb54ffc
commit
08e817026d
3 changed files with 180 additions and 0 deletions
32
src/plugins/roleMembers/README.md
Normal file
32
src/plugins/roleMembers/README.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# RoleMembersViewer
|
||||||
|
|
||||||
|
A Vencord plugin that displays all members with a specific role when you right-click on a role in a user profile or when a message mentions roles.
|
||||||
|
|
||||||
|
The plugin fetches as many members as possible, including offline ones, and organizes them in a nested submenu for a clear and efficient view.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Dev Context:** Right-click on a role in a user profile to see a list of all members with that role.
|
||||||
|
- **Message Context:** Right-click on a message containing role mentions to display a submenu with all the roles and their corresponding members. This also works if a message has multiple role mentions!
|
||||||
|
- **Automatic Member Fetching:** Uses FluxDispatcher to fetch as many members as possible, efficently.
|
||||||
|
- **Live Updates:** The plugin takes advantage of GuildMemberStore updates so that the list can reflect changes
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Dev Context Menu
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Message Context Menu
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
- You can find installation instructions [here](https://docs.vencord.dev/installing/custom-plugins/)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
- **In User Profiles:** Right-click on a role to see the "View Members" submenu populated with all users having that role.
|
||||||
|
- **In Messages:** Right-click on a message with role mentions to open a submenu where you can view users for each mentioned role. Upon right-clicking on a message with multiple, it will display a submenu with all the roles that have been mentioned, and then you're free to view the members of each one!
|
||||||
|
- **In both scenarios**, you can click a user in the submenu to pop up their profile!
|
144
src/plugins/roleMembers/index.tsx
Normal file
144
src/plugins/roleMembers/index.tsx
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2025 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
import {
|
||||||
|
FluxDispatcher,
|
||||||
|
GuildMemberStore,
|
||||||
|
GuildStore,
|
||||||
|
Menu,
|
||||||
|
SelectedChannelStore,
|
||||||
|
SelectedGuildStore,
|
||||||
|
UserProfileActions,
|
||||||
|
UserStore
|
||||||
|
} from "@webpack/common";
|
||||||
|
|
||||||
|
function fetchMembersWithRole(guildId: string, roleId: string, memberCount: number) {
|
||||||
|
const guildMembers = GuildMemberStore.getMembers(guildId);
|
||||||
|
if (Object.keys(guildMembers).length < memberCount) {
|
||||||
|
const chunk = 100;
|
||||||
|
const requestCount = Math.ceil(memberCount / chunk);
|
||||||
|
for (let i = 0; i < requestCount; i++) {
|
||||||
|
FluxDispatcher.dispatch({
|
||||||
|
type: "GUILD_MEMBERS_REQUEST",
|
||||||
|
guildId,
|
||||||
|
userIds: [],
|
||||||
|
query: "",
|
||||||
|
limit: chunk,
|
||||||
|
withPresences: true,
|
||||||
|
notifyOnLimit: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updatedGuildMembers = GuildMemberStore.getMembers(guildId);
|
||||||
|
return Object.values(updatedGuildMembers)
|
||||||
|
.filter(m => m.roles.includes(roleId))
|
||||||
|
.map(m => ({
|
||||||
|
...m,
|
||||||
|
user: UserStore.getUser(m.userId)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => a.user.username.localeCompare(b.user.username));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "RoleMembersViewer",
|
||||||
|
description: "Shows members with a role when right clicking roles in user profiles or role mentions in messages",
|
||||||
|
authors: [Devs.okiso],
|
||||||
|
|
||||||
|
contextMenus: {
|
||||||
|
"dev-context"(children, { id }: { id: string; }) {
|
||||||
|
const guild = GuildStore.getGuild(SelectedGuildStore.getGuildId());
|
||||||
|
if (!guild) return;
|
||||||
|
|
||||||
|
const role = GuildStore.getRole(guild.id, id);
|
||||||
|
if (!role) return;
|
||||||
|
|
||||||
|
const guildId = guild.id;
|
||||||
|
const membersWithRole = fetchMembersWithRole(guildId, id, role.memberCount);
|
||||||
|
|
||||||
|
const memberItems = membersWithRole.map(member => (
|
||||||
|
<Menu.MenuItem
|
||||||
|
key={member.userId}
|
||||||
|
id={`role-member-${member.userId}`}
|
||||||
|
label={member.user.username}
|
||||||
|
action={() => {
|
||||||
|
UserProfileActions.openUserProfileModal({
|
||||||
|
userId: member.userId,
|
||||||
|
guildId,
|
||||||
|
channelId: SelectedChannelStore.getChannelId()
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
children.push(
|
||||||
|
<Menu.MenuItem
|
||||||
|
id="role-members-viewer"
|
||||||
|
label={`View Members (${role.name}) - ${membersWithRole.length}`}
|
||||||
|
>
|
||||||
|
<Menu.MenuGroup>{memberItems}</Menu.MenuGroup>
|
||||||
|
</Menu.MenuItem>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
"message"(children, { message }: { message: any; }) {
|
||||||
|
const guild = GuildStore.getGuild(SelectedGuildStore.getGuildId());
|
||||||
|
if (!guild) return;
|
||||||
|
|
||||||
|
const roleMentions = message.content.match(/<@&(\d+)>/g);
|
||||||
|
if (!roleMentions?.length) return;
|
||||||
|
|
||||||
|
// Extract unique role IDs from the mentions.
|
||||||
|
const roleIds = roleMentions.map(mention => mention.match(/<@&(\d+)>/)![1]);
|
||||||
|
const uniqueRoleIds = [...new Set(roleIds)];
|
||||||
|
|
||||||
|
const guildId = guild.id;
|
||||||
|
const roleMenuItems: JSX.Element[] = [];
|
||||||
|
|
||||||
|
for (const roleId of uniqueRoleIds) {
|
||||||
|
const role = GuildStore.getRole(guildId, roleId);
|
||||||
|
if (!role) continue;
|
||||||
|
|
||||||
|
const membersWithRole = fetchMembersWithRole(guildId, roleId, role.memberCount);
|
||||||
|
const memberItems = membersWithRole.map(member => (
|
||||||
|
<Menu.MenuItem
|
||||||
|
key={member.userId}
|
||||||
|
id={`role-member-${member.userId}`}
|
||||||
|
label={member.user?.username ?? "Unknown User"}
|
||||||
|
action={() => {
|
||||||
|
UserProfileActions.openUserProfileModal({
|
||||||
|
userId: member.userId,
|
||||||
|
guildId,
|
||||||
|
channelId: SelectedChannelStore.getChannelId()
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
roleMenuItems.push(
|
||||||
|
<Menu.MenuItem
|
||||||
|
id={`role-members-viewer-${roleId}`}
|
||||||
|
label={`@${role.name} - ${membersWithRole.length}`}
|
||||||
|
>
|
||||||
|
<Menu.MenuGroup>{memberItems}</Menu.MenuGroup>
|
||||||
|
</Menu.MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roleMenuItems.length > 0) {
|
||||||
|
children.push(
|
||||||
|
<Menu.MenuItem
|
||||||
|
id="role-members-viewer"
|
||||||
|
label={`View Role Members (${roleMenuItems.length} roles)`}
|
||||||
|
>
|
||||||
|
<Menu.MenuGroup>{roleMenuItems}</Menu.MenuGroup>
|
||||||
|
</Menu.MenuItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -579,6 +579,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "jamesbt365",
|
name: "jamesbt365",
|
||||||
id: 158567567487795200n,
|
id: 158567567487795200n,
|
||||||
},
|
},
|
||||||
|
okiso: {
|
||||||
|
name: "okiso",
|
||||||
|
id: 274178934143451137n,
|
||||||
|
},
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
|
Loading…
Add table
Reference in a new issue