psyduck-plugin-quotes
Version:
205 lines (195 loc) • 9.36 kB
text/typescript
import 'reflect-metadata';
import commandLineArgs, { CommandLineOptions } from 'command-line-args';
import * as Discord from 'discord.js';
import _ from 'lodash';
import moment from 'moment';
import { BotPlugin } from 'psyduck-contracts';
import { PluginQuoteUser, UserQuote, GuildUser, Guild } from 'psyduck-models';
import { ConnectionOptions, createConnection, Repository } from 'typeorm';
export const commandOptions = [
{ name: 'pluck', alias: 'p', type: Number },
{ name: 'help', alias: 'h', type: Boolean },
{ name: 'user', alias: 'u', type: String, defaultOption: true }
];
export const userFormatRegex = new RegExp(/^(<@.+>)|(<!.+>)$/);
export enum CommandType { Quote, Random }
export default class PsyduckQuotes implements BotPlugin {
public users!: Repository<PluginQuoteUser>;
public quotes!: Repository<UserQuote>;
public connectionOptions: ConnectionOptions;
constructor(options: any) {
this.connectionOptions = options;
}
connect() {
createConnection({
...this.connectionOptions,
synchronize: true,
logging: false,
entities: [
PluginQuoteUser, UserQuote
]
}).then(async connection => {
this.users = connection.getRepository(PluginQuoteUser);
this.quotes = connection.getRepository(UserQuote);
}).catch(error => console.log(error));
}
onMessage(message: Discord.Message) {
if (message.author.bot) {
return;
}
if (message.content.length <= 1) {
return;
}
const dupe = RegExp(/^(.)\1*$/gm);
if (dupe.test(message.content)) {
return;
}
if (message.content.startsWith('!')) {
const commandCode = message.content.slice(1, message.content.indexOf(' '));
if (_.includes(['quote', 'q', 'grab', 'g'], commandCode)) {
this.prepareHandler(message, CommandType.Quote);
} else if (_.includes(['random', 'r'], commandCode)) {
this.prepareHandler(message, CommandType.Random);
}
}
}
parseParameters(messageContent: string): string[] {
const params = messageContent.match(/ (?=\S)[^'\s]*(?:'[^\\']*(?:\\[\s\S][^\\']*)*'[^'\s]*)*/g) || [];
return params.map(p => p.trim());
}
hydrateOptions(parameters: string[]): CommandLineOptions {
return commandLineArgs(commandOptions, { argv: parameters, partial: true });
}
extractUserId(userId: string): string | undefined {
const matches = userId.match(/(?<=@|!)(.*)(?=>)/gm);
if (matches && matches.length === 1) {
return matches[0].replace('!', '');
}
return undefined;
}
prepareHandler(message: Discord.Message, commandType: CommandType) {
const parameters = this.parseParameters(message.content);
if (parameters.length > 0) {
const options = this.hydrateOptions(parameters);
if (options.help === true) {
this.sendHelp(message);
} else {
if (!options.user || !userFormatRegex.test(options.user)) {
if (commandType === CommandType.Quote) {
message.channel.send(`Unable to quote. User missing or wrong format.`);
} else if (commandType === CommandType.Random) {
message.channel.send(`Unable to recall. User missing or wrong format.`);
}
} else {
const userId = this.extractUserId(options.user);
if (!userId) {
message.channel.send('Unable to parse user id from supplied parameters.');
}
if (userId === message.author.id) {
if (commandType === CommandType.Quote) {
message.channel.send('It looks like you are trying to quote yourself! Don\'t take all the fun out of it!');
} else if (commandType === CommandType.Random) {
message.channel.send('It looks like you are trying to recall yourself! Don\'t take all the fun out of it!');
}
} else {
if (commandType === CommandType.Quote) {
this.saveQuote(message, userId!, options.pluck);
} else if (commandType === CommandType.Random) {
this.recallQuote(message, userId!);
}
}
}
}
}
}
sendHelp(message: Discord.Message) {
const embed = {
'title': 'Psyduck Quote Plugin Help',
'description': 'Use this plugin to quote users and recall random quotes from the past.',
'url': 'https://github.com/galactic0wl/psyduck-plugin-quotes',
'color': 3201505,
'author': {
'name': 'Marky Mark',
'url': 'https://github.com/galactic0wl/psyduck-plugin-quotes',
'icon_url': 'https://cdn.discordapp.com/avatars/341668692133806080/a_252e33ff6363f93849e75a5decf9f10a.gif'
},
'fields': [
{
'name': 'Commands',
'value': 'The following commands are available to standard users.\n\n'
},
{
'name': '[q]uote & [g]rab',
'value': 'To save a quote from a user run `!q @MentionUser` to grab the last message. If you would like to target a specific message within reason you can run `!quote @MentionUser -p 3` to save the second to the last message of the user, or, 3 messages up.\n\n**Parameters** ```--pluck <count> or -p <count>```'
},
{
'name': '[r]andom',
'value': 'To recall a quote from a user run `!r @MentionUser` to grab a random message from the past.'
}
]
};
message.channel.send({ embed });
}
async getUser(userId: string) {
return await this.users.findOne({
user: {
id: userId
}
}, { relations: ['quotes'] });
}
createUser(guildId: string, serverName: string, guildAvatarUrl: string, userId: string, userName: string, avatarUrl: string) {
const user = new PluginQuoteUser();
const guildUser = new GuildUser();
guildUser.id = userId;
guildUser.userName = userName;
guildUser.avatarUrl = avatarUrl;
const guild = new Guild();
guild.id = guildId;
guild.serverName = serverName;
guild.avatarUrl = guildAvatarUrl;
guildUser.guild = guild;
user.user = guildUser;
user.quotes = [];
return user;
}
async recallQuote(message: Discord.Message, userId: string) {
const user = await this.getUser(userId);
if (!user || user.quotes.length === 0) {
message.channel.send(`Our records indicated that the user has zero quotes. While this is unfortunate, it may be resolved.`);
} else {
const quote = _.sample(user.quotes)!;
message.channel.send(`Remember this one <@${userId}>? '${quote.content}' - Snagged by <@${quote.capturerId}> at ${quote.captureTime}`);
}
}
buildQuote(capturerId: string, content: string): UserQuote {
const quote = new UserQuote();
quote.capturerId = capturerId;
quote.content = content;
quote.captureTime = moment().format('ddd MMM DD YYYY h:mm A');
return quote;
}
async saveQuote(message: Discord.Message, userId: string, pluck = 1) {
const messageMap = await message.channel.fetchMessages({ limit: 100 });
const messages = [...messageMap.values()].filter(m => m.author.id === userId);
if (messages.length > 0) {
if (messages.length < pluck) {
message.channel.send('You attempted to pluck a message out of my range. Please try again.');
} else {
let user = await this.getUser(userId);
if (!user) {
user = this.createUser(message.guild.id, message.guild.name, message.guild.iconURL, message.author.id, message.author.username, message.author.avatarURL);
user.quotes.push(this.buildQuote(message.author.id, messages[pluck - 1].content));
} else {
if (user.quotes.filter(u => u.content === messages[pluck - 1].content).length <= 0) {
user.quotes.push(this.buildQuote(message.author.id, messages[pluck - 1].content));
} else {
message.channel.send('It looks like I already have a record of this quote.');
return;
}
}
await this.users.save(user);
message.channel.send(`Plucked: ${messages[pluck - 1]}`);
}
}
}
}