Skip to content

Instantly share code, notes, and snippets.

@TheBrokenRail
Last active January 22, 2023 03:41
Show Gist options
  • Save TheBrokenRail/4b37e7c44e8f721d8bd845050d034c16 to your computer and use it in GitHub Desktop.
Save TheBrokenRail/4b37e7c44e8f721d8bd845050d034c16 to your computer and use it in GitHub Desktop.
ShakespeareBot
import { ChatGPTAPIBrowser, ChatResponse, SendMessageOptions } from 'chatgpt';
import { Client, GatewayIntentBits, Events, PermissionsBitField, WebhookCreateMessageOptions, MessageType, MessageFlags, TextChannel, ThreadChannel, WebhookEditMessageOptions, ForumChannel, Webhook } from 'discord.js';
import { RateLimiter } from 'limiter';
// Login To ChatGPT
const api = new ChatGPTAPIBrowser({
email: '<Your Email>',
password: '<Your Password>',
isGoogleLogin: true
});
await api.initSession();
// Create Discord Client
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent
]
});
// Login To Discord
const TOKEN = '<Your Token>';
client.once(Events.ClientReady, c => {
console.log(`Ready! Logged in as ${c.user.tag}`);
});
client.login(TOKEN);
// Rate Limiter
const limiter = new RateLimiter({tokensPerInterval: 10, interval: 'minute'});
// Prevent ChatGPT Message Interleaving
let lastChatPromise: Promise<ChatResponse | string | null> = Promise.resolve(null);
function sendMessage(api: ChatGPTAPIBrowser, prompt: string, options: SendMessageOptions): Promise<ChatResponse | string | null> {
lastChatPromise = lastChatPromise.then(async () => {
try {
await limiter.removeTokens(1);
return await api.sendMessage(prompt, options);
} catch (e) {
return String(e);
}
});
return lastChatPromise;
}
async function sendMessageSafe(api: ChatGPTAPIBrowser, prompt: string, options: SendMessageOptions): Promise<ChatResponse | null> {
const result = await sendMessage(api, prompt, options);
if (typeof result === 'string') {
throw result;
}
return result;
}
// Message Handler
client.on(Events.MessageCreate, async message => {
// Check Thread
let channel: TextChannel | ForumChannel | null = null;
let thread: ThreadChannel | null = null;
if (message.channel instanceof ThreadChannel) {
const parent = message.channel.parent
if (parent instanceof TextChannel || parent instanceof ForumChannel) {
channel = parent;
thread = message.channel;
}
} else if (message.channel instanceof TextChannel) {
channel = message.channel;
}
// Check Role
if (!channel || message.system || !message.deletable || !message.member || !message.member.roles.cache.has('<Target Role>')) {
return;
}
// Check Permission
if (!channel.permissionsFor(client.user!)?.has(PermissionsBitField.Flags.SendMessages)) {
return;
};
// Fake User
const webhookName = 'ShakespeareBot';
let webhook: Webhook | null = null;
const webhooks = (await channel.fetchWebhooks()).values();
for (const potentialWebhook of webhooks) {
if (potentialWebhook.name === webhookName) {
webhook = potentialWebhook;
break;
}
}
if (!webhook) {
webhook = await channel.createWebhook({
name: webhookName
});
}
// Send Temporary Message
const newMessage: WebhookCreateMessageOptions = {
content: '*Translating message...*',
files: Array.from(message.attachments.values()),
embeds: message.embeds,
tts: message.tts,
avatarURL: message.member.displayAvatarURL(),
username: message.member.displayName
};
if (message.flags.has(MessageFlags.SuppressEmbeds)) {
newMessage.flags = MessageFlags.SuppressEmbeds;
}
if (thread) {
newMessage.threadId = thread.id;
}
const sendPromise = webhook.send(newMessage);
// Delete Old Message
const deletePromise = message.delete();
// Wait
await Promise.all([sendPromise, deletePromise]);
// Get New Message
const prompt = 'Rewrite the following message in the style of Shakespeare.\n\n' + message.content;
const timeout = 2 * 60 * 1000; // 2 minutes
let response: string | null = null;
try {
let result = await sendMessageSafe(api, prompt, {timeoutMs: timeout});
// Remove Quotes
result = await sendMessageSafe(api, 'Remove extra quotation marks if present.', {
conversationId: result!.conversationId,
parentMessageId: result!.messageId,
timeoutMs: timeout
});
// Store
response = result!.response;
} catch (e) {
response = '**ChatGPT Error:** ' + e + '\n**Original Message:** ' + message.content;
}
// Edit Message
const editedMessage: WebhookEditMessageOptions = {
content: response.trim(),
files: newMessage.files,
embeds: newMessage.embeds,
threadId: newMessage.threadId
};
if (message.type == MessageType.Reply) {
editedMessage.content = '*Replying to <https://discord.com/channels/' + message.reference?.guildId! + '/' + message.reference?.channelId! + '/' + message.reference?.messageId! + '>*\n\n' + editedMessage.content;
}
await webhook.editMessage((await sendPromise).id, editedMessage);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment