Last active
January 22, 2023 03:41
-
-
Save TheBrokenRail/4b37e7c44e8f721d8bd845050d034c16 to your computer and use it in GitHub Desktop.
ShakespeareBot
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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