mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-16 14:11:50 +00:00
Add SelfGrantableRoles
This commit is contained in:
parent
8cb382979b
commit
457d57fb8c
6 changed files with 368 additions and 5 deletions
38
src/data/GuildSelfGrantableRoles.ts
Normal file
38
src/data/GuildSelfGrantableRoles.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { BaseRepository } from "./BaseRepository";
|
||||||
|
import { getRepository, Repository } from "typeorm";
|
||||||
|
import { SelfGrantableRole } from "./entities/SelfGrantableRole";
|
||||||
|
|
||||||
|
export class GuildSelfGrantableRoles extends BaseRepository {
|
||||||
|
private selfGrantableRoles: Repository<SelfGrantableRole>;
|
||||||
|
|
||||||
|
constructor(guildId) {
|
||||||
|
super(guildId);
|
||||||
|
this.selfGrantableRoles = getRepository(SelfGrantableRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getForChannel(channelId: string): Promise<SelfGrantableRole[]> {
|
||||||
|
return this.selfGrantableRoles.find({
|
||||||
|
where: {
|
||||||
|
guild_id: this.guildId,
|
||||||
|
channel_id: channelId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(channelId: string, roleId: string) {
|
||||||
|
await this.selfGrantableRoles.delete({
|
||||||
|
guild_id: this.guildId,
|
||||||
|
channel_id: channelId,
|
||||||
|
role_id: roleId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(channelId: string, roleId: string, aliases: string[]) {
|
||||||
|
await this.selfGrantableRoles.insert({
|
||||||
|
guild_id: this.guildId,
|
||||||
|
channel_id: channelId,
|
||||||
|
role_id: roleId,
|
||||||
|
aliases,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
16
src/data/entities/SelfGrantableRole.ts
Normal file
16
src/data/entities/SelfGrantableRole.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Entity, Column, PrimaryColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity("self_grantable_roles")
|
||||||
|
export class SelfGrantableRole {
|
||||||
|
@Column()
|
||||||
|
@PrimaryColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column() guild_id: string;
|
||||||
|
|
||||||
|
@Column() channel_id: string;
|
||||||
|
|
||||||
|
@Column() role_id: string;
|
||||||
|
|
||||||
|
@Column("simple-array") aliases: string[];
|
||||||
|
}
|
12
src/index.ts
12
src/index.ts
|
@ -72,6 +72,7 @@ import { StarboardPlugin } from "./plugins/Starboard";
|
||||||
import { NameHistoryPlugin } from "./plugins/NameHistory";
|
import { NameHistoryPlugin } from "./plugins/NameHistory";
|
||||||
import { AutoReactions } from "./plugins/AutoReactions";
|
import { AutoReactions } from "./plugins/AutoReactions";
|
||||||
import { PingableRoles } from "./plugins/PingableRoles";
|
import { PingableRoles } from "./plugins/PingableRoles";
|
||||||
|
import { SelfGrantableRoles } from "./plugins/SelfGrantableRoles";
|
||||||
|
|
||||||
// Run latest database migrations
|
// Run latest database migrations
|
||||||
logger.info("Running database migrations");
|
logger.info("Running database migrations");
|
||||||
|
@ -79,7 +80,7 @@ connect().then(async conn => {
|
||||||
await conn.runMigrations();
|
await conn.runMigrations();
|
||||||
|
|
||||||
const client = new Client(process.env.TOKEN, {
|
const client = new Client(process.env.TOKEN, {
|
||||||
getAllUsers: true
|
getAllUsers: true,
|
||||||
});
|
});
|
||||||
client.setMaxListeners(100);
|
client.setMaxListeners(100);
|
||||||
|
|
||||||
|
@ -112,7 +113,8 @@ connect().then(async conn => {
|
||||||
SlowmodePlugin,
|
SlowmodePlugin,
|
||||||
StarboardPlugin,
|
StarboardPlugin,
|
||||||
AutoReactions,
|
AutoReactions,
|
||||||
PingableRoles
|
PingableRoles,
|
||||||
|
SelfGrantableRoles,
|
||||||
],
|
],
|
||||||
|
|
||||||
globalPlugins: [BotControlPlugin, LogServerPlugin],
|
globalPlugins: [BotControlPlugin, LogServerPlugin],
|
||||||
|
@ -149,9 +151,9 @@ connect().then(async conn => {
|
||||||
performanceDebug: {
|
performanceDebug: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
size: 30,
|
size: 30,
|
||||||
threshold: 200
|
threshold: 200,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info("Starting the bot");
|
logger.info("Starting the bot");
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { MigrationInterface, QueryRunner, Table } from "typeorm";
|
||||||
|
|
||||||
|
export class CreateSelfGrantableRolesTable1550521627877 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "self_grantable_roles",
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
type: "int",
|
||||||
|
unsigned: true,
|
||||||
|
isGenerated: true,
|
||||||
|
generationStrategy: "increment",
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "guild_id",
|
||||||
|
type: "bigint",
|
||||||
|
unsigned: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "channel_id",
|
||||||
|
type: "bigint",
|
||||||
|
unsigned: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "role_id",
|
||||||
|
type: "bigint",
|
||||||
|
unsigned: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "aliases",
|
||||||
|
type: "varchar",
|
||||||
|
length: "255",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
indices: [
|
||||||
|
{
|
||||||
|
columnNames: ["guild_id", "channel_id", "role_id"],
|
||||||
|
isUnique: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.dropTable("self_grantable_roles", true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ export class ReactionRolesPlugin extends ZeppelinPlugin {
|
||||||
|
|
||||||
permissions: {
|
permissions: {
|
||||||
manage: false,
|
manage: false,
|
||||||
|
fallback_command: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
overrides: [
|
overrides: [
|
||||||
|
|
255
src/plugins/SelfGrantableRoles.ts
Normal file
255
src/plugins/SelfGrantableRoles.ts
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
import { Plugin, decorators as d } from "knub";
|
||||||
|
import { GuildSelfGrantableRoles } from "../data/GuildSelfGrantableRoles";
|
||||||
|
import { GuildChannel, Message, Role, TextChannel } from "eris";
|
||||||
|
import { chunkArray, errorMessage, sorter, successMessage } from "../utils";
|
||||||
|
|
||||||
|
export class SelfGrantableRoles extends Plugin {
|
||||||
|
public static pluginName = "self_grantable_roles";
|
||||||
|
|
||||||
|
protected selfGrantableRoles: GuildSelfGrantableRoles;
|
||||||
|
|
||||||
|
getDefaultOptions() {
|
||||||
|
return {
|
||||||
|
permissions: {
|
||||||
|
manage: false,
|
||||||
|
use: false,
|
||||||
|
ignore_cooldown: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
level: ">=50",
|
||||||
|
permissions: {
|
||||||
|
ignore_cooldown: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: ">=100",
|
||||||
|
permissions: {
|
||||||
|
manage: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad() {
|
||||||
|
this.selfGrantableRoles = GuildSelfGrantableRoles.getInstance(this.guildId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.command("role remove", "<roleNames:string...>")
|
||||||
|
@d.permission("use")
|
||||||
|
@d.cooldown(2500, "ignore_cooldown")
|
||||||
|
async roleRemoveCmd(msg: Message, args: { roleNames: string[] }) {
|
||||||
|
const lock = await this.locks.acquire(`grantableRoles:${msg.author.id}`);
|
||||||
|
|
||||||
|
const channelGrantableRoles = await this.selfGrantableRoles.getForChannel(msg.channel.id);
|
||||||
|
if (channelGrantableRoles.length === 0) {
|
||||||
|
lock.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonMatchingRoleNames: string[] = [];
|
||||||
|
const rolesToRemove: Set<Role> = new Set();
|
||||||
|
|
||||||
|
// Match given role names with actual grantable roles
|
||||||
|
for (const roleName of args.roleNames) {
|
||||||
|
const normalized = roleName.toLowerCase();
|
||||||
|
let matched = false;
|
||||||
|
|
||||||
|
for (const grantableRole of channelGrantableRoles) {
|
||||||
|
if (grantableRole.aliases.includes(normalized)) {
|
||||||
|
if (this.guild.roles.has(grantableRole.role_id)) {
|
||||||
|
rolesToRemove.add(this.guild.roles.get(grantableRole.role_id));
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched) {
|
||||||
|
nonMatchingRoleNames.push(roleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the roles
|
||||||
|
if (rolesToRemove.size) {
|
||||||
|
const rolesToRemoveArr = Array.from(rolesToRemove.values());
|
||||||
|
const roleIdsToRemove = rolesToRemoveArr.map(r => r.id);
|
||||||
|
const newRoleIds = msg.member.roles.filter(roleId => !roleIdsToRemove.includes(roleId));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await msg.member.edit({
|
||||||
|
roles: newRoleIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
const removedRolesStr = rolesToRemoveArr.map(r => `**${r.name}**`);
|
||||||
|
const removedRolesWord = rolesToRemoveArr.length === 1 ? "role" : "roles";
|
||||||
|
|
||||||
|
if (nonMatchingRoleNames.length) {
|
||||||
|
msg.channel.createMessage(
|
||||||
|
successMessage(
|
||||||
|
`<@!${msg.author.id}> Removed ${removedRolesStr.join(", ")} ${removedRolesWord};` +
|
||||||
|
` couldn't recognize the other roles you mentioned`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
msg.channel.createMessage(
|
||||||
|
successMessage(`<@!${msg.author.id}> Removed ${removedRolesStr.join(", ")} ${removedRolesWord}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
msg.channel.createMessage(errorMessage(`<@!${msg.author.id}> Got an error while trying to remove the roles`));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg.channel.createMessage(
|
||||||
|
errorMessage(`<@!${msg.author.id}> Unknown ${args.roleNames.length === 1 ? "role" : "roles"}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.command("role", "<roleNames:string...>")
|
||||||
|
@d.permission("use")
|
||||||
|
@d.cooldown(2500, "ignore_cooldown")
|
||||||
|
async roleCmd(msg: Message, args: { roleNames: string[] }) {
|
||||||
|
const lock = await this.locks.acquire(`grantableRoles:${msg.author.id}`);
|
||||||
|
|
||||||
|
const channelGrantableRoles = await this.selfGrantableRoles.getForChannel(msg.channel.id);
|
||||||
|
if (channelGrantableRoles.length === 0) {
|
||||||
|
lock.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonMatchingRoleNames: string[] = [];
|
||||||
|
const rolesToGrant: Set<Role> = new Set();
|
||||||
|
|
||||||
|
// Match given role names with actual grantable roles
|
||||||
|
for (const roleName of args.roleNames) {
|
||||||
|
const normalized = roleName.toLowerCase();
|
||||||
|
let matched = false;
|
||||||
|
|
||||||
|
for (const grantableRole of channelGrantableRoles) {
|
||||||
|
if (grantableRole.aliases.includes(normalized)) {
|
||||||
|
if (this.guild.roles.has(grantableRole.role_id)) {
|
||||||
|
rolesToGrant.add(this.guild.roles.get(grantableRole.role_id));
|
||||||
|
matched = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched) {
|
||||||
|
nonMatchingRoleNames.push(roleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grant the roles
|
||||||
|
if (rolesToGrant.size) {
|
||||||
|
const rolesToGrantArr = Array.from(rolesToGrant.values());
|
||||||
|
const roleIdsToGrant = rolesToGrantArr.map(r => r.id);
|
||||||
|
const newRoleIds = Array.from(new Set(msg.member.roles.concat(roleIdsToGrant)).values());
|
||||||
|
try {
|
||||||
|
await msg.member.edit({
|
||||||
|
roles: newRoleIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
const grantedRolesStr = rolesToGrantArr.map(r => `**${r.name}**`);
|
||||||
|
const grantedRolesWord = rolesToGrantArr.length === 1 ? "role" : "roles";
|
||||||
|
|
||||||
|
if (nonMatchingRoleNames.length) {
|
||||||
|
msg.channel.createMessage(
|
||||||
|
successMessage(
|
||||||
|
`<@!${msg.author.id}> Granted you the ${grantedRolesStr.join(", ")} ${grantedRolesWord};` +
|
||||||
|
` couldn't recognize the other roles you mentioned`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
msg.channel.createMessage(
|
||||||
|
successMessage(`<@!${msg.author.id}> Granted you the ${grantedRolesStr.join(", ")} ${grantedRolesWord}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
msg.channel.createMessage(
|
||||||
|
errorMessage(`<@!${msg.author.id}> Got an error while trying to grant you the roles`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg.channel.createMessage(
|
||||||
|
errorMessage(`<@!${msg.author.id}> Unknown ${args.roleNames.length === 1 ? "role" : "roles"}`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.command("self_grantable_roles add", "<channel:channel> <roleId:string> [aliases:string...]")
|
||||||
|
@d.permission("manage")
|
||||||
|
async addSelfGrantableRoleCmd(msg: Message, args: { channel: GuildChannel; roleId: string; aliases?: string[] }) {
|
||||||
|
if (!(args.channel instanceof TextChannel)) {
|
||||||
|
msg.channel.createMessage(errorMessage("Invalid channel (must be a text channel)"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = this.guild.roles.get(args.roleId);
|
||||||
|
if (!role) {
|
||||||
|
msg.channel.createMessage(errorMessage("Unknown role"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aliases = [role.name].concat(args.aliases || []);
|
||||||
|
const normalizedAliases = aliases.map(a => a.toLowerCase());
|
||||||
|
const uniqueAliases = Array.from(new Set(normalizedAliases).values());
|
||||||
|
|
||||||
|
// Remove existing self grantable role on that channel, if one exists
|
||||||
|
await this.selfGrantableRoles.delete(args.channel.id, role.id);
|
||||||
|
|
||||||
|
// Add new one
|
||||||
|
await this.selfGrantableRoles.add(args.channel.id, role.id, uniqueAliases);
|
||||||
|
|
||||||
|
msg.channel.createMessage(
|
||||||
|
successMessage(`Self-grantable role **${role.name}** added to **#${args.channel.name}**`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.command("self_grantable_roles delete", "<channel:channel> <roleId:string>")
|
||||||
|
@d.permission("manage")
|
||||||
|
async deleteSelfGrantableRoleCmd(msg: Message, args: { channel: GuildChannel; roleId: string }) {
|
||||||
|
await this.selfGrantableRoles.delete(args.channel.id, args.roleId);
|
||||||
|
|
||||||
|
const roleName = this.guild.roles.has(args.roleId) ? this.guild.roles.get(args.roleId).name : args.roleId;
|
||||||
|
|
||||||
|
msg.channel.createMessage(
|
||||||
|
successMessage(`Self-grantable role **${roleName}** removed from **#${args.channel.name}**`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@d.command("self_grantable_roles", "<channel:channel>")
|
||||||
|
@d.permission("manage")
|
||||||
|
async selfGrantableRolesCmd(msg: Message, args: { channel: GuildChannel }) {
|
||||||
|
if (!(args.channel instanceof TextChannel)) {
|
||||||
|
msg.channel.createMessage(errorMessage("Invalid channel (must be a text channel)"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelGrantableRoles = await this.selfGrantableRoles.getForChannel(args.channel.id);
|
||||||
|
if (channelGrantableRoles.length === 0) {
|
||||||
|
msg.channel.createMessage(errorMessage(`No self-grantable roles on **#${args.channel.name}**`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channelGrantableRoles.sort(sorter(gr => gr.aliases.join(", ")));
|
||||||
|
|
||||||
|
const longestId = channelGrantableRoles.reduce((longest, gr) => Math.max(longest, gr.role_id.length), 0);
|
||||||
|
const lines = channelGrantableRoles.map(gr => {
|
||||||
|
const paddedId = gr.role_id.padEnd(longestId, " ");
|
||||||
|
return `${paddedId} ${gr.aliases.join(", ")}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const batches = chunkArray(lines, 20);
|
||||||
|
for (const batch of batches) {
|
||||||
|
await msg.channel.createMessage(`\`\`\`js\n${batch.join("\n")}\n\`\`\``);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue