You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
14 KiB
413 lines
14 KiB
/**
|
|
* pm2 管理启动
|
|
* 启动: pm2 start server
|
|
* 停止:pm2 stop server
|
|
*
|
|
**/
|
|
|
|
const wppconnect = require('@wppconnect-team/wppconnect');
|
|
const bodyParser = require("body-parser");
|
|
const express = require("express");
|
|
const {upload_files, saveToLocal} = require("./upload_files");
|
|
const utils = require("./utils");
|
|
const {push_data, create_kafka_client} = require("./kafka_utils");
|
|
const topic_message = "whatsapp_test1";
|
|
// const topic_status = "whatsapp_test2";
|
|
const user_iphone = '8615201683893@c.us'; // TODO:需要修改当前登陆账号手机 用于判断被踢的是不是自己
|
|
|
|
// 封装部分日志打印内容 新增日期显示
|
|
{
|
|
const newLog = function () {
|
|
process.stdout.write(`${utils.timestampToTime(new Date() / 1000)} `)
|
|
arguments.callee.oLog.apply(this, arguments);
|
|
};
|
|
const newError = function () {
|
|
process.stdout.write(`${utils.timestampToTime(new Date() / 1000)} `)
|
|
arguments.callee.oError.apply(this, arguments);
|
|
};
|
|
newLog.oLog = console.log;
|
|
newError.oError = console.error;
|
|
console.log = newLog;
|
|
console.error = newError;
|
|
}
|
|
|
|
// 初始化express服务
|
|
const app = express();
|
|
const PORT = 3000;
|
|
app.use(bodyParser.json());
|
|
app.use(bodyParser.urlencoded({extended: true}));
|
|
|
|
|
|
// 反复登陆可能是网络问题
|
|
|
|
wppconnect.create({
|
|
session: 'olive',
|
|
statusFind: (statusSession, session) => {
|
|
console.log('Status Session: ', statusSession); //return isLogged || notLogged || browserClose || qrReadSuccess || qrReadFail || autocloseCalled || desconnectedMobile || deleteToken
|
|
console.log('Session name: ', session);
|
|
},
|
|
headless: "new",
|
|
disableWelcome:true,
|
|
browserArgs: [
|
|
'--disable-setuid-sandbox',
|
|
'--no-sandbox',
|
|
'--safebrowsing-disable-auto-update',
|
|
'--disable-features=LeakyPeeker'
|
|
],
|
|
puppeteerOptions: {
|
|
args: ["--no-sandbox", '--disable-setuid-sandbox'],
|
|
}
|
|
}).then((client) => start(client)).catch((error) => console.log(error));
|
|
|
|
async function _download_file(client, message_id, mine, file_name){
|
|
let response = undefined;
|
|
try{
|
|
var base64 = await client.downloadMedia(message_id);
|
|
response = await upload_files(base64, mine, file_name);
|
|
}catch(error){
|
|
console.error("[onMessage] 下载异常: ", error);
|
|
response = "protocolTimeout";
|
|
}
|
|
return response;
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* 下载文件流程
|
|
**/
|
|
async function download_file(client, message_id, message_type, mine){
|
|
console.log("下载文件流程: ", message_type, mine);
|
|
let response = {media_data: undefined, local_data: undefined};
|
|
switch(message_type){
|
|
case "image":
|
|
var file_name = "image.jpg";
|
|
response = await _download_file(client, message_id, mine, file_name); // 新增下载方法
|
|
break;
|
|
case "video":
|
|
var file_name = "video.mp4";
|
|
response = await _download_file(client,message_id, mine, file_name); // 新增下载方法
|
|
break;
|
|
case "ptt": // opus
|
|
var file_name = "audio.opus";
|
|
response = await _download_file(client,message_id, mine, file_name); // 新增下载方法
|
|
break;
|
|
case "sticker": // webp
|
|
var file_name = "sticker.webp";
|
|
response = await _download_file(client,message_id, mine, file_name); // 新增下载方法
|
|
|
|
break;
|
|
case "audio":
|
|
var file_name = mine.replace("/", ".");
|
|
response = await _download_file(client,message_id, mine, file_name); // 新增下载方法
|
|
|
|
break;
|
|
case "document":
|
|
var file_name = mine.replace("/", ".");
|
|
response = await _download_file(client,message_id, mine, file_name); // 新增下载方法
|
|
break;
|
|
}
|
|
|
|
// if(response === "error"){
|
|
// let result = saveToLocal(base64, mine);
|
|
// console.error("上传失败: 保存到本地 ", result);
|
|
// return {media_data: response, local_data: result}
|
|
// }
|
|
|
|
return response;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 服务启动入口
|
|
**/
|
|
function start(client) {
|
|
|
|
// 获得kafka对象的生产者
|
|
const producer = create_kafka_client();
|
|
|
|
// 监控消息部分
|
|
// ----------------------------------------------------------
|
|
client.onMessage(async (message) => {
|
|
console.log("[onMessage] 原始数据:", message);
|
|
// 拦截消息类型
|
|
let chats_type = ["chat", "image", "video", "sticker", "ptt", "document", "audio"]
|
|
// 拦截群组信息类型
|
|
let other_type = ["remove"]
|
|
console.log("[onMessage] 原始数据类型:", message.type);
|
|
if(message.isGroupMsg === true && chats_type.indexOf(message.type) > -1){
|
|
let data = { // subtype
|
|
group_id: message.chatId, // 没有群组名称
|
|
sender_name: message.sender.pushname,
|
|
sender_id: message.sender.id,
|
|
message_id :message.id,
|
|
reply_to_msg_id: message.quotedMsgId, // 这个不一定有这个字段 表示为回复内容
|
|
message_text: undefined,
|
|
media: undefined,
|
|
datetime: utils.timestampToTime(message.timestamp), // 修改时间戳为 yymmdd hhmmss
|
|
type: message.type,
|
|
mimetype: message.mimetype,
|
|
subtype: message.subtype
|
|
}
|
|
|
|
// 下载资源
|
|
let {media_data, local_data} = await download_file(client, message.id, message.type, message.mimetype);
|
|
data["media"] = media_data;
|
|
data["local_data"] = local_data; // 失败的话下载到本地
|
|
|
|
// 是否有资源 影响对应的字段
|
|
if(media_data){ // 有资源文件的
|
|
data["message_text"] = message.caption;
|
|
if(media_data === "protocolTimeout"){ // 如果是异常状态则修改为undefined
|
|
data["media"] = undefined;
|
|
console.log("下载失败 舍弃本条数据: ", data);
|
|
return;
|
|
}
|
|
}else{
|
|
data["message_text"] = message.content;
|
|
}
|
|
|
|
if(message.subtype === "url"){ // chat下的url 表示为引用有外部链接
|
|
// message.thumbnail // 缩略图 base64
|
|
media_data = await upload_files(message.thumbnail, "image/jpeg", "thubm.jpeg");
|
|
console.log("[onMessage] 下载相关链接缩略图")
|
|
data["media"] = media_data;
|
|
data["link_title"] = message.title;
|
|
data["description"] = message.description;
|
|
}
|
|
|
|
console.log("[onMessage] 封装数据:", data);
|
|
|
|
try{ // 推送 消息
|
|
await push_data(producer, data, topic_message);
|
|
}catch(error){
|
|
console.log("[onMessage] 推送异常: ", error);
|
|
}
|
|
|
|
}else if(message.type === "gp2" && other_type.indexOf(message.subtype) > -1 && message.recipients.indexOf(user_iphone) > -1){
|
|
// 被踢出群 且还得是自己 别人被踢也能监听到
|
|
let data = {
|
|
group_id: message.chatId,
|
|
sender_name: message.sender.pushname,
|
|
sender_id: message.sender.id,
|
|
subtype: message.subtype
|
|
}
|
|
|
|
let group_data = await client.getContact(message.chatId);
|
|
console.log("[onMessage] 被踢群 ", group_data)
|
|
data["group_title"] = group_data["name"];
|
|
|
|
try{ // 推送 消息
|
|
await push_data(producer, data, topic_message);
|
|
}catch(error){
|
|
console.error("[onMessage] 推送异常: ", error.message);
|
|
}
|
|
|
|
console.log("[onMessage] kick out of group", data);
|
|
}
|
|
|
|
});
|
|
|
|
// 接口部分
|
|
// ----------------------------------------------------------
|
|
|
|
// 初始化接口 获取所有群组信息
|
|
app.get('/whatsapp/index', async (req, res) => {
|
|
console.log("[index] 获取所有接口信息");
|
|
let result = {code: 200, data: undefined}; // template
|
|
try{
|
|
let message = await client.listChats({onlyGroups: true}); // 获取的是聊天框里的所有聊天 即使被踢了
|
|
result["data"] = message
|
|
}catch(error){
|
|
console.log(error);
|
|
result = {code: 500, data: error.message}
|
|
}
|
|
res.send(result);
|
|
});
|
|
|
|
|
|
|
|
|
|
// http://127.0.0.1:3000/whatsapp/join_group?link=https://chat.whatsapp.com/G1pxOV1USEAGmlvhzYGS8k
|
|
// 加群接口
|
|
app.get("/whatsapp/join_group", async (req, res) => {
|
|
// 被踢的群就没有办法再加、已经加过的群返回群id {"id": ""}
|
|
// TODO: 需要区分不同失败的异常 收集
|
|
// 1. 需要认证的群,返回:
|
|
// 2. 提交认证后,返回: already-exists
|
|
// 3. 提交认证后且入群,返回 conflict
|
|
let result = {code: 200, data: undefined}; // template
|
|
let join_link = req.query.link;
|
|
console.log("[join_group] 需要添加的群链接为:", join_link);
|
|
|
|
try{
|
|
|
|
let data = await client.joinGroup(join_link);
|
|
result["data"] = data;
|
|
|
|
}catch(error){
|
|
console.log(error);
|
|
// 测试error
|
|
result = {code: 500, data: error.message}
|
|
|
|
if(error.message === "conflict"){ // 针对加完群需要批准的 过滤异常
|
|
let data = await client.getGroupInfoFromInviteLink(join_link);
|
|
console.log("[join_group] 通过邀请码反查数据:", data);
|
|
result["code"] = 203
|
|
result["data"] = {id: data.id};
|
|
}
|
|
|
|
}
|
|
|
|
console.log("[join_group] result:", result);
|
|
res.send(result);
|
|
});
|
|
|
|
|
|
// 获取联系人信息
|
|
app.get("/whatsapp/get_profile", async (req, res) => {
|
|
let result = {code: 200, data: undefined}; // template
|
|
|
|
try{
|
|
let chat_id = req.query.userId; // 593968824284@c.us
|
|
console.log("[get_profile] 需要获得用户id:", chat_id);
|
|
let data = await client.getProfilePicFromServer(chat_id);
|
|
result["data"] = data;
|
|
}catch(error){
|
|
console.log(error);
|
|
result = {code: 500, data: error.message}
|
|
}
|
|
|
|
console.log("[get_profile] result:", result);
|
|
res.send(result);
|
|
});
|
|
|
|
|
|
// 检查在线状态
|
|
app.get("/whatsapp/check_online", async (req, res) => {
|
|
let result = {code: 200, data: undefined}; // template
|
|
|
|
try{
|
|
console.log("[check_online] 检查在线状态");
|
|
let data = await client.isOnline();
|
|
result["data"] = data;
|
|
}catch(error){
|
|
console.log(error);
|
|
result = {code: 500, data: error.message}
|
|
}
|
|
|
|
console.log("[check_online] result:", result);
|
|
res.send(result);
|
|
});
|
|
|
|
|
|
// 获得某个聊天的详情介绍 包括头像图片 这个获取群组信息的最全 包含群成员******
|
|
app.get('/whatsapp/get_chat', async (req, res) => {
|
|
let result = {code: 200, data: undefined}; // template
|
|
|
|
try{
|
|
let chat_id = req.query.chatId; // 593968824284@c.us
|
|
console.log("[get_chat] 需要获得内容的chatId:", chat_id);
|
|
let data = await client.getChatById(chat_id);
|
|
result["data"] = data;
|
|
}catch(error){
|
|
console.log(error);
|
|
result = {code: 500, data: error.message}
|
|
}
|
|
console.log("[get_chat] result:", result);
|
|
res.send(result);
|
|
});
|
|
|
|
|
|
// 获得某个群组
|
|
app.get('/whatsapp/get_contact', async (req, res) => {
|
|
let result = {code: 200, data: undefined}; // template
|
|
|
|
try{
|
|
let chat_id = req.query.contactId; // 593968824284@c.us
|
|
let invoke_id = req.query.inviteCode;
|
|
console.log("[get_contact] 需要获得群组内容:", chat_id, invoke_id);
|
|
let data = await client.getContact(chat_id);
|
|
console.log("[get_contact] data: ", data)
|
|
// console.log(data.getProfilePicFromServer())
|
|
result["data"] = data;
|
|
if(invoke_id){ // 如果存在邀请码则拼接其他数据
|
|
let data1 = await client.getGroupInfoFromInviteLink(invoke_id);
|
|
data1.profilePicThumbObj = data.profilePicThumbObj; // 合并字段
|
|
result["data"] = data1;
|
|
}
|
|
}catch(error){
|
|
console.log(error);
|
|
result = {code: 500, data: error.message}
|
|
}
|
|
|
|
console.log("[get_contact] result:", result);
|
|
res.send(result);
|
|
});
|
|
|
|
// 邀请码反查群信息
|
|
app.get('/whatsapp/invoke', async (req, res) => {
|
|
let result = {code: 200, data: undefined}; // template
|
|
|
|
try{
|
|
let chat_id = req.query.inviteCode; // 593968824284@c.us
|
|
console.log("[invoke] 需要获得群组内容:", chat_id);
|
|
let data = await client.getGroupInfoFromInviteLink(chat_id);
|
|
result["data"] = data;
|
|
}catch(error){
|
|
console.log(error);
|
|
result = {code: 500, data: error.message}
|
|
}
|
|
|
|
console.log("[invoke] result:", result);
|
|
res.send(result);
|
|
});
|
|
|
|
app.post('/whatsapp/send', async (req, res) => {
|
|
console.log("enter send!");
|
|
let result = {code: 200, data: undefined}; // template
|
|
|
|
|
|
try{
|
|
let body = req.body; // 获取消息内容以及类型 120363335479943723@g.us
|
|
let message_type = body["message_type"];
|
|
let content = body["content"]
|
|
let chat_id = body["chat_id"];
|
|
var data = ""
|
|
if(message_type === "text"){ // 文本
|
|
data = await client.sendText(chat_id, content); // 指定目标群组 + 内容
|
|
}else if(message_type === "file"){ // 文件 content 内容支持 本地文件/ data:text/plain;base64
|
|
data = await client.sendFile(chat_id, content); // 指定目标群组 + 内容
|
|
}else if(message_type === "image"){ // 图片 content 内容支持 本地文件/link
|
|
data = await client.sendImage(chat_id, content);
|
|
}else{
|
|
data = "";
|
|
}
|
|
console.log("[invoke] data => ", data);
|
|
console.log("[invoke] 发送内容的群组id为:", chat_id, content);
|
|
result["data"] = "发送成功"; // 由于没有发送成功的回执消息
|
|
}catch(error){
|
|
console.log(error);
|
|
result = {code: 500, data: error.message}
|
|
}
|
|
|
|
console.log("[invoke] result:", result);
|
|
res.send(result);
|
|
|
|
});
|
|
|
|
|
|
// 开起服务
|
|
app.listen(PORT, '0.0.0.0', () => console.log("开启服务,端口3000", new Date().toString()));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|