Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
166d9b5
refactor: adapt to [email protected]
su-chang Feb 3, 2023
168c223
fix: update @juzi/wechaty-puppet-cache
su-chang Feb 7, 2023
227c95e
fix: add listenerId to message payload
su-chang Feb 8, 2023
f2d93c8
fix: use currentUserId replace selfId()
su-chang Feb 8, 2023
d01e92e
fix: file ready & get type of file
su-chang Feb 9, 2023
b59b3ac
fix: adapt file-box url
su-chang Feb 9, 2023
bc335f9
fix: room member brief info, UserName -> userName NickName -> nickName
su-chang Feb 13, 2023
debe12a
fix: error message for contactRawPayloadParser
su-chang Feb 13, 2023
cea2102
1.0.7
su-chang Feb 13, 2023
212602f
fix: adapt userName & nickName
su-chang Feb 13, 2023
4722ac6
fix: judge im contact as contact
su-chang Feb 13, 2023
f7c287a
fix: room del member
su-chang Feb 13, 2023
852f3a8
1.0.8
su-chang Feb 13, 2023
f2e14da
fix: add typescript & ts-node to dev dependencies
su-chang Feb 13, 2023
f0f455d
fix: clear this.__currentUserId after receive scan & logout event
su-chang Feb 22, 2023
a824b13
1.0.9
su-chang Mar 7, 2023
a36b661
fix: :bug: convert scan status to enum literals
su-chang Mar 7, 2023
de67631
1.0.10
su-chang Mar 7, 2023
0377fb7
fix: :bug: emit logout event and then clear __currentUserId
su-chang Mar 7, 2023
535a4ed
1.0.11
su-chang Mar 7, 2023
a2bd8f4
1.0.12
su-chang Mar 7, 2023
456bb54
fix: :bug: remove set __currentUserId as undefined
su-chang Mar 7, 2023
13a5722
fix: :bug: call super logout when padplus logout & replace reset by o…
su-chang Mar 7, 2023
04f199d
1.0.13
su-chang Mar 7, 2023
09ce495
fix: :bug: set contact/room alias/topic timeout from 5s to 50s
su-chang Mar 8, 2023
6aa8277
fix: :bug: do not clear room cache after set room topic
su-chang Mar 8, 2023
e8bb348
docs: :memo: add detail info in error message
su-chang Mar 8, 2023
07d5bf3
1.0.14
su-chang Mar 9, 2023
24ec656
fix: :bug: check wechatUser when try to auto login
su-chang Mar 13, 2023
c33642a
fix: update default grpc endpoint, only worked for beijing region
su-chang Mar 22, 2023
9c37ffa
1.2.0
su-chang Mar 22, 2023
1762c0b
fix: :bug: add default error message when login status is differ with…
su-chang Mar 23, 2023
7a300f3
1.2.1
su-chang Mar 23, 2023
4bc1e35
docs: :memo: change reconnectStatus from boolean to enum & adapt stre…
su-chang Mar 23, 2023
d7e845b
1.2.2
su-chang Mar 23, 2023
be9b361
1.2.4
su-chang Mar 23, 2023
7e6f39b
fix: :bug: throw error when sync im contact or room
su-chang Mar 28, 2023
8985c70
fix: :bug: ignore im message
su-chang Mar 28, 2023
ae0299e
1.2.5
su-chang Mar 28, 2023
7597a8c
fix: :bug: filter im contact / room id for contactIdList and roomIdList
su-chang Mar 28, 2023
0f40975
1.2.6
su-chang Mar 28, 2023
0ac7a43
feat: :sparkles: add @ffmpeg-installer/ffmpeg to config ffmpeg path f…
su-chang Mar 28, 2023
7a672cf
1.2.7
su-chang Mar 28, 2023
4aa6e86
feat: :sparkles: update GET_CDN_DATA & CDN_DATA_RSP in proto file
su-chang Mar 29, 2023
34f069e
feat: :sparkles: support get image cdn data & could set image url as …
su-chang Mar 30, 2023
dedb97b
1.2.9
su-chang Mar 30, 2023
0c0d5b2
fix: :bug: add room join system message template to ROOM_JOIN_OTHER_I…
su-chang Apr 12, 2023
811fdeb
feat: :sparkles: support dismiss room & emit room-leave event
su-chang Apr 12, 2023
79758af
1.2.10
su-chang Apr 13, 2023
b852aec
fix: :bug: dirty local cache
su-chang Apr 14, 2023
f76923f
fix: :bug: get qrcode use memory card uin & wxid first when qrcode ti…
su-chang Apr 19, 2023
88c42d8
fix: :bug: get room topic from cache
su-chang Apr 19, 2023
2965bbb
1.2.12
su-chang Apr 19, 2023
7c36d4c
fix: add super dirtyPayload
su-chang Apr 20, 2023
627f0c1
1.2.15
su-chang Apr 20, 2023
1a4ecc3
fix: :bug: change request cache expre time from 10 seconds to 0.5 sec…
su-chang Apr 21, 2023
37b183f
fix: :bug: adapt sync room & room member info when the bot has alread…
su-chang Apr 21, 2023
5516256
1.3.0
su-chang Apr 21, 2023
55e4f4f
fix: :bug: check room exsit or not when get room avatar
su-chang Apr 21, 2023
96feaa8
fix: :bug: emit leave event when bot leave room
su-chang Apr 21, 2023
263ab70
1.3.1
su-chang Apr 21, 2023
be6a630
fix: :bug: parse removerId by nickname
su-chang Apr 25, 2023
7f9e941
1.3.2
su-chang Apr 27, 2023
b2de8ab
fix: :bug: sync room member if members are empty
su-chang May 6, 2023
3c8f6ad
1.3.3
su-chang May 6, 2023
2ea6840
fix: :bug: do not update room member cache when only one member info …
su-chang May 10, 2023
ba4c9cc
1.3.4
su-chang May 10, 2023
db5d08a
fix: :bug: change timeout & add env for timeout variable
su-chang Jun 29, 2023
22852ec
fix: :bug: add env for expire time, default is 1 second
su-chang Jun 29, 2023
fc42a6b
1.3.5
su-chang Jun 29, 2023
d7fd059
fix: :bug: remove bot by friend which has remark
su-chang Jun 29, 2023
3391c61
1.3.6
su-chang Jun 29, 2023
19aa24e
fix: :bug: do not fetch room member data when receive ROOM_MEMBER_LIS…
su-chang Jul 3, 2023
a4e31e4
fix: :bug: get api type key name in dedupe api
su-chang Jul 4, 2023
58758b6
fix: :bug: do not put get contact in dedupe queue
su-chang Jul 4, 2023
e53243b
1.3.8
su-chang Jul 4, 2023
8c7798f
feat: :sparkles: add rate manager for get contact/room info in queue
su-chang Jul 5, 2023
4f6f7ac
1.3.9
su-chang Jul 5, 2023
9e7055f
fix: :bug: remove get room member request from dedupe queue
su-chang Jul 5, 2023
30ea709
1.4.0
su-chang Jul 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wechaty-puppet-padplus",
"version": "0.7.43",
"name": "@juzi/wechaty-puppet-padplus",
"version": "1.4.0",
"description": "Puppet Padplus for Wechaty",
"directories": {
"test": "tests"
Expand All @@ -26,6 +26,9 @@
"type": "git",
"url": "git+https://github.com/botorange/wechaty-puppet-padplus.git"
},
"files": [
"dist/"
],
"keywords": [
"chatie",
"wechaty",
Expand Down Expand Up @@ -58,30 +61,33 @@
"memory-card": "^0.7.0",
"pkg-jq": "^0.2.2",
"shx": "^0.3.1",
"ts-node": "^10.9.1",
"ts-protoc-gen": "^0.12.0",
"tstest": "^0.4.1",
"wechaty-puppet": "^0.32.0"
},
"peerDependencies": {
"wechaty-puppet": "^0.32.0"
"typescript": "~4.7.4"
},
"dependencies": {
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"@juzi/wechaty-puppet": "^1.0.48",
"@juzi/wechaty-puppet-cache": "^1.0.0",
"array-flatten": "^3.0.0",
"axios": "^0.19.2",
"brolog": "^1.6.5",
"ffmpeg-extract-frames": "^2.0.2",
"ffmpeg-probe": "^1.0.6",
"file-box": "^1.5.5",
"flash-store": "^0.14.5",
"fs-extra": "^8.1.0",
"google-protobuf": "^3.6.1",
"grpc": "^1.22.0",
"install": "^0.13.0",
"lru-cache": "^5.1.1",
"npm": "^9.6.2",
"promise-retry": "^1.1.1",
"rx-queue": "^0.8.5",
"state-switch": "^0.9.7",
"uuid": "^7.0.0",
"watchdog": "^0.8.10",
"wechaty-puppet-cache": "^0.1.10",
"xml2js": "^0.4.22"
},
"publishConfig": {
Expand Down
45 changes: 39 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@ export const padplusToken = () => {
return token
}

export const NO_USER_INFO_IN_REDIS = `


===========================================================================================

WARNING!!! Something wrong with your memory-card file!
Please remove it and restart the bot, then it will be ok.
This default path of memory-card file in the root dir of your project,
Path like: ${process.cwd()}/your-name-memory-card.json

MemoryCard文件出现问题,请删除后重启bot,即可正常使用。
MemoryCard文件默认在项目根目录下,路径示例:${process.cwd()}/your-name-memory-card.json

===========================================================================================


`

export const INVALID_TOKEN_MESSAGE = `


Expand Down Expand Up @@ -50,22 +68,37 @@ export const EXPIRED_TOKEN_MESSAGE = `

`

const SECOND = 1000
const MINUTE = 60 * SECOND

export const PADPLUS_REPLAY_MESSAGE = process.env.PADPLUS_REPLAY_MESSAGE === 'true'

/**
* GRPC server
*/
const WECHATY_PUPPET_PADPLUS_ENDPOINT_ENV_VAR = 'WECHATY_PUPPET_PADPLUS_ENDPOINT'
export const GRPC_ENDPOINT = process.env[WECHATY_PUPPET_PADPLUS_ENDPOINT_ENV_VAR] || 'padplus.juzibot.com:50051'
export const GRPC_ENDPOINT = process.env[WECHATY_PUPPET_PADPLUS_ENDPOINT_ENV_VAR] || 'beijing.padplus.juzibot.com:50052'

export const MESSAGE_CACHE_AGE = 60 * MINUTE
export const MESSAGE_CACHE_MAX = SECOND

export const WAIT_FOR_READY_TIME = 10 * MINUTE

export const COMPACT_CACHE_FIRST_START = 15 * MINUTE

export const MESSAGE_CACHE_AGE = 1000 * 60 * 60
export const MESSAGE_CACHE_MAX = 1000
export const COMPACT_CACHE_INTERVAL = 60 * MINUTE

export const WAIT_FOR_READY_TIME = 1000 * 60 * 10
// sync data timeout in minute
export const SYNC_CONTACT_TIMEOUT = (Number(process.env.SYNC_CONTACT_TIMEOUT) || 2) * MINUTE
export const SYNC_ROOM_TIMEOUT = (Number(process.env.SYNC_ROOM_TIMEOUT) || 2) * MINUTE
export const SYNC_MEMBER_TIMEOUT = (Number(process.env.SYNC_MEMBER_TIMEOUT) || 2) * MINUTE

export const COMPACT_CACHE_FIRST_START = 1000 * 60 * 15
// delay get data interval in second
export const MEMBER_DELAY_INTERVAL = (Number(process.env.MEMBER_DELAY_INTERVAL) || 0.5) * SECOND

export const COMPACT_CACHE_INTERVAL = 1000 * 60 * 60
// Expire time for api call data that persist in the pool
// Number of seconds
export const EXPIRE_TIME = (Number(process.env.EXPIRE_TIME) || 1) * SECOND

const logLevel = process.env.PADPLUS_LOG || process.env.WECHATY_LOG
if (logLevel) {
Expand Down
10 changes: 5 additions & 5 deletions src/convert-manager/contact-convertor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ContactGender, ContactType } from 'wechaty-puppet'
import { types } from '@juzi/wechaty-puppet'
import { PadplusContactPayload, GrpcContactPayload, TagNewOrListResponse, TagNewOrListGrpcResponse, GetContactSelfInfoGrpcResponse, GrpcSearchContact } from '../schemas'

export const convertFromGrpcContact = (contactPayload: GrpcContactPayload, isSync?: boolean): PadplusContactPayload => {
Expand All @@ -12,7 +12,7 @@ export const convertFromGrpcContact = (contactPayload: GrpcContactPayload, isSyn
nickName : contactPayload.NickName,
province : contactPayload.Province,
remark : contactPayload.RemarkName,
sex : contactPayload.Sex as ContactGender,
sex : contactPayload.Sex as types.ContactGender,
signature : contactPayload.Signature,
smallHeadUrl : contactPayload.SmallHeadImgUrl,
stranger : contactPayload.EncryptUsername,
Expand Down Expand Up @@ -48,7 +48,7 @@ export const convertFromGrpcContactSelf = (contactPayload: GetContactSelfInfoGrp
nickName : contactPayload.nickName,
province : contactPayload.province,
remark : '',
sex : contactPayload.sex as ContactGender,
sex : contactPayload.sex as types.ContactGender,
signature : contactPayload.signature,
smallHeadUrl : contactPayload.smallHeadImg,
stranger : '',
Expand All @@ -65,13 +65,13 @@ export const convertSearchContactToContact = (searchContact: GrpcSearchContact,
alias: isNumber ? '' : searchContact.searchId,
bigHeadUrl: searchContact.avatar,
city: '',
contactFlag: ContactType.Unknown,
contactFlag: types.Contact.Unknown,
contactType: 0,
country: '',
nickName: searchContact.nickName,
province: '',
remark: '',
sex: ContactGender.Unknown,
sex: types.ContactGender.Unknown,
signature: '',
smallHeadUrl: searchContact.avatar,
stranger: searchContact.v1,
Expand Down
4 changes: 2 additions & 2 deletions src/convert-manager/message-convertor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
GrpcMessagePayload,
WechatAppMessageType,
} from '../schemas'
import { MiniProgramPayload } from 'wechaty-puppet'
import { payloads } from '@juzi/wechaty-puppet'

export async function convertMessageFromGrpcToPadplus (rawMessage: GrpcMessagePayload): Promise<PadplusMessagePayload> {
const messagePayload: PadplusMessagePayload = {
Expand Down Expand Up @@ -34,7 +34,7 @@ export async function convertMessageFromGrpcToPadplus (rawMessage: GrpcMessagePa
return messagePayload
}

export function convertMiniProgramPayloadToParams (miniProgramPayload: MiniProgramPayload): MiniProgramParamsPayload {
export function convertMiniProgramPayloadToParams (miniProgramPayload: payloads.MiniProgram): MiniProgramParamsPayload {
const content: MiniProgramParamsPayload = {
aeskey: miniProgramPayload.thumbKey || '',
appid: miniProgramPayload.appid || '',
Expand Down
32 changes: 16 additions & 16 deletions src/convert-manager/room-convertor.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { RoomMemberPayload } from 'wechaty-puppet'
import { payloads } from '@juzi/wechaty-puppet'
import { PadplusRoomPayload, PadplusRoomMemberPayload, GrpcRoomPayload } from '../schemas'

export const convertRoomFromGrpc = (room: GrpcRoomPayload): PadplusRoomPayload => {
const roomPayload: PadplusRoomPayload = {
alias : room.Alias,
bigHeadUrl : room.BigHeadImgUrl,
chatRoomOwner : room.ChatRoomOwner,
chatroomId : room.UserName,
chatroomVersion: room.ChatroomVersion,
contactType : room.ContactType,
memberCount : JSON.parse(room.ExtInfo).length,
members : JSON.parse(room.ExtInfo),
nickName : room.NickName,
smallHeadUrl : room.SmallHeadImgUrl,
stranger : room.EncryptUsername,
tagList : room.LabelLists,
ticket : room.Ticket,
alias : room?.Alias || '',
bigHeadUrl : room?.BigHeadImgUrl || '',
chatRoomOwner : room?.ChatRoomOwner || '',
chatroomId : room?.UserName || '',
chatroomVersion: room?.ChatroomVersion || 0,
contactType : room?.ContactType || 0,
memberCount : JSON.parse(room?.ExtInfo || '[]').length,
members : JSON.parse(room?.ExtInfo || '[]'),
nickName : room?.NickName || '',
smallHeadUrl : room?.SmallHeadImgUrl || '',
stranger : room?.EncryptUsername || '',
tagList : room?.LabelLists || '',
ticket : room?.Ticket || '',
}
return roomPayload
}

export const convertToPuppetRoomMember = (input: PadplusRoomMemberPayload): RoomMemberPayload => {
const resut: RoomMemberPayload = {
export const convertToPuppetRoomMember = (input: PadplusRoomMemberPayload): payloads.RoomMember => {
const resut: payloads.RoomMember = {
avatar : input.smallHeadUrl,
id : input.contactId,
inviterId : input.inviterId, // 'wxid_7708837087612',
Expand Down
36 changes: 19 additions & 17 deletions src/padplus-manager/api-request/dedupeApi.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { DelayQueueExecutor } from 'rx-queue'
import { log } from '../../config'
import { EXPIRE_TIME, log } from '../../config'
import { ApiType } from '../../server-manager/proto-ts/PadPlusServer_pb'
import { ApiTypeDic } from '../../utils/util'

// Expire time for api call data that persist in the pool
// Number of seconds
const EXPIRE_TIME = 10

const DEDUPE_API = [
ApiType.GET_CONTACT,
ApiType.GET_ROOM_MEMBER,
const DEDUPE_API: ApiType[] = [
// ApiType.GET_CONTACT,
// ApiType.GET_ROOM_MEMBER,
]

interface ApiCall {
Expand Down Expand Up @@ -45,7 +41,7 @@ export class DedupeApi {

constructor () {
this.pool = {}
this.cleaner = setInterval(this.cleanData, EXPIRE_TIME * 1000)
this.cleaner = setInterval(this.cleanData, EXPIRE_TIME)
this.apiQueue = new DelayQueueExecutor(200)
}

Expand All @@ -60,19 +56,19 @@ export class DedupeApi {
log.silly(PRE, `dedupe() no need to dedupe api ${ApiTypeDic[apiName]}.`)
return func(apiName, uin, params)
}
log.silly(PRE, `dedupeApi(${apiName}, ${uin}, ${params ? JSON.stringify(params) : ''})`)
log.silly(PRE, `dedupeApi(${this.getApiTypeKey(apiName)}, ${uin}, ${params ? JSON.stringify(params) : ''})`)
const key = this.getKey(apiName, uin, params)
if (forceCall) {
delete this.pool[key]
}
const existCall = this.pool[key]
const now = new Date().getTime()
if (existCall && now - existCall.timestamp < EXPIRE_TIME * 1000) {
if (existCall && now - existCall.timestamp < EXPIRE_TIME) {
if (existCall.returned) {
log.silly(PRE, `dedupeApi(${apiName}) dedupe api call with existing results.`)
log.silly(PRE, `dedupeApi(${this.getApiTypeKey(apiName)}) dedupe api call with existing results.`)
return existCall.result
} else {
log.silly(PRE, `dedupeApi(${apiName}) dedupe api call with pending listener.`)
log.silly(PRE, `dedupeApi(${this.getApiTypeKey(apiName)}) dedupe api call with pending listener.`)
return new Promise((resolve, reject) => {
existCall.listener.push({
reject,
Expand All @@ -81,7 +77,7 @@ export class DedupeApi {
})
}
} else {
log.silly(PRE, `dedupeApi(${apiName}) dedupe api call missed, call the external service.`)
log.silly(PRE, `dedupeApi(${this.getApiTypeKey(apiName)}) dedupe api call missed, call the external service.`)
this.pool[key] = {
listener: [],
returned: false,
Expand All @@ -91,7 +87,7 @@ export class DedupeApi {
try {
result = await this.apiQueue.execute(() => func(apiName, uin, params))
} catch (e) {
log.silly(PRE, `dedupeApi(${apiName}) failed from external service, reject ${this.pool[key].listener.length} duplicate api calls.`)
log.silly(PRE, `dedupeApi(${this.getApiTypeKey(apiName)}) failed from external service, reject ${this.pool[key].listener.length} duplicate api calls.`)
this.pool[key].listener.map(api => {
api.reject(e)
})
Expand All @@ -101,7 +97,7 @@ export class DedupeApi {

this.pool[key].result = result
this.pool[key].returned = true
log.silly(PRE, `dedupeApi(${apiName}) got results from external service, resolve ${this.pool[key].listener.length} duplicate api calls.`)
log.silly(PRE, `dedupeApi(${this.getApiTypeKey(apiName)}) got results from external service, resolve ${this.pool[key].listener.length} duplicate api calls.`)
this.pool[key].listener.map(api => {
api.resolve(result)
})
Expand Down Expand Up @@ -129,7 +125,7 @@ export class DedupeApi {
for (const key in this.pool) {
if (this.pool.hasOwnProperty(key)) {
const apiCache = this.pool[key]
if (apiCache.timestamp - now > EXPIRE_TIME * 1000) {
if (apiCache.timestamp - now > EXPIRE_TIME) {
delete this.pool[key]
}
}
Expand All @@ -140,4 +136,10 @@ export class DedupeApi {
return `${apiName}-${uin}-${params ? JSON.stringify(params) : ''}`
}

private getApiTypeKey (apiType: number): string | undefined {
const keys = Object.keys(ApiType).filter(key => isNaN(Number(key)))
const apiTypeKey = keys.find(key => ApiType[key as keyof typeof ApiType] === apiType)
return apiTypeKey || undefined
}

}
29 changes: 22 additions & 7 deletions src/padplus-manager/api-request/message.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { log } from '../../config'
import { RequestClient } from './request'
import { ApiType, StreamResponse } from '../../server-manager/proto-ts/PadPlusServer_pb'
import { PadplusMessageType, PadplusRichMediaData, GrpcResponseMessageData, PadplusRecallData, PadplusUploadFileData } from '../../schemas'
import { WechatAppMessageType } from 'wechaty-puppet/dist/src/schemas/message'
import { FileBox } from 'wechaty-puppet'
import { PadplusMessageType, PadplusRichMediaData, GrpcResponseMessageData, PadplusRecallData, PadplusUploadFileData, PadplusGetCDNRequestData } from '../../schemas'
import { types } from '@juzi/wechaty-puppet'
import { FileBox } from 'file-box'

const PRE = 'PadplusMessage'

Expand Down Expand Up @@ -122,7 +122,7 @@ export class PadplusMessage {
url,
}
data = {
appMsgType: WechatAppMessageType.Attach,
appMsgType: types.Message.Attachment,
content: JSON.stringify(content),
fileName,
fromUserName: selfId,
Expand Down Expand Up @@ -191,8 +191,23 @@ export class PadplusMessage {
}
}

public async loadRichMeidaData (mediaData: PadplusRichMediaData): Promise<StreamResponse> {
log.silly(PRE, `loadRichMeidaData()`)
public async getCDNData (data: PadplusGetCDNRequestData): Promise<StreamResponse> {
log.silly(PRE, `getCDNData()`)

const response = await this.requestClient.request({
apiType: ApiType.GET_CDN_DATA,
data,
})

if (response) {
return response
} else {
throw new Error(`can not get callback result of GET_CDN_DATA`)
}
}

public async loadRichMediaData (mediaData: PadplusRichMediaData): Promise<StreamResponse> {
log.silly(PRE, `loadRichMediaData()`)

const response = await this.requestClient.request({
apiType: ApiType.GET_MESSAGE_MEDIA,
Expand Down Expand Up @@ -235,7 +250,7 @@ export class PadplusMessage {
}

public async uploadFile (fileBox: FileBox): Promise<string> {
log.verbose(PRE, `recallMessage`)
log.verbose(PRE, `uploadFile`)
const data = {
data: await fileBox.toBase64(),
filename: fileBox.name,
Expand Down
Loading