Utility: add !vcmove command
This commit is contained in:
parent
30f34747d4
commit
4f5eb0689d
2 changed files with 98 additions and 0 deletions
|
@ -1,10 +1,13 @@
|
||||||
import { decorators as d } from "knub";
|
import { decorators as d } from "knub";
|
||||||
import { Channel, EmbedOptions, Member, Message, Role, TextChannel, User, VoiceChannel } from "eris";
|
import { Channel, EmbedOptions, Member, Message, Role, TextChannel, User, VoiceChannel } from "eris";
|
||||||
import {
|
import {
|
||||||
|
channelMentionRegex,
|
||||||
chunkArray,
|
chunkArray,
|
||||||
embedPadding,
|
embedPadding,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
|
isSnowflake,
|
||||||
noop,
|
noop,
|
||||||
|
simpleClosestStringMatch,
|
||||||
stripObjectToScalars,
|
stripObjectToScalars,
|
||||||
successMessage,
|
successMessage,
|
||||||
trimLines,
|
trimLines,
|
||||||
|
@ -49,6 +52,7 @@ export class UtilityPlugin extends ZeppelinPlugin {
|
||||||
nickname: false,
|
nickname: false,
|
||||||
ping: false,
|
ping: false,
|
||||||
source: false,
|
source: false,
|
||||||
|
vcmove: false,
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
|
@ -61,6 +65,7 @@ export class UtilityPlugin extends ZeppelinPlugin {
|
||||||
info: true,
|
info: true,
|
||||||
server: true,
|
server: true,
|
||||||
nickname: true,
|
nickname: true,
|
||||||
|
vcmove: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -553,6 +558,63 @@ export class UtilityPlugin extends ZeppelinPlugin {
|
||||||
msg.channel.createMessage(`Message source: ${url}`);
|
msg.channel.createMessage(`Message source: ${url}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@d.command("vcmove", "<member:Member> <channel:string$>")
|
||||||
|
@d.permission("vcmove")
|
||||||
|
async vcmoveCmd(msg: Message, args: { member: Member; channel: string }) {
|
||||||
|
let channel: VoiceChannel;
|
||||||
|
|
||||||
|
if (isSnowflake(args.channel)) {
|
||||||
|
// Snowflake -> resolve channel directly
|
||||||
|
const potentialChannel = this.guild.channels.get(args.channel);
|
||||||
|
if (!potentialChannel || !(potentialChannel instanceof VoiceChannel)) {
|
||||||
|
msg.channel.createMessage(errorMessage("Unknown or non-voice channel"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel = potentialChannel;
|
||||||
|
} else if (channelMentionRegex.test(args.channel)) {
|
||||||
|
// Channel mention -> parse channel id and resolve channel from that
|
||||||
|
const channelId = args.channel.match(channelMentionRegex)[1];
|
||||||
|
const potentialChannel = this.guild.channels.get(channelId);
|
||||||
|
if (!potentialChannel || !(potentialChannel instanceof VoiceChannel)) {
|
||||||
|
msg.channel.createMessage(errorMessage("Unknown or non-voice channel"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel = potentialChannel;
|
||||||
|
} else {
|
||||||
|
// Search string -> find closest matching voice channel name
|
||||||
|
const voiceChannels = this.guild.channels.filter(theChannel => {
|
||||||
|
return theChannel instanceof VoiceChannel;
|
||||||
|
}) as VoiceChannel[];
|
||||||
|
const closestMatch = simpleClosestStringMatch(args.channel, voiceChannels, ch => ch.name);
|
||||||
|
if (!closestMatch) {
|
||||||
|
msg.channel.createMessage(errorMessage("No matching voice channels"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel = closestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!args.member.voiceState || !args.member.voiceState.channelID) {
|
||||||
|
msg.channel.createMessage(errorMessage("Member is not in a voice channel"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await args.member.edit({
|
||||||
|
channelID: channel.id,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
msg.channel.createMessage(errorMessage("Failed to move member"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.channel.createMessage(
|
||||||
|
successMessage(`**${args.member.user.username}#${args.member.user.discriminator}** moved to **${channel.name}**`),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@d.command("reload_guild")
|
@d.command("reload_guild")
|
||||||
@d.permission("reload_guild")
|
@d.permission("reload_guild")
|
||||||
reloadGuildCmd(msg: Message) {
|
reloadGuildCmd(msg: Message) {
|
||||||
|
|
36
src/utils.ts
36
src/utils.ts
|
@ -204,6 +204,7 @@ export const embedPadding = "\n" + emptyEmbedValue;
|
||||||
|
|
||||||
export const userMentionRegex = /<@!?([0-9]+)>/g;
|
export const userMentionRegex = /<@!?([0-9]+)>/g;
|
||||||
export const roleMentionRegex = /<@&([0-9]+)>/g;
|
export const roleMentionRegex = /<@&([0-9]+)>/g;
|
||||||
|
export const channelMentionRegex = /<#([0-9]+)>/g;
|
||||||
|
|
||||||
export function getUserMentions(str: string) {
|
export function getUserMentions(str: string) {
|
||||||
const regex = new RegExp(userMentionRegex.source, "g");
|
const regex = new RegExp(userMentionRegex.source, "g");
|
||||||
|
@ -347,6 +348,41 @@ export function downloadFile(attachmentUrl: string, retries = 3): Promise<{ path
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ItemWithRanking<T> = [T, number];
|
||||||
|
export function simpleClosestStringMatch<T>(searchStr, haystack: T[], getter = null): T {
|
||||||
|
const normalizedSearchStr = searchStr.toLowerCase();
|
||||||
|
|
||||||
|
// See if any haystack item contains a part of the search string
|
||||||
|
const itemsWithRankings: Array<ItemWithRanking<T>> = haystack.map(item => {
|
||||||
|
const itemStr: string = getter ? getter(item) : item;
|
||||||
|
const normalizedItemStr = itemStr.toLowerCase();
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
do {
|
||||||
|
if (!normalizedItemStr.includes(normalizedSearchStr.slice(0, i + 1))) break;
|
||||||
|
i++;
|
||||||
|
} while (i < normalizedSearchStr.length);
|
||||||
|
|
||||||
|
if (i > 0 && normalizedItemStr.startsWith(normalizedSearchStr.slice(0, i))) {
|
||||||
|
// Slightly prioritize items that *start* with the search string
|
||||||
|
i += 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [item, i] as ItemWithRanking<T>;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort by best match
|
||||||
|
itemsWithRankings.sort((a, b) => {
|
||||||
|
return a[1] > b[1] ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (itemsWithRankings[0][1] === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemsWithRankings[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
export function noop() {
|
export function noop() {
|
||||||
// IT'S LITERALLY NOTHING
|
// IT'S LITERALLY NOTHING
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue