Skip to content

Commit

Permalink
feat(message-resolver): search in cache when only given ID (#438)
Browse files Browse the repository at this point in the history
* feat(message-resolver): search in cache when only given ID

* feat: add options.scan, add docs, respect options.channel

* fix: make channel take precedence over scan

* fix: order checks correctly
  • Loading branch information
noftaly committed Jun 29, 2022
1 parent 99b4446 commit cfb3547
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 9 deletions.
6 changes: 3 additions & 3 deletions src/arguments/CoreMessage.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import type { PieceContext } from '@sapphire/pieces';
import type { Message } from 'discord.js';
import { resolveMessage } from '../lib/resolvers';
import { MessageResolverOptions, resolveMessage } from '../lib/resolvers';
import { Argument } from '../lib/structures/Argument';

export class CoreArgument extends Argument<Message> {
public constructor(context: PieceContext) {
super(context, { name: 'message' });
}

public async run(parameter: string, context: { channel?: Message['channel'] } & Argument.Context): Argument.AsyncResult<Message> {
public async run(parameter: string, context: Omit<MessageResolverOptions, 'message'> & Argument.Context): Argument.AsyncResult<Message> {
const channel = context.channel ?? context.message.channel;
const resolved = await resolveMessage(parameter, { message: context.message, channel: context.channel });
const resolved = await resolveMessage(parameter, { message: context.message, channel: context.channel, scan: context.scan ?? false });
if (resolved.success) return this.ok(resolved.value);
return this.error({
parameter,
Expand Down
47 changes: 41 additions & 6 deletions src/lib/resolvers/message.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,63 @@
import { ChannelMessageRegex, MessageLinkRegex, SnowflakeRegex } from '@sapphire/discord-utilities';
import { GuildBasedChannelTypes, isNewsChannel, isTextChannel, TextBasedChannelTypes } from '@sapphire/discord.js-utilities';
import {
GuildBasedChannelTypes,
isGuildBasedChannel,
isNewsChannel,
isTextBasedChannel,
isTextChannel,
TextBasedChannelTypes
} from '@sapphire/discord.js-utilities';
import { container } from '@sapphire/pieces';
import { err, ok, Result } from '@sapphire/result';
import type { Awaitable } from '@sapphire/utilities';
import { Message, Permissions, Snowflake, User } from 'discord.js';
import { Identifiers } from '../errors/Identifiers';

interface MessageResolverOptions {
/**
* Options to resolve a message from a string, given a certain context.
*/
export interface MessageResolverOptions {
/**
* Channel to resolve the message in.
* @default message.channel
*/
channel?: TextBasedChannelTypes;
/**
* Base message to resolve the message from (e.g. pick the channel if not given).
*/
message: Message;
/**
* Whether to scan the entire guild cache for the message.
* If channel is given with this option, this option is ignored.
* @default false
*/
scan?: boolean;
}

export async function resolveMessage(parameter: string, options: MessageResolverOptions): Promise<Result<Message, Identifiers.ArgumentMessageError>> {
const channel = options.channel ?? options.message.channel;
const message =
(await resolveById(parameter, channel)) ??
(await resolveById(parameter, options)) ??
(await resolveByLink(parameter, options.message)) ??
(await resolveByChannelAndMessage(parameter, options.message));
if (message) return ok(message);
return err(Identifiers.ArgumentMessageError);
}

function resolveById(parameter: string, channel: TextBasedChannelTypes): Awaitable<Message | null> {
return SnowflakeRegex.test(parameter) ? channel.messages.fetch(parameter as Snowflake) : null;
function resolveById(parameter: string, options: MessageResolverOptions): Awaitable<Message | null> {
if (!SnowflakeRegex.test(parameter)) return null;

if (options.channel) return options.channel.messages.fetch(parameter as Snowflake);

if (options.scan && isGuildBasedChannel(options.message.channel)) {
for (const channel of options.message.channel.guild.channels.cache.values()) {
if (!isTextBasedChannel(channel)) continue;

const message = channel.messages.cache.get(parameter);
if (message) return message;
}
}

return options.message.channel.messages.fetch(parameter as Snowflake);
}

async function resolveByLink(parameter: string, message: Message): Promise<Message | null> {
Expand Down

0 comments on commit cfb3547

Please sign in to comment.