├── (XT[3C8SV0TO4~`T2A([N`7.png
├── .github
└── workflows
│ └── node.js.yml
├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── misc.xml
├── modules.xml
├── vcs.xml
├── workspace.xml
└── 微信服务.iml
├── .prettierrc
├── .releaserc
├── 1557823227950.png
├── 2994169387.txt
├── 4ca97265e7616b13f73dbf03a93_p43_mk42.jpg
├── DATA.json
├── DATABASE.json
├── TKMU(RLCF%{PM]@E8]$291V.png
├── api
├── 2994169387.txt
├── index.html
└── test.js
├── assets
└── avatar
│ ├── AbTDic.png
│ ├── Hz6dGT.png
│ ├── iJk7EN.png
│ ├── rMbt4N.png
│ ├── rRR5dz.png
│ ├── sanwei
│ ├── 1.png
│ ├── 10.png
│ ├── 2.png
│ ├── 3.png
│ ├── 6.png
│ ├── 7.png
│ └── 8.png
│ ├── tGfXc5.png
│ ├── temp
│ ├── 1.png
│ ├── 2.png
│ └── 3.png
│ └── xuesong
│ ├── 500_1.png
│ ├── 500_2.png
│ ├── 500_3.png
│ ├── xs1.png
│ ├── xs2.png
│ ├── xs3.png
│ └── xs4.png
├── bin
└── run.js
├── jest.config.js
├── package.json
├── pnpm-lock.yaml
├── project
├── DATA.json
├── assets
│ └── avatar
│ │ └── xuesong
│ │ └── xs1.png
├── config.json
├── index.html
├── index.js
├── package.json
└── 自行更改文件名称与内容验证微信.txt
├── readme.md
├── src
├── client
│ ├── client.ts
│ ├── index.html
│ └── tsconfig.json
├── node
│ ├── Activity
│ │ └── Avatar.ts
│ ├── Encrypt.ts
│ ├── MessageParser
│ │ └── index.ts
│ ├── index.ts
│ ├── plugins
│ │ ├── ParsePlatFormMessagePlugin.ts
│ │ ├── SelfWeChatPlugin.ts
│ │ └── ThirdPartWeChatPlugins.ts
│ ├── root.d.ts
│ ├── server.ts
│ ├── tsconfig.json
│ └── util.ts
└── util
│ └── Log.ts
├── test.jpg
├── test
├── project.spec.ts
└── project
│ ├── DATA.json
│ ├── assets
│ └── avatar
│ │ ├── sanwei
│ │ ├── 1.png
│ │ ├── 10.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ └── 8.png
│ │ ├── xuesong
│ │ ├── xs1.png
│ │ ├── xs2.png
│ │ ├── xs3.png
│ │ └── xs4.png
│ │ └── zYJ3hD.png
│ ├── config.json
│ ├── index.js
│ └── package.json
├── tsconfig.base.json
├── util
├── Log.d.ts
└── Log.js
├── vercel.json
└── watch-bob.jpg
/(XT[3C8SV0TO4~`T2A([N`7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/(XT[3C8SV0TO4~`T2A([N`7.png
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | tags:
9 | - v*.*.*
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Use Node.js 12
17 | uses: actions/setup-node@v2
18 | with:
19 | node-version: 12
20 | cache: 'npm'
21 | registry-url: 'https://registry.npmjs.org'
22 | - run: npm install
23 | - run: npm run build
24 | - run: npm publish
25 | env:
26 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .idea
4 | .vercel
5 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | 1623564911824
113 |
114 |
115 | 1623564911824
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
--------------------------------------------------------------------------------
/.idea/微信服务.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | semi: false
2 | singleQuote: true
3 | printWidth: 80
4 | trailingComma: none
5 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "branches": "npm",
3 | "plugins": [
4 | "@semantic-release/commit-analyzer",
5 | "@semantic-release/release-notes-generator",
6 | "@semantic-release/changelog",
7 | "@semantic-release/npm",
8 | [
9 | "@semantic-release/git",
10 | {
11 | "assets": ["package.json", "CHANGELOG.md"],
12 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
13 | }
14 | ],
15 | "@semantic-release/github"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/1557823227950.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/1557823227950.png
--------------------------------------------------------------------------------
/2994169387.txt:
--------------------------------------------------------------------------------
1 | d786a41263e04bb88df44bc0a7c69d0b
--------------------------------------------------------------------------------
/4ca97265e7616b13f73dbf03a93_p43_mk42.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/4ca97265e7616b13f73dbf03a93_p43_mk42.jpg
--------------------------------------------------------------------------------
/DATA.json:
--------------------------------------------------------------------------------
1 | {
2 | "self": {
3 | "Encrypt": "ticket@@@jo-pIncA347Zd5Sk7NmOUWnT3BIGtRmLf2LOKrp9QB9fl8uuPzYd-OESWQd4aW52BPgC7G5S2ZlsnTCFuLieKQ",
4 | "update": 1625848469303
5 | },
6 | "thirdPart": [
7 | {
8 | "appid": ""
9 | },
10 | {
11 | "appid": "wx0ea308250417bd30",
12 | "authorizer_access_token": "46_VRh3xzLPKiNGTCm3Db9NGLmxqlZPtnzwF5AKMQhDcZLP02VkU637_xOgYZ9kU6PXvjqubelCqBRqKLazhaPso0hZpcPPfQnBOL0p1KYxBPpU3Jlqj459VOkdrW7sLsFu7br2GvooLssQnSMPHHSiAGDXXI",
13 | "refresh_authorizer_refresh_token": "refreshtoken@@@aoXF2D5-AonY6zK8ZTZ-vVA4UNyFBY0uJXCPYe-4M8I",
14 | "update": 1625057501899,
15 | "create": 1624789173830,
16 | "qrcode_url": "http://mmbiz.qpic.cn/mmbiz_jpg/Y2LExAS5IQEafY1lqsfvx0DsibSTuSIg8VdU1bHJYs1UMJVg5YsIRyusWlic3QiaicKvwmpQic3JLliaachNqwTfDgibA/0",
17 | "name": "单行线oneway"
18 | },
19 | {
20 | "appid": "wx436fcd3fdc77dc4f",
21 | "authorizer_access_token": "46_yiI2MUgFmdSINGGTkDJjY0ExdaDPAaMSDiu3sHOIVaIW1e8lWCNUvBlZq2U36R4hxoUfLAJ5-QRPK44mBQPNUErS5cQloSsmsDh6aMn2X7gk4GYZfcsBZtvZq3vk8QlA-Dqe1iowRgleHezuLMKjAEDYZX",
22 | "refresh_authorizer_refresh_token": "refreshtoken@@@a9LyquHlIXZBe0abXlPj8qydUvzvkmpLpVRfsEhSjl4",
23 | "update": 1625057501904,
24 | "create": 1624815270396,
25 | "qrcode_url": "http://mmbiz.qpic.cn/mmbiz_jpg/8e89TsEShR5uIlYUYfibBmDWGdlNHuYVvRhdSDVcI9ZYDZiaSW1MyZLwZBrFTQLoMgB9czNb7j2Wz2v5Ht6u568w/0",
26 | "name": "微读读"
27 | },
28 | {
29 | "appid": "wx85df74b62aad79ed",
30 | "authorizer_access_token": "46_MqSWuwouHD3AF8QB_W2_qkskvV6Fc45J5VI7Z292QMtapqWbUm7Ts4_d-9AOqI9OP_LWH7ZkOMDdlK6jGNW98CkS36bG9opAvpwo3PBvSvz5piJBBEglbFmV1ANYeU08oay-ik1np02Fp1eqJIViAGDONA",
31 | "refresh_authorizer_refresh_token": "refreshtoken@@@JFv0RMxhMtWp7rwD4ExJZRD_UDnQV32JvlUa0uAwmQE",
32 | "update": 1625057501886,
33 | "create": 1624841939866,
34 | "qrcode_url": "http://mmbiz.qpic.cn/mmbiz/yolMztZrK8cHGMnvIZPDfClRibj9Og2GRqibc46Gm8bY4kCl4J5FpCmT9joh2yqMLnZklMBYbAU7sUiagleUbQ7iaw/0",
35 | "name": "雪松控股"
36 | },
37 | {
38 | "appid": "wx7630866bd98a50de",
39 | "authorizer_access_token": "46_15aFf8C9bga4utjWIa8NYX63IBMZG5enMtkqLjqD8Fzq9iWNQZiiRuw6c25hPIhqHnOs-z6TTaTl15iYRVdGCwvUngmT2lAlJAN2M3o6Q79cOVuF9_YrXHGs124gz1J9s0lwsRU6zlpfdOarMXMdAKDYJY",
40 | "refresh_authorizer_refresh_token": "refreshtoken@@@1vMm3Gx0ebkeHCRMFxECAx7xLls1-hoXYktSZVq2p5w",
41 | "update": 1625057501961,
42 | "create": 1624842809037,
43 | "qrcode_url": "http://mmbiz.qpic.cn/mmbiz_jpg/N4Yia8kulhG4sibL1PNxicoWKt50mOib2VgvicuZ7EaBQeOyZicr2tVWsllDG7lK7BjZOdEwM4MOLian2yNWUSib9mmkWQ/0",
44 | "name": "三维家软件"
45 | }
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/DATABASE.json:
--------------------------------------------------------------------------------
1 | {
2 | "self": {
3 | "Encrypt": ""
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/TKMU(RLCF%{PM]@E8]$291V.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/TKMU(RLCF%{PM]@E8]$291V.png
--------------------------------------------------------------------------------
/api/2994169387.txt:
--------------------------------------------------------------------------------
1 | d786a41263e04bb88df44bc0a7c69d0b
--------------------------------------------------------------------------------
/api/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
36 |
37 |
38 | hello
39 |
40 |
41 |
--------------------------------------------------------------------------------
/api/test.js:
--------------------------------------------------------------------------------
1 | asd
2 |
--------------------------------------------------------------------------------
/assets/avatar/AbTDic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/AbTDic.png
--------------------------------------------------------------------------------
/assets/avatar/Hz6dGT.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/Hz6dGT.png
--------------------------------------------------------------------------------
/assets/avatar/iJk7EN.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/iJk7EN.png
--------------------------------------------------------------------------------
/assets/avatar/rMbt4N.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/rMbt4N.png
--------------------------------------------------------------------------------
/assets/avatar/rRR5dz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/rRR5dz.png
--------------------------------------------------------------------------------
/assets/avatar/sanwei/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/sanwei/1.png
--------------------------------------------------------------------------------
/assets/avatar/sanwei/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/sanwei/10.png
--------------------------------------------------------------------------------
/assets/avatar/sanwei/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/sanwei/2.png
--------------------------------------------------------------------------------
/assets/avatar/sanwei/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/sanwei/3.png
--------------------------------------------------------------------------------
/assets/avatar/sanwei/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/sanwei/6.png
--------------------------------------------------------------------------------
/assets/avatar/sanwei/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/sanwei/7.png
--------------------------------------------------------------------------------
/assets/avatar/sanwei/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/sanwei/8.png
--------------------------------------------------------------------------------
/assets/avatar/tGfXc5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/tGfXc5.png
--------------------------------------------------------------------------------
/assets/avatar/temp/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/temp/1.png
--------------------------------------------------------------------------------
/assets/avatar/temp/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/temp/2.png
--------------------------------------------------------------------------------
/assets/avatar/temp/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/temp/3.png
--------------------------------------------------------------------------------
/assets/avatar/xuesong/500_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/xuesong/500_1.png
--------------------------------------------------------------------------------
/assets/avatar/xuesong/500_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/xuesong/500_2.png
--------------------------------------------------------------------------------
/assets/avatar/xuesong/500_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/xuesong/500_3.png
--------------------------------------------------------------------------------
/assets/avatar/xuesong/xs1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/xuesong/xs1.png
--------------------------------------------------------------------------------
/assets/avatar/xuesong/xs2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/xuesong/xs2.png
--------------------------------------------------------------------------------
/assets/avatar/xuesong/xs3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/xuesong/xs3.png
--------------------------------------------------------------------------------
/assets/avatar/xuesong/xs4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/assets/avatar/xuesong/xs4.png
--------------------------------------------------------------------------------
/bin/run.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | const fs = require('fs').promises
3 | const argv = require('minimist')(process.argv.slice(2))
4 | const path = require('path')
5 |
6 | try {
7 | function start() {
8 | // todo 创建模板
9 | if (process.argv.includes('create')) {
10 | if (!argv.appid || !argv.url) {
11 | console.log('\x1B[31m%s\x1B[0m', '请配置appid或url参数')
12 | return
13 | }
14 | createTemp({ appid: argv.appid, url: argv.url })
15 | } else {
16 | let json
17 | // 3@CZ9RXf3-x_y_$
18 | try {
19 | json = require(path.join(process.cwd(), './config.json'))
20 | } catch (e) {
21 | console.log('请配置config.json文件')
22 | return
23 | }
24 |
25 | if (argv.test) {
26 | global.__TEST__ = true
27 | }
28 |
29 | if (
30 | ['appid', 'secret', 'encodingAESKey', 'token'].some((e) => {
31 | if (json && json.wechat && !json.wechat[e]) {
32 | return true
33 | } else if (!(json && json.wechat) && !argv[e]) {
34 | return true
35 | }
36 | return false
37 | })
38 | ) {
39 | console.error('请正确配置appid secret plugins encodingAESKey token')
40 | return
41 | }
42 |
43 | const config = json.wechat || argv || {}
44 |
45 | config.input = json.input
46 |
47 | config.data = json.data
48 |
49 | console.log(config, argv, json)
50 |
51 | global.__CONFIG__ = config
52 |
53 | try {
54 | // 磁盘数据
55 | config.DATA = require(path.join(process.cwd(), json.data))
56 | } catch (e) {
57 | // 无则创建
58 | if (!config.DATA) {
59 | const temp = {
60 | self: {
61 | Encrypt: ''
62 | },
63 | thirdPart: []
64 | }
65 |
66 | config.DATA = temp
67 |
68 | fs.writeFile(
69 | path.join(process.cwd(), path.relative(process.cwd(), json.data)),
70 | JSON.stringify(temp)
71 | )
72 | }
73 | }
74 |
75 | const server = require('../dist/node').createServer(config)
76 |
77 | let port = argv.port || 3000
78 | server.on('error', (e) => {
79 | if (e.code === 'EADDRINUSE') {
80 | console.log(`port ${port} is in use, trying another one...`)
81 | setTimeout(() => {
82 | server.close()
83 | server.listen(++port)
84 | }, 100)
85 | } else {
86 | console.error(e)
87 | }
88 | })
89 |
90 | server.on('listening', () => {
91 | console.log(`Running at http://localhost:${port}`)
92 | })
93 |
94 | server.listen(port)
95 |
96 | module.exports = server
97 | }
98 | }
99 |
100 | start();
101 | } catch (e) {
102 | console.log(e)
103 | }
104 |
105 | async function createTemp({ appid, url }) {
106 | console.time('Done')
107 | const projectDir = path.join(__dirname, '../project')
108 | const tempDir = path.join(process.cwd(), 'temp')
109 | await fs.mkdir(tempDir)
110 | await circleCopy(projectDir, tempDir, appid, url)
111 | console.log(
112 | '\033[42;30m DONE \033[40;32m Create ' + tempDir + ' successfully\033[0m'
113 | )
114 | console.timeEnd('Done')
115 | }
116 |
117 | async function circleCopy(projectDir, tempDir, appid, url) {
118 | for (const file of await fs.readdir(projectDir)) {
119 | if (!file.toString().includes('.')) {
120 | const dir = path.join(tempDir, file)
121 | await fs.mkdir(dir)
122 | await circleCopy(path.join(projectDir, file), dir, appid, url)
123 | } else {
124 | if (/.html$/.exec(file)) {
125 | await rewriteIndexHtml({
126 | tempDir,
127 | file: file.toString(),
128 | projectDir,
129 | appid,
130 | url
131 | })
132 | } else {
133 | await fs.copyFile(path.join(projectDir, file), path.join(tempDir, file))
134 | }
135 | }
136 | }
137 | }
138 |
139 | async function rewriteIndexHtml({ tempDir, file, projectDir, appid, url }) {
140 | let source = (await fs.readFile(path.join(projectDir, file))).toString()
141 | source = source
142 | .replace('$component_appid$', appid)
143 | .replace('$redirect_uri$', url)
144 | await fs.writeFile(path.join(tempDir, file), source)
145 | }
146 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'ts-jest',
3 | globals: {
4 | __DEV__: true,
5 | __TEST__: true,
6 | __VERSION__: require('./package.json').version,
7 | __NODE_JS__: true,
8 | },
9 | testTimeout: 50000,
10 | moduleNameMapper: {
11 | '^@/(.*?)$': '/src/$1'
12 | },
13 | watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'],
14 | moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
15 | rootDir: __dirname,
16 | testMatch: ['/src/**/__test__/**.spec.[jt]s?(x)', '/**/**.spec.[jt]s?(x)'],
17 | testPathIgnorePatterns: ['/node_modules/']
18 | }
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wx-serve",
3 | "version": "0.8.26",
4 | "description": "微信工具",
5 | "main": "dist/node/index.js",
6 | "types": "dist/node/index.d.ts",
7 | "repository": {
8 | "type": "git",
9 | "url": "git+https://github.com/Kingbultsea/wechat-server-tool.git"
10 | },
11 | "files": [
12 | "bin",
13 | "dist",
14 | "project"
15 | ],
16 | "bin": {
17 | "wx-serve": "bin/run.js"
18 | },
19 | "scripts": {
20 | "semantic-release": "semantic-release",
21 | "run": "node ./bin/run.js --port 8089",
22 | "dev": "run-p dev-node",
23 | "dev-client": "tsc -w --p src/client",
24 | "dev-node": "tsc -w --p src/node",
25 | "dev-bin": "tsc -w --p src/api",
26 | "build": "tsc -p src/client && tsc -p src/node",
27 | "test": "jest",
28 | "lint": "prettier --write --parser typescript \"src/**/*.ts\""
29 | },
30 | "gitHooks": {},
31 | "lint-staged": {
32 | "*.js": [
33 | "prettier --write"
34 | ],
35 | "*.ts": [
36 | "prettier --parser=typescript --write"
37 | ]
38 | },
39 | "author": "Kingbultsea",
40 | "license": "ISC",
41 | "dependencies": {
42 | "@types/koa": "^2.13.3",
43 | "chokidar": "^3.5.2",
44 | "koa": "^2.13.1",
45 | "koa-bodyparser": "^4.3.0",
46 | "koa-router": "^10.0.0",
47 | "koa-static": "^5.0.0",
48 | "lru-cache": "^6.0.0",
49 | "madge": "^5.0.1",
50 | "minimist": "^1.2.5",
51 | "superagent": "^7.0.2",
52 | "typescript": "^4.3.2"
53 | },
54 | "devDependencies": {
55 | "@semantic-release/changelog": "^5.0.1",
56 | "@semantic-release/git": "^9.0.0",
57 | "@types/jest": "^26.0.23",
58 | "@types/koa-bodyparser": "^4.3.1",
59 | "@types/koa-router": "^7.4.2",
60 | "@types/superagent": "^4.1.11",
61 | "axios": "^0.21.1",
62 | "chalk": "^4.1.1",
63 | "execa": "^5.1.1",
64 | "jest": "^27.0.5",
65 | "npm-run-all": "^4.1.5",
66 | "prettier": "^2.3.1",
67 | "semantic-release": "^17.4.4",
68 | "ts-jest": "^27.0.3",
69 | "vercel": "^23.0.0",
70 | "yorkie": "^2.0.0"
71 | }
72 | }
--------------------------------------------------------------------------------
/project/DATA.json:
--------------------------------------------------------------------------------
1 | {"self":{"Encrypt":""},"thirdPart":[]}
2 |
--------------------------------------------------------------------------------
/project/assets/avatar/xuesong/xs1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/project/assets/avatar/xuesong/xs1.png
--------------------------------------------------------------------------------
/project/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "wechat": {
3 | "appid": "",
4 | "secret": "",
5 | "encodingAESKey": "",
6 | "token": ""
7 | },
8 | "data": "./DATA.json",
9 | "input": "./index.js"
10 | }
11 |
--------------------------------------------------------------------------------
/project/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 晚安睡务局
6 |
37 |
38 |
39 |
40 |
41 |
42 |
85 |
86 |
87 |
88 |

89 |

90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/project/index.js:
--------------------------------------------------------------------------------
1 | const { avatarPlugins } = require('wx-serve')
2 |
3 | async function test({ target, Content, FromUserName, root, rawContent }) {
4 | // 图片活动
5 | await avatarPlugins({ targetInfo: target, uid: FromUserName, content: Content, root, frameName: ['xs1'], dir: 'xuesong' })
6 | }
7 |
8 | module.exports = test
9 |
--------------------------------------------------------------------------------
/project/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "temp",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | },
8 | "author": "",
9 | "license": "ISC",
10 | "description": ""
11 | }
12 |
--------------------------------------------------------------------------------
/project/自行更改文件名称与内容验证微信.txt:
--------------------------------------------------------------------------------
1 | 自行更改文件名称与内容验证微信
2 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | **wx-serve** 是一个基于微信第三方公众号而搭建的框架,目的在于解决快速开发与部署微信服务,让开发者可以专注于插件的开发之中。
11 |
12 | 
13 |
14 | #### 和我们平时的微信第三方搭建方式有什么区别?
15 | 1.通过npm命令行,开箱即用,无需额外代码
16 |
17 | 2.监听业务代码,实行热更新功能
18 |
19 | 3.线上服务启动后,只需要开发者关注业务代码本身,功能持续运行
20 |
21 | 4.插件式开发(希望开发者的插件可以共享起来)
22 |
23 | 5.记录微信数据的文件,可以随时通过磁盘文件的方式同步,这意味着线上环境的数据可以与测试环境的数据进行同步,不用担心过期丢失刷新的问题。
24 |
25 | #### 目前谁在用?
26 | 
27 | 
28 |
29 | 刚拿出来共享,还需要完善的API还有很多。
30 |
31 |
32 | ## Installation
33 | ```shell script
34 | npm install wx-serve --save
35 | ```
36 |
37 | ## Usgae
38 | 在使用该服务前,请先在微信开放平台上进行注册
39 |
40 | https://open.weixin.qq.com/
41 |
42 | 注册成功后,配置参数,在域名上运行wx-serve即可,可以自动监听入口依赖文件的改变。
43 |
44 | (注意不会监听```config.json```的变动,如需改变,请重新开启服务)
45 |
46 | **step1创建模板**:
47 | ```shell script
48 | wx-serve create --appid yourAppid --url yourUrl
49 | ```
50 |
51 | **step2修改config**:
52 |
53 | 创建模板后,修改```config.json```,根据微信配置输入appid,secret,encodingAESKey,token。
54 |
55 | **step3修改txt验证**:
56 |
57 | 修改文件```自行更改文件名称与内容验证微信```(该文件用于微信域名验证)
58 |
59 | **step4开启服务**:
60 | ```shell script
61 | wx-serve --port 3000
62 | ```
63 |
64 | 微信公众号拥有者扫码打开localhost:3000,点击按钮进行授权。
65 |
66 | 微信公众号窗口发送任意信息,可以获得一张图片,服务运行成功。
67 |
68 | ## config.json
69 | ```json
70 | {
71 | "wechat": {
72 | "appid": "微信平台提供",
73 | "secret": "微信平台提供",
74 | "encodingAESKey": "微信平台提供",
75 | "token": "微信平台提供"
76 | },
77 | "data": "./DATA.json",
78 | "input": "./index.js"
79 | }
80 | ```
81 |
82 | ```wechat```: 微信配置提供
83 |
84 | ```data```: 储存运行状态信息的文件
85 |
86 | ```input```: 插件入口
87 |
88 | ## 插件入口参数
89 | ```typescript
90 | function input({ target, Content, FromUserName, root, rawContent }) {
91 | }
92 | ```
93 |
94 | ```target```: 第三方平台信息
95 |
96 | ```Content```: 用户发送的消息内容
97 |
98 | ```FromUserName```: 用户平台id
99 |
100 | ```root```: 命令行运行路径
101 |
102 | ```rawContent```: 用户发送的消息内容XML
103 |
104 | ## API
105 | ### sendContent
106 | ```typescript
107 | function sendContent(toUser: any, content: any, serveAccessToken: any, type: 'voice' | 'video' | 'image' | 'text') { // type voice video image
108 | }
109 | ```
110 |
111 | ```toUser```: 用户平台id (入口```FromUserName```参数)
112 |
113 | ```type```: 消息类型
114 |
115 | ```content```: 内容|媒体ID
116 |
117 | ```serveAccessToken```: 目标平台的serveAccessToken(入口```target.authorizer_access_token```参数可取)
118 |
119 |
120 | ## TODO
121 | [] 完善测试action
122 |
123 | [√] 消息API兼容
124 |
125 | [] 开源交友插件
126 |
--------------------------------------------------------------------------------
/src/client/client.ts:
--------------------------------------------------------------------------------
1 | console.log('client ~')
2 |
--------------------------------------------------------------------------------
/src/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 晚安睡务局
6 |
37 |
38 |
39 |
40 |
41 |
42 |
85 |
91 |
92 |
277 |
278 |
279 |

280 |

281 |
282 |
283 |
284 |
285 |
286 |
287 |
--------------------------------------------------------------------------------
/src/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist",
5 | "module": "esnext",
6 | "lib": ["ESNext", "DOM"]
7 | },
8 | "include": ["./"]
9 | }
10 |
--------------------------------------------------------------------------------
/src/node/Activity/Avatar.ts:
--------------------------------------------------------------------------------
1 | // @ts-ignore
2 | import { createCanvas, loadImage } from 'canvas'
3 | import { randomString } from '../util';
4 | // @ts-ignore
5 | import request from 'request'
6 | import { getUserInfo, sendContent } from '../MessageParser'
7 |
8 | const fs = require('fs')
9 | const path = require("path")
10 |
11 | // 边框贴图渲染
12 | export async function parseBlockTypeAvatar({ root, frameName, userPicUrl = '', dir = '' }: { root?: any, frameName?: any, userPicUrl?: string, dir?: string } = {}) {
13 | const width = 512
14 | const height = 512
15 | const canvas = createCanvas(width, height)
16 | const ctx = canvas.getContext('2d')
17 |
18 | // 绘制头像
19 | await loadImage(userPicUrl.replace(/132$/, '0')).then((image: any) => {
20 | ctx.drawImage(image, 0, 0, width, height)
21 | })
22 |
23 | const data = fs.readFileSync(path.join(root, dir + frameName))
24 | // // 绘制叠加的框框
25 | await loadImage(data).then((image: any) => {
26 | ctx.drawImage(image, 0, 0, width, height)
27 | })
28 |
29 | // todo 不要使用写进本地文件的方式
30 | const promise = new Promise((resolve) => {
31 | const hash = randomString(6)
32 | let done: boolean = false
33 | setTimeout(() => {
34 | if (!done) {
35 | resolve(undefined)
36 | }
37 | }, 5000)
38 |
39 | // @ts-ignore
40 | fs.writeFile(path.join(root, `${dir}${hash}.png`), canvas.toBuffer('image/jpeg', { quality: 1 }), (err: any) => {
41 | done = true
42 | if (err) {
43 | console.log(err)
44 | resolve(undefined)
45 | return
46 | }
47 | resolve(path.join(root, `${dir}${hash}.png`))
48 | })
49 | }).catch((e) => {
50 | console.log(e)
51 | })
52 |
53 | return promise
54 | }
55 |
56 | async function avatarPlugins({ targetInfo, uid, frameName, root, dir, index = 0 }: any = {}) {
57 | console.log('图片渲染活动')
58 | let formData = {
59 | my_field: 'my_value',
60 | my_file: ''
61 | }
62 |
63 | // 获取用户信息头像
64 | const userInfo = await getUserInfo({ serveAccessToken: targetInfo.authorizer_access_token, uid, platFormName: targetInfo.name })
65 |
66 | console.log(userInfo)
67 |
68 | let resultPath: any = ''
69 | if (userInfo && userInfo.picUrl) {
70 | console.log('userInfo')
71 | resultPath = await parseBlockTypeAvatar({ root, frameName: frameName[index] + '.png', userPicUrl: (userInfo || {}).picUrl, dir })
72 | if (resultPath) {
73 | formData.my_file = fs.createReadStream(resultPath)
74 | } else {
75 | console.log('没有用户信息,不进行头像渲染')
76 | return
77 | }
78 | }
79 |
80 | if (global.__TEST__) {
81 | // 测试模式下 不需要上传图片并发送给用户
82 | return ''
83 | }
84 |
85 | // 上传图片 并发送
86 | request.post({url:`https://api.weixin.qq.com/cgi-bin/media/upload?access_token=${targetInfo.authorizer_access_token}&type=image`, formData: formData}, async function(err: any, httpResponse: any, body: any) {
87 | // 删除文件 免得占用内存
88 | if (resultPath) {
89 | fs.unlink(resultPath, function(err: any){
90 | if(err){
91 | throw err;
92 | }
93 | })
94 | }
95 |
96 | if (err) {
97 | return console.error('upload failed: ', err)
98 | }
99 |
100 | if (JSON.parse(body).media_id) {
101 | // 发送消息给用户
102 | sendContent(uid, JSON.parse(body).media_id, targetInfo.authorizer_access_token, 'image')
103 | if (frameName.length > index + 1) {
104 | avatarPlugins({ userInfo, formData, targetInfo, uid, frameName, index: index + 1, root, dir })
105 | }
106 | }
107 | })
108 | }
109 |
110 | export default avatarPlugins
111 |
--------------------------------------------------------------------------------
/src/node/Encrypt.ts:
--------------------------------------------------------------------------------
1 | import crypto from 'crypto';
2 |
3 | var Encrypt = function (options: any) {
4 | this.checkParams(options)
5 | this.appId = options.appId
6 | this.encodingAESKey = options.encodingAESKey
7 | this.token = options.token
8 | this.aesKey = this.getAesKey(this.encodingAESKey)
9 | this.iv = this.getIv(this.aesKey)
10 | }
11 |
12 | Encrypt.prototype = {
13 | checkParams: function (options: any) {
14 | var keys = ['appId', 'encodingAESKey', 'token']
15 | keys.forEach(function (key) {
16 | if (typeof options[key] !== 'string') {
17 | throw new Error('Encrypt constructor() params is all String')
18 | }
19 | })
20 | },
21 | getAesKey: function (encodingAESKey: any) {
22 | return Buffer.from(encodingAESKey + '=', 'base64')
23 | },
24 | getIv: function (aesKey: any) {
25 | return aesKey.slice(0, 16)
26 | },
27 | encode: function (xmlMsg: any) {
28 | if (typeof xmlMsg !== 'string') {
29 | throw new TypeError('encode() required a String!')
30 | }
31 | var random = crypto.randomFillSync(Buffer.alloc(16))
32 | var buf = Buffer.from(xmlMsg)
33 | var msgLength = Buffer.alloc(4)
34 | msgLength.writeUInt32BE(buf.length, 0)
35 | var idBuf = Buffer.from(this.appId)
36 | var xmlBuf = Buffer.from(xmlMsg)
37 | var totalBuf = Buffer.concat([random, msgLength, xmlBuf, idBuf])
38 | totalBuf = this.PKCS7Encode(totalBuf)
39 | var cipher = crypto.createCipheriv('aes-256-cbc', this.aesKey, this.iv)
40 | cipher.setAutoPadding(false)
41 | var encryptBuf = Buffer.concat([cipher.update(totalBuf), cipher.final()])
42 | return encryptBuf.toString('base64')
43 | },
44 | decode: function (encryptMsg: any) {
45 | if (typeof encryptMsg !== 'string') {
46 | throw new TypeError('decode() required a String!')
47 | }
48 | var decipher = crypto.createDecipheriv('aes-256-cbc', this.aesKey, this.iv)
49 | decipher.setAutoPadding(false)
50 | var decipherBuffer = Buffer.concat([decipher.update(encryptMsg, 'base64'), decipher.final()])
51 | decipherBuffer = this.PKCS7Decode(decipherBuffer)
52 | var msgLength = decipherBuffer.slice(16, 20).readUInt32BE(0)
53 | var result = decipherBuffer.slice(20, msgLength + 20).toString()
54 | return result
55 | },
56 | getSignature: function (data: any) {
57 | var str = ([
58 | data.nonce,
59 | data.timestamp,
60 | 'LnrDkrmurYPe3yPgziYAdwaTuk2obn7s'
61 | ]).sort().join('')
62 |
63 | return crypto.createHash('sha1').update(str).digest('hex')
64 | },
65 | verify: function (data: any) {
66 | var msg_signature = data.msg_signature
67 | var result = this.getSignature(data)
68 | return result === msg_signature
69 | },
70 | PKCS7Decode: function (buf: any) {
71 | var len = buf[buf.length - 1]
72 | if (len < 1 || len > 32) {
73 | len = 0
74 | }
75 | return buf.slice(0, buf.length - len)
76 | },
77 | PKCS7Encode: function (buf: any) {
78 | var blockSize = 32
79 | var len = buf.length
80 | var paddingLength = blockSize - (len % blockSize)
81 | var paddingBuffer = Buffer.alloc(paddingLength, paddingLength)
82 | return Buffer.concat([buf, paddingBuffer])
83 | }
84 | }
85 |
86 | export default Encrypt
87 |
--------------------------------------------------------------------------------
/src/node/MessageParser/index.ts:
--------------------------------------------------------------------------------
1 | import SuperAgent from 'superagent'
2 | import { userInfoCache, UserInfo } from '../server'
3 |
4 | // 发送媒体信息给用户
5 | export function sendContent(
6 | toUser: any,
7 | content: any,
8 | serveAccessToken: any,
9 | type: 'voice' | 'video' | 'image' | 'text'
10 | ) {
11 | // type voice video image
12 | return new Promise((resolve) => {
13 | const serviceData = {
14 | touser: toUser,
15 | msgtype: type,
16 | [type]: {
17 | media_id: content
18 | }
19 | }
20 |
21 | if (type === 'text') {
22 | serviceData[type] = {
23 | content
24 | }
25 | }
26 |
27 | console.log(serviceData)
28 |
29 | SuperAgent.post(
30 | `https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=${serveAccessToken}`
31 | )
32 | .send(serviceData)
33 | .end((err, res) => {
34 | console.log('消息发送回调结果: ', err, res?.body)
35 | resolve(null)
36 | })
37 | })
38 | }
39 |
40 | // 获取用户信息
41 | export async function getUserInfo({
42 | serveAccessToken,
43 | uid,
44 | platFormName
45 | }: {
46 | serveAccessToken: string
47 | uid: string
48 | platFormName: string
49 | }): Promise {
50 | return new Promise((resolve) => {
51 | if (global.__TEST__) {
52 | resolve({
53 | name: 'foo',
54 | picUrl: 'https://res.psy-1.com/Fr0Rww96T0CK_lG0y36fm-IE75XD',
55 | openid: 'foo',
56 | sex: '1',
57 | all: {}
58 | })
59 | return
60 | }
61 | let cache = userInfoCache.get(uid)
62 | if (cache) {
63 | resolve(cache)
64 | return
65 | }
66 | SuperAgent.get(
67 | `https://api.weixin.qq.com/cgi-bin/user/info?access_token=${serveAccessToken}&openid=${uid}&lang=zh_CN`
68 | ).end((err, res) => {
69 | if (!res) {
70 | console.log('获取用户信息没有响应')
71 | return
72 | }
73 | if (res.body) {
74 | const data = {
75 | name: res.body.nickname,
76 | picUrl: res.body.headimgurl,
77 | openid: res.body.openid,
78 | sex: res.body.sex,
79 | all: res.body
80 | }
81 | if (res.body.openid) {
82 | userInfoCache.set(res.body.openid, data)
83 | }
84 | resolve(data)
85 | return
86 | }
87 | resolve(undefined)
88 | })
89 | })
90 | }
91 |
--------------------------------------------------------------------------------
/src/node/index.ts:
--------------------------------------------------------------------------------
1 | export * from './server'
2 | // import avatarPlugins from './Activity/Avatar'
3 |
4 | // export { avatarPlugins }
5 |
--------------------------------------------------------------------------------
/src/node/plugins/ParsePlatFormMessagePlugin.ts:
--------------------------------------------------------------------------------
1 | // 处理第三方平台的信息
2 | import { Plugin } from '../server'
3 | import { getData } from '../util'
4 | import _Log from '../../util/Log'
5 |
6 | const madge = require('madge')
7 | const path = require('path')
8 |
9 | // 需插件引入
10 | // import avatarPlugins from '../Activity/Avatar'
11 |
12 | const ParsePlatFormMessagePlugin: Plugin = ({
13 | app,
14 | Router,
15 | encrypt,
16 | root,
17 | DATA,
18 | input,
19 | watcher
20 | }) => {
21 | let inputMth: Function
22 |
23 | try {
24 | const Log = _Log(`热更新:`)
25 | watcher.on('change', (file) => {
26 | madge(path.join(root, input)).then(
27 | (res: { tree: Record }) => {
28 | if (Object.keys(res.tree).includes(path.relative(root, file))) {
29 | Log(`文件${file}改动,将重加载入口方法`)
30 | // 消息处理
31 | inputMth = require(path.join(root, input))
32 | }
33 | }
34 | )
35 | })
36 |
37 | // 消息处理
38 | inputMth = require(path.join(root, input))
39 | } catch (e) {
40 | console.log(e)
41 | }
42 |
43 | if (app) {
44 | app.use(async (ctx, next) => {})
45 | }
46 |
47 | // 监听第三方平台信息
48 | Router.post(`/wechat_open_platform/:id/message`, async (ctx) => {
49 | const platFormId = ctx.params.id
50 | let target: any = {}
51 |
52 | for (let i = 0; i < DATA.thirdPart.length; i++) {
53 | const _target = DATA.thirdPart[i]
54 | if (_target.appid === platFormId) {
55 | target = _target
56 | break
57 | }
58 | }
59 |
60 | const { result } = await getData(ctx, encrypt)
61 |
62 | const Content =
63 | (/]*>\<\!\[CDATA\[([\s\S]*?)\]\]\><\/Content>/gm.exec(
64 | result
65 | ) || [])![1]
66 | const FromUserName =
67 | (/]*>\<\!\[CDATA\[([\s\S]*?)\]\]\><\/FromUserName>/gm.exec(
68 | result
69 | ) || [])![1]
70 |
71 | const Log = _Log(`收到来自${target.name}(${platFormId})的消息:`)
72 |
73 | Log(Content)
74 | Log(result)
75 |
76 | ctx.response.body = 'success'
77 |
78 | console.log(inputMth)
79 |
80 | // todo 消息插件 target content FromUserName
81 | if (inputMth && typeof inputMth === 'function') {
82 | inputMth({ target, Content, FromUserName, root, rawContent: result })
83 | }
84 |
85 | // @ts-ignore
86 | if (inputMth?.default && typeof inputMth?.default === 'function') {
87 | inputMth({ target, Content, FromUserName, root, rawContent: result })
88 | }
89 | })
90 | }
91 |
92 | export default ParsePlatFormMessagePlugin
93 |
--------------------------------------------------------------------------------
/src/node/plugins/SelfWeChatPlugin.ts:
--------------------------------------------------------------------------------
1 | import { Plugin } from '../server'
2 | import _Log from '../../util/Log'
3 | import SuperAgent from 'superagent'
4 | import { writeFile, getData } from '../util'
5 |
6 | export let EnctypeTicket = ''
7 |
8 | const Log = _Log('Message from 自身平台:')
9 | Log(`读取本地DATA文件,获取EnctypeTicket: ${EnctypeTicket}`)
10 |
11 | // 微信第三方自身授权
12 | const SelfWeChatPlugin: Plugin = ({
13 | app,
14 | Router,
15 | root,
16 | encrypt,
17 | appid,
18 | secret,
19 | DATA
20 | }) => {
21 | if (app) {
22 | app.use(async (ctx, next) => {})
23 | }
24 |
25 | EnctypeTicket = DATA && DATA.self && DATA.self.Encrypt
26 |
27 | // 每10分钟会有请求进来
28 | Router.post(
29 | '/wechat_open_platform/auth/callback',
30 | async (ctx: any, res: any) => {
31 | const { result: _EnctypeTicket, bodyXML } = await getData(
32 | ctx,
33 | encrypt,
34 | 'ComponentVerifyTicket'
35 | )
36 |
37 | if (_EnctypeTicket) {
38 | EnctypeTicket = _EnctypeTicket
39 |
40 | // todo 抓获setter
41 | DATA.self.Encrypt = EnctypeTicket
42 | writeFile(root, DATA)
43 |
44 | Log(`微信端接收EnctypeTicket:${EnctypeTicket}`)
45 | ctx.response.body = 'success'
46 | } else {
47 | Log(`微信端接收EnctypeTicket异常: ${bodyXML}`)
48 | }
49 | }
50 | )
51 |
52 | getSelfAccessComponentToken({ appid, root, secret, DATA })
53 |
54 | refleash({ appid, root, DATA })
55 | }
56 |
57 | // 获取自身平台的令牌
58 | export function getComponentAccessToken({
59 | appid,
60 | secret,
61 | enctypeTicket
62 | }: Record = {}): Promise {
63 | const params = {
64 | component_appid: appid,
65 | component_appsecret: secret,
66 | component_verify_ticket: enctypeTicket
67 | }
68 |
69 | console.log(params)
70 |
71 | return new Promise((resolve) => {
72 | // 这个方法怎么不是返回promise?
73 | // todo 改写为request
74 | SuperAgent.post(
75 | `https://api.weixin.qq.com/cgi-bin/component/api_component_token`
76 | )
77 | .send(params)
78 | .end((err, res) => {
79 | if (!res) {
80 | return
81 | }
82 |
83 | console.log(res.body)
84 | Log(`获取令牌access_token:${res.body.component_access_token}`)
85 | resolve(res.body.component_access_token)
86 | })
87 | })
88 | }
89 |
90 | // 获取预授权码
91 | // https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/pre_auth_code.html
92 | export async function getPreCode({
93 | appid,
94 | access_token
95 | }: Record = {}) {
96 | return new Promise((resolve) => {
97 | const _URL = `https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=${access_token}`
98 | const _Params = {
99 | component_appid: appid
100 | }
101 | return SuperAgent.post(_URL)
102 | .send(_Params)
103 | .end((err, res) => {
104 | if (!res) {
105 | return ''
106 | resolve(null)
107 | }
108 | const code: string = res.body.pre_auth_code
109 | Log(`获取预授权码: ${code}`)
110 | resolve(code)
111 | return code
112 | })
113 | })
114 | }
115 |
116 | // 获取账号自身的AccessComponentToken 用于刷新
117 | function getSelfAccessComponentToken({ appid, root, secret, DATA }: any = {}) {
118 | const gapTime = new Date().getTime() - parseInt(DATA.self.update || 0)
119 | const time = 1000 * 60 * 50
120 |
121 | if (gapTime >= time) {
122 | const params = {
123 | component_appid: appid,
124 | component_appsecret: secret,
125 | component_verify_ticket: DATA.self.Encrypt
126 | }
127 |
128 | SuperAgent.post(
129 | `https://api.weixin.qq.com/cgi-bin/component/api_component_token`
130 | )
131 | .send(params)
132 | .end((err, res) => {
133 | if (res) {
134 | if (res.body.component_access_token) {
135 | Log(`获取access_token:${res.body.component_access_token}`)
136 | DATA.self.component_access_token = res.body.component_access_token
137 | DATA.self.update = new Date().getTime()
138 | writeFile(root, DATA)
139 | } else {
140 | Log('获取失败,请查看异常提示')
141 | console.log(res.body, params)
142 | }
143 | }
144 | })
145 | }
146 |
147 | // 每一小时请求一次
148 | setTimeout(() => {
149 | getSelfAccessComponentToken({ appid, root, secret, DATA })
150 | }, 1000 * 60 * 20)
151 | }
152 |
153 | // 刷新机制
154 | // todo 删除
155 | function refleash({ appid, root, DATA }: any = {}) {
156 | DATA.thirdPart.forEach((v: any, index: number) => {
157 | const minTime = new Date().getTime() - parseInt(v.update)
158 | const time = 1000 * 60 * 60
159 |
160 | const params = {
161 | component_appid: appid,
162 | authorizer_appid: v.appid, // 授权方的appid
163 | authorizer_refresh_token: v.refresh_authorizer_refresh_token // 授权方的刷新令牌
164 | }
165 |
166 | if (v.appid && minTime >= time) {
167 | const target = DATA.thirdPart[index]
168 | Log(`刷新${target.name}的accessToken`)
169 | SuperAgent.post(
170 | `https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=${DATA.self.component_access_token}`
171 | )
172 | .send(params)
173 | .end(async (err, res) => {
174 | if (res.body.authorizer_access_token) {
175 | target.update = new Date().getTime()
176 | target.authorizer_access_token = res.body.authorizer_access_token
177 | target.refresh_authorizer_refresh_token =
178 | res.body.authorizer_refresh_token
179 | writeFile(root, DATA)
180 | } else {
181 | Log(`${target.name}刷新后,没有数据,请查看异常提示`)
182 | console.log(res.body)
183 | }
184 | })
185 | }
186 | })
187 |
188 | // 1小时请求一次
189 | setTimeout(() => {
190 | refleash({ appid, root, DATA })
191 | }, 1000 * 60 * 20)
192 | }
193 |
194 | export default SelfWeChatPlugin
195 |
--------------------------------------------------------------------------------
/src/node/plugins/ThirdPartWeChatPlugins.ts:
--------------------------------------------------------------------------------
1 | import SuperAgent from 'superagent'
2 | import _Log from '../../util/Log'
3 | import { Plugin } from '../server'
4 | import {
5 | EnctypeTicket,
6 | getComponentAccessToken,
7 | getPreCode,
8 | } from './SelfWeChatPlugin'
9 | import { writeFile } from '../util'
10 | import { DataType } from '../server'
11 |
12 | const Log = _Log('Message from 第三方:')
13 |
14 | const ThirdPartWeChatPlugin: Plugin = ({ app, appid, secret, Router, root, DATA }) => {
15 | if (app) {
16 | app.use(async (ctx, next) => {
17 |
18 | })
19 | }
20 |
21 | let ACCESS_TOKEN = ''
22 |
23 | // step1 发送第三方的预授权码
24 | Router.get('/wechat_open_platform/preauthcode', async (ctx: any, res: any) => {
25 | if (!EnctypeTicket) {
26 | Log(`EnctypeTicket(${EnctypeTicket})错误,发送预授权码失败`)
27 | ctx.response.body = 'error'
28 | return
29 | }
30 |
31 | ACCESS_TOKEN = await getComponentAccessToken({
32 | appid,
33 | secret,
34 | enctypeTicket: EnctypeTicket
35 | })
36 | const code = await getPreCode({ access_token: ACCESS_TOKEN, appid })
37 | ctx.response.body = code
38 | })
39 |
40 | // step2 接收从前端页面跳转发来的authorization_code
41 | Router.get(`/wechat_open_platform/submitac`, async (ctx: any, res: any) => {
42 |
43 | if (!ACCESS_TOKEN) {
44 | Log(
45 | `ACCESS_TOKEN(${ACCESS_TOKEN})令牌为空,需要获取自身平台的令牌,才可以进行授权。`
46 | )
47 | ctx.response.body = 'error'
48 | return
49 | }
50 |
51 | Authorization(ctx.query.ac as string, ACCESS_TOKEN, appid, root!, DATA!)
52 | ctx.response.body = 'success'
53 | })
54 | }
55 |
56 | function Authorization(
57 | authorization_code: string,
58 | ACCESS_TOKEN: string,
59 | appid: string,
60 | root: string,
61 | DATA: DataType
62 | ): Promise {
63 | Log(`授权开始,authorization_code: ${authorization_code}`)
64 |
65 | return new Promise((resolve) => {
66 | SuperAgent.post(
67 | `https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=${ACCESS_TOKEN}`
68 | )
69 | .send({
70 | component_appid: appid,
71 | authorization_code: authorization_code
72 | })
73 | .end(async (err, res) => {
74 | if (!res) {
75 | return
76 | }
77 |
78 | if (res.body.hasOwnProperty('errcode')) {
79 | Log(`无效的authorization_code:${ACCESS_TOKEN}`)
80 | Log(`本次授权失败`)
81 | return
82 | }
83 |
84 | const AUTHORIZATION_INFO = res.body.authorization_info
85 |
86 | let authorizer_access_token = AUTHORIZATION_INFO.authorizer_access_token
87 | let refresh_authorizer_refresh_token =
88 | AUTHORIZATION_INFO.authorizer_refresh_token
89 |
90 | Log(
91 | `获取成功!\r\nauthorizer_access_token:${authorizer_access_token}\r\nrefresh_authorizer_refresh_token:${refresh_authorizer_refresh_token}`
92 | )
93 |
94 | // 获取第三方平台的信息
95 | const platFormInfo: any = await new Promise((resolve) => {
96 | SuperAgent.post(
97 | `https://api.weixin.qq.com/cgi-bin/component/api_get_authorizer_info?component_access_token=${ACCESS_TOKEN}`
98 | )
99 | .send({
100 | component_appid: appid,
101 | authorizer_appid: AUTHORIZATION_INFO.authorizer_appid
102 | })
103 | .then((err) => {
104 | if (!err) {
105 | return
106 | }
107 | resolve(err.body.authorizer_info)
108 | })
109 | })
110 |
111 | Log(
112 | `${platFormInfo.nick_name}第三方授权完成,将凭借refresh_authorizer_refresh_token,每一个小时刷新一次authorizer_access_token`
113 | )
114 |
115 | // 写入&更新第三方平台的信息
116 | setupPlatFormData({ AUTHORIZATION_INFO, authorizer_access_token, refresh_authorizer_refresh_token, platFormInfo, root, DATA })
117 |
118 | // 返回第三方平台的id
119 | resolve(AUTHORIZATION_INFO.authorizer_appid)
120 | })
121 | })
122 | }
123 |
124 | // todo 类型
125 | // 写入或更新第三方平台的信息
126 | function setupPlatFormData({ AUTHORIZATION_INFO, authorizer_access_token, refresh_authorizer_refresh_token, platFormInfo, root, DATA }: any = {}) {
127 | let targetIndex: null | number = null
128 | const thirdPlatForm = {
129 | appid: AUTHORIZATION_INFO.authorizer_appid,
130 | authorizer_access_token,
131 | refresh_authorizer_refresh_token,
132 | update: new Date().getTime(),
133 | create: new Date().getTime(),
134 | qrcode_url: platFormInfo.qrcode_url,
135 | name: platFormInfo.nick_name
136 | }
137 |
138 | for (let i = 0; i < DATA.thirdPart.length; i++) {
139 | const old = DATA.thirdPart[i]
140 | if (old.appid === AUTHORIZATION_INFO.authorizer_appid) {
141 | targetIndex = i
142 | thirdPlatForm.create = old.create
143 | break
144 | }
145 | }
146 |
147 | if (!targetIndex) {
148 | DATA.thirdPart.push(thirdPlatForm)
149 | } else {
150 | DATA.thirdPart[targetIndex] = thirdPlatForm
151 | }
152 |
153 | // todo 数据库保存平台的信息 与刷新token
154 | writeFile(root, DATA)
155 | }
156 |
157 | export default ThirdPartWeChatPlugin
158 |
--------------------------------------------------------------------------------
/src/node/root.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.json' {
2 | const value: any;
3 | export default value;
4 | }
5 |
6 | declare var __TEST__: boolean
7 | declare var __CONFIG__: {
8 | data: string // 数据文件
9 | input: string // 入口文件
10 | }
11 |
--------------------------------------------------------------------------------
/src/node/server.ts:
--------------------------------------------------------------------------------
1 | import http, { Server } from 'http'
2 | import Koa from 'koa'
3 | import SelfWeChatPlugin from './plugins/SelfWeChatPlugin'
4 | import ThirdPartWeChatPlugins from './plugins/ThirdPartWeChatPlugins'
5 | import _Router from 'koa-router'
6 | import _BodyParser from 'koa-bodyparser'
7 | import Encrypt from './Encrypt'
8 | import chokidar, { FSWatcher } from 'chokidar'
9 | import ParsePlatFormMessagePlugins from './plugins/ParsePlatFormMessagePlugin';
10 |
11 | // @ts-ignore
12 | import LRUCache from 'lru-cache'
13 |
14 | export interface UserInfo {
15 | name: string,
16 | picUrl: string,
17 | openid: string,
18 | sex: string,
19 | all: any
20 | }
21 |
22 | export const userInfoCache = new LRUCache({
23 | max: 65535
24 | })
25 |
26 | export type Plugin = (ctx: PluginContext) => void
27 | export const Router = new _Router()
28 |
29 | export let ROOT = ''
30 |
31 | export interface DataType {
32 | self: {
33 | Encrypt: string
34 | },
35 | thirdPart: {
36 | appid: string
37 | authorizer_access_token: string
38 | refresh_authorizer_refresh_token: string
39 | update: number
40 | create: number
41 | qrcode_url: string
42 | name: string
43 | }[]
44 | }
45 |
46 | export interface ServerConfig {
47 | root?: string
48 | plugins?: Plugin[]
49 | appid?: string
50 | secret?: string
51 | encodingAESKey?: string
52 | token?: string
53 | DATA: DataType
54 | input: string
55 | }
56 |
57 | export interface PluginContext {
58 | encrypt: any
59 | appid: string
60 | secret: string
61 | app?: Koa
62 | type: 'koa'
63 | Router: typeof Router
64 | root?: string
65 | watcher: FSWatcher
66 | DATA: DataType
67 | input: string
68 | }
69 |
70 | export const internalPlugins: Plugin[] = [
71 | SelfWeChatPlugin,
72 | ThirdPartWeChatPlugins,
73 | ParsePlatFormMessagePlugins
74 | ]
75 |
76 | export function createServer({
77 | root = process.cwd(),
78 | appid = '',
79 | secret = '',
80 | plugins = [],
81 | encodingAESKey,
82 | token,
83 | DATA,
84 | input = 'index.js'
85 | }: ServerConfig): Server {
86 | ROOT = root
87 | const app = new Koa()
88 | const watcher = chokidar.watch(root, {
89 | ignored: [/node_modules/]
90 | })
91 |
92 | app.use(Router.routes())
93 | app.use(_BodyParser())
94 | app.use(require('koa-static')(root))
95 |
96 | const server = http.createServer(app.callback())
97 |
98 | // @ts-ignore
99 | const encrypt = new Encrypt({
100 | appId: appid,
101 | encodingAESKey,
102 | token
103 | })
104 |
105 | ;[...internalPlugins, ...plugins].forEach((m) =>
106 | m({
107 | encrypt,
108 | appid,
109 | secret,
110 | app,
111 | type: 'koa',
112 | Router,
113 | root,
114 | watcher,
115 | DATA,
116 | input
117 | })
118 | )
119 |
120 | return server
121 | }
122 |
123 | process.on('uncaughtException', function(err){
124 | console.log(err)
125 | })
126 |
--------------------------------------------------------------------------------
/src/node/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.base.json",
3 | "compilerOptions": {
4 | "outDir": "../../dist",
5 | "module": "commonjs",
6 | "lib": ["ESNext"]
7 | },
8 | "typeRoots": [
9 | "./root.d.ts"
10 | ],
11 | "include": ["./"],
12 | "exclude": ["./__test__/*"]
13 | }
14 |
--------------------------------------------------------------------------------
/src/node/util.ts:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'fs'
2 |
3 | const path = require('path')
4 |
5 | const getPostData = (ctx: any): Promise => {
6 | return new Promise(function (resolve, reject) {
7 | try {
8 | let str = ''
9 | ctx.req.on('data', function (data: any) {
10 | str += data;
11 | })
12 | ctx.req.on('end', function () {
13 | resolve(str)
14 | })
15 | } catch (e) {
16 | reject(e)
17 | }
18 | })
19 | }
20 |
21 | const writeFile = (ROOT: string = process.cwd(), data: Record) => {
22 | let dataJSON = JSON.stringify(data)
23 | console.log(ROOT, global.__CONFIG__, '写入')
24 | fs.writeFile(
25 | path.join(ROOT, global.__CONFIG__.data),
26 | dataJSON
27 | )
28 | }
29 |
30 | const getData = async (ctx: any, encrypt: any, tagName?: string): Promise<{ result: any, bodyXML: any }> => {
31 | const bodyXML: string = await getPostData(ctx)
32 | let result = ''
33 | let match: RegExpExecArray | null = null
34 | if (match = /]*>\<\!\[CDATA\[([\s\S]*?)\]\]\><\/Encrypt>/gm.exec(bodyXML)) {
35 | result = encrypt.decode(match[1])
36 | if (tagName === 'ComponentVerifyTicket') {
37 | result = // (eval(`/<${tagName}\\b[^>]*>\\<\\!\\[CDATA\\[([\\s\\S]*?)\\]\\]\\><\\/${tagName}>/gm`)).exec(result)![1]
38 | (/]*>\<\!\[CDATA\[([\s\S]*?)\]\]\><\/ComponentVerifyTicket>/gm.exec(result) || [])![1]
39 | }
40 | }
41 | return {
42 | result,
43 | bodyXML
44 | }
45 | }
46 |
47 | export function randomString(len: number): string {
48 | len = len || 32;
49 | let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
50 | let maxPos = $chars.length;
51 | let pwd = '';
52 | for (let i = 0; i < len; i++) {
53 | pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
54 | }
55 | return pwd;
56 | }
57 |
58 | export { getPostData, writeFile, getData }
59 |
--------------------------------------------------------------------------------
/src/util/Log.ts:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk'
2 |
3 | export default function Log(from: string) {
4 | return function _Log(msg: string) {
5 | console.log(chalk.blue(`\r\n${chalk.red(from)}\r\n${msg}`))
6 | }
7 | }
8 |
9 |
10 |
11 | export function convertPlugins(_ctx: any, res: any, type: 'express' | 'koa') {
12 | let ctx: any = {}
13 |
14 | if (type === 'express') {
15 | ctx.request = ctx
16 | ctx.response = res
17 | } else {
18 | return _ctx
19 | }
20 |
21 | return ctx
22 | }
23 |
24 | export function WechatLog() {
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test.jpg
--------------------------------------------------------------------------------
/test/project.spec.ts:
--------------------------------------------------------------------------------
1 | import _axios from 'axios'
2 |
3 | const axios = _axios.create()
4 |
5 | const execa = require('execa')
6 | const path = require('path')
7 |
8 | const projectDir = path.join(__dirname, 'project')
9 | let server
10 |
11 | describe('hmr', () => {
12 | test('图片测试', () => {
13 | const activityFlow = require('../dist/node/Activity/Avatar.js').default
14 | activityFlow({ targetInfo: {}, uid: '123', content: '123', root: process.cwd(), frameName: ['xs1'], dir: 'xuesong' })
15 | })
16 |
17 | test('消息hmr测试', async () => {
18 | const port = 3007
19 | server = execa(path.resolve(__dirname, '../bin/run.js'), ['--port', port, '--test'], {
20 | cwd: projectDir
21 | })
22 | axios.defaults.baseURL = `http://localhost:${port}`
23 |
24 | // 等待服务运行
25 | await new Promise((resolve) => {
26 | server.stdout.on('data', (data) => {
27 | if (data.toString().match('Running')) {
28 | resolve()
29 | }
30 | })
31 | })
32 |
33 | // 消息回调测试
34 | await axios.post('/wechat_open_platform/wx0ea308250417bd30/message', `
35 |
36 |
39 | `, { headers: { 'Content-Type': 'text/xml' } }).then((res) => {
40 | expect(res.data).toEqual('success')
41 | })
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/test/project/DATA.json:
--------------------------------------------------------------------------------
1 | {"self":{"Encrypt":"ticket@@@jo-pIncA347Zd5Sk7NmOUWnT3BIGtRmLf2LOKrp9QB9fl8uuPzYd-OESWQd4aW52BPgC7G5S2ZlsnTCFuLieKQ","update":1625916049282},"thirdPart":[{"appid":""},{"appid":"wx0ea308250417bd30","authorizer_access_token":"46_VRh3xzLPKiNGTCm3Db9NGLmxqlZPtnzwF5AKMQhDcZLP02VkU637_xOgYZ9kU6PXvjqubelCqBRqKLazhaPso0hZpcPPfQnBOL0p1KYxBPpU3Jlqj459VOkdrW7sLsFu7br2GvooLssQnSMPHHSiAGDXXI","refresh_authorizer_refresh_token":"refreshtoken@@@aoXF2D5-AonY6zK8ZTZ-vVA4UNyFBY0uJXCPYe-4M8I","update":1625057501899,"create":1624789173830,"qrcode_url":"http://mmbiz.qpic.cn/mmbiz_jpg/Y2LExAS5IQEafY1lqsfvx0DsibSTuSIg8VdU1bHJYs1UMJVg5YsIRyusWlic3QiaicKvwmpQic3JLliaachNqwTfDgibA/0","name":"单行线oneway"},{"appid":"wx436fcd3fdc77dc4f","authorizer_access_token":"46_yiI2MUgFmdSINGGTkDJjY0ExdaDPAaMSDiu3sHOIVaIW1e8lWCNUvBlZq2U36R4hxoUfLAJ5-QRPK44mBQPNUErS5cQloSsmsDh6aMn2X7gk4GYZfcsBZtvZq3vk8QlA-Dqe1iowRgleHezuLMKjAEDYZX","refresh_authorizer_refresh_token":"refreshtoken@@@a9LyquHlIXZBe0abXlPj8qydUvzvkmpLpVRfsEhSjl4","update":1625057501904,"create":1624815270396,"qrcode_url":"http://mmbiz.qpic.cn/mmbiz_jpg/8e89TsEShR5uIlYUYfibBmDWGdlNHuYVvRhdSDVcI9ZYDZiaSW1MyZLwZBrFTQLoMgB9czNb7j2Wz2v5Ht6u568w/0","name":"微读读"},{"appid":"wx85df74b62aad79ed","authorizer_access_token":"46_MqSWuwouHD3AF8QB_W2_qkskvV6Fc45J5VI7Z292QMtapqWbUm7Ts4_d-9AOqI9OP_LWH7ZkOMDdlK6jGNW98CkS36bG9opAvpwo3PBvSvz5piJBBEglbFmV1ANYeU08oay-ik1np02Fp1eqJIViAGDONA","refresh_authorizer_refresh_token":"refreshtoken@@@JFv0RMxhMtWp7rwD4ExJZRD_UDnQV32JvlUa0uAwmQE","update":1625057501886,"create":1624841939866,"qrcode_url":"http://mmbiz.qpic.cn/mmbiz/yolMztZrK8cHGMnvIZPDfClRibj9Og2GRqibc46Gm8bY4kCl4J5FpCmT9joh2yqMLnZklMBYbAU7sUiagleUbQ7iaw/0","name":"雪松控股"},{"appid":"wx7630866bd98a50de","authorizer_access_token":"46_15aFf8C9bga4utjWIa8NYX63IBMZG5enMtkqLjqD8Fzq9iWNQZiiRuw6c25hPIhqHnOs-z6TTaTl15iYRVdGCwvUngmT2lAlJAN2M3o6Q79cOVuF9_YrXHGs124gz1J9s0lwsRU6zlpfdOarMXMdAKDYJY","refresh_authorizer_refresh_token":"refreshtoken@@@1vMm3Gx0ebkeHCRMFxECAx7xLls1-hoXYktSZVq2p5w","update":1625057501961,"create":1624842809037,"qrcode_url":"http://mmbiz.qpic.cn/mmbiz_jpg/N4Yia8kulhG4sibL1PNxicoWKt50mOib2VgvicuZ7EaBQeOyZicr2tVWsllDG7lK7BjZOdEwM4MOLian2yNWUSib9mmkWQ/0","name":"三维家软件"}]}
--------------------------------------------------------------------------------
/test/project/assets/avatar/sanwei/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/sanwei/1.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/sanwei/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/sanwei/10.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/sanwei/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/sanwei/2.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/sanwei/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/sanwei/3.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/sanwei/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/sanwei/6.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/sanwei/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/sanwei/7.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/sanwei/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/sanwei/8.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/xuesong/xs1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/xuesong/xs1.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/xuesong/xs2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/xuesong/xs2.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/xuesong/xs3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/xuesong/xs3.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/xuesong/xs4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/xuesong/xs4.png
--------------------------------------------------------------------------------
/test/project/assets/avatar/zYJ3hD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/test/project/assets/avatar/zYJ3hD.png
--------------------------------------------------------------------------------
/test/project/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "wechat": {
3 | "appid": "",
4 | "secret": "",
5 | "encodingAESKey": "",
6 | "token": ""
7 | },
8 | "data": "./DATA.json",
9 | "input": "./index.js"
10 | }
11 |
--------------------------------------------------------------------------------
/test/project/index.js:
--------------------------------------------------------------------------------
1 | const avatarPlugins = require('../../dist/node/Activity/Avatar.js').default
2 |
3 | async function test({ target, Content, FromUserName, root }) {
4 | // 图片活动
5 | await avatarPlugins({ targetInfo: target, uid: FromUserName, content: Content, root, frameName: ['xs1'], dir: 'xuesong' })
6 | }
7 |
8 | module.exports = test
9 |
--------------------------------------------------------------------------------
/test/project/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | }
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "sourceMap": false,
4 | "target": "esnext",
5 | "moduleResolution": "node",
6 | "esModuleInterop": true,
7 | "declaration": true,
8 | "allowJs": false,
9 | "allowSyntheticDefaultImports": true,
10 | "noUnusedLocals": true,
11 | "strictNullChecks": true,
12 | "noImplicitAny": true,
13 | "removeComments": false,
14 |
15 | "baseUrl": "./"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/util/Log.d.ts:
--------------------------------------------------------------------------------
1 | export default function Log(from: string): (msg: string) => void;
2 | export declare function convertPlugins(_ctx: any, res: any, type: 'express' | 'koa'): any;
3 |
--------------------------------------------------------------------------------
/util/Log.js:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 | export default function Log(from) {
3 | return function _Log(msg) {
4 | console.log(chalk.blue(`${chalk.red(from)}\r\n${msg}`));
5 | };
6 | }
7 | export function convertPlugins(_ctx, res, type) {
8 | let ctx = {};
9 | if (type === 'express') {
10 | ctx.request = ctx;
11 | ctx.response = res;
12 | }
13 | else {
14 | return _ctx;
15 | }
16 | return ctx;
17 | }
18 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [{ "source": "/api/(.*)", "destination": "/api" }]
3 | }
4 |
--------------------------------------------------------------------------------
/watch-bob.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kingbultsea/wechat-server-tool/4c782186dd9cfc6f930f0d3770e00b4762ef5f5e/watch-bob.jpg
--------------------------------------------------------------------------------