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