🎉 wechat corp message supported

This commit is contained in:
Song
2021-03-28 11:15:57 +08:00
parent 7478128b6b
commit 4713373c54
10 changed files with 184 additions and 29 deletions
+2 -1
View File
@@ -4,4 +4,5 @@ node_modules
data.db-journal
.vscode
*.db
package-lock.json
package-lock.json
yarn.lock
+1 -1
View File
@@ -25,7 +25,7 @@ app.locals.isErrorMessage = false;
setTimeout(async () => {
// TODO: Here we need an improvement! I have tried EventEmitter but it's not working. :(
await initializeTokenStore();
await refreshToken(app);
await refreshToken();
setInterval(async () => refreshToken(), 100 * 60 * 1000);
}, 1000);
+18 -11
View File
@@ -1,30 +1,37 @@
const { getUserDefaultMethod } = require('./token');
const { pushWeChatMessage } = require('./wechat');
const { pushWeChatCorpMessage } = require('./wechat-corp');
const { pushEmailMessage } = require('./email');
const { Message } = require('../models');
async function processMessage(userPrefix, message) {
if (message.email) {
// If message has the attribute "email", override its type.
message.type = '1';
message.type = 'email';
}
if (message.content && message.type === '0') {
message = await Message.create(message);
if (!message.type) {
message.type = getUserDefaultMethod(userPrefix);
}
let result = {
success: false,
message: `unsupported message type ${message.type}`,
};
if (message.content && message.type !== 'email') {
// If message is not email type, we should save it because we have to serve the page.
message = await Message.create(message, { raw: true });
}
let result;
switch (message.type) {
case '0': // WeChat message
case 'test': // WeChat message
result = await pushWeChatMessage(userPrefix, message);
break;
case '1': // Email message
case 'email': // Email message
result = await pushEmailMessage(userPrefix, message);
break;
case '2': // HTTP GET request
// TODO: HTTP GET request
case 'corp': // WeChar corp message
result = await pushWeChatCorpMessage(userPrefix, message);
break;
default:
result = {
success: false,
message: `unsupported message type ${message.type}`,
};
break;
}
return result;
+40 -4
View File
@@ -7,41 +7,77 @@ async function initializeTokenStore() {
if (process.env.MODE === '1') {
console.log('Current mode is Heroku mode.');
let user = {
// Common
prefix: process.env.PREFIX,
accessToken: process.env.ACCESS_TOKEN,
defaultMethod: process.env.DEFAULT_METHOD,
// WeChat public account
wechatAppId: process.env.WECHAT_APP_ID,
wechatAppSecret: process.env.WECHAT_APP_SECRET,
wechatTemplateId: process.env.WECHAT_TEMPLATE_ID,
wechatOpenId: process.env.WECHAT_OPEN_ID,
wechatVerifyToken: process.env.WECHAT_VERIFY_TOKEN,
prefix: process.env.PREFIX,
// Email
email: process.env.EMAIL,
smtpServer: process.env.SMTP_SERVER,
smtpUser: process.env.SMTP_USER,
smtpPass: process.env.SMTP_PASS,
// WeChat corporation account
corpId: process.env.CORP_ID,
corpAgentId: process.env.CORP_AGENT_ID,
corpAppSecret: process.env.CORP_APP_SECRET,
corpUserId: process.env.CORP_USER_ID,
};
users.push(user);
} else {
users = await User.findAll();
users = await User.findAll({
raw: true,
});
}
users.forEach((user) => {
if (user.wechatAppId) {
if (user.prefix) {
tokenStore.set(user.prefix, {
// Common
accessToken: user.accessToken,
defaultMethod: user.defaultMethod,
// WeChat test account
wechatAppId: user.wechatAppId,
wechatAppSecret: user.wechatAppSecret,
wechatTemplateId: user.wechatTemplateId,
wechatOpenId: user.wechatOpenId,
wechatVerifyToken: user.wechatVerifyToken,
token: '',
wechatToken: '',
// Email
email: user.email,
smtpServer: user.smtpServer,
smtpUser: user.smtpUser,
smtpPass: user.smtpPass,
// WeChat corporation account
corpId: user.corpId,
corpAgentId: user.corpAgentId,
corpAppSecret: user.corpAppSecret,
corpUserId: user.corpUserId,
corpToken: '',
});
}
});
console.log('Token store initialized.');
}
function updateTokenStore(prefix, key, value) {
let user = tokenStore.get(prefix);
user[key] = value;
tokenStore.set(prefix, user);
}
function getUserDefaultMethod(prefix) {
let user = tokenStore.get(prefix);
return user.defaultMethod;
}
module.exports = {
initializeTokenStore,
updateTokenStore,
getUserDefaultMethod,
tokenStore,
};
+94
View File
@@ -0,0 +1,94 @@
const axios = require('axios');
const { tokenStore, updateTokenStore } = require('./token');
const config = require('../config');
async function refreshToken() {
for (let [key, value] of tokenStore) {
if (value.corpId) {
value.corpToken = await requestToken(value.corpId, value.corpAppSecret);
tokenStore.set(key, value);
}
}
console.log('Token refreshed.');
}
async function requestToken(corpId, corpAppSecret) {
// Reference: https://work.weixin.qq.com/api/doc/90000/90135/91039
let token = '';
try {
let res = await axios.get(
`https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${corpId}&corpsecret=${corpAppSecret}`
);
// console.debug(res);
if (res && res.data) {
if (res.data.access_token) {
token = res.data.access_token;
} else {
console.error(res.data);
}
}
} catch (e) {
console.error(e);
}
return token;
}
async function pushWeChatCorpMessage(userPrefix, message) {
// Reference: https://work.weixin.qq.com/api/doc/90000/90135/90236
let user = tokenStore.get(userPrefix);
if (!user) {
return {
success: false,
message: `不存在的前缀:${userPrefix},请注意大小写`,
};
}
let access_token = user.corpToken;
let request_data = {
msgtype: 'textcard',
touser: user.corpUserId,
agentid: user.corpAgentId,
textcard: {
title: message.title,
description: message.description,
},
};
if (message.content) {
request_data.textcard.url = `${config.href}message/${message.id}`;
}
let requestUrl = `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${access_token}`;
try {
let response = await axios.post(requestUrl, request_data);
if (response && response.data && response.data.errcode !== 0) {
// Failed to push message, get a new token and try again.
access_token = await requestToken(user.corpId, user.corpAppSecret);
updateTokenStore(userPrefix, 'corpToken', access_token);
requestUrl = `https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${access_token}`;
response = await axios.post(requestUrl, request_data);
}
if (response.data.errcode === 0) {
return {
success: true,
message: 'ok',
};
} else {
return {
success: false,
message: response.data.errmsg,
};
}
} catch (e) {
console.error(e);
return {
success: false,
message: e.message,
};
}
}
module.exports = {
refreshToken,
requestToken,
pushWeChatCorpMessage,
};
+14 -7
View File
@@ -1,11 +1,16 @@
const axios = require('axios');
const { tokenStore } = require('./token');
const { tokenStore, updateTokenStore } = require('./token');
const config = require('../config');
async function refreshToken() {
for (let [key, value] of tokenStore) {
value.token = await requestToken(value.wechatAppId, value.wechatAppSecret);
tokenStore.set(key, value);
if (value.wechatAppId) {
value.wechatToken = await requestToken(
value.wechatAppId,
value.wechatAppSecret
);
tokenStore.set(key, value);
}
}
console.log('Token refreshed.');
}
@@ -39,22 +44,24 @@ async function pushWeChatMessage(userPrefix, message) {
message: `不存在的前缀:${userPrefix},请注意大小写`,
};
}
let access_token = user.token;
let access_token = user.wechatToken;
let request_data = {
touser: user.wechatOpenId,
template_id: user.wechatTemplateId,
};
if (message.content) {
request_data.url = `${config.href}message/${message.id}`;
console.debug(`http://localhost:3000/message/${message.id}`);
}
request_data.data = { text: { value: message.description } };
let requestUrl = `https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=${access_token}`;
try {
let response = await axios.post(requestUrl, request_data);
if (response && response.data && response.data.errcode !== 0) {
await requestToken(user.wechatAppId, user.wechatAppSecret);
await axios.post(requestUrl, request_data);
// Failed to push message, get a new token and try again.
let token = await requestToken(user.wechatAppId, user.wechatAppSecret);
updateTokenStore(userPrefix, 'wechatToken', token);
requestUrl = `https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=${access_token}`;
response = await axios.post(requestUrl, request_data);
}
if (response.data.errcode === 0) {
return {
+1 -1
View File
@@ -5,7 +5,7 @@ const sequelize = require('../common/database');
Message.belongsTo(User);
(async () => {
await sequelize.sync({ alter: true });
await sequelize.sync();
console.log('Database initialized.');
const isNoAdminExisted =
(await User.findOne({ where: { isAdmin: true } })) === null;
+2 -2
View File
@@ -14,8 +14,8 @@ Message.init(
description: DataTypes.TEXT,
content: DataTypes.TEXT,
type: {
type: DataTypes.INTEGER,
defaultValue: 0,
type: DataTypes.STRING,
defaultValue: 'test',
},
},
{ sequelize }
+11
View File
@@ -37,17 +37,28 @@ User.init(
type: DataTypes.BOOLEAN,
defaultValue: false,
},
defaultMethod:{
type: DataTypes.STRING,
defaultValue: 'test'
},
// WeChat public account
wechatAppId: DataTypes.STRING,
wechatAppSecret: DataTypes.STRING,
wechatTemplateId: DataTypes.STRING,
wechatOpenId: DataTypes.STRING,
wechatVerifyToken: DataTypes.STRING,
// Email
smtpServer: {
type: DataTypes.STRING,
defaultValue: 'smtp.qq.com',
},
smtpUser: DataTypes.STRING,
smtpPass: DataTypes.STRING,
// WeChat corporation
corpId: DataTypes.STRING,
corpAgentId: DataTypes.STRING,
corpAppSecret: DataTypes.STRING,
corpUserId: DataTypes.STRING
},
{ sequelize }
);
+1 -2
View File
@@ -24,7 +24,6 @@ router.all('/:userPrefix/verify', (req, res, next) => {
router.all('/:userPrefix/:description', async (req, res, next) => {
const userPrefix = req.params.userPrefix;
let message = {
type: '0',
title: '无标题',
description: req.params.description,
};
@@ -34,7 +33,7 @@ router.all('/:userPrefix/:description', async (req, res, next) => {
router.all('/:userPrefix', async (req, res, next) => {
const userPrefix = req.params.userPrefix;
let message = {
type: req.query.type || req.body.type || '0',
type: req.query.type || req.body.type,
title: req.query.title || req.body.title || '无标题',
description: req.query.description || req.body.description || '无描述',
content: req.query.content || req.body.content,