Comparing version 1.0.4 to 1.1.0
import EventEmitter from "events"; | ||
import { GroupMessage, Message } from "../models/Message.js"; | ||
import { GroupMessage, Message, Reaction, Undo } from "../models/index.js"; | ||
import { GroupEvent } from "../models/GroupEvent.js"; | ||
type MessageEventData = Message | GroupMessage; | ||
@@ -8,7 +9,4 @@ type UploadEventData = { | ||
}; | ||
export type ListenerOptions = Partial<{ | ||
selfListen: boolean; | ||
}>; | ||
export type OnMessageCallback = (message: MessageEventData) => void | Promise<void>; | ||
interface ListenerBaseEvents { | ||
interface ListenerEvents { | ||
connected: []; | ||
@@ -18,6 +16,8 @@ closed: []; | ||
message: [message: MessageEventData]; | ||
reaction: [reaction: Reaction]; | ||
upload_attachment: [data: UploadEventData]; | ||
undo: [data: Undo]; | ||
group_event: [data: GroupEvent]; | ||
} | ||
export declare class ListenerBase extends EventEmitter<ListenerBaseEvents> { | ||
private options; | ||
export declare class Listener extends EventEmitter<ListenerEvents> { | ||
private url; | ||
@@ -31,3 +31,4 @@ private cookie; | ||
private cipherKey?; | ||
constructor(url: string, options?: ListenerOptions); | ||
private selfListen; | ||
constructor(url: string); | ||
onConnected(cb: Function): void; | ||
@@ -34,0 +35,0 @@ onClosed(cb: Function): void; |
import EventEmitter from "events"; | ||
import WebSocket from "ws"; | ||
import { appContext } from "../context.js"; | ||
import { GroupMessage, Message } from "../models/Message.js"; | ||
import { decodeEventData } from "../utils.js"; | ||
export class ListenerBase extends EventEmitter { | ||
constructor(url, options) { | ||
import { GroupMessage, Message, Reaction, Undo } from "../models/index.js"; | ||
import { decodeEventData, getGroupEventType, logger } from "../utils.js"; | ||
import { GroupEvent } from "../models/GroupEvent.js"; | ||
export class Listener extends EventEmitter { | ||
constructor(url) { | ||
super(); | ||
@@ -13,6 +14,6 @@ if (!appContext.cookie) | ||
throw new Error("User agent is not available"); | ||
this.options = options || { selfListen: false }; | ||
this.url = url; | ||
this.cookie = appContext.cookie; | ||
this.userAgent = appContext.userAgent; | ||
this.selfListen = appContext.options.selfListen; | ||
this.onConnectedCallback = () => { }; | ||
@@ -79,7 +80,15 @@ this.onClosedCallback = () => { }; | ||
for (const msg of msgs) { | ||
const messageObject = new Message(msg); | ||
if (messageObject.isSelf && !this.options.selfListen) | ||
continue; | ||
this.onMessageCallback(messageObject); | ||
this.emit("message", messageObject); | ||
if (msg.at == 0) { | ||
const messageObject = new Message(msg); | ||
if (messageObject.isSelf && !this.selfListen) | ||
continue; | ||
this.onMessageCallback(messageObject); | ||
this.emit("message", messageObject); | ||
} | ||
else { | ||
const undoObject = new Undo(msg, false); | ||
if (undoObject.isSelf && !this.selfListen) | ||
continue; | ||
this.emit("undo", undoObject); | ||
} | ||
} | ||
@@ -91,7 +100,15 @@ } | ||
for (const msg of groupMsgs) { | ||
const messageObject = new GroupMessage(msg); | ||
if (messageObject.isSelf && !this.options.selfListen) | ||
continue; | ||
this.onMessageCallback(messageObject); | ||
this.emit("message", messageObject); | ||
if (msg.at == 0) { | ||
const messageObject = new GroupMessage(msg); | ||
if (messageObject.isSelf && !this.selfListen) | ||
continue; | ||
this.onMessageCallback(messageObject); | ||
this.emit("message", messageObject); | ||
} | ||
else { | ||
const undoObject = new Undo(msg, true); | ||
if (undoObject.isSelf && !this.selfListen) | ||
continue; | ||
this.emit("undo", undoObject); | ||
} | ||
} | ||
@@ -103,13 +120,40 @@ } | ||
for (const control of controls) { | ||
const data = { | ||
fileUrl: control.content.data.url, | ||
fileId: control.content.fileId, | ||
}; | ||
const uploadCallback = appContext.uploadCallbacks.get(String(control.content.fileId)); | ||
if (uploadCallback) | ||
uploadCallback(data); | ||
appContext.uploadCallbacks.delete(String(control.content.fileId)); | ||
this.emit("upload_attachment", data); | ||
if (control.content.act_type == "file_done") { | ||
const data = { | ||
fileUrl: control.content.data.url, | ||
fileId: control.content.fileId, | ||
}; | ||
const uploadCallback = appContext.uploadCallbacks.get(String(control.content.fileId)); | ||
if (uploadCallback) | ||
uploadCallback(data); | ||
appContext.uploadCallbacks.delete(String(control.content.fileId)); | ||
this.emit("upload_attachment", data); | ||
} | ||
else if (control.content.act_type == "group") { | ||
const groupEvent = new GroupEvent(JSON.parse(control.content.data), getGroupEventType(control.content.act)); | ||
this.emit("group_event", groupEvent); | ||
} | ||
} | ||
} | ||
if (cmd == 612) { | ||
const parsedData = (await decodeEventData(parsed, this.cipherKey)).data; | ||
const { reacts, reactGroups } = parsedData; | ||
for (const react of reacts) { | ||
react.content = JSON.parse(react.content); | ||
const reactionObject = new Reaction(react, false); | ||
this.emit("reaction", reactionObject); | ||
} | ||
for (const reactGroup of reactGroups) { | ||
reactGroup.content = JSON.parse(reactGroup.content); | ||
const reactionObject = new Reaction(reactGroup, true); | ||
this.emit("reaction", reactionObject); | ||
} | ||
} | ||
if (n == 1 && cmd == 3000 && s == 0) { | ||
console.log(); | ||
logger.error("Another connection is opened, closing this one"); | ||
console.log(); | ||
if (ws.readyState !== WebSocket.CLOSED) | ||
ws.close(); | ||
} | ||
} | ||
@@ -116,0 +160,0 @@ catch (error) { |
@@ -1,2 +0,2 @@ | ||
import { Message, GroupMessage } from "../models/Message.js"; | ||
export declare function undoFactory(): (message: Message | GroupMessage) => Promise<string | null>; | ||
import { Message, GroupMessage, Reaction } from "../models/index.js"; | ||
export declare function undoFactory(): (message: Message | GroupMessage | Reaction) => Promise<string | null>; |
import { appContext } from "../context.js"; | ||
import { Message, GroupMessage, MessageType } from "../models/Message.js"; | ||
import { GroupMessage, MessageType } from "../models/index.js"; | ||
import { decodeAES, encodeAES, request } from "../utils.js"; | ||
@@ -24,12 +24,14 @@ import { Zalo } from "../index.js"; | ||
throw new Error("User agent is not available"); | ||
if (!message.data.quote) | ||
throw new Error("Message does not have quote"); | ||
if (message instanceof Message && message.data.uidFrom !== String(message.data.quote.ownerId)) | ||
throw new Error("You can only undo your own messages"); | ||
// if (!message.data.quote) throw new Error("Message does not have quote"); | ||
// if (message instanceof Message && message.data.uidFrom !== String(message.data.quote.ownerId)) | ||
// throw new Error("You can only undo your own messages"); | ||
console.log("undoing message", message); | ||
const params = { | ||
msgId: message.data.quote.globalMsgId, | ||
// msgId: message.data.quote.globalMsgId, | ||
msgId: message.data.content.rMsg[0].gMsgID, | ||
clientId: Date.now(), | ||
cliMsgIdUndo: message.data.quote.cliMsgId, | ||
// cliMsgIdUndo: message.data.quote.cliMsgId, | ||
cliMsgIdUndo: message.data.content.rMsg[0].cMsgID, | ||
}; | ||
if (message instanceof GroupMessage) { | ||
if (message instanceof GroupMessage || message.isGroup) { | ||
params["grid"] = message.threadId; | ||
@@ -44,3 +46,3 @@ params["visibility"] = 0; | ||
throw new Error("Failed to encrypt message"); | ||
const response = await request(URLType[message.type], { | ||
const response = await request(URLType[message.isGroup ? MessageType.GroupMessage : MessageType.DirectMessage], { | ||
method: "POST", | ||
@@ -47,0 +49,0 @@ body: new URLSearchParams({ |
@@ -38,2 +38,6 @@ type UploadEventData = { | ||
}; | ||
export type Options = { | ||
selfListen: boolean; | ||
checkUpdate: boolean; | ||
}; | ||
declare class CallbacksMap extends Map<string, UploadCallback> { | ||
@@ -47,4 +51,5 @@ /** | ||
uploadCallbacks: CallbacksMap; | ||
options: Options; | ||
}; | ||
export declare const appContext: Partial<AppContextBase> & AppContextExtended; | ||
export {}; |
@@ -15,2 +15,6 @@ const _5_MINUTES = 5 * 60 * 1000; | ||
uploadCallbacks: new CallbacksMap(), | ||
options: { | ||
selfListen: false, | ||
checkUpdate: true, | ||
}, | ||
}; |
export * from "./Message.js"; | ||
export * from "./Reaction.js"; | ||
export * from "./Undo.js"; | ||
export * from "./GroupEvent.js"; |
export * from "./Message.js"; | ||
export * from "./Reaction.js"; | ||
export * from "./Undo.js"; | ||
export * from "./GroupEvent.js"; |
import cryptojs from "crypto-js"; | ||
import { GroupEventType } from "./models/GroupEvent.js"; | ||
export declare function getSignKey(type: string, params: Record<string, any>): cryptojs.lib.WordArray; | ||
@@ -78,1 +79,2 @@ export declare function makeURL(baseURL: string, params: Record<string, any>): string; | ||
export declare function removeUndefinedKeys(e: Record<string, any>): Record<string, any>; | ||
export declare function getGroupEventType(act: string): GroupEventType; |
import cryptojs from "crypto-js"; | ||
import crypto from "crypto"; | ||
import { appContext } from "./context.js"; | ||
import fs from "node:fs"; | ||
import fs from "fs"; | ||
import sharp from "sharp"; | ||
@@ -9,2 +9,3 @@ import pako from "pako"; | ||
import path from "path"; | ||
import { GroupEventType } from "./models/GroupEvent.js"; | ||
export function getSignKey(type, params) { | ||
@@ -311,2 +312,3 @@ let n = []; | ||
let start = currentChunk * chunkSize, end = start + chunkSize >= fileSize ? fileSize : start + chunkSize; | ||
// @ts-ignore | ||
spark.append(buffer.subarray(start, end)); | ||
@@ -393,1 +395,18 @@ currentChunk++; | ||
} | ||
export function getGroupEventType(act) { | ||
if (act == "join") | ||
return GroupEventType.JOIN; | ||
if (act == "leave") | ||
return GroupEventType.LEAVE; | ||
if (act == "remove_member") | ||
return GroupEventType.REMOVE_MEMBER; | ||
if (act == "block_member") | ||
return GroupEventType.BLOCK_MEMBER; | ||
if (act == "update_setting") | ||
return GroupEventType.UPDATE_SETTING; | ||
if (act == "update") | ||
return GroupEventType.UPDATE; | ||
if (act == "new_link") | ||
return GroupEventType.NEW_LINK; | ||
return GroupEventType.UNKNOWN; | ||
} |
import { getOwnId } from "./apis/getOwnId.js"; | ||
import { ListenerBase, ListenerOptions } from "./apis/listen.js"; | ||
import { Listener } from "./apis/listen.js"; | ||
import { Options } from "./context.js"; | ||
import { addReactionFactory } from "./apis/addReaction.js"; | ||
@@ -44,4 +45,3 @@ import { addUserToGroupFactory } from "./apis/addUserToGroup.js"; | ||
private enableEncryptParam; | ||
private listenerOptions?; | ||
constructor(credentials: Credentials, options?: ListenerOptions); | ||
constructor(credentials: Credentials, options?: Partial<Options>); | ||
private parseCookies; | ||
@@ -54,3 +54,3 @@ private validateParams; | ||
zpwServiceMap: Record<string, string[]>; | ||
listener: ListenerBase; | ||
listener: Listener; | ||
sendMessage: ReturnType<typeof sendMessageFactory>; | ||
@@ -72,3 +72,3 @@ addReaction: ReturnType<typeof addReactionFactory>; | ||
changeGroupName: ReturnType<typeof changeGroupNameFactory>; | ||
constructor(secretKey: string, zpwServiceMap: Record<string, string[]>, wsUrl: string, options?: ListenerOptions); | ||
constructor(secretKey: string, zpwServiceMap: Record<string, string[]>, wsUrl: string); | ||
} |
import { getOwnId } from "./apis/getOwnId.js"; | ||
import { ListenerBase } from "./apis/listen.js"; | ||
import { Listener } from "./apis/listen.js"; | ||
import { getServerInfo, login } from "./apis/login.js"; | ||
import { appContext } from "./context.js"; | ||
import { makeURL } from "./utils.js"; | ||
import { logger, makeURL } from "./utils.js"; | ||
import { addReactionFactory } from "./apis/addReaction.js"; | ||
@@ -21,2 +21,3 @@ import { addUserToGroupFactory } from "./apis/addUserToGroup.js"; | ||
import { uploadAttachmentFactory } from "./apis/uploadAttachment.js"; | ||
import { checkUpdate } from "./update.js"; | ||
export class Zalo { | ||
@@ -31,3 +32,4 @@ constructor(credentials, options) { | ||
appContext.secretKey = null; | ||
this.listenerOptions = options; | ||
if (options) | ||
Object.assign(appContext.options, options); | ||
} | ||
@@ -46,2 +48,3 @@ parseCookies(cookie) { | ||
async login() { | ||
await checkUpdate(); | ||
const loginData = await login(this.enableEncryptParam); | ||
@@ -56,3 +59,3 @@ const serverInfo = await getServerInfo(this.enableEncryptParam); | ||
appContext.settings = serverInfo.setttings || serverInfo.settings; | ||
console.log("Logged in as", loginData.data.uid); | ||
logger.info("Logged in as", loginData.data.uid); | ||
return new API(appContext.secretKey, loginData.data.zpw_service_map_v3, makeURL(`${loginData.data.zpw_ws[0]}`, { | ||
@@ -62,3 +65,3 @@ zpw_ver: Zalo.API_VERSION, | ||
t: Date.now(), | ||
}), this.listenerOptions); | ||
})); | ||
} | ||
@@ -69,6 +72,6 @@ } | ||
export class API { | ||
constructor(secretKey, zpwServiceMap, wsUrl, options) { | ||
constructor(secretKey, zpwServiceMap, wsUrl) { | ||
this.secretKey = secretKey; | ||
this.zpwServiceMap = zpwServiceMap; | ||
this.listener = new ListenerBase(wsUrl, options); | ||
this.listener = new Listener(wsUrl); | ||
this.sendMessage = sendMessageFactory(this); | ||
@@ -75,0 +78,0 @@ this.addReaction = addReactionFactory(makeURL(`${zpwServiceMap.reaction[0]}/api/message/reaction`, { |
{ | ||
"name": "zca-js", | ||
"version": "1.0.4", | ||
"version": "1.1.0", | ||
"description": "Unofficial Zalo API for JavaScript", | ||
@@ -34,2 +34,3 @@ "main": "dist/index.js", | ||
"pako": "^2.1.0", | ||
"semver": "^7.6.3", | ||
"sharp": "^0.33.4", | ||
@@ -43,2 +44,3 @@ "spark-md5": "^3.0.2", | ||
"@types/pako": "^2.0.3", | ||
"@types/semver": "^7.5.8", | ||
"@types/spark-md5": "^3.0.4", | ||
@@ -45,0 +47,0 @@ "@types/ws": "^8.5.11", |
@@ -28,2 +28,3 @@ # ZCA-JS | ||
selfListen: true, | ||
checkUpdate: true, | ||
}, | ||
@@ -48,2 +49,3 @@ ); | ||
selfListen: true, | ||
checkUpdate: true, | ||
}, | ||
@@ -59,2 +61,3 @@ ); | ||
- `selfListen`: Listen for messages sent by yourself. Default is `false`. | ||
- `checkUpdate`: Check for zca-js update. Default is `true`. | ||
@@ -61,0 +64,0 @@ ### Listen for new messages |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
120447
61
2744
167
7
8
3
2
+ Addedsemver@^7.6.3