mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-10 12:25:02 +00:00
Merge branch 'newStarboard' of https://github.com/DarkView/ZeppelinBot into DarkView-newStarboard
This commit is contained in:
commit
b36fcb41fe
8 changed files with 463 additions and 243 deletions
|
@ -1,58 +1,147 @@
|
|||
import { decorators as d, waitForReply, utils as knubUtils, IBasePluginConfig, IPluginOptions } from "knub";
|
||||
import { ZeppelinPlugin } from "./ZeppelinPlugin";
|
||||
import { GuildStarboards } from "../data/GuildStarboards";
|
||||
import { decorators as d, IPluginOptions } from "knub";
|
||||
import { ZeppelinPlugin, trimPluginDescription } from "./ZeppelinPlugin";
|
||||
import { GuildChannel, Message, TextChannel } from "eris";
|
||||
import {
|
||||
customEmojiRegex,
|
||||
errorMessage,
|
||||
getEmojiInString,
|
||||
getUrlsInString,
|
||||
noop,
|
||||
snowflakeRegex,
|
||||
successMessage,
|
||||
} from "../utils";
|
||||
import { Starboard } from "../data/entities/Starboard";
|
||||
import { errorMessage, getUrlsInString, noop, successMessage, tNullable } from "../utils";
|
||||
import path from "path";
|
||||
import moment from "moment-timezone";
|
||||
import { GuildSavedMessages } from "../data/GuildSavedMessages";
|
||||
import { SavedMessage } from "../data/entities/SavedMessage";
|
||||
import * as t from "io-ts";
|
||||
import { GuildStarboardMessages } from "../data/GuildStarboardMessages";
|
||||
import { StarboardMessage } from "../data/entities/StarboardMessage";
|
||||
import { GuildStarboardReactions } from "../data/GuildStarboardReactions";
|
||||
|
||||
const StarboardOpts = t.type({
|
||||
source_channel_ids: t.array(t.string),
|
||||
starboard_channel_id: t.string,
|
||||
positive_emojis: tNullable(t.array(t.string)),
|
||||
positive_required: tNullable(t.number),
|
||||
enabled: tNullable(t.boolean),
|
||||
});
|
||||
type TStarboardOpts = t.TypeOf<typeof StarboardOpts>;
|
||||
|
||||
const ConfigSchema = t.type({
|
||||
can_manage: t.boolean,
|
||||
entries: t.record(t.string, StarboardOpts),
|
||||
|
||||
can_migrate: t.boolean,
|
||||
});
|
||||
type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
|
||||
|
||||
const defaultStarboardOpts: Partial<TStarboardOpts> = {
|
||||
positive_emojis: ["⭐"],
|
||||
positive_required: 5,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
|
||||
public static pluginName = "starboard";
|
||||
public static showInDocs = false;
|
||||
public static configSchema = ConfigSchema;
|
||||
|
||||
protected starboards: GuildStarboards;
|
||||
public static pluginInfo = {
|
||||
prettyName: "Starboards",
|
||||
description: trimPluginDescription(`
|
||||
This plugin contains all functionality needed to use discord channels as starboards.
|
||||
`),
|
||||
configurationGuide: trimPluginDescription(`
|
||||
You can customize multiple settings for starboards.
|
||||
Any emoji that you want available needs to be put into the config in its raw form.
|
||||
To obtain a raw form of an emoji, please write out the emoji and put a backslash in front of it.
|
||||
Example with default emoji: "\:star:" => "⭐"
|
||||
Example with custom emoji: "\:mrvnSmile:" => "<:mrvnSmile:543000534102310933>"
|
||||
Now, past the result into the config, but make sure to exclude all less-than and greater-than signs like in the second example.
|
||||
|
||||
|
||||
### Starboard with one source channel
|
||||
All messages in the source channel that get enough positive reactions will be posted into the starboard channel.
|
||||
The only positive reaction counted here is the default emoji "⭐".
|
||||
Only users with a role matching the allowed_roles role-id will be counted.
|
||||
|
||||
~~~yml
|
||||
starboard:
|
||||
config:
|
||||
entries:
|
||||
exampleOne:
|
||||
source_channel_ids: ["604342623569707010"]
|
||||
starboard_channel_id: "604342689038729226"
|
||||
positive_emojis: ["⭐"]
|
||||
positive_required: 5
|
||||
allowed_roles: ["556110793058287637"]
|
||||
enabled: true
|
||||
~~~
|
||||
|
||||
### Starboard with two sources and two emoji
|
||||
All messages in any of the source channels that get enough positive reactions will be posted into the starboard channel.
|
||||
Both the default emoji "⭐" and the custom emoji ":mrvnSmile:543000534102310933" are counted.
|
||||
|
||||
~~~yml
|
||||
starboard:
|
||||
config:
|
||||
entries:
|
||||
exampleTwo:
|
||||
source_channel_ids: ["604342623569707010", "604342649251561487"]
|
||||
starboard_channel_id: "604342689038729226"
|
||||
positive_emojis: ["⭐", ":mrvnSmile:543000534102310933"]
|
||||
positive_required: 10
|
||||
enabled: true
|
||||
~~~
|
||||
`),
|
||||
};
|
||||
|
||||
protected savedMessages: GuildSavedMessages;
|
||||
protected starboardMessages: GuildStarboardMessages;
|
||||
protected starboardReactions: GuildStarboardReactions;
|
||||
|
||||
private onMessageDeleteFn;
|
||||
|
||||
public static getStaticDefaultOptions(): IPluginOptions<TConfigSchema> {
|
||||
return {
|
||||
config: {
|
||||
can_manage: false,
|
||||
can_migrate: false,
|
||||
entries: {},
|
||||
},
|
||||
|
||||
overrides: [
|
||||
{
|
||||
level: ">=100",
|
||||
config: {
|
||||
can_manage: true,
|
||||
can_migrate: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
protected getStarboardOptsForSourceChannel(sourceChannel): TStarboardOpts[] {
|
||||
const config = this.getConfigForChannel(sourceChannel);
|
||||
|
||||
const configs = Object.values(config.entries).filter(opts => opts.source_channel_ids.includes(sourceChannel.id));
|
||||
configs.forEach(cfg => {
|
||||
if (cfg.enabled == null) cfg.enabled = defaultStarboardOpts.enabled;
|
||||
if (cfg.positive_emojis == null) cfg.positive_emojis = defaultStarboardOpts.positive_emojis;
|
||||
if (cfg.positive_required == null) cfg.positive_required = defaultStarboardOpts.positive_required;
|
||||
});
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
protected getStarboardOptsForStarboardChannel(starboardChannel): TStarboardOpts[] {
|
||||
const config = this.getConfigForChannel(starboardChannel);
|
||||
|
||||
const configs = Object.values(config.entries).filter(opts => opts.starboard_channel_id === starboardChannel.id);
|
||||
configs.forEach(cfg => {
|
||||
if (cfg.enabled == null) cfg.enabled = defaultStarboardOpts.enabled;
|
||||
if (cfg.positive_emojis == null) cfg.positive_emojis = defaultStarboardOpts.positive_emojis;
|
||||
if (cfg.positive_required == null) cfg.positive_required = defaultStarboardOpts.positive_required;
|
||||
});
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
onLoad() {
|
||||
this.starboards = GuildStarboards.getGuildInstance(this.guildId);
|
||||
this.savedMessages = GuildSavedMessages.getGuildInstance(this.guildId);
|
||||
this.starboardMessages = GuildStarboardMessages.getGuildInstance(this.guildId);
|
||||
this.starboardReactions = GuildStarboardReactions.getGuildInstance(this.guildId);
|
||||
|
||||
this.onMessageDeleteFn = this.onMessageDelete.bind(this);
|
||||
this.savedMessages.events.on("delete", this.onMessageDeleteFn);
|
||||
|
@ -62,143 +151,13 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
|
|||
this.savedMessages.events.off("delete", this.onMessageDeleteFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* An interactive setup for creating a starboard
|
||||
*/
|
||||
@d.command("starboard create")
|
||||
@d.permission("can_manage")
|
||||
async setupCmd(msg: Message) {
|
||||
const cancelMsg = () => msg.channel.createMessage("Cancelled");
|
||||
|
||||
msg.channel.createMessage(
|
||||
`⭐ Let's make a starboard! What channel should we use as the board? ("cancel" to cancel)`,
|
||||
);
|
||||
|
||||
let starboardChannel;
|
||||
do {
|
||||
const reply = await waitForReply(this.bot, msg.channel as TextChannel, msg.author.id, 60000);
|
||||
if (reply.content == null || reply.content === "cancel") return cancelMsg();
|
||||
|
||||
starboardChannel = knubUtils.resolveChannel(this.guild, reply.content || "");
|
||||
if (!starboardChannel) {
|
||||
msg.channel.createMessage("Invalid channel. Try again?");
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingStarboard = await this.starboards.getStarboardByChannelId(starboardChannel.id);
|
||||
if (existingStarboard) {
|
||||
msg.channel.createMessage("That channel already has a starboard. Try again?");
|
||||
starboardChannel = null;
|
||||
continue;
|
||||
}
|
||||
} while (starboardChannel == null);
|
||||
|
||||
msg.channel.createMessage(`Ok. Which emoji should we use as the trigger? ("cancel" to cancel)`);
|
||||
|
||||
let emoji;
|
||||
do {
|
||||
const reply = await waitForReply(this.bot, msg.channel as TextChannel, msg.author.id);
|
||||
if (reply.content == null || reply.content === "cancel") return cancelMsg();
|
||||
|
||||
const allEmojis = getEmojiInString(reply.content || "");
|
||||
if (!allEmojis.length) {
|
||||
msg.channel.createMessage("Invalid emoji. Try again?");
|
||||
continue;
|
||||
}
|
||||
|
||||
emoji = allEmojis[0];
|
||||
|
||||
const customEmojiMatch = emoji.match(customEmojiRegex);
|
||||
if (customEmojiMatch) {
|
||||
// <:name:id> to name:id, as Eris puts them in the message reactions object
|
||||
emoji = `${customEmojiMatch[1]}:${customEmojiMatch[2]}`;
|
||||
}
|
||||
} while (emoji == null);
|
||||
|
||||
msg.channel.createMessage(
|
||||
`And how many reactions are required to immortalize a message in the starboard? ("cancel" to cancel)`,
|
||||
);
|
||||
|
||||
let requiredReactions;
|
||||
do {
|
||||
const reply = await waitForReply(this.bot, msg.channel as TextChannel, msg.author.id);
|
||||
if (reply.content == null || reply.content === "cancel") return cancelMsg();
|
||||
|
||||
requiredReactions = parseInt(reply.content || "", 10);
|
||||
|
||||
if (Number.isNaN(requiredReactions)) {
|
||||
msg.channel.createMessage("Invalid number. Try again?");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof requiredReactions === "number") {
|
||||
if (requiredReactions <= 0) {
|
||||
msg.channel.createMessage("The number must be higher than 0. Try again?");
|
||||
continue;
|
||||
} else if (requiredReactions > 65536) {
|
||||
msg.channel.createMessage("The number must be smaller than 65536. Try again?");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} while (requiredReactions == null);
|
||||
|
||||
msg.channel.createMessage(
|
||||
`And finally, which channels can messages be starred in? "All" for any channel. ("cancel" to cancel)`,
|
||||
);
|
||||
|
||||
let channelWhitelist;
|
||||
do {
|
||||
const reply = await waitForReply(this.bot, msg.channel as TextChannel, msg.author.id);
|
||||
if (reply.content == null || reply.content === "cancel") return cancelMsg();
|
||||
|
||||
if (reply.content.toLowerCase() === "all") {
|
||||
channelWhitelist = null;
|
||||
break;
|
||||
}
|
||||
|
||||
channelWhitelist = reply.content.match(new RegExp(snowflakeRegex, "g"));
|
||||
|
||||
let hasInvalidChannels = false;
|
||||
for (const id of channelWhitelist) {
|
||||
const channel = this.guild.channels.get(id);
|
||||
if (!channel || !(channel instanceof TextChannel)) {
|
||||
msg.channel.createMessage(`Couldn't recognize channel <#${id}> (\`${id}\`). Try again?`);
|
||||
hasInvalidChannels = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasInvalidChannels) continue;
|
||||
} while (channelWhitelist == null);
|
||||
|
||||
await this.starboards.create(starboardChannel.id, channelWhitelist, emoji, requiredReactions);
|
||||
|
||||
msg.channel.createMessage(successMessage("Starboard created!"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the starboard from the specified channel. The already-posted starboard messages are retained.
|
||||
*/
|
||||
@d.command("starboard delete", "<channelId:channelId>")
|
||||
@d.permission("can_manage")
|
||||
async deleteCmd(msg: Message, args: { channelId: string }) {
|
||||
const starboard = await this.starboards.getStarboardByChannelId(args.channelId);
|
||||
if (!starboard) {
|
||||
msg.channel.createMessage(errorMessage(`Channel <#${args.channelId}> doesn't have a starboard!`));
|
||||
return;
|
||||
}
|
||||
|
||||
await this.starboards.delete(starboard.channel_id);
|
||||
|
||||
msg.channel.createMessage(successMessage(`Starboard deleted from <#${args.channelId}>!`));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a reaction is added to a message, check if there are any applicable starboards and if the reactions reach
|
||||
* the required threshold. If they do, post the message in the starboard channel.
|
||||
*/
|
||||
@d.event("messageReactionAdd")
|
||||
@d.lock("starboardReaction")
|
||||
async onMessageReactionAdd(msg: Message, emoji: { id: string; name: string }) {
|
||||
async onMessageReactionAdd(msg: Message, emoji: { id: string; name: string }, userId: string) {
|
||||
if (!msg.author) {
|
||||
// Message is not cached, fetch it
|
||||
try {
|
||||
|
@ -209,52 +168,46 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
|
|||
}
|
||||
}
|
||||
|
||||
const emojiStr = emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name;
|
||||
const applicableStarboards = await this.starboards.getStarboardsByEmoji(emojiStr);
|
||||
const applicableStarboards = await this.getStarboardOptsForSourceChannel(msg.channel);
|
||||
|
||||
for (const starboard of applicableStarboards) {
|
||||
// Instantly continue if the starboard is disabled
|
||||
if (!starboard.enabled) continue;
|
||||
// Can't star messages in the starboard channel itself
|
||||
if (msg.channel.id === starboard.channel_id) continue;
|
||||
|
||||
if (starboard.channel_whitelist) {
|
||||
const allowedChannelIds = starboard.channel_whitelist.split(",");
|
||||
if (!allowedChannelIds.includes(msg.channel.id)) continue;
|
||||
}
|
||||
|
||||
if (msg.channel.id === starboard.starboard_channel_id) continue;
|
||||
// Move reaction into DB at this point
|
||||
await this.starboardReactions.createStarboardReaction(msg.id, userId).catch();
|
||||
// If the message has already been posted to this starboard, we don't need to do anything else here
|
||||
const existingSavedMessage = await this.starboards.getStarboardMessageByStarboardIdAndMessageId(
|
||||
starboard.id,
|
||||
const starboardMessages = await this.starboardMessages.getMessagesForStarboardIdAndSourceMessageId(
|
||||
starboard.starboard_channel_id,
|
||||
msg.id,
|
||||
);
|
||||
if (existingSavedMessage) return;
|
||||
if (starboardMessages.length > 0) continue;
|
||||
|
||||
const reactionsCount = await this.countReactions(msg, emojiStr);
|
||||
|
||||
if (reactionsCount >= starboard.reactions_required) {
|
||||
await this.saveMessageToStarboard(msg, starboard);
|
||||
const reactions = await this.starboardReactions.getAllReactionsForMessageId(msg.id);
|
||||
const reactionsCount = reactions.length;
|
||||
if (reactionsCount >= starboard.positive_required) {
|
||||
await this.saveMessageToStarboard(msg, starboard.starboard_channel_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the specific reactions in the message, ignoring the message author
|
||||
*/
|
||||
async countReactions(msg: Message, reaction) {
|
||||
let reactionsCount = (msg.reactions[reaction] && msg.reactions[reaction].count) || 0;
|
||||
@d.event("messageReactionRemove")
|
||||
async onStarboardReactionRemove(msg: Message, emoji: { id: string; name: string }, userId: string) {
|
||||
await this.starboardReactions.deleteStarboardReaction(msg.id, userId);
|
||||
}
|
||||
|
||||
// Ignore self-stars
|
||||
const reactors = await msg.getReaction(reaction);
|
||||
if (reactors.some(u => u.id === msg.author.id)) reactionsCount--;
|
||||
|
||||
return reactionsCount;
|
||||
@d.event("messageReactionRemoveAll")
|
||||
async onMessageReactionRemoveAll(msg: Message) {
|
||||
await this.starboardReactions.deleteAllStarboardReactionsForMessageId(msg.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves/posts a message to the specified starboard. The message is posted as an embed and image attachments are
|
||||
* included as the embed image.
|
||||
*/
|
||||
async saveMessageToStarboard(msg: Message, starboard: Starboard) {
|
||||
const channel = this.guild.channels.get(starboard.channel_id);
|
||||
async saveMessageToStarboard(msg: Message, starboardChannelId: string) {
|
||||
const channel = this.guild.channels.get(starboardChannelId);
|
||||
if (!channel) return;
|
||||
|
||||
const time = moment(msg.timestamp, "x").format("YYYY-MM-DD [at] HH:mm:ss [UTC]");
|
||||
|
@ -308,18 +261,18 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
|
|||
content: `https://discordapp.com/channels/${this.guildId}/${msg.channel.id}/${msg.id}`,
|
||||
embed,
|
||||
});
|
||||
await this.starboards.createStarboardMessage(starboard.id, msg.id, starboardMessage.id);
|
||||
await this.starboardMessages.createStarboardMessage(channel.id, msg.id, starboardMessage.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a message from the specified starboard
|
||||
*/
|
||||
async removeMessageFromStarboard(msgId: string, starboard: Starboard) {
|
||||
const starboardMessage = await this.starboards.getStarboardMessageByStarboardIdAndMessageId(starboard.id, msgId);
|
||||
if (!starboardMessage) return;
|
||||
async removeMessageFromStarboard(msg: StarboardMessage) {
|
||||
await this.bot.deleteMessage(msg.starboard_channel_id, msg.starboard_message_id).catch(noop);
|
||||
}
|
||||
|
||||
await this.bot.deleteMessage(starboard.channel_id, starboardMessage.starboard_message_id).catch(noop);
|
||||
await this.starboards.deleteStarboardMessage(starboard.id, msgId);
|
||||
async removeMessageFromStarboardMessages(starboard_message_id: string, starboard_channel_id: string) {
|
||||
await this.starboardMessages.deleteStarboardMessage(starboard_message_id, starboard_channel_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,44 +281,71 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
|
|||
* TODO: When a message is removed from the starboard itself, i.e. the bot's embed is removed, also remove that message from the starboard_messages database table
|
||||
*/
|
||||
async onMessageDelete(msg: SavedMessage) {
|
||||
const starboardMessages = await this.starboards.with("starboard").getStarboardMessagesByMessageId(msg.id);
|
||||
if (!starboardMessages.length) return;
|
||||
let messages = await this.starboardMessages.getStarboardMessagesForMessageId(msg.id);
|
||||
if (messages.length > 0) {
|
||||
for (const starboardMessage of messages) {
|
||||
if (!starboardMessage.starboard_message_id) continue;
|
||||
this.removeMessageFromStarboard(starboardMessage).catch(noop);
|
||||
}
|
||||
} else {
|
||||
messages = await this.starboardMessages.getStarboardMessagesForStarboardMessageId(msg.id);
|
||||
if (messages.length === 0) return;
|
||||
|
||||
for (const starboardMessage of starboardMessages) {
|
||||
if (!starboardMessage.starboard) continue;
|
||||
this.removeMessageFromStarboard(starboardMessage.message_id, starboardMessage.starboard);
|
||||
for (const starboardMessage of messages) {
|
||||
if (!starboardMessage.starboard_channel_id) continue;
|
||||
this.removeMessageFromStarboardMessages(
|
||||
starboardMessage.starboard_message_id,
|
||||
starboardMessage.starboard_channel_id,
|
||||
).catch(noop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@d.command("starboard migrate_pins", "<pinChannelId:channelId> <starboardChannelId:channelId>")
|
||||
@d.command("starboard migrate_pins", "<pinChannelId:channelId> <starboardChannelId:channelId>", {
|
||||
extra: {
|
||||
info: {
|
||||
description:
|
||||
"Migrates all of a channels pins to starboard messages, posting them in the starboard channel. The old pins are not unpinned.",
|
||||
},
|
||||
},
|
||||
})
|
||||
@d.permission("can_migrate")
|
||||
async migratePinsCmd(msg: Message, args: { pinChannelId: string; starboardChannelId }) {
|
||||
const starboard = await this.starboards.getStarboardByChannelId(args.starboardChannelId);
|
||||
if (!starboard) {
|
||||
msg.channel.createMessage(errorMessage("The specified channel doesn't have a starboard!"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const starboards = await this.getStarboardOptsForStarboardChannel(this.bot.getChannel(args.starboardChannelId));
|
||||
if (!starboards) {
|
||||
msg.channel.createMessage(errorMessage("The specified channel doesn't have a starboard!")).catch(noop);
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = (await this.guild.channels.get(args.pinChannelId)) as GuildChannel & TextChannel;
|
||||
if (!channel) {
|
||||
msg.channel.createMessage(errorMessage("Could not find the specified channel to migrate pins from!"));
|
||||
return;
|
||||
}
|
||||
const channel = (await this.guild.channels.get(args.pinChannelId)) as GuildChannel & TextChannel;
|
||||
if (!channel) {
|
||||
msg.channel
|
||||
.createMessage(errorMessage("Could not find the specified channel to migrate pins from!"))
|
||||
.catch(noop);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.channel.createMessage(`Migrating pins from <#${channel.id}> to <#${args.starboardChannelId}>...`);
|
||||
msg.channel.createMessage(`Migrating pins from <#${channel.id}> to <#${args.starboardChannelId}>...`).catch(noop);
|
||||
|
||||
const pins = await channel.getPins();
|
||||
pins.reverse(); // Migrate pins starting from the oldest message
|
||||
const pins = await channel.getPins();
|
||||
pins.reverse(); // Migrate pins starting from the oldest message
|
||||
|
||||
for (const pin of pins) {
|
||||
const existingStarboardMessage = await this.starboards.getStarboardMessageByStarboardIdAndMessageId(
|
||||
starboard.id,
|
||||
pin.id,
|
||||
for (const pin of pins) {
|
||||
const existingStarboardMessage = await this.starboardMessages.getMessagesForStarboardIdAndSourceMessageId(
|
||||
args.starboardChannelId,
|
||||
pin.id,
|
||||
);
|
||||
if (existingStarboardMessage.length > 0) continue;
|
||||
await this.saveMessageToStarboard(pin, args.starboardChannelId);
|
||||
}
|
||||
|
||||
msg.channel.createMessage(successMessage("Pins migrated!")).catch(noop);
|
||||
} catch (error) {
|
||||
this.sendErrorMessage(
|
||||
msg.channel,
|
||||
"Sorry, but something went wrong!\nSyntax: `starboard migrate_pins <sourceChannelId> <starboardChannelid>`",
|
||||
);
|
||||
if (existingStarboardMessage) continue;
|
||||
|
||||
await this.saveMessageToStarboard(pin, starboard);
|
||||
}
|
||||
|
||||
msg.channel.createMessage(successMessage("Pins migrated!"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue