Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

新增钉钉通知、邮件通知 #191

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 17 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,20 @@ ALIAS_WHITELIST=微信名1,备注名2
ROOM_WHITELIST=XX群1,群2

# 默认服务 ChatGPT、Kimi、Xunfei、deepseek-free 四选一,不填则键盘交互
SERVICE_TYPE=''
SERVICE_TYPE=''

# 通知间隔,单位为分钟,不填默认为30
MASSAGE_INTERVAL=60

# 邮件通知
MAIL_HOST=smtp.qq.com
MAIL_PORT=465
[email protected]
#你的邮箱密码或授权码
MAIL_PASSWORD=******
#收件人邮箱
MAIL_TO=*****@qq.com

# 钉钉机器人 仅测试加签机器人
DING_TOKEN=*****
DING_SIGN=******
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,29 @@ ROOM_WHITELIST=XX群1,群2

![](https://assets.fedtop.com/picbed/202212131123257.png)

4. 消息通知

- 运行报错通知
- 登录扫码通知

目前仅支持邮件通知,钉钉机器人通知,后续会继续增加。
.env文件填写对应的配置,不填则不会通知

```sh
# 邮件通知
MAIL_HOST=smtp.qq.com
MAIL_PORT=465
[email protected]
#你的邮箱密码或授权码
MAIL_PASSWORD=******
#收件人邮箱
MAIL_TO=*****@qq.com

# 钉钉机器人 仅测试加签机器人
DING_TOKEN=*****
DING_SIGN=******
```

## 常见问题

可以进交流群,一起交流探讨相关问题和解决方案,添加的时候记得备注来意。(如果项目对你有所帮助,也可以请我喝杯咖啡 ☕️ ~)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"crypto-js": "^4.2.0",
"dotenv": "^16.4.5",
"inquirer": "^9.2.16",
"nodemailer": "^6.9.14",
"openai": "^4.52.0",
"p-timeout": "^6.0.0",
"qrcode-terminal": "^0.12.0",
Expand Down
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import fs from 'fs'
import path, { dirname } from 'path'
import { fileURLToPath } from 'url'
import { defaultMessage } from './wechaty/sendMessage.js'
import { sendMessage } from './utils/sendMessage.js'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
Expand All @@ -20,6 +21,8 @@ function onScan(qrcode, status) {
// 在控制台显示二维码
qrTerminal.generate(qrcode, { small: true })
const qrcodeImageUrl = ['https://api.qrserver.com/v1/create-qr-code/?data=', encodeURIComponent(qrcode)].join('')
sendMessage(qrcodeImageUrl, 'qr')

console.log('onScan:', qrcodeImageUrl, ScanStatus[status], status)
} else {
log.info('onScan: %s(%s)', ScanStatus[status], status)
Expand Down Expand Up @@ -88,6 +91,7 @@ bot.on('friendship', onFriendShip)
// 错误
bot.on('error', (e) => {
console.error('❌ bot error handle: ', e)
sendMessage(e, 'error')
// console.log('❌ 程序退出,请重新运行程序')
// bot.stop()

Expand Down
157 changes: 157 additions & 0 deletions src/utils/sendMessage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import dotenv from 'dotenv'
import CryptoJS from 'crypto-js'
import nodemailer from 'nodemailer'

const env = dotenv.config().parsed // 环境参数

export function sendMessage(resultText, messageType = 'qr') {
if (checkDate()) {
return false
}
try {
switch (messageType) {
case 'qr':
sendMail(resultText, messageType)
dingSend(resultText, 'markdown')
break
case 'error':
sendMail(resultText, messageType)
dingSend(resultText, 'text')
break
default:
sendMail(resultText)
dingSend(resultText)
}
} catch (e) {
console.log(e)
}
}

export function sendMail(resultText, messageType = 'text') {
// 环境变量判空
if (!env.MAIL_HOST || !env.MAIL_USERNAME || !env.MAIL_PASSWORD || !env.MAIL_TO) {
return false
}
// 创建一个SMTP传输器
const transporter = nodemailer.createTransport({
host: env.MAIL_HOST, // QQ邮箱的SMTP服务器地址
port: env.MAIL_HOST || 465, // 通常SMTP服务器使用的端口是465(SSL)或587(TLS)
secure: env.MAIL_HOST.toString() === '465', // 如果端口是465,则设置为true
auth: {
user: env.MAIL_USERNAME, // 你的邮箱地址
pass: env.MAIL_PASSWORD, // 你的邮箱密码或授权码
},
})
// 假设你有一个包含多个邮箱地址的字符串,用分号分隔
const emailString = env.MAIL_TO
// 使用分号将字符串分割成数组
const emailAddresses = emailString.split(';')

// 设置邮件选项
let mailOptions = {
from: env.MAIL_USERNAME, // 发件人邮箱地址
to: emailAddresses, // 收件人邮箱地址
subject: 'wechat-bot通知', // 邮件主题
}
if (messageType === 'text') {
mailOptions.text = `【wechat-bot】:${resultText}`
} else if (messageType === 'error') {
mailOptions.html = `【wechat-bot报错】:${resultText}`
} else if (messageType === 'qr') {
mailOptions.html = `<p>【wechat-bot】微信扫码登录</p><img src="${resultText}" alt="Network Image" />`
}

// 发送邮件
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.log(error)
} else {
console.log('Email sent: ' + info.response)
}
})
}

// 钉钉机器人
export async function dingSend(resultText, msgtype = 'text') {
// 环境变量判空
if (!env.DING_TOKEN) {
return false
}
// 发送钉钉消息
try {
const timestamp = new Date().getTime() // 时间戳
const secret = env.DING_SIGN // 钉钉机器人密钥
let stringToSign = `${timestamp}\n${secret}`
let signature = CryptoJS.HmacSHA256(stringToSign, secret)
const hashInBase64 = CryptoJS.enc.Base64.stringify(signature) //base64加密
const encodesign = encodeURI(hashInBase64) //解密
// 钉钉机器人地址
let url = `https://oapi.dingtalk.com/robot/send?access_token=${env.DING_TOKEN}&timestamp=${timestamp}&sign=${encodesign}`
// 钉钉消息内容
let text = {
msgtype: msgtype,
text: {
content: resultText,
},
markdown: {
title: 'QR Code for your URL',
text: `### 【wechat-bot】微信扫码登录\n\n![Base64 Image](${resultText})`,
},
}
let response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(text),
})
let responseData = await response.json()
if (response.ok) {
console.log('钉钉消息发送成功,返回信息:', responseData)
} else {
console.log('钉钉消息发送失败', responseData)
}
} catch (error) {
console.log('钉钉消息发送失败', error)
}
}

// 检测目录下是否有date.json文件,没有则创建,读取其中的date时间,超过30分钟则返回true
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
function checkDate() {
let massageInterval
try {
massageInterval = Number(env.MASSAGE_INTERVAL) || 30 // 默认30分钟
} catch (e) {
massageInterval = 30 // 默认30分钟
}
let date = new Date().getTime() // 当前时间
const fileDir = path.resolve(__dirname, 'date.json') // 文件路径
const dirPath = path.dirname(fileDir) // 获取目录路径

// 确保目录存在
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true })
}

if (fs.existsSync(fileDir)) {
// 读取文件内容
const data = fs.readFileSync(fileDir, 'utf-8')
const jsonData = JSON.parse(data)
console.log('读取到date.json文件,内容为:', jsonData)
return (date - jsonData.time) / 60000 > massageInterval
} else {
// 创建date.json文件
const obj = { time: date }
fs.writeFileSync(fileDir, JSON.stringify(obj), 'utf-8')
return true
}
}

// 示例调用
console.log(checkDate())