Patching Functions

Run code before, after or instead of Discord's functions.

Wrap one of Discord's functions so every call to it also runs your callback. Pass the object, the function's name on it, and your callback.

Quick Start

src/message-log/index.ts
import { metro, patcher } from '@unbound-app/api';

export function startMessageLog() {
	const Messages = metro.findByProps('sendMessage', 'receiveMessage');

	if (!Messages) {
		logger.warn('Message log skipped: Discord moved sendMessage.');
		return;
	}

	patcher.before(Messages, 'sendMessage', ({ args }) => {
		logger.log('Sending to channel', args[0]);
	});
}

Reference

Every function is on patcher, from @unbound-app/api. Each returns a function that removes the patch.

FunctionRunsCallback getsCallback returns
patcher.before(parent, key, callback, options?)Before the original{ args, this }New args array, or nothing
patcher.instead(parent, key, callback, options?)In place of the original{ args, original, this }The result
patcher.after(parent, key, callback, options?)After the original{ args, result, this }New result, or nothing
patcher.react.before|instead|after(...)The same, for a renderSee Patching Components
patcher.unpatchAll()Removes every patch your plugin made

this is the object the function was called on. The only option is once: true, which removes the patch after one run. Full signatures: patcher in the SDK reference.

Patch Kinds

Return a new array to replace the arguments.

import { metro, patcher } from '@unbound-app/api';

export function patchSendMessage() {
	const Messages = metro.findByProps('sendMessage', 'receiveMessage');

	if (!Messages) {
		logger.warn('Trimming skipped: Discord moved sendMessage.');
		return;
	}

	patcher.before(Messages, 'sendMessage', ({ args }) => {
		const [channelId, message] = args;

		return [channelId, { ...message, content: message.content.trim() }];
	});
}

Undoing a Patch

The Addon scope removes your patches after stop returns. To remove one sooner, call the function the patch returns.

import { flux, patcher } from '@unbound-app/api';

const UserStore = flux.getStore('User');

const unpatch = patcher.after(UserStore, 'getCurrentUser', ({ result }) => {
	logger.log('Current user is', result?.username);
});

// later
unpatch();

Unloaded Modules

You can patch a lazy lookup before its module loads. The patch applies when Discord loads it, never earlier.

Shared Functions

Other plugins may patch the same function. Every patch runs, chained.

  • Change only what you need; return the rest unchanged.
  • Expect result already changed by another after patch.
  • In instead, call original unless you mean to replace Discord's behavior.

Next