3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-17 23:25:02 +00:00

Fully functioning and loaded from the config

This commit is contained in:
Nils Blömeke 2019-07-27 22:29:29 +02:00
parent c2935bd1d7
commit 703b24c64e
6 changed files with 293 additions and 317 deletions

View file

@ -0,0 +1,54 @@
import { BaseGuildRepository } from "./BaseGuildRepository";
import { getRepository, Repository } from "typeorm";
import { StarboardMessage } from "./entities/StarboardMessage";
export class GuildStarboardMessages extends BaseGuildRepository {
private allStarboardMessages: Repository<StarboardMessage>;
constructor(guildId) {
super(guildId);
this.allStarboardMessages = getRepository(StarboardMessage);
}
async getStarboardMessagesForMessageId(messageId: string) {
return this.allStarboardMessages
.createQueryBuilder()
.where("guild_id = :gid", { gid: this.guildId })
.andWhere("message_id = :msgid", { msgid: messageId })
.getMany();
}
async getStarboardMessagesForStarboardMessageId(starboardMessageId: string) {
return this.allStarboardMessages
.createQueryBuilder()
.where("guild_id = :gid", { gid: this.guildId })
.andWhere("starboard_message_id = :messageId", { messageId: starboardMessageId })
.getMany();
}
async getMessagesForStarboardIdAndSourceMessageId(starboardId: string, sourceMessageId: string) {
return await this.allStarboardMessages
.createQueryBuilder()
.where("guild_id = :gid", { gid: this.guildId })
.andWhere("message_id = :msgId", { msgId: sourceMessageId })
.andWhere("starboard_channel_id = :sbId", { sbId: starboardId })
.getMany();
}
async createStarboardMessage(starboardId: string, messageId: string, starboardMessageId: string) {
await this.allStarboardMessages.insert({
message_id: messageId,
starboard_message_id: starboardMessageId,
starboard_channel_id: starboardId,
guild_id: this.guildId,
});
}
async deleteStarboardMessage(starboardMessageId: string, starboardChannelId: string) {
await this.allStarboardMessages.delete({
guild_id: this.guildId,
starboard_message_id: starboardMessageId,
starboard_channel_id: starboardChannelId,
});
}
}

View file

@ -1,84 +0,0 @@
import { BaseGuildRepository } from "./BaseGuildRepository";
import { getRepository, Repository } from "typeorm";
import { Starboard } from "./entities/Starboard";
import { StarboardMessage } from "./entities/StarboardMessage";
export class GuildStarboards extends BaseGuildRepository {
private starboards: Repository<Starboard>;
private starboardMessages: Repository<StarboardMessage>;
constructor(guildId) {
super(guildId);
this.starboards = getRepository(Starboard);
this.starboardMessages = getRepository(StarboardMessage);
}
getStarboardByChannelId(channelId): Promise<Starboard> {
return this.starboards.findOne({
where: {
guild_id: this.guildId,
channel_id: channelId,
},
});
}
getStarboardsByEmoji(emoji): Promise<Starboard[]> {
return this.starboards.find({
where: {
guild_id: this.guildId,
emoji,
},
});
}
getStarboardMessageByStarboardIdAndMessageId(starboardId, messageId): Promise<StarboardMessage> {
return this.starboardMessages.findOne({
relations: this.getRelations(),
where: {
starboard_id: starboardId,
message_id: messageId,
},
});
}
getStarboardMessagesByMessageId(id): Promise<StarboardMessage[]> {
return this.starboardMessages.find({
relations: this.getRelations(),
where: {
message_id: id,
},
});
}
async createStarboardMessage(starboardId, messageId, starboardMessageId): Promise<void> {
await this.starboardMessages.insert({
starboard_id: starboardId,
message_id: messageId,
starboard_message_id: starboardMessageId,
});
}
async deleteStarboardMessage(starboardId, messageId): Promise<void> {
await this.starboardMessages.delete({
starboard_id: starboardId,
message_id: messageId,
});
}
async create(channelId: string, channelWhitelist: string[], emoji: string, reactionsRequired: number): Promise<void> {
await this.starboards.insert({
guild_id: this.guildId,
channel_id: channelId,
channel_whitelist: channelWhitelist ? channelWhitelist.join(",") : null,
emoji,
reactions_required: reactionsRequired,
});
}
async delete(channelId: string): Promise<void> {
await this.starboards.delete({
guild_id: this.guildId,
channel_id: channelId,
});
}
}

View file

@ -1,23 +0,0 @@
import { Entity, Column, PrimaryColumn, OneToMany } from "typeorm";
import { CaseNote } from "./CaseNote";
import { StarboardMessage } from "./StarboardMessage";
@Entity("starboards")
export class Starboard {
@Column()
@PrimaryColumn()
id: number;
@Column() guild_id: string;
@Column() channel_id: string;
@Column() channel_whitelist: string;
@Column() emoji: string;
@Column() reactions_required: number;
@OneToMany(type => StarboardMessage, msg => msg.starboard)
starboardMessages: StarboardMessage[];
}

View file

@ -1,23 +1,20 @@
import { Entity, Column, PrimaryColumn, OneToMany, ManyToOne, JoinColumn, OneToOne } from "typeorm"; import { Entity, Column, PrimaryColumn, OneToMany, ManyToOne, JoinColumn, OneToOne } from "typeorm";
import { Starboard } from "./Starboard";
import { Case } from "./Case";
import { SavedMessage } from "./SavedMessage"; import { SavedMessage } from "./SavedMessage";
@Entity("starboard_messages") @Entity("starboard_messages")
export class StarboardMessage { export class StarboardMessage {
@Column() @Column()
@PrimaryColumn() message_id: string;
starboard_id: number;
@Column() @Column()
@PrimaryColumn() @PrimaryColumn()
message_id: string; starboard_message_id: string;
@Column() starboard_message_id: string; @Column()
starboard_channel_id: string;
@ManyToOne(type => Starboard, sb => sb.starboardMessages) @Column()
@JoinColumn({ name: "starboard_id" }) guild_id: string;
starboard: Starboard;
@OneToOne(type => SavedMessage) @OneToOne(type => SavedMessage)
@JoinColumn({ name: "message_id" }) @JoinColumn({ name: "message_id" })

View file

@ -0,0 +1,103 @@
import { MigrationInterface, QueryRunner, Table, TableColumn, createQueryBuilder } from "typeorm";
export class moveStarboardsToConfig1564150449297 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<any> {
// Create the new column for the channels id
const chanid_column = new TableColumn({
name: "starboard_channel_id",
type: "bigint",
unsigned: true,
});
await queryRunner.addColumn("starboard_messages", chanid_column);
// Since we are removing the guild_id with the starboards table, we might want it here
const guid_column = new TableColumn({
name: "guild_id",
type: "bigint",
unsigned: true,
});
await queryRunner.addColumn("starboard_messages", guid_column);
// Migrate the old starboard_id to the new starboard_channel_id
await queryRunner.query(`
UPDATE starboard_messages AS sm
JOIN starboards AS sb
ON sm.starboard_id = sb.id
SET sm.starboard_channel_id = sb.channel_id, sm.guild_id = sb.guild_id;
`);
// Drop the starboard_id column as it is now obsolete
await queryRunner.dropColumn("starboard_messages", "starboard_id");
// Set new Primary Key
await queryRunner.dropPrimaryKey("starboard_messages");
await queryRunner.createPrimaryKey("starboard_messages", ["starboard_message_id"]);
// Finally, drop the starboards channel as it is now obsolete
await queryRunner.dropTable("starboards", true);
}
public async down(queryRunner: QueryRunner): Promise<any> {
await queryRunner.dropColumn("starboard_messages", "starboard_channel_id");
await queryRunner.dropColumn("starboard_messages", "guild_id");
const sbId = new TableColumn({
name: "starboard_id",
type: "int",
unsigned: true,
});
await queryRunner.addColumn("starboard_messages", sbId);
await queryRunner.dropPrimaryKey("starboard_messages");
await queryRunner.createPrimaryKey("starboard_messages", ["starboard_id", "message_id"]);
await queryRunner.createTable(
new Table({
name: "starboards",
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: "channel_whitelist",
type: "text",
isNullable: true,
default: null,
},
{
name: "emoji",
type: "varchar",
length: "64",
},
{
name: "reactions_required",
type: "smallint",
unsigned: true,
},
],
indices: [
{
columnNames: ["guild_id", "emoji"],
},
{
columnNames: ["guild_id", "channel_id"],
isUnique: true,
},
],
}),
);
}
}

View file

@ -1,34 +1,48 @@
import { decorators as d, waitForReply, utils as knubUtils, IBasePluginConfig, IPluginOptions } from "knub"; import { decorators as d, IPluginOptions } from "knub";
import { ZeppelinPlugin } from "./ZeppelinPlugin"; import { ZeppelinPlugin } from "./ZeppelinPlugin";
import { GuildStarboards } from "../data/GuildStarboards";
import { GuildChannel, Message, TextChannel } from "eris"; import { GuildChannel, Message, TextChannel } from "eris";
import { import { errorMessage, getUrlsInString, noop, successMessage, tNullable } from "../utils";
customEmojiRegex,
errorMessage,
getEmojiInString,
getUrlsInString,
noop,
snowflakeRegex,
successMessage,
} from "../utils";
import { Starboard } from "../data/entities/Starboard";
import path from "path"; import path from "path";
import moment from "moment-timezone"; import moment from "moment-timezone";
import { GuildSavedMessages } from "../data/GuildSavedMessages"; import { GuildSavedMessages } from "../data/GuildSavedMessages";
import { SavedMessage } from "../data/entities/SavedMessage"; import { SavedMessage } from "../data/entities/SavedMessage";
import * as t from "io-ts"; import * as t from "io-ts";
import { GuildStarboardMessages } from "../data/GuildStarboardMessages";
import { StarboardMessage } from "src/data/entities/StarboardMessage";
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),
allow_multistar: tNullable(t.boolean),
negative_emojis: tNullable(t.array(t.string)),
bot_reacts: tNullable(t.boolean),
enabled: tNullable(t.boolean),
});
type TStarboardOpts = t.TypeOf<typeof StarboardOpts>;
const ConfigSchema = t.type({ const ConfigSchema = t.type({
entries: t.record(t.string, StarboardOpts),
can_manage: t.boolean, can_manage: t.boolean,
}); });
type TConfigSchema = t.TypeOf<typeof ConfigSchema>; type TConfigSchema = t.TypeOf<typeof ConfigSchema>;
const defaultStarboardOpts: Partial<TStarboardOpts> = {
positive_emojis: ["⭐"],
positive_required: 5,
allow_multistar: false,
negative_emojis: [],
enabled: true,
};
export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> { export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
public static pluginName = "starboard"; public static pluginName = "starboard";
protected static configSchema = ConfigSchema; protected static configSchema = ConfigSchema;
protected starboards: GuildStarboards;
protected savedMessages: GuildSavedMessages; protected savedMessages: GuildSavedMessages;
protected starboardMessages: GuildStarboardMessages;
private onMessageDeleteFn; private onMessageDeleteFn;
@ -36,6 +50,7 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
return { return {
config: { config: {
can_manage: false, can_manage: false,
entries: {},
}, },
overrides: [ overrides: [
@ -49,9 +64,23 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
}; };
} }
protected getStarboardOptsForSourceChannelId(sourceChannel): TStarboardOpts[] {
const config = this.getConfigForChannel(sourceChannel);
return Object.values(config.entries)
.filter(opts => opts.source_channel_ids.includes(sourceChannel.id))
.map(opts => Object.assign({}, defaultStarboardOpts, opts));
}
protected getStarboardOptsForStarboardChannelId(starboardChannel): TStarboardOpts[] {
const config = this.getConfigForChannel(starboardChannel);
return Object.values(config.entries)
.filter(opts => opts.starboard_channel_id === starboardChannel.id)
.map(opts => Object.assign({}, defaultStarboardOpts, opts));
}
onLoad() { onLoad() {
this.starboards = GuildStarboards.getGuildInstance(this.guildId);
this.savedMessages = GuildSavedMessages.getGuildInstance(this.guildId); this.savedMessages = GuildSavedMessages.getGuildInstance(this.guildId);
this.starboardMessages = GuildStarboardMessages.getGuildInstance(this.guildId);
this.onMessageDeleteFn = this.onMessageDelete.bind(this); this.onMessageDeleteFn = this.onMessageDelete.bind(this);
this.savedMessages.events.on("delete", this.onMessageDeleteFn); this.savedMessages.events.on("delete", this.onMessageDeleteFn);
@ -61,136 +90,6 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
this.savedMessages.events.off("delete", this.onMessageDeleteFn); 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 * 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. * the required threshold. If they do, post the message in the starboard channel.
@ -208,52 +107,65 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
} }
} }
const emojiStr = emoji.id ? `${emoji.name}:${emoji.id}` : emoji.name; const applicableStarboards = await this.getStarboardOptsForSourceChannelId(msg.channel);
const applicableStarboards = await this.starboards.getStarboardsByEmoji(emojiStr);
for (const starboard of applicableStarboards) { 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 // Can't star messages in the starboard channel itself
if (msg.channel.id === starboard.channel_id) continue; if (msg.channel.id === starboard.starboard_channel_id) continue;
if (starboard.channel_whitelist) {
const allowedChannelIds = starboard.channel_whitelist.split(",");
if (!allowedChannelIds.includes(msg.channel.id)) continue;
}
// If the message has already been posted to this starboard, we don't need to do anything else here // 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( const starboardMessages = await this.starboardMessages.getMessagesForStarboardIdAndSourceMessageId(
starboard.id, starboard.starboard_channel_id,
msg.id, msg.id,
); );
if (existingSavedMessage) return; if (starboardMessages.length > 0) continue;
const reactionsCount = await this.countReactions(msg, emojiStr); const reactionsCount = await this.countReactions(msg, starboard.positive_emojis, starboard.allow_multistar);
if (reactionsCount >= starboard.positive_required) {
if (reactionsCount >= starboard.reactions_required) { await this.saveMessageToStarboard(msg, starboard.starboard_channel_id);
await this.saveMessageToStarboard(msg, starboard);
} }
} }
} }
/** /**
* Counts the specific reactions in the message, ignoring the message author * Tallys the reaction count of ALL reactions in the array
*/ */
async countReactions(msg: Message, reaction) { async countReactions(msg: Message, counted: string[], countDouble: boolean) {
let reactionsCount = (msg.reactions[reaction] && msg.reactions[reaction].count) || 0; let totalCount = [];
countDouble = countDouble || false;
// Ignore self-stars for (const emoji of counted) {
totalCount = await this.countReactionsForEmoji(msg, emoji, totalCount, countDouble);
}
return totalCount.length;
}
/**
* Counts the emoji specific reactions in the message, ignoring the message author and the bot
*/
async countReactionsForEmoji(msg: Message, reaction, usersAlreadyCounted: string[], countDouble: boolean) {
countDouble = countDouble || false;
// Ignore self-stars, bot-stars and multi-stars
const reactors = await msg.getReaction(reaction); const reactors = await msg.getReaction(reaction);
if (reactors.some(u => u.id === msg.author.id)) reactionsCount--; for (const user of reactors) {
if (user.id === msg.author.id) continue;
if (user.id === this.bot.user.id) continue;
if (!countDouble && usersAlreadyCounted.includes(user.id)) continue;
usersAlreadyCounted.push(user.id);
}
return reactionsCount; return usersAlreadyCounted;
} }
/** /**
* Saves/posts a message to the specified starboard. The message is posted as an embed and image attachments are * Saves/posts a message to the specified starboard. The message is posted as an embed and image attachments are
* included as the embed image. * included as the embed image.
*/ */
async saveMessageToStarboard(msg: Message, starboard: Starboard) { async saveMessageToStarboard(msg: Message, starboardChannelId: string) {
const channel = this.guild.channels.get(starboard.channel_id); const channel = this.guild.channels.get(starboardChannelId);
if (!channel) return; if (!channel) return;
const time = moment(msg.timestamp, "x").format("YYYY-MM-DD [at] HH:mm:ss [UTC]"); const time = moment(msg.timestamp, "x").format("YYYY-MM-DD [at] HH:mm:ss [UTC]");
@ -307,18 +219,18 @@ export class StarboardPlugin extends ZeppelinPlugin<TConfigSchema> {
content: `https://discordapp.com/channels/${this.guildId}/${msg.channel.id}/${msg.id}`, content: `https://discordapp.com/channels/${this.guildId}/${msg.channel.id}/${msg.id}`,
embed, 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 * Remove a message from the specified starboard
*/ */
async removeMessageFromStarboard(msgId: string, starboard: Starboard) { async removeMessageFromStarboard(msg: StarboardMessage) {
const starboardMessage = await this.starboards.getStarboardMessageByStarboardIdAndMessageId(starboard.id, msgId); await this.bot.deleteMessage(msg.starboard_channel_id, msg.starboard_message_id).catch(noop);
if (!starboardMessage) return; }
await this.bot.deleteMessage(starboard.channel_id, starboardMessage.starboard_message_id).catch(noop); async removeMessageFromStarboardMessages(starboard_message_id: string, starboard_channel_id: string) {
await this.starboards.deleteStarboardMessage(starboard.id, msgId); await this.starboardMessages.deleteStarboardMessage(starboard_message_id, starboard_channel_id);
} }
/** /**
@ -327,44 +239,61 @@ 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 * 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) { async onMessageDelete(msg: SavedMessage) {
const starboardMessages = await this.starboards.with("starboard").getStarboardMessagesByMessageId(msg.id); let messages = await this.starboardMessages.getStarboardMessagesForMessageId(msg.id);
if (!starboardMessages.length) return; if (messages.length > 0) {
for (const starboardMessage of messages) {
if (!starboardMessage.starboard_message_id) continue;
this.removeMessageFromStarboard(starboardMessage);
}
} else {
messages = await this.starboardMessages.getStarboardMessagesForStarboardMessageId(msg.id);
if (messages.length === 0) return;
for (const starboardMessage of starboardMessages) { for (const starboardMessage of messages) {
if (!starboardMessage.starboard) continue; if (!starboardMessage.starboard_channel_id) continue;
this.removeMessageFromStarboard(starboardMessage.message_id, starboardMessage.starboard); this.removeMessageFromStarboardMessages(
starboardMessage.starboard_message_id,
starboardMessage.starboard_channel_id,
);
}
} }
} }
@d.command("starboard migrate_pins", "<pinChannelId:channelid> <starboardChannelId:channelid>") @d.command("starboard migrate_pins", "<pinChannelId:channelid> <starboardChannelId:channelid>")
async migratePinsCmd(msg: Message, args: { pinChannelId: string; starboardChannelId }) { async migratePinsCmd(msg: Message, args: { pinChannelId: string; starboardChannelId }) {
const starboard = await this.starboards.getStarboardByChannelId(args.starboardChannelId); try {
if (!starboard) { const starboards = await this.getStarboardOptsForStarboardChannelId(this.bot.getChannel(args.starboardChannelId));
msg.channel.createMessage(errorMessage("The specified channel doesn't have a starboard!")); if (!starboards) {
return; msg.channel.createMessage(errorMessage("The specified channel doesn't have a starboard!"));
} return;
}
const channel = (await this.guild.channels.get(args.pinChannelId)) as GuildChannel & TextChannel; const channel = (await this.guild.channels.get(args.pinChannelId)) as GuildChannel & TextChannel;
if (!channel) { if (!channel) {
msg.channel.createMessage(errorMessage("Could not find the specified channel to migrate pins from!")); msg.channel.createMessage(errorMessage("Could not find the specified channel to migrate pins from!"));
return; return;
} }
msg.channel.createMessage(`Migrating pins from <#${channel.id}> to <#${args.starboardChannelId}>...`); msg.channel.createMessage(`Migrating pins from <#${channel.id}> to <#${args.starboardChannelId}>...`);
const pins = await channel.getPins(); const pins = await channel.getPins();
pins.reverse(); // Migrate pins starting from the oldest message pins.reverse(); // Migrate pins starting from the oldest message
for (const pin of pins) { for (const pin of pins) {
const existingStarboardMessage = await this.starboards.getStarboardMessageByStarboardIdAndMessageId( const existingStarboardMessage = await this.starboardMessages.getMessagesForStarboardIdAndSourceMessageId(
starboard.id, args.starboardChannelId,
pin.id, pin.id,
);
if (existingStarboardMessage.length > 0) continue;
await this.saveMessageToStarboard(pin, args.starboardChannelId);
}
msg.channel.createMessage(successMessage("Pins migrated!"));
} 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!"));
} }
} }