Skip to content

Commit

Permalink
2.0.9: Added mentions support
Browse files Browse the repository at this point in the history
  • Loading branch information
Bjornskjald committed May 30, 2019
2 parents 0a561e7 + d1d37b6 commit e651df2
Show file tree
Hide file tree
Showing 18 changed files with 153 additions and 116 deletions.
12 changes: 11 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "libfb",
"version": "2.0.8",
"version": "2.0.9",
"description": "Facebook MQTT library for Node.js",
"repository": "https://github.com/ChatPlug/libfb-js",
"main": "dist/index.js",
Expand All @@ -17,9 +17,11 @@
"Bjornskjald <github@bjorn.ml>"
],
"dependencies": {
"@types/long": "^4.0.0",
"buffer-hexdump": "^1.0.0",
"debug": "^4.1.1",
"length-stream": "^0.1.1",
"long": "^4.0.0",
"mime-types": "^2.1.24",
"node-fetch": "^2.5.0",
"strict-event-emitter-types": "^2.0.0",
Expand Down
6 changes: 3 additions & 3 deletions src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import User, { parseUser } from './types/User'
import debug from 'debug'
import { Readable } from 'stream'
import { PublishPacket } from './mqtt/messages/Publish'
import Message, { parseThreadMessage } from './types/Message'
import Message, { MessageOptions, parseThreadMessage } from './types/Message'
import parseDeltaMessage from './types/message/parseDeltaMessage'
import parseDeltaEvent from './types/events/parseDeltaEvent'
import EventEmitter from 'events'
Expand Down Expand Up @@ -131,8 +131,8 @@ export default class Client extends (EventEmitter as { new(): ClientEmitter }) {
return this.session
}

sendMessage (threadId: string, message: string) {
return this.mqttApi.sendMessage(threadId, message)
sendMessage (threadId: string, message: string, options?: MessageOptions) {
return this.mqttApi.sendMessage(threadId, message, options)
}

getThreadList = async (count: number): Promise<Thread[]> => {
Expand Down
13 changes: 12 additions & 1 deletion src/RandomIntGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import Long from 'long'

export default class RandomIntGenerator {
// static generate () {
// return Math.floor(Math.random() * (Math.pow(2, 32) + 1))
// }
static getAttemptId (): Long {
const sysTime: Long = Long.fromNumber(Date.now()).shiftLeft(22)
const randomBit: Long = Long.fromNumber(RandomIntGenerator.generate() & 4194303).and(Long.MAX_VALUE)
return sysTime.or(randomBit)
}

static generate () {
return Math.floor(Math.random() * (Math.pow(2, 32) + 1))
return Math.floor(Math.random() * Math.floor(Number.MAX_SAFE_INTEGER))
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { default as Thread } from './types/Thread'
export { default as User } from './types/User'
export * from './types/Attachment'
export * from './types/Events'
export * from './types/Message'
64 changes: 38 additions & 26 deletions src/mqtt/MqttApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import hexdump from 'buffer-hexdump'
import { EventEmitter } from 'events'
import AuthTokens from '../types/AuthTokens'
import DeviceId from '../types/DeviceId'
import { MessageType } from './messages/MessageTypes'
import { encodeConnectMessage } from './messages/Connect'
import { encodePing } from './messages/Ping'
import { decodePublish, encodePublish, PublishPacket } from './messages/Publish'
Expand All @@ -11,10 +12,11 @@ import { encodeSubscribeMessage } from './messages/Subscribe'
import { encodeUnsubscribe } from './messages/Unsubscribe'
import MqttConnection from './MqttConnection'
import MqttMessage from './MqttMessage'
import MqttPacket, { FacebookMessageType } from './MqttPacket'
import MqttPacket from './MqttPacket'
import { MqttMessageFlag } from './MqttTypes'
import debug from 'debug'
import RandomIntGenerator from '../RandomIntGenerator'
import { MessageOptions } from '../types/Message'

const debugLog = debug('fblib')

Expand All @@ -39,11 +41,11 @@ export default class MqttApi extends EventEmitter {
return this.connection.writeMessage(msg)
}

/**
* Connects the MQTT socket and binds listeners for receiving messages.
* @param tokens
* @param deviceId
*/
/**
* Connects the MQTT socket and binds listeners for receiving messages.
* @param tokens
* @param deviceId
*/
async connect (tokens: AuthTokens, deviceId: DeviceId) {
this.tokens = tokens
this.deviceId = deviceId
Expand All @@ -64,14 +66,14 @@ export default class MqttApi extends EventEmitter {
if (this.lastPingMilis < 0 || (this.lastPingMilis) < (60 * 1000) + new Date().getTime()) {
return this.connection.writeMessage(encodePing())
} else {
// Attempt to reconnect
// Attempt to reconnect
return this.reconnect()
}
}

/**
* Sends a CONNECT mqtt message
*/
/**
* Sends a CONNECT mqtt message
*/
async sendConnectMessage () {
setInterval(this.sendPing, 60 * 1000)
debugLog('sending connect message')
Expand All @@ -82,9 +84,9 @@ export default class MqttApi extends EventEmitter {
await this.waitForAck('Connect')

await this.sendPublish(
'/foreground_state',
'{"foreground":true,"keepalive_timeout":60}'
)
'/foreground_state',
'{"foreground":true,"keepalive_timeout":60}'
)

await this.sendSubscribe(encodeSubscribeMessage(this.lastMsgId))
await this.waitForAck('Subscribe')
Expand Down Expand Up @@ -118,21 +120,31 @@ export default class MqttApi extends EventEmitter {
})
}

/**
* Sends a facebook messenger message to someone.
*/
sendMessage (threadId: string, message: string): Promise<{ succeeded: boolean, errStr?: string }> {
/**
* Sends a facebook messenger message to someone.
*/
sendMessage (threadId: string, message: string, options?: MessageOptions): Promise<{ succeeded: boolean, errStr?: string }> {
return new Promise(async (resolve, reject) => {
const milliseconds = Math.floor(new Date().getTime() / 1000)
const rand = RandomIntGenerator.generate()
const msgid = Math.abs((rand & 0x3fffff) | (milliseconds << 22))
const msg = {
const msg: any = {
body: message,
msgid,
sender_fbid: this.tokens.uid,
to: threadId
to: (threadId.startsWith('100') || threadId.length < 16) ? threadId : `tfbid_${threadId}`
}
this.once('sentMessage:' + msgid, resolve)
if (options && options.mentions && options.mentions.length) {
msg.generic_metadata = {
prng: JSON.stringify(options.mentions.map(mention => ({
o: mention.offset,
l: mention.length,
i: mention.id,
t: 'p'
})))
}
}
this.once('sentMessage:' + msgid.toString(), resolve)
await this.sendPublish('/send_message2', JSON.stringify(msg))
})
}
Expand All @@ -156,29 +168,29 @@ export default class MqttApi extends EventEmitter {

parsePacket = async (packet: MqttPacket) => {
switch (packet.type) {
case FacebookMessageType.ConnectAck:
case MessageType.ConnectAck:
debugLog('Packet type: ConnectAck')
this.emit('ConnectAck')
break
case FacebookMessageType.Publish:
case MessageType.Publish:
debugLog('Packet type: Publish')
const publish = decodePublish(packet)
this.emit('publish', publish)
await this.sendPublishConfirmation(packet.flag, publish)
break
case FacebookMessageType.SubscribeAck:
case MessageType.SubscribeAck:
debugLog('Packet type: SubscribeAck')
this.emit('SubscribeAck')
break
case FacebookMessageType.PublishAck:
case MessageType.PublishAck:
debugLog('Packet type: PublishAck')
this.emit('PublishAck')
break
case FacebookMessageType.UnsubscribeAck:
case MessageType.UnsubscribeAck:
debugLog('Packet type: UnsubscribeAck')
this.emit('UnsubscribeAck')
break
case FacebookMessageType.Pong:
case MessageType.Pong:
debugLog('Packet type: Pong')
this.lastPingMilis = new Date().getTime()
break
Expand Down
19 changes: 16 additions & 3 deletions src/mqtt/MqttMessage.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,59 @@
import { FacebookMessageType } from './messages/MessageTypes'
import { MessageType } from './messages/MessageTypes'

/**
* Represents an outgoing message. This class allows to buffer different kinds of binary data so that messages can be assembled.
*/
export default class MqttMessage {
toSend: Buffer
type: FacebookMessageType
type: MessageType
flags: number

constructor () {
constructor (type?: MessageType) {
this.toSend = Buffer.alloc(0)
if (type) this.type = type
}

setFlags (flags: number) {
this.flags = flags
return this
}

writeU16 (data: number) {
const newBuf = Buffer.alloc(2)
newBuf.writeUInt16BE(data, 0)
this.toSend = Buffer.concat([this.toSend, newBuf])
return this
}

writeU32 (data: number) {
const newBuf = Buffer.alloc(4)
newBuf.writeUInt32BE(data, 0)
this.toSend = Buffer.concat([this.toSend, newBuf])
return this
}

writeU8 (data: number) {
const newBuf = Buffer.alloc(1)
newBuf.writeUInt8(data, 0)
this.toSend = Buffer.concat([this.toSend, newBuf])
return this
}

writeString (strToAdd: string) {
this.writeU16(strToAdd.length)
const newBuf = Buffer.from(strToAdd, 'utf8')
this.toSend = Buffer.concat([this.toSend, newBuf])
return this
}

writeRawString (strToAdd: string) {
const newBuf = Buffer.from(strToAdd, 'utf8')
this.toSend = Buffer.concat([this.toSend, newBuf])
return this
}

writeRaw (raw: Buffer) {
this.toSend = Buffer.concat([this.toSend, raw])
return this
}
}
20 changes: 2 additions & 18 deletions src/mqtt/MqttPacket.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
import { MqttMessageFlag } from './MqttTypes'
import { MessageType } from './messages/MessageTypes'

export interface MqttHeader {
size: number
i: number
}

export enum FacebookMessageType {
Connect = 1,
ConnectAck = 2,
Publish = 3,
PublishAck = 4,
PublishRecorded = 5,
PublishReleased = 6,
PubComp = 7,
Subscribe = 8,
SubscribeAck = 9,
Unsubscribe = 10,
UnsubscribeAck = 11,
Ping = 12,
Pong = 13,
Disconnect = 14
}

export default interface MqttPacket {
header: MqttHeader
type: FacebookMessageType
type: MessageType
content: Buffer
flag: MqttMessageFlag
}
19 changes: 9 additions & 10 deletions src/mqtt/messages/Connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as zlib from 'zlib'
import AuthTokens from '../../types/AuthTokens'
import DeviceId from '../../types/DeviceId'
import MqttMessage from '../MqttMessage'
import { MqttConnectFlag } from '../MqttTypes'
import { FacebookMessageType } from './MessageTypes'
import { MqttConnectFlag, MqttMessageFlag } from '../MqttTypes'
import { MessageType } from './MessageTypes'

const USER_AGENT =
'Facebook plugin / LIBFB-JS / [FBAN/Orca-Android;FBAV/148.0.0.5.83;FBPN/com.facebook.orca;FBLC/en_US;FBBV/26040814]'
Expand Down Expand Up @@ -33,19 +33,18 @@ export const encodeConnectMessage = (
return new Promise<MqttMessage>((resolve, reject) => {
const trans = new TBufferedTransport() as any
trans.onFlush = d => {
const message = new MqttMessage()
const flags =
MqttConnectFlag.User |
MqttConnectFlag.Pass |
MqttConnectFlag.Clr |
MqttConnectFlag.QoS1
message.writeString('MQTToT')
message.writeU8(3)
message.writeU8(flags)
message.writeU16(60) // KEEP ALIVE
message.writeRaw(zlib.deflateSync(d))
message.flags = 0
message.type = FacebookMessageType.Connect
const message = new MqttMessage(MessageType.Connect)
.setFlags(MqttMessageFlag.QoS0)
.writeString('MQTToT')
.writeU8(3)
.writeU8(flags)
.writeU16(60) // KEEP ALIVE
.writeRaw(zlib.deflateSync(d))
resolve(message)
}

Expand Down
2 changes: 1 addition & 1 deletion src/mqtt/messages/MessageTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export enum FacebookMessageType {
export enum MessageType {
Connect = 1,
ConnectAck = 2,
Publish = 3,
Expand Down
9 changes: 4 additions & 5 deletions src/mqtt/messages/Ping.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import MqttMessage from '../MqttMessage'
import { FacebookMessageType } from '../MqttPacket'
import { MessageType } from './MessageTypes'
import { MqttMessageFlag } from '../MqttTypes'

/**
* Assembles a ping message.
*/
export const encodePing = (): MqttMessage => {
const message = new MqttMessage()
message.flags = 0
message.type = FacebookMessageType.Ping
return message
return new MqttMessage(MessageType.Ping)
.setFlags(MqttMessageFlag.QoS0)
}
Loading

0 comments on commit e651df2

Please sign in to comment.