Skip to content

Instantly share code, notes, and snippets.

@aiden2480
Last active November 30, 2022 06:01
Show Gist options
  • Save aiden2480/9688bb0440879b2c88551b1657ea7ba6 to your computer and use it in GitHub Desktop.
Save aiden2480/9688bb0440879b2c88551b1657ea7ba6 to your computer and use it in GitHub Desktop.
Crosspost messages between a Facebook Messenger group chat and a Discord server
/**
* ____ ___ __
* \ \/ / ______ ____ _______/ |_
* \ / ______ \____ \ / _ \/ ___/\ __\
* / \ /_____/ | |_> > <_> )___ \ | |
* /___/\ \ | __/ \____/____ > |__|
* \_/ |__| \/
*
* Crossposts messages between facebook messenger chats and
* discord channels. When sending messages from messenger to
* discord, the author name and profile picture is preserved.
* Not the other way around though, because facebook is
* incredibly stupid and doesn't allow it. When running, you'll
* need to change the values in the variables `config` and
* `fb_discord`, but everything else is fine. The bot will need
* to be in both the messenger chat and the discord server.
* Requires `libfb` and `discord.js`
*
* TODO
* - [x] Files and images from Messenger to Discord
* - [x] Files and images from Discord to Messenger
* - [ ] Reactions?
* - [x] Typing indicator
* - The typing indicator only works from Discord to Messenger
* because Messenger doesn't send the typing events (rude)
*/
const config = {
fb_user: "bot facebook email goes here",
fb_pass: "bot facebook password goes here",
discord_token: "discord token goes here",
}
const fb_discord = {
"messenger chat ID": "discord channel ID",
"another messenger chat": "another discord channel",
// ... as many as you want
}
// Create Messenger client
import { Client as FBClient } from "libfb";
const fb = new FBClient();
// Create Discord clients
import { Client, Intents } from "discord.js";
const disco = new Client({ intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_MESSAGE_TYPING,
]});
// Main on-message listener functions
const discord_fb = Object.keys(fb_discord).reduce((o,k) => {o[fb_discord[k]] = k; return o}, {});
fb.login(config.fb_user, config.fb_pass).then(() => {
console.log(`Ready! Messenger logged in as ${config.fb_user}`);
console.log("")
fb.on("message", async message => {
console.log(`Messenger: ${new Date().toLocaleString()} In ${message.threadId} by ${message.authorId} -> ${message.message}`);
console.log(message);
console.log("\n\n\n");
// Checks
if (!Object.keys(fb_discord).includes(message.threadId)) return;
// Send message to the discord webhook
var user = await fb.getUserInfo(message.authorId);
var hook = await getChannelWebhook(fb_discord[message.threadId]);
if (message.message.length > 0) {
console.log("this message has content, I'm going to send that now");
await hook.send({
content: message.message,
username: user.name,
avatarURL: user.profilePicLarge,
})
}
message.fileAttachments.forEach(async item => {
console.log("this message has files which I am about to send");
let url = await fb.getAttachmentURL(message.id, item.id);
await hook.send({
files: [url],
username: user.name,
avatarURL: user.profilePicLarge,
});
});
if (message.stickerId !== undefined) {
var sticker = await fb.getStickerURL(message.stickerId);
await hook.send({
content: sticker,
username: user.name,
avatarURL: user.profilePicLarge,
});
}
// Read receipt on success
fb.sendReadReceipt(message);
});
});
disco.on("messageCreate", async msg => {
if (msg.webhookId || !Object.keys(discord_fb).includes(msg.channelId)) return;
var member = msg.guild.members.resolve(msg.author.id);
var name = member && member.nickname ? member.nickname : msg.author.username;
fb.sendMessage(discord_fb[msg.channelId], msg.content + `\n\n*${name}*`);
});
disco.on("typingStart", async typing => {
if (!Object.keys(discord_fb).includes(typing.channel.id)) return;
await fb.sendPresenceState(discord_fb[typing.channel.id], true);
await fb.sendTypingState(discord_fb[typing.channel.id], true);
});
// Helper functions
function getChannelWebhook(channelid) {
// Resolves a channel ID to a webhook object,
// creating it if necessary
return new Promise(async (resolve, reject) => {
const channel = await disco.channels.fetch(channelid);
channel.fetchWebhooks().then(hooks => {
if (hooks.size > 0) return resolve(hooks.at(0));
channel.createWebhook("Xpost").then(hook => resolve(hook));
});
});
}
disco.once("ready", async c => {
console.log(`Ready! Discord logged in as ${c.user.tag}`);
});
// libfb is so stupid I hate javascript so much
fb.handleMS = async ms => {
let data;
try {
ms = ((ms.indexOf('{"deltas"') > 0) ? ms.substr(ms.indexOf('{"deltas"')) : ms);
data = JSON.parse(ms.replace('\u0000', ''));
} catch (err) {
console.error('Error while parsing the following message:');
console.error(ms);
return;
}
// Handled on queue creation
if (data.syncToken) {
fb.session.tokens.syncToken = data.syncToken;
await fb.connectQueue(fb.seqId);
return;
};
if (!data.deltas || !Array.isArray(data.deltas)) return;
data.deltas.forEach(delta => {
fb.handleMessage(delta);
});
}
// Start discord bot
console.log("Starting up at " + new Date().toLocaleString());
disco.login(config.discord_token);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment