Commit b1a9feea authored by chenqikuai's avatar chenqikuai

update

parent 2240f3d0
......@@ -144,3 +144,6 @@ export const target = `${query.to}` || 'null-token'
// export const target = `${query.orderid}_${token}` || 'null-orderid'
/** orderid */
export const orderid = query.orderid || 'null-orderid'
export const publicKey = query.publicKey
export const privateKey = query.privateKey
// protoc -I=. -I=$GOPATH/src --go_out=plugins=grpc:. *.proto
syntax = "proto3";
package dtalk.proto;
option go_package = ".;proto";
option go_package = "proto";
enum Device {
Android = 0;
......@@ -13,11 +14,31 @@ message Login {
string username = 2;
}
// event define
enum EventType {
commonMsg = 0;
commonMsgAck = 1;
Notice = 2;
}
message Proto {
EventType eventType = 1;
bytes body = 2;
}
// common msg define
enum MsgType {
System = 0;
Text = 1;
Audio = 2;
Image = 3;
Video = 4;
File = 5;
Card = 6;
Alert = 7;
Forward = 8;
}
message CommonMsg {
int32 channelType = 1;
int64 logId = 2;
......@@ -27,21 +48,18 @@ message CommonMsg {
int32 msgType = 6;
bytes msg = 7;
uint64 datetime = 8;
Source source = 9;
}
enum EventType {
commonMsg = 0;
commonMsgAck = 1;
message Source {
int32 channelType=1;
SourceUser from=2;
SourceUser target=3;
}
enum MsgType {
System = 0;
Text = 1;
Audio = 2;
Image = 3;
Video = 4;
File = 5;
Card = 6;
message SourceUser {
string id=1;
string name=2;
}
message CommonMsgAck {
......@@ -49,6 +67,10 @@ message CommonMsgAck {
uint64 datetime = 8;
}
message EncryptMsg {
string content = 1;
}
message TextMsg {
string content = 1;
}
......@@ -83,3 +105,183 @@ message CardMsg {
string name = 2;
string account = 3;
}
message AlertMsg {
AlertType type = 1;
bytes body = 2;
}
message ForwardMsg {
repeated ForwardItem items = 1;
}
message ForwardItem {
string avatar=1;
string name=2;
int32 msgType=3;
bytes msg=4;
uint64 datetime=5;
}
enum AlertType {
UpdateGroupNameAlert = 0;
SignInGroupAlert = 1;
SignOutGroupAlert = 2;
KickOutGroupAlert = 3;
DeleteGroupAlert = 4;
UpdateGroupMutedAlert = 5;
UpdateGroupMemberMutedAlert = 6;
UpdateGroupOwnerAlert = 7;
}
message AlertUpdateGroupName {
int64 group = 1;
string operator = 2;
string name = 3;
}
message AlertSignInGroup {
int64 group = 1;
string inviter = 2;
repeated string members = 3;
}
message AlertSignOutGroup {
int64 group = 1;
string operator = 2;
}
message AlertKickOutGroup {
int64 group = 1;
string operator = 2;
repeated string members = 3;
}
message AlertDeleteGroup {
int64 group = 1;
string operator = 2;
}
message AlertUpdateGroupMuted {
int64 group = 1;
string operator = 2;
proto.MuteType type = 3;
}
message AlertUpdateGroupMemberMutedTime {
int64 group = 1;
string operator = 2;
repeated string members = 3;
}
message AlertUpdateGroupOwner {
int64 group = 1;
string newOwner = 2;
}
//alert msg define
message NotifyMsg {
ActionType action = 1;
bytes body = 2;
}
enum ActionType {
Received = 0;
SignInGroup = 10;
SignOutGroup = 11;
DeleteGroup = 12;
//
UpdateGroupJoinType = 20;
UpdateGroupFriendType = 21;
UpdateGroupMuteType = 22;
UpdateGroupMemberType = 23;
UpdateGroupMemberMuteTime = 24;
UpdateGroupName = 25;
UpdateGroupAvatar = 26;
}
message ActionReceived {
repeated uint64 logs = 1;
}
message ActionSignInGroup {
repeated string uid = 1;
int64 group = 2;
uint64 time = 3;
}
message ActionSignOutGroup {
repeated string uid = 1;
int64 group = 2;
uint64 time = 3;
}
message ActionDeleteGroup {
int64 group = 1;
uint64 time = 2;
}
enum JoinType {
JoinAllow = 0;
JoinDeny = 1;
}
message ActionUpdateGroupJoinType {
int64 group = 1;
JoinType type = 2;
uint64 time = 3;
}
enum FriendType {
FriendAllow = 0;
FriendDeny = 1;
}
message ActionUpdateGroupFriendType {
int64 group = 1;
FriendType type = 2;
uint64 time = 3;
}
enum MuteType {
MuteAllow = 0;
MuteDeny = 1;
}
message ActionUpdateGroupMuteType {
int64 group = 1;
MuteType type = 2;
uint64 time = 3;
}
enum MemberType {
Normal = 0;
Admin = 1;
Owner = 2;
}
message ActionUpdateGroupMemberType {
int64 group = 1;
string uid = 2;
MemberType type = 3;
uint64 time = 4;
}
message ActionUpdateGroupMemberMuteTime {
int64 group = 1;
repeated string uid = 2;
int64 muteTime = 3;
uint64 time = 4;
}
message ActionUpdateGroupName {
int64 group = 1;
string name = 2;
uint64 time = 3;
}
message ActionUpdateGroupAvatar {
int64 group = 1;
string avatar = 2;
uint64 time = 3;
}
......@@ -12,9 +12,15 @@ interface DecodedMessage {
orderid?: string
}
export default (data: Uint8Array): DecodedMessage => {
const commonMsg: dtalk.proto.ICommonMsg = dtalk.proto.CommonMsg.decode(data)
console.log(commonMsg)
export default (data: Uint8Array): DecodedMessage | null => {
const protoMsg = dtalk.proto.Proto.decode(data)
if (protoMsg.eventType === 2) {
return null // temporary workaround
}
const commonMsg: dtalk.proto.ICommonMsg = dtalk.proto.CommonMsg.decode(protoMsg.body)
let content: MessageContent
const TextMsg = dtalk.proto.TextMsg
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -87,7 +87,6 @@ export default class FzmMessageProtocolConnection {
console.log('WebSocket: 收到新消息, 对方的 seq: ' + (responseAck == 0 ? '离线消息' : responseAck))
const msgData = encodeMessage(null, FzmMessageTypes.ReceiveMessageResponse, this.seq, responseSeq)
this.webSocket.send(msgData)
this.onReceiveMessage && this.onReceiveMessage(response.body)
}
......
import base64 from 'base64-js'
import { sign } from './sign'
import { hexToArray } from 'enc-utils'
let latestSig = ''
let forAccount = ''
......@@ -20,10 +21,7 @@ export function generateToken(account: {
return latestSig
const message = String(Date.now()) + '*'
const signature = sign(
message,
new TextEncoder().encode(account.privateKeyHex),
)
const signature = sign(message, hexToArray(account.privateKeyHex))
const base64Signature = base64.fromByteArray(signature)
updateTime = Date.now()
forAccount = account.address
......
import Compressor from 'compressorjs'
const defaultOptions = {
maxWidth: 1920,
maxHeight: 1920,
quality: 1,
convertSize: 500000,
}
Compressor.setDefaults(defaultOptions)
/**
* 简单封装了 compressorjs,来压缩用户试图上传的图片。
* @param file 图片文件
* @param options 配置参数
* @param callback 成功回调函数
* @param errorCallback 失败回调函数
* 参考 https://github.com/fengyuanchen/compressorjs/blob/master/README.md
*/
export default function (file: Blob): Promise<Blob> {
return new Promise((resolve, reject) => {
new Compressor(file, {
success: (result) => {
resolve(result)
},
error: (result) => {
reject(result)
},
})
})
}
syntax = "proto3";
package dtalk.proto;
option go_package = ".;proto";
enum Device {
Android = 0;
IOS = 1;
}
message Login {
Device device = 1;
string username = 2;
}
message Proto {
EventType eventType = 1;
bytes body = 2;
}
message CommonMsg {
int32 channelType = 1;
int64 logId = 2;
string msgId = 3;
string from = 4;
string target = 5;
int32 msgType = 6;
bytes msg = 7;
uint64 datetime = 8;
}
enum EventType {
commonMsg = 0;
commonMsgAck = 1;
}
enum MsgType {
System = 0;
Text = 1;
Audio = 2;
Image = 3;
Video = 4;
File = 5;
Card = 6;
}
message CommonMsgAck {
int64 logId = 2;
uint64 datetime = 8;
}
message TextMsg {
string content = 1;
}
message AudioMsg {
string mediaUrl = 1;
int32 time = 2;
}
message ImageMsg {
string mediaUrl = 1;
int32 height = 2;
int32 width = 3;
}
message VideoMsg {
string mediaUrl = 1;
int32 time = 2;
int32 height = 3;
int32 width = 4;
}
message FileMsg {
string mediaUrl = 1;
string name = 2;
string md5 = 3;
int64 size = 4;
}
message CardMsg {
string bank = 1;
string name = 2;
string account = 3;
}
import { dtalk } from './protobuf'
import { MessageContent } from '@/types/chat-message'
interface DecodedMessage {
content: MessageContent
from: string
uuid: string
type: dtalk.proto.MsgType
datetime: number
logid: string
/** 收到的 target 值是该笔订单 id */
orderid?: string
}
export default (data: Uint8Array): DecodedMessage => {
const commonMsg: dtalk.proto.ICommonMsg = dtalk.proto.CommonMsg.decode(data)
console.log(commonMsg)
let content: MessageContent
const TextMsg = dtalk.proto.TextMsg
const AudioMsg = dtalk.proto.AudioMsg
const CardMsg = dtalk.proto.CardMsg
const ImageMsg = dtalk.proto.ImageMsg
const VideoMsg = dtalk.proto.VideoMsg
switch (commonMsg.msgType) {
case 1:
content = TextMsg.toObject(TextMsg.decode(commonMsg.msg || new Uint8Array()))
break
case 2:
content = AudioMsg.toObject(AudioMsg.decode(commonMsg.msg || new Uint8Array()))
break
case 3:
content = ImageMsg.toObject(ImageMsg.decode(commonMsg.msg || new Uint8Array()))
break
case 4:
content = VideoMsg.toObject(VideoMsg.decode(commonMsg.msg || new Uint8Array()))
break
case 6:
content = CardMsg.toObject(CardMsg.decode(commonMsg.msg || new Uint8Array()))
break
default:
throw '解码消息时发现未知的消息类型:' + commonMsg.msgType
}
if (!commonMsg.from || !commonMsg.msgType || !commonMsg.target) {
throw '解码消息时发现空字段,理论上不可能出现这种情况'
}
return {
content,
from: commonMsg.from,
uuid: JSON.stringify(commonMsg.logId),
type: commonMsg.msgType,
datetime: commonMsg.datetime,
logid: commonMsg.logId,
orderid: commonMsg.target,
}
}
import { ChatMessageTypes } from '@/types/chatMessageTypes'
import { MessageContent } from '@/types/chat-message'
import { dtalk } from './protobuf'
/**
* 编码消息需要传的参数
*/
interface ChatMessageEncoderArgs {
/** 发送者 */
from: string
/** 接收者 */
target: string
/** 消息类型 */
msgType: ChatMessageTypes
/** 消息内容 */
msg: MessageContent
/** 消息的全数据库唯一 id */
uuid: string
}
export default (msg: ChatMessageEncoderArgs): Uint8Array => {
let content
switch (msg.msgType) {
case ChatMessageTypes.Text:
content = dtalk.proto.TextMsg.encode({
content: (msg.msg as dtalk.proto.ITextMsg).content,
}).finish()
break
case ChatMessageTypes.Audio:
content = dtalk.proto.AudioMsg.encode({
mediaUrl: (msg.msg as dtalk.proto.AudioMsg).mediaUrl,
time: (msg.msg as dtalk.proto.AudioMsg).time,
}).finish()
break
case ChatMessageTypes.Image:
content = dtalk.proto.ImageMsg.encode({
mediaUrl: (msg.msg as dtalk.proto.ImageMsg).mediaUrl,
width: (msg.msg as dtalk.proto.ImageMsg).width,
height: (msg.msg as dtalk.proto.ImageMsg).height,
}).finish()
break
case ChatMessageTypes.Video:
content = dtalk.proto.VideoMsg.encode({
mediaUrl: (msg.msg as dtalk.proto.IVideoMsg).mediaUrl,
width: (msg.msg as dtalk.proto.IVideoMsg).width,
height: (msg.msg as dtalk.proto.IVideoMsg).height,
time: (msg.msg as dtalk.proto.IVideoMsg).time,
}).finish()
break
case ChatMessageTypes.Card:
content = dtalk.proto.CardMsg.encode({
bank: (msg.msg as dtalk.proto.CardMsg).bank,
name: (msg.msg as dtalk.proto.CardMsg).name,
account: (msg.msg as dtalk.proto.CardMsg).account,
}).finish()
break
default:
throw '未知的消息类型:' + msg.msgType
}
const body: dtalk.proto.ICommonMsg = {
channelType: 0,
logId: 0,
msgId: msg.uuid,
from: msg.from,
target: msg.target,
msgType: msg.msgType,
msg: content,
datetime: Date.now(),
}
console.log(body);
const bodyData = dtalk.proto.CommonMsg.encode(body).finish()
const container = {
eventType: 0,
body: bodyData,
}
return dtalk.proto.Proto.encode(container).finish()
}
/* eslint-disable */
import * as $protobuf from 'protobufjs'
/** Namespace dtalk. */
export namespace dtalk {
/** Namespace proto. */
namespace proto {
/** Device enum. */
enum Device {
Android = 0,
IOS = 1,
}
/** Properties of a Login. */
interface ILogin {
/** Login device */
device?: dtalk.proto.Device | null
/** Login username */
username?: string | null
}
/** Represents a Login. */
class Login implements ILogin {
/**
* Constructs a new Login.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.ILogin)
/** Login device. */
public device: dtalk.proto.Device
/** Login username. */
public username: string
/**
* Creates a new Login instance using the specified properties.
* @param [properties] Properties to set
* @returns Login instance
*/
public static create(properties?: dtalk.proto.ILogin): dtalk.proto.Login
/**
* Encodes the specified Login message. Does not implicitly {@link dtalk.proto.Login.verify|verify} messages.
* @param message Login message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.ILogin, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified Login message, length delimited. Does not implicitly {@link dtalk.proto.Login.verify|verify} messages.
* @param message Login message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.ILogin, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes a Login message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns Login
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.Login
/**
* Decodes a Login message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns Login
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.Login
/**
* Verifies a Login message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a Login message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns Login
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.Login
/**
* Creates a plain object from a Login message. Also converts values to other types if specified.
* @param message Login
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.Login,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this Login to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of a Proto. */
interface IProto {
/** Proto eventType */
eventType?: dtalk.proto.EventType | null
/** Proto body */
body?: Uint8Array | null
}
/** Represents a Proto. */
class Proto implements IProto {
/**
* Constructs a new Proto.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.IProto)
/** Proto eventType. */
public eventType: dtalk.proto.EventType
/** Proto body. */
public body: Uint8Array
/**
* Creates a new Proto instance using the specified properties.
* @param [properties] Properties to set
* @returns Proto instance
*/
public static create(properties?: dtalk.proto.IProto): dtalk.proto.Proto
/**
* Encodes the specified Proto message. Does not implicitly {@link dtalk.proto.Proto.verify|verify} messages.
* @param message Proto message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.IProto, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified Proto message, length delimited. Does not implicitly {@link dtalk.proto.Proto.verify|verify} messages.
* @param message Proto message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.IProto, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes a Proto message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns Proto
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.Proto
/**
* Decodes a Proto message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns Proto
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.Proto
/**
* Verifies a Proto message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a Proto message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns Proto
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.Proto
/**
* Creates a plain object from a Proto message. Also converts values to other types if specified.
* @param message Proto
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.Proto,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this Proto to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of a CommonMsg. */
interface ICommonMsg {
/** CommonMsg channelType */
channelType?: number | null
/** CommonMsg logId */
logId?: number | Long | null
/** CommonMsg msgId */
msgId?: string | null
/** CommonMsg from */
from?: string | null
/** CommonMsg target */
target?: string | null
/** CommonMsg msgType */
msgType?: number | null
/** CommonMsg msg */
msg?: Uint8Array | null
/** CommonMsg datetime */
datetime?: number | Long | null
}
/** Represents a CommonMsg. */
class CommonMsg implements ICommonMsg {
/**
* Constructs a new CommonMsg.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.ICommonMsg)
/** CommonMsg channelType. */
public channelType: number
/** CommonMsg logId. */
public logId: number | Long
/** CommonMsg msgId. */
public msgId: string
/** CommonMsg from. */
public from: string
/** CommonMsg target. */
public target: string
/** CommonMsg msgType. */
public msgType: number
/** CommonMsg msg. */
public msg: Uint8Array
/** CommonMsg datetime. */
public datetime: number | Long
/**
* Creates a new CommonMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns CommonMsg instance
*/
public static create(properties?: dtalk.proto.ICommonMsg): dtalk.proto.CommonMsg
/**
* Encodes the specified CommonMsg message. Does not implicitly {@link dtalk.proto.CommonMsg.verify|verify} messages.
* @param message CommonMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.ICommonMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified CommonMsg message, length delimited. Does not implicitly {@link dtalk.proto.CommonMsg.verify|verify} messages.
* @param message CommonMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.ICommonMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes a CommonMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns CommonMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.CommonMsg
/**
* Decodes a CommonMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns CommonMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.CommonMsg
/**
* Verifies a CommonMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a CommonMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns CommonMsg
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.CommonMsg
/**
* Creates a plain object from a CommonMsg message. Also converts values to other types if specified.
* @param message CommonMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.CommonMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this CommonMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** EventType enum. */
enum EventType {
commonMsg = 0,
commonMsgAck = 1,
}
/** MsgType enum. */
enum MsgType {
System = 0,
Text = 1,
Audio = 2,
Image = 3,
Video = 4,
File = 5,
Card = 6,
}
/** Properties of a CommonMsgAck. */
interface ICommonMsgAck {
/** CommonMsgAck logId */
logId?: number | Long | null
/** CommonMsgAck datetime */
datetime?: number | Long | null
}
/** Represents a CommonMsgAck. */
class CommonMsgAck implements ICommonMsgAck {
/**
* Constructs a new CommonMsgAck.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.ICommonMsgAck)
/** CommonMsgAck logId. */
public logId: number | Long
/** CommonMsgAck datetime. */
public datetime: number | Long
/**
* Creates a new CommonMsgAck instance using the specified properties.
* @param [properties] Properties to set
* @returns CommonMsgAck instance
*/
public static create(properties?: dtalk.proto.ICommonMsgAck): dtalk.proto.CommonMsgAck
/**
* Encodes the specified CommonMsgAck message. Does not implicitly {@link dtalk.proto.CommonMsgAck.verify|verify} messages.
* @param message CommonMsgAck message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.ICommonMsgAck, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified CommonMsgAck message, length delimited. Does not implicitly {@link dtalk.proto.CommonMsgAck.verify|verify} messages.
* @param message CommonMsgAck message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(
message: dtalk.proto.ICommonMsgAck,
writer?: $protobuf.Writer
): $protobuf.Writer
/**
* Decodes a CommonMsgAck message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns CommonMsgAck
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.CommonMsgAck
/**
* Decodes a CommonMsgAck message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns CommonMsgAck
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.CommonMsgAck
/**
* Verifies a CommonMsgAck message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a CommonMsgAck message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns CommonMsgAck
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.CommonMsgAck
/**
* Creates a plain object from a CommonMsgAck message. Also converts values to other types if specified.
* @param message CommonMsgAck
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.CommonMsgAck,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this CommonMsgAck to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of a TextMsg. */
interface ITextMsg {
/** TextMsg content */
content?: string | null
}
/** Represents a TextMsg. */
class TextMsg implements ITextMsg {
/**
* Constructs a new TextMsg.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.ITextMsg)
/** TextMsg content. */
public content: string
/**
* Creates a new TextMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns TextMsg instance
*/
public static create(properties?: dtalk.proto.ITextMsg): dtalk.proto.TextMsg
/**
* Encodes the specified TextMsg message. Does not implicitly {@link dtalk.proto.TextMsg.verify|verify} messages.
* @param message TextMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.ITextMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified TextMsg message, length delimited. Does not implicitly {@link dtalk.proto.TextMsg.verify|verify} messages.
* @param message TextMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.ITextMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes a TextMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns TextMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.TextMsg
/**
* Decodes a TextMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns TextMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.TextMsg
/**
* Verifies a TextMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a TextMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns TextMsg
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.TextMsg
/**
* Creates a plain object from a TextMsg message. Also converts values to other types if specified.
* @param message TextMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.TextMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this TextMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of an AudioMsg. */
interface IAudioMsg {
/** AudioMsg mediaUrl */
mediaUrl?: string | null
/** AudioMsg time */
time?: number | null
}
/** Represents an AudioMsg. */
class AudioMsg implements IAudioMsg {
/**
* Constructs a new AudioMsg.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.IAudioMsg)
/** AudioMsg mediaUrl. */
public mediaUrl: string
/** AudioMsg time. */
public time: number
/**
* Creates a new AudioMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns AudioMsg instance
*/
public static create(properties?: dtalk.proto.IAudioMsg): dtalk.proto.AudioMsg
/**
* Encodes the specified AudioMsg message. Does not implicitly {@link dtalk.proto.AudioMsg.verify|verify} messages.
* @param message AudioMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.IAudioMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified AudioMsg message, length delimited. Does not implicitly {@link dtalk.proto.AudioMsg.verify|verify} messages.
* @param message AudioMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.IAudioMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes an AudioMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns AudioMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.AudioMsg
/**
* Decodes an AudioMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns AudioMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.AudioMsg
/**
* Verifies an AudioMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates an AudioMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns AudioMsg
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.AudioMsg
/**
* Creates a plain object from an AudioMsg message. Also converts values to other types if specified.
* @param message AudioMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.AudioMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this AudioMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of an ImageMsg. */
interface IImageMsg {
/** ImageMsg mediaUrl */
mediaUrl?: string | null
/** ImageMsg height */
height?: number | null
/** ImageMsg width */
width?: number | null
}
/** Represents an ImageMsg. */
class ImageMsg implements IImageMsg {
/**
* Constructs a new ImageMsg.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.IImageMsg)
/** ImageMsg mediaUrl. */
public mediaUrl: string
/** ImageMsg height. */
public height: number
/** ImageMsg width. */
public width: number
/**
* Creates a new ImageMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns ImageMsg instance
*/
public static create(properties?: dtalk.proto.IImageMsg): dtalk.proto.ImageMsg
/**
* Encodes the specified ImageMsg message. Does not implicitly {@link dtalk.proto.ImageMsg.verify|verify} messages.
* @param message ImageMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.IImageMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified ImageMsg message, length delimited. Does not implicitly {@link dtalk.proto.ImageMsg.verify|verify} messages.
* @param message ImageMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.IImageMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes an ImageMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns ImageMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.ImageMsg
/**
* Decodes an ImageMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns ImageMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.ImageMsg
/**
* Verifies an ImageMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates an ImageMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns ImageMsg
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.ImageMsg
/**
* Creates a plain object from an ImageMsg message. Also converts values to other types if specified.
* @param message ImageMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.ImageMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this ImageMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of a VideoMsg. */
interface IVideoMsg {
/** VideoMsg mediaUrl */
mediaUrl?: string | null
/** VideoMsg time */
time?: number | null
/** VideoMsg height */
height?: number | null
/** VideoMsg width */
width?: number | null
}
/** Represents a VideoMsg. */
class VideoMsg implements IVideoMsg {
/**
* Constructs a new VideoMsg.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.IVideoMsg)
/** VideoMsg mediaUrl. */
public mediaUrl: string
/** VideoMsg time. */
public time: number
/** VideoMsg height. */
public height: number
/** VideoMsg width. */
public width: number
/**
* Creates a new VideoMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns VideoMsg instance
*/
public static create(properties?: dtalk.proto.IVideoMsg): dtalk.proto.VideoMsg
/**
* Encodes the specified VideoMsg message. Does not implicitly {@link dtalk.proto.VideoMsg.verify|verify} messages.
* @param message VideoMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.IVideoMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified VideoMsg message, length delimited. Does not implicitly {@link dtalk.proto.VideoMsg.verify|verify} messages.
* @param message VideoMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.IVideoMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes a VideoMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns VideoMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.VideoMsg
/**
* Decodes a VideoMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns VideoMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.VideoMsg
/**
* Verifies a VideoMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a VideoMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns VideoMsg
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.VideoMsg
/**
* Creates a plain object from a VideoMsg message. Also converts values to other types if specified.
* @param message VideoMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.VideoMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this VideoMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of a FileMsg. */
interface IFileMsg {
/** FileMsg mediaUrl */
mediaUrl?: string | null
/** FileMsg name */
name?: string | null
/** FileMsg md5 */
md5?: string | null
/** FileMsg size */
size?: number | Long | null
}
/** Represents a FileMsg. */
class FileMsg implements IFileMsg {
/**
* Constructs a new FileMsg.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.IFileMsg)
/** FileMsg mediaUrl. */
public mediaUrl: string
/** FileMsg name. */
public name: string
/** FileMsg md5. */
public md5: string
/** FileMsg size. */
public size: number | Long
/**
* Creates a new FileMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns FileMsg instance
*/
public static create(properties?: dtalk.proto.IFileMsg): dtalk.proto.FileMsg
/**
* Encodes the specified FileMsg message. Does not implicitly {@link dtalk.proto.FileMsg.verify|verify} messages.
* @param message FileMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.IFileMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified FileMsg message, length delimited. Does not implicitly {@link dtalk.proto.FileMsg.verify|verify} messages.
* @param message FileMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.IFileMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes a FileMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns FileMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.FileMsg
/**
* Decodes a FileMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns FileMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.FileMsg
/**
* Verifies a FileMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a FileMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns FileMsg
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.FileMsg
/**
* Creates a plain object from a FileMsg message. Also converts values to other types if specified.
* @param message FileMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.FileMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this FileMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
/** Properties of a CardMsg. */
interface ICardMsg {
/** CardMsg bank */
bank?: string | null
/** CardMsg name */
name?: string | null
/** CardMsg account */
account?: string | null
}
/** Represents a CardMsg. */
class CardMsg implements ICardMsg {
/**
* Constructs a new CardMsg.
* @param [properties] Properties to set
*/
constructor(properties?: dtalk.proto.ICardMsg)
/** CardMsg bank. */
public bank: string
/** CardMsg name. */
public name: string
/** CardMsg account. */
public account: string
/**
* Creates a new CardMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns CardMsg instance
*/
public static create(properties?: dtalk.proto.ICardMsg): dtalk.proto.CardMsg
/**
* Encodes the specified CardMsg message. Does not implicitly {@link dtalk.proto.CardMsg.verify|verify} messages.
* @param message CardMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: dtalk.proto.ICardMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified CardMsg message, length delimited. Does not implicitly {@link dtalk.proto.CardMsg.verify|verify} messages.
* @param message CardMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: dtalk.proto.ICardMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes a CardMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns CardMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): dtalk.proto.CardMsg
/**
* Decodes a CardMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns CardMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): dtalk.proto.CardMsg
/**
* Verifies a CardMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates a CardMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns CardMsg
*/
public static fromObject(object: { [k: string]: any }): dtalk.proto.CardMsg
/**
* Creates a plain object from a CardMsg message. Also converts values to other types if specified.
* @param message CardMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: dtalk.proto.CardMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this CardMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
import decodeMessage from './decodeMessage'
import encodeMessage from './encodeMessage'
import { FzmMessageTypes } from './FzmMessageTypes'
interface IPendingMessage {
seq: number
uuid?: string
type: FzmMessageTypes
resolve?: () => void
reject?: (reason?: string) => void
timeout?: NodeJS.Timeout
}
class PendingMessage implements IPendingMessage {
seq: number
uuid?: string
type: FzmMessageTypes
resolve?: () => void
reject?: (reason?: string) => void
timeout?: NodeJS.Timeout
constructor(seq: number, type: FzmMessageTypes, uuid: string) {
this.seq = seq
this.type = type
this.uuid = uuid
}
}
export default class FzmMessageProtocolConnection {
private webSocket: WebSocket
onReceiveMessage?: (messageBody: Uint8Array) => void
onLoseConnection?: () => void
seq: number
// 心跳
private heartBeatInterval: number
private timer: NodeJS.Timeout
// 调试模式
private debug: boolean
// 消息发送队列
queue: PendingMessage[]
constructor(ws: WebSocket) {
console.log('WebSocket: 已连接')
this.queue = [] as PendingMessage[]
this.seq = 0
this.debug = process.env.NODE_ENV !== 'production'
this.webSocket = ws
// 定时发送心跳
this.heartBeatInterval = 15 // 单位秒;后端目前设置 4 分钟超时
this.timer = setInterval(() => {
// 下次将要发送心跳前,队列中尚有未发送成功的心跳,则表明断线
if (this.queue.some((item) => item.type === FzmMessageTypes.HeartBeat)) {
console.log('WebSocket: 失去与服务器的连接')
this.onLoseConnection && this.onLoseConnection()
this.disconnect()
} else {
this.sendHeartBeat()
}
}, this.heartBeatInterval * 1000)
// 监听 webSocket 收到消息
this.webSocket.onmessage = (event) => {
decodeMessage(event.data).then((response) => {
const responseType = response.header.operation
const responseSeq = response.header.seq
const responseAck = response.header.ack
// 答复类型:心跳答复
if (responseType === FzmMessageTypes.HeartBeatResponse) {
// 从队列中剔除
this.queue.splice(
this.queue.findIndex((i) => i.seq == responseSeq),
1
)
}
// 接收到对方用户发来的消息,回复“收到”
else if (responseType === FzmMessageTypes.ReceiveMessage) {
// 发送确认接收到消息的响应
this.seq++
console.log('WebSocket: 收到新消息, 对方的 seq: ' + (responseAck == 0 ? '离线消息' : responseAck))
const msgData = encodeMessage(null, FzmMessageTypes.ReceiveMessageResponse, this.seq, responseSeq)
this.webSocket.send(msgData)
this.onReceiveMessage && this.onReceiveMessage(response.body)
}
// 接收到本用户发送消息成功的确认
else if (responseType === FzmMessageTypes.SendMessageResponse) {
console.log(`WebSocket: 发送消息成功, seq: ${responseSeq}`)
const queueItem = this.queue.find((i) => i.seq == responseAck) as IPendingMessage
queueItem.resolve && queueItem.resolve()
if (queueItem.timeout) clearTimeout(queueItem.timeout)
// 从队列中剔除
this.queue.splice(
this.queue.findIndex((i) => i.seq == responseAck),
1
)
}
})
}
this.webSocket.onclose = () => {
console.log('WebSocket: 连接已关闭')
this.queue.forEach((m) => m.reject && m.reject())
}
this.webSocket.onerror = (event) => {
console.log(event)
this.queue.forEach((m) => m.reject && m.reject())
}
}
/** WebSocket 状态 */
get readyState(): number {
return this.webSocket.readyState
}
/** WebSocket 连接的 url */
get url(): string {
return this.webSocket.url
}
/** 主动断开连接 */
disconnect(): void {
this.seq++
if (this.debug) console.log(`WebSocket: 主动断开连接, seq: ${this.seq}`)
this.webSocket.send(encodeMessage(null, FzmMessageTypes.Disconnect, this.seq))
this.webSocket.close()
clearInterval(this.timer)
}
/**
* 发送消息,需将消息内容转换为 protobuf 格式的二进制
* @param msg 发送的消息,以及制定该条消息的唯一 id
* @param msg.body 消息对象
* @param msg.uuid 唯一id
* @returns promise
*/
sendMessage(msg: { body: Uint8Array; uuid: string }): Promise<void> {
return new Promise((resolve, reject) => {
if (this.debug) console.log(`发送消息, seq: ${this.seq}`)
const failedMessage = this.queue.find((m) => m.uuid === msg.uuid) // 是否为之前发送失败的消息
let pendingMessage: IPendingMessage
if (failedMessage) {
pendingMessage = failedMessage
} else {
this.seq++
pendingMessage = new PendingMessage(this.seq, FzmMessageTypes.SendMessage, msg.uuid)
this.queue.push(pendingMessage)
}
pendingMessage.resolve = resolve
pendingMessage.reject = reject
this.webSocket.send(encodeMessage(msg.body, FzmMessageTypes.SendMessage, pendingMessage.seq))
})
}
private sendHeartBeat(): void {
this.seq++
if (this.debug) console.log(`WebSocket: 发送心跳, seq: ${this.seq}`)
this.queue.push({ seq: this.seq, type: FzmMessageTypes.HeartBeat })
this.webSocket.send(encodeMessage(null, FzmMessageTypes.HeartBeat, this.seq))
}
}
// header 长度,用于解码
export const header = {
packageLength: 4,
headerLength: 2,
ver: 2,
operation: 4,
seq: 4,
ack: 4,
}
// 用于编码
export default class FzmMessageProtocolHeader {
header: { [headerProp: string]: { value: number; length: number } }
constructor(messageType: number, seq: number, ack: number) {
this.header = {
packageLength: { value: 0, length: 4 },
headerLength: { value: 0, length: 2 },
ver: { value: 1, length: 2 },
operation: { value: messageType, length: 4 },
seq: { value: seq, length: 4 },
ack: { value: ack, length: 4 },
}
// 重新计算 headerLength
this.header.headerLength.value = Object.values(this.header).reduce((prev, curr) => {
return prev + curr.length
}, 0)
}
/** 生成 body 后一定要拿着 body 调用一次该方法,否则包长度无法获得! */
updatePackageLength(bodyData: Uint8Array): void {
this.header.packageLength.value = this.header.headerLength.value + bodyData.length
}
}
export enum FzmMessageTypes {
Auth = 1,
AuthResponse,
HeartBeat,
HeartBeatResponse,
Disconnect,
DisconnectResponse,
SendMessage,
SendMessageResponse,
ReceiveMessage,
ReceiveMessageResponse,
}
# protobufjs 笔记
> Google Protocol Buffer (简称 Protobuf) 是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存储等领域。
`protobufjs` 是谷歌官方提供的,用于在 JavaScript 中使用 `Protobuf`
如果你还不了解 protobufjs,推荐先阅读[这篇文章](https://juejin.cn/post/6844903699458818062)
## 使用 `pbjs`
本目录下的 `protobuf.js``protobuf.d.ts``protobufjs` 自带的命令行工具 `pbjs` 生成,源文件是目录下的 `comet.proto`
执行下面的命令之前先确保局部安装了 [protobufjs](https://www.npmjs.com/package/protobufjs)
### 生成的 `.js` 文件
生成的 `.js` 文件可以将相应的数据结构构造成 `protobuf` 格式的二进制数据(二进制数据在 JavaScript 中使用 Uint8Array 类型表示)。
```shell
npx pbjs -t static-module -w es6 -o src/utils/fzm-message-protocol/protobuf.js src/utils/fzm-message-protocol/comet.proto
```
### 生成的 `.d.ts` 文件
生成的 `.d.ts` 文件使得 TypeScript 项目获得完整的代码提示。
```shell
npx pbts -o src/utils/fzm-message-protocol/protobuf.d.ts src/utils/fzm-message-protocol/protobuf.js
```
## 使用方法
例如有个 `proto 文件` 如下:
```proto
package demo.proto;
message Person {
string name = 1;
int32 age = 2;
}
```
其中第一行为包名,下面的 `message Proto {...}` 为一个数据结构。要在 TypeScript 中将对应的 Object 编码成 proto 格式的二进制流,只需要:
```TypeScript
import { demo } from '...'
const person = { name: '张三', age: 18 }
const dataEncoded = dtalk.proto.Person.encode(person).finish()
// dataEncoded 为 UInt8Array 类型,可以被解码回 `{ name: '张三', age: 18 }`
```
由于 protobuf 为我们生成了 `.d.ts` 文件,因此在书写代码时能获得智能提示:
![](http://image-hosting-service.oss-cn-hangzhou.aliyuncs.com/20210512_%E5%B1%8F%E5%B9%95%E5%BD%95%E5%88%B62021-05-12%2011.13.17.jpg)
上图中的 `dtalk.proto.Proto` 是一个 [Type](https://protobufjs.github.io/protobuf.js/Type.html), 可以直接对目标 Object 编码。
syntax = "proto3";
package chat33.comet;
message AuthMsg {
string appId = 1;
string token = 2;
bytes ext = 3; // 其它业务方可能需要的信息
}
import { arrayToNumber } from 'enc-utils'
import { header } from './FzmMessageProtocolHeader'
interface Header {
[headerProp: string]: number
}
const decode = (data: Uint8Array): { header: Header; body: Uint8Array } => {
// 先解析 header
let key: keyof typeof header
// 计算 header 中所有字段的开始位置
const startIndexesMap: number[] = []
Object.values(header).reduce((prev, curr, currIndex, arr) => {
let currValue: number
if (currIndex === 0) {
currValue = 0
} else {
currValue = arr[currIndex - 1]
}
startIndexesMap.push(prev + currValue)
return prev + currValue
}, 0)
// 计算 header 中所有字段的字段名、开始位置、结束位置
const headerPropsPointer: { propName: string; startIndex: number; endIndex: number }[] = []
let i = 0
for (key in header) {
const length = header[key]
headerPropsPointer.push({
propName: key,
startIndex: startIndexesMap[i],
endIndex: startIndexesMap[i] + length,
})
i++
}
// 计算 header 中所有字段的值
const headerObj: { [headerProp: string]: number } = {}
headerPropsPointer.forEach((prop) => {
Object.defineProperty(headerObj, prop.propName, {
value: arrayToNumber(data.slice(prop.startIndex, prop.endIndex)),
configurable: true,
enumerable: true,
writable: true,
})
})
// 拿到 body 部分
const bodyData = data.slice(headerObj.headerLength)
let body: Uint8Array
if (bodyData.length) {
body = bodyData
} else {
body = new Uint8Array()
}
return {
header: headerObj,
body,
}
}
export default (rawMsg: Blob): Promise<{ header: Header; body: Uint8Array }> => {
return new Promise((resolve) => {
rawMsg.arrayBuffer().then((bufferMsg: ArrayBuffer) => {
const msg = decode(new Uint8Array(bufferMsg))
resolve(msg)
})
})
}
// 兼容部分安卓无法调用 Blob.prototype.arrayBuffer
// https://gist.github.com/hanayashiki/8dac237671343e7f0b15de617b0051bd
;(function () {
File.prototype.arrayBuffer = File.prototype.arrayBuffer || myArrayBuffer
Blob.prototype.arrayBuffer = Blob.prototype.arrayBuffer || myArrayBuffer
function myArrayBuffer(this: Blob) {
// this: File or Blob
return new Promise((resolve) => {
const fr = new FileReader()
fr.onload = () => {
resolve(fr.result)
}
fr.readAsArrayBuffer(this)
})
}
})()
import { numberToArray, concatArrays } from 'enc-utils'
import { AuthMsg } from '.'
import FzmMessageProtocolHeader from './FzmMessageProtocolHeader'
import { FzmMessageTypes } from './FzmMessageTypes'
import { chat33 } from './protobuf'
/**
* 将一个数字转换为一定长度的二进制数据
* @param num 想转换的数字
* @param byteLength 转换后的二进制数据长度(单位为字节)。
* `byteLength` 不能小于 `num` 的字节长度。
* 例如输入的 num 为 256,而 256 用二进制表示为 `00000001 00000000`,因此 `num` 的字节长度为 2,即至少需要 2 个字节来表示。
* 因此 `byteLength` 不能为 1,只能等于或大于 2。
* @returns 长度为 `byteLength` 的二进制的 `num`
*/
const encodeNumberWithFixedLength = (num: number, byteLength: number): Uint8Array => {
const uint8arr = numberToArray(num)
if (uint8arr.length > byteLength) throw '输入的数字过大!'
if (uint8arr.length == byteLength) return uint8arr
const arr = Array.from(uint8arr)
while (arr.length < byteLength) {
arr.splice(0, 0, 0) // 在数组的最前面插入一个 0
}
return Uint8Array.from(arr)
}
export type MessageEncoderPayload = AuthMsg | Uint8Array | null
/** 对消息进行编码 */
export default (payload: MessageEncoderPayload, messageType: FzmMessageTypes, seq = 0, ack?: number): Uint8Array => {
// 1. 构造 body
let bodyData: Uint8Array
// 判断 `messageType`:
// 如果是初次连接鉴权, 则内部解析成二进制流;
// 如果是正常发送消息, 则直接接收二进制流。
if (payload && messageType === FzmMessageTypes.Auth) {
bodyData = chat33.comet.AuthMsg.encode(payload as AuthMsg).finish()
} else if (payload && messageType !== FzmMessageTypes.Auth) {
bodyData = payload as Uint8Array
} else {
bodyData = new Uint8Array()
}
// 2. 基于 body 构造对应的 header
const header = new FzmMessageProtocolHeader(messageType, seq, ack || 0)
header.updatePackageLength(bodyData)
// 3. 对 header 编码
const headerData = Object.values(header.header).reduce((prev, curr) => {
return concatArrays(prev, encodeNumberWithFixedLength(curr.value, curr.length))
}, new Uint8Array())
// 4. 拼接 header 和 body
const data = concatArrays(headerData, bodyData)
return data
}
import encodeMessage from './encodeMessage'
import FzmMessageProtocolConnection from './FzmMessageProtocolConnection'
import { FzmMessageTypes } from './FzmMessageTypes'
export interface AuthMsg {
appId: string
token: string
ext?: Uint8Array
}
export default class FzmMessageProtocol {
url: string
ws?: WebSocket
constructor(url: string) {
this.url = url
}
authorize(authMsg: AuthMsg): Promise<FzmMessageProtocolConnection> {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url)
this.ws.onopen = () => {
// 发送鉴权消息
this.ws?.send(encodeMessage(authMsg, FzmMessageTypes.Auth))
}
this.ws.onmessage = () => {
resolve(new FzmMessageProtocolConnection(this.ws as WebSocket))
}
this.ws.onclose = () => {
reject(`WebSocket 连接被服务器端关闭,这大概率是鉴权失败导致的`)
}
})
}
}
/* eslint-disable */
import * as $protobuf from 'protobufjs'
/** Namespace chat33. */
export namespace chat33 {
/** Namespace comet. */
namespace comet {
/** Properties of an AuthMsg. */
interface IAuthMsg {
/** AuthMsg appId */
appId?: string | null
/** AuthMsg token */
token?: string | null
/** AuthMsg ext */
ext?: Uint8Array | null
}
/** Represents an AuthMsg. */
class AuthMsg implements IAuthMsg {
/**
* Constructs a new AuthMsg.
* @param [properties] Properties to set
*/
constructor(properties?: chat33.comet.IAuthMsg)
/** AuthMsg appId. */
public appId: string
/** AuthMsg token. */
public token: string
/** AuthMsg ext. */
public ext: Uint8Array
/**
* Creates a new AuthMsg instance using the specified properties.
* @param [properties] Properties to set
* @returns AuthMsg instance
*/
public static create(properties?: chat33.comet.IAuthMsg): chat33.comet.AuthMsg
/**
* Encodes the specified AuthMsg message. Does not implicitly {@link chat33.comet.AuthMsg.verify|verify} messages.
* @param message AuthMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encode(message: chat33.comet.IAuthMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Encodes the specified AuthMsg message, length delimited. Does not implicitly {@link chat33.comet.AuthMsg.verify|verify} messages.
* @param message AuthMsg message or plain object to encode
* @param [writer] Writer to encode to
* @returns Writer
*/
public static encodeDelimited(message: chat33.comet.IAuthMsg, writer?: $protobuf.Writer): $protobuf.Writer
/**
* Decodes an AuthMsg message from the specified reader or buffer.
* @param reader Reader or buffer to decode from
* @param [length] Message length if known beforehand
* @returns AuthMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decode(reader: $protobuf.Reader | Uint8Array, length?: number): chat33.comet.AuthMsg
/**
* Decodes an AuthMsg message from the specified reader or buffer, length delimited.
* @param reader Reader or buffer to decode from
* @returns AuthMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
public static decodeDelimited(reader: $protobuf.Reader | Uint8Array): chat33.comet.AuthMsg
/**
* Verifies an AuthMsg message.
* @param message Plain object to verify
* @returns `null` if valid, otherwise the reason why it is not
*/
public static verify(message: { [k: string]: any }): string | null
/**
* Creates an AuthMsg message from a plain object. Also converts values to their respective internal types.
* @param object Plain object
* @returns AuthMsg
*/
public static fromObject(object: { [k: string]: any }): chat33.comet.AuthMsg
/**
* Creates a plain object from an AuthMsg message. Also converts values to other types if specified.
* @param message AuthMsg
* @param [options] Conversion options
* @returns Plain object
*/
public static toObject(
message: chat33.comet.AuthMsg,
options?: $protobuf.IConversionOptions
): { [k: string]: any }
/**
* Converts this AuthMsg to JSON.
* @returns JSON object
*/
public toJSON(): { [k: string]: any }
}
}
}
/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/
import * as $protobuf from "protobufjs/minimal";
// Common aliases
const $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util;
// Exported root namespace
const $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {});
export const chat33 = $root.chat33 = (() => {
/**
* Namespace chat33.
* @exports chat33
* @namespace
*/
const chat33 = {};
chat33.comet = (function() {
/**
* Namespace comet.
* @memberof chat33
* @namespace
*/
const comet = {};
comet.AuthMsg = (function() {
/**
* Properties of an AuthMsg.
* @memberof chat33.comet
* @interface IAuthMsg
* @property {string|null} [appId] AuthMsg appId
* @property {string|null} [token] AuthMsg token
* @property {Uint8Array|null} [ext] AuthMsg ext
*/
/**
* Constructs a new AuthMsg.
* @memberof chat33.comet
* @classdesc Represents an AuthMsg.
* @implements IAuthMsg
* @constructor
* @param {chat33.comet.IAuthMsg=} [properties] Properties to set
*/
function AuthMsg(properties) {
if (properties)
for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i)
if (properties[keys[i]] != null)
this[keys[i]] = properties[keys[i]];
}
/**
* AuthMsg appId.
* @member {string} appId
* @memberof chat33.comet.AuthMsg
* @instance
*/
AuthMsg.prototype.appId = "";
/**
* AuthMsg token.
* @member {string} token
* @memberof chat33.comet.AuthMsg
* @instance
*/
AuthMsg.prototype.token = "";
/**
* AuthMsg ext.
* @member {Uint8Array} ext
* @memberof chat33.comet.AuthMsg
* @instance
*/
AuthMsg.prototype.ext = $util.newBuffer([]);
/**
* Creates a new AuthMsg instance using the specified properties.
* @function create
* @memberof chat33.comet.AuthMsg
* @static
* @param {chat33.comet.IAuthMsg=} [properties] Properties to set
* @returns {chat33.comet.AuthMsg} AuthMsg instance
*/
AuthMsg.create = function create(properties) {
return new AuthMsg(properties);
};
/**
* Encodes the specified AuthMsg message. Does not implicitly {@link chat33.comet.AuthMsg.verify|verify} messages.
* @function encode
* @memberof chat33.comet.AuthMsg
* @static
* @param {chat33.comet.IAuthMsg} message AuthMsg message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
AuthMsg.encode = function encode(message, writer) {
if (!writer)
writer = $Writer.create();
if (message.appId != null && Object.hasOwnProperty.call(message, "appId"))
writer.uint32(/* id 1, wireType 2 =*/10).string(message.appId);
if (message.token != null && Object.hasOwnProperty.call(message, "token"))
writer.uint32(/* id 2, wireType 2 =*/18).string(message.token);
if (message.ext != null && Object.hasOwnProperty.call(message, "ext"))
writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.ext);
return writer;
};
/**
* Encodes the specified AuthMsg message, length delimited. Does not implicitly {@link chat33.comet.AuthMsg.verify|verify} messages.
* @function encodeDelimited
* @memberof chat33.comet.AuthMsg
* @static
* @param {chat33.comet.IAuthMsg} message AuthMsg message or plain object to encode
* @param {$protobuf.Writer} [writer] Writer to encode to
* @returns {$protobuf.Writer} Writer
*/
AuthMsg.encodeDelimited = function encodeDelimited(message, writer) {
return this.encode(message, writer).ldelim();
};
/**
* Decodes an AuthMsg message from the specified reader or buffer.
* @function decode
* @memberof chat33.comet.AuthMsg
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @param {number} [length] Message length if known beforehand
* @returns {chat33.comet.AuthMsg} AuthMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
AuthMsg.decode = function decode(reader, length) {
if (!(reader instanceof $Reader))
reader = $Reader.create(reader);
let end = length === undefined ? reader.len : reader.pos + length, message = new $root.chat33.comet.AuthMsg();
while (reader.pos < end) {
let tag = reader.uint32();
switch (tag >>> 3) {
case 1:
message.appId = reader.string();
break;
case 2:
message.token = reader.string();
break;
case 3:
message.ext = reader.bytes();
break;
default:
reader.skipType(tag & 7);
break;
}
}
return message;
};
/**
* Decodes an AuthMsg message from the specified reader or buffer, length delimited.
* @function decodeDelimited
* @memberof chat33.comet.AuthMsg
* @static
* @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from
* @returns {chat33.comet.AuthMsg} AuthMsg
* @throws {Error} If the payload is not a reader or valid buffer
* @throws {$protobuf.util.ProtocolError} If required fields are missing
*/
AuthMsg.decodeDelimited = function decodeDelimited(reader) {
if (!(reader instanceof $Reader))
reader = new $Reader(reader);
return this.decode(reader, reader.uint32());
};
/**
* Verifies an AuthMsg message.
* @function verify
* @memberof chat33.comet.AuthMsg
* @static
* @param {Object.<string,*>} message Plain object to verify
* @returns {string|null} `null` if valid, otherwise the reason why it is not
*/
AuthMsg.verify = function verify(message) {
if (typeof message !== "object" || message === null)
return "object expected";
if (message.appId != null && message.hasOwnProperty("appId"))
if (!$util.isString(message.appId))
return "appId: string expected";
if (message.token != null && message.hasOwnProperty("token"))
if (!$util.isString(message.token))
return "token: string expected";
if (message.ext != null && message.hasOwnProperty("ext"))
if (!(message.ext && typeof message.ext.length === "number" || $util.isString(message.ext)))
return "ext: buffer expected";
return null;
};
/**
* Creates an AuthMsg message from a plain object. Also converts values to their respective internal types.
* @function fromObject
* @memberof chat33.comet.AuthMsg
* @static
* @param {Object.<string,*>} object Plain object
* @returns {chat33.comet.AuthMsg} AuthMsg
*/
AuthMsg.fromObject = function fromObject(object) {
if (object instanceof $root.chat33.comet.AuthMsg)
return object;
let message = new $root.chat33.comet.AuthMsg();
if (object.appId != null)
message.appId = String(object.appId);
if (object.token != null)
message.token = String(object.token);
if (object.ext != null)
if (typeof object.ext === "string")
$util.base64.decode(object.ext, message.ext = $util.newBuffer($util.base64.length(object.ext)), 0);
else if (object.ext.length)
message.ext = object.ext;
return message;
};
/**
* Creates a plain object from an AuthMsg message. Also converts values to other types if specified.
* @function toObject
* @memberof chat33.comet.AuthMsg
* @static
* @param {chat33.comet.AuthMsg} message AuthMsg
* @param {$protobuf.IConversionOptions} [options] Conversion options
* @returns {Object.<string,*>} Plain object
*/
AuthMsg.toObject = function toObject(message, options) {
if (!options)
options = {};
let object = {};
if (options.defaults) {
object.appId = "";
object.token = "";
if (options.bytes === String)
object.ext = "";
else {
object.ext = [];
if (options.bytes !== Array)
object.ext = $util.newBuffer(object.ext);
}
}
if (message.appId != null && message.hasOwnProperty("appId"))
object.appId = message.appId;
if (message.token != null && message.hasOwnProperty("token"))
object.token = message.token;
if (message.ext != null && message.hasOwnProperty("ext"))
object.ext = options.bytes === String ? $util.base64.encode(message.ext, 0, message.ext.length) : options.bytes === Array ? Array.prototype.slice.call(message.ext) : message.ext;
return object;
};
/**
* Converts this AuthMsg to JSON.
* @function toJSON
* @memberof chat33.comet.AuthMsg
* @instance
* @returns {Object.<string,*>} JSON object
*/
AuthMsg.prototype.toJSON = function toJSON() {
return this.constructor.toObject(this, $protobuf.util.toJSONOptions);
};
return AuthMsg;
})();
return comet;
})();
return chat33;
})();
export { $root as default };
/**
* 为满足业务需求,连接 WebSocket 时需要携带 ext 字段
* 在本文件计算
*/
import { from, OrderInfo } from '@/store/appCallerStore'
import { Platform } from 'quasar'
import { dtalk } from './fzm-message-protocol-chat/protobuf'
function getDevice() {
if (Platform.is.ios) {
return dtalk.proto.Device.IOS
} else if (Platform.is.android) {
return dtalk.proto.Device.Android
} else return null
}
/**
* 鉴权时需要携带的额外信息
*/
interface Ext {
device: dtalk.proto.Device | null
username: string
}
export default function (orderInfo: OrderInfo): Uint8Array {
const ext: Ext = {
device: getDevice(),
username: from == orderInfo.userZbId ? orderInfo.userNick : orderInfo.merchantNick,
}
const extData = dtalk.proto.Login.encode(ext).finish()
return extData
}
const protocol = '(((ws(s)?)|(http(s)?))\\:\\/\\/)'
const domain = '[a-zA-Z0-9_-]+'
const other = '([a-zA-Z/0-9$-/:-?{#-~!"^_`\\[\\]]+)?'
const ext = '(\\.' + other + ')'
const port = '(\\:[0-9]+)?'
const ip = '([a-zA-Z0-9]{4}:)+[a-zA-Z0-9]'
/**
* 检查一串字符串是否为有效的 url
* Checks if a string is a valid url
*
* @param {string} val - the string to check
* @param {object} [options] - defaults to `{}`
* - **options.requireProtocol** {boolean} - set to true if you only want URLs with a protocol to be considered valid. Defaults to `false`
* @returns {boolean} valid - `true` if *val* is a valid url, `false` otherwise
*
*/
export default function(val: string | undefined, options?: { requireProtocol?: boolean }): boolean {
if (!val) return false
if (!options) {
options = {}
}
const re = new RegExp(
'^' +
protocol +
(!options.requireProtocol ? '?' : '') +
'(' +
domain +
ext +
'|localhost|' +
ip +
')' +
port +
other +
'$'
)
return typeof val === 'string' && re.test(val)
}
declare module "@/utils/jsBridge"
\ No newline at end of file
/*eslint-disable*/
const ANDROID = 1
const IOS = 2
// app return callback response
// standard:
// { code: 0, error: 'error msg'}
const SUCCESS_CODE = 0
// const SUCCESS_RES = { code: SUCCESS_CODE, error: 'error msg' }
class jsBridge {
constructor(bridgeName = 'WebViewJavascriptBridge', bridgeScheme = 'https') {
this.bridgeName = bridgeName
this.bridgeScheme = bridgeScheme
let ua = navigator.userAgent || navigator.vendor || window.opera
// ua = "Mozilla/5.0 (Linux; Android 6.0.1; Redmi 4A Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36;wallet;1.2.9"
ua = ua.toLowerCase()
if (/android/.test(ua)) {
this.app = ANDROID
} else if (/(iphone|ipad|ipod|ios)/.test(ua)) {
this.app = IOS
}
// 1. 判断 是否在 钱包app 内
// this.app.isWallet = true/false
// 2. 记录 app 版本号
// ua = Mozilla/5.0 (Linux; Android 6.0.1; Redmi 4A Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36;wallet;1.2.9
// ua = Mozilla/5.0 (iPhone; CPU iPhone OS 11_2 like Mac OS X) AppleWebKit/604.4.7 (KHTML, like Gecko) Mobile/15C114;wallet;1.2.8
// this.app.version = '1.2.0'
this.registDefaultHandlerForApp()
}
app = null
appType = {
isWallet: false,
version: ''
}
/* 与ios交互 */
setupWebViewJavascriptBridge(callback) {
// WebViewJavascriptBridge 由native在注入
// https://github.com/marcuswestin/WebViewJavascriptBridge
if (window[this.bridgeName]) {
return callback(window[this.bridgeName]);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = `https://__bridge_loaded__`;
document.documentElement.appendChild(WVJBIframe);
setTimeout(function () {
document.documentElement.removeChild(WVJBIframe);
}, 0)
}
/* 与android交互 */
getAndroidBridge() {
return new Promise(resolve => {
if (window[this.bridgeName]) {
resolve(window[this.bridgeName])
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady', () => {
resolve(window[this.bridgeName])
},
false
)
}
})
}
/**
* 给ios注册方法 jsHandler需要返回一个promise
* @param {string} event
* @param {fun} jsHandler
* @return JSON对象
*/
registerHandlerForIOS(event, jsHandler) {
this.setupWebViewJavascriptBridge(function (bridge) {
bridge.registerHandler(event, (data2js, responseCallback = () => {}) => {
jsHandler(data2js).then(responseCallback)
})
})
}
/**
* 给安卓注册方法 jsHandler需要返回一个promise
* @param {string} event
* @param {fun} jsHandler
* @return JSON对象
*/
registerHandlerForAndroid(event, jsHandler) {
this.getAndroidBridge().then(bridge => {
bridge.registerHandler(event, (data2js, responseCallback = () => {}) => {
jsHandler(data2js).then(responseCallback)
})
})
}
/**
* 调用ios
* @param {string} event
* @param {object} params
* @param {fun} callback
* @return JSON对象
*/
callIOSHandler(event, params = {}) {
return new Promise(resolve => {
this.setupWebViewJavascriptBridge(function (bridge) {
bridge.callHandler(event, params, resolve)
})
})
}
/**
* 调用android
* @param {string} event
* @param {object} params
* @param {fun} callback
* @return JSON对象
*/
callAndroidHandler(event, params = {}) {
return new Promise(resolve => {
this.getAndroidBridge().then(bridge => {
bridge.callHandler(event, params, (res) => {
if (typeof res === "string" && /{|\[/.test(res)) {
try {
res = JSON.parse(res)
} catch (error) {
// console.error(error)
throw error
}
}
resolve(res)
})
})
})
}
/**
* 注册一堆事件处理函数
*
* @memberof jsBridge
*/
registDefaultHandlerForApp() {
this.on('reload', () => {
location.reload()
return Promise.resolve('done!')
})
}
/**
* 给ios和android注册事件监听
*
* @memberof jsBridge
*/
on(...args) {
if (this.app === ANDROID) {
this.registerHandlerForAndroid(...args)
} else if (this.app === IOS) {
this.registerHandlerForIOS(...args)
}
}
/**
* 调用app方法
*
* @param {*} args
* @returns
* @memberof jsBridge
*/
callHandler(...args) {
if (this.app === ANDROID) {
return this.callAndroidHandler(...args)
} else if (this.app === IOS) {
return this.callIOSHandler(...args)
}
}
/**
* 关闭当前webview
*
* @memberof jsBridge
*/
closeCurrentWebview() {
return this.callHandler('closeCurrentWebview')
}
}
window.jsBridge=jsBridge
// console.log(window)
export default jsBridge;
\ No newline at end of file
......@@ -60,13 +60,14 @@ import ChatContentVue from "./ChatContent.vue";
import ChatInputVue from "./ChatInput.vue";
import { connectionState } from "@/store/connectionStore";
import FzmMessageProtocol from "@/utils/fzm-message-protocol";
import { getOrderInfo } from "@/store/appCallerStore";
import { getOrderInfo, privateKey, publicKey } from "@/store/appCallerStore";
import { token, from, orderid } from "@/store/appCallerStore";
import computeExt from "@/utils/getFzmMesageProtocolExt";
import { watch } from "@vue/runtime-core";
import decodeChatMessage from "@/utils/fzm-message-protocol-chat/decodeChatMessage";
import { messageStore } from "@/store/messagesStore";
import { ChatMessageTypes } from "@/types/chatMessageTypes";
import { generateToken } from "@/utils/generateToken/generate-token";
export default defineComponent({
components: { ChatContentVue, ChatInputVue, NavBar },
......@@ -88,7 +89,11 @@ export default defineComponent({
fmp
.authorize({
appId: "dtalk",
token: token,
token: generateToken({
address: from,
privateKeyHex: privateKey,
publicKeyHex: publicKey,
}),
})
.then((conn) => {
connectionState.connection = conn;
......@@ -125,12 +130,11 @@ export default defineComponent({
// 连接后,指定每次收到消息要做的事
connectionState.connection.onReceiveMessage = (msgData) => {
const msg = decodeChatMessage(msgData);
console.log(msg, "show msg");
// 收到的非本笔订单的消息不处理
if (msg.orderid !== orderid) return;
// if (msg.orderid !== orderid) return;
messageStore.displayNewMessage({
msg && messageStore.displayNewMessage({
content: msg.content,
from: msg.from,
uuid: msg.uuid,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment