Last active
November 30, 2022 06:01
-
-
Save aiden2480/9688bb0440879b2c88551b1657ea7ba6 to your computer and use it in GitHub Desktop.
Crosspost messages between a Facebook Messenger group chat and a Discord server
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
/** | |
* ____ ___ __ | |
* \ \/ / ______ ____ _______/ |_ | |
* \ / ______ \____ \ / _ \/ ___/\ __\ | |
* / \ /_____/ | |_> > <_> )___ \ | | | |
* /___/\ \ | __/ \____/____ > |__| | |
* \_/ |__| \/ | |
* | |
* 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