wechaty-web-panel 1.0.0 → 1.0.3-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,18 +2,8 @@
2
2
  <project version="4">
3
3
  <component name="ChangeListManager">
4
4
  <list default="true" id="2e980355-75b9-47a9-b344-f25eda9493b2" name="Default Changelist" comment="feat(模块): 添加了个很棒的功能&#10;fix(模块): 修复了一些 bug&#10;docs(模块): 更新了一下文档&#10;UI(模块): 修改了一下样式&#10;chore(模块): 对脚手架做了些更改&#10;locale(模块): 为国际化做了微小的贡献">
5
- <change afterPath="$PROJECT_DIR$/.npmrc" afterDir="false" />
6
- <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
7
5
  <change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
8
- <change beforePath="$PROJECT_DIR$/src/common/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/common/index.js" afterDir="false" />
9
- <change beforePath="$PROJECT_DIR$/src/handlers/on-login.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/handlers/on-login.js" afterDir="false" />
10
- <change beforePath="$PROJECT_DIR$/src/handlers/on-message.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/handlers/on-message.js" afterDir="false" />
11
- <change beforePath="$PROJECT_DIR$/src/lib/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/lib/index.js" afterDir="false" />
12
- <change beforePath="$PROJECT_DIR$/src/proxy/api.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/proxy/api.js" afterDir="false" />
13
- <change beforePath="$PROJECT_DIR$/src/proxy/mqtt.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/proxy/mqtt.js" afterDir="false" />
14
- <change beforePath="$PROJECT_DIR$/src/service/event-dispatch-service.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/service/event-dispatch-service.js" afterDir="false" />
15
6
  <change beforePath="$PROJECT_DIR$/src/service/room-async-service.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/service/room-async-service.js" afterDir="false" />
16
- <change beforePath="$PROJECT_DIR$/test/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/test/index.js" afterDir="false" />
17
7
  </list>
18
8
  <option name="SHOW_DIALOG" value="false" />
19
9
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -46,7 +36,7 @@
46
36
  </component>
47
37
  <component name="PropertiesComponent">
48
38
  <property name="WebServerToolWindowFactoryState" value="false" />
49
- <property name="last_opened_file_path" value="$PROJECT_DIR$" />
39
+ <property name="last_opened_file_path" value="$PROJECT_DIR$/test" />
50
40
  <property name="node.js.detected.package.eslint" value="true" />
51
41
  <property name="node.js.selected.package.eslint" value="(autodetect)" />
52
42
  <property name="nodejs_package_manager_path" value="npm" />
@@ -57,11 +47,11 @@
57
47
  </component>
58
48
  <component name="RecentsManager">
59
49
  <key name="CopyFile.RECENT_KEYS">
60
- <recent name="$PROJECT_DIR$" />
61
50
  <recent name="$PROJECT_DIR$/test" />
51
+ <recent name="$PROJECT_DIR$/src/puppeteer-paint" />
52
+ <recent name="$PROJECT_DIR$" />
62
53
  <recent name="$PROJECT_DIR$/koa/bash" />
63
54
  <recent name="$PROJECT_DIR$/koa/pubilc/static" />
64
- <recent name="$PROJECT_DIR$/koa/pubilc/static/img" />
65
55
  </key>
66
56
  </component>
67
57
  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
@@ -107,7 +97,15 @@
107
97
  <workItem from="1642212984216" duration="11645000" />
108
98
  <workItem from="1642384832300" duration="994000" />
109
99
  <workItem from="1642386021423" duration="4268000" />
110
- <workItem from="1642647009185" duration="3767000" />
100
+ <workItem from="1642647009185" duration="3937000" />
101
+ <workItem from="1642657426395" duration="780000" />
102
+ <workItem from="1642678719784" duration="603000" />
103
+ <workItem from="1643094529038" duration="490000" />
104
+ <workItem from="1644319873832" duration="1868000" />
105
+ <workItem from="1645362804718" duration="427000" />
106
+ <workItem from="1645411323461" duration="15523000" />
107
+ <workItem from="1645440841278" duration="516000" />
108
+ <workItem from="1645757384419" duration="1249000" />
111
109
  </task>
112
110
  <servers />
113
111
  </component>
@@ -126,4 +124,8 @@
126
124
  </option>
127
125
  <option name="oldMeFiltersMigrated" value="true" />
128
126
  </component>
127
+ <component name="XSLT-Support.FileAssociations.UIState">
128
+ <expand />
129
+ <select />
130
+ </component>
129
131
  </project>
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## 更新日志
2
2
 
3
+ ### V1.0.2(2022-02-21)
4
+
5
+ 1、去除canvas依赖,使用puppeteer生成图片
6
+
7
+ ### V1.0.1(2022-02-08)
8
+
9
+ 1、修复参数取值问题
10
+
3
11
  ### V1.0.1(2022-01-20)
4
12
 
5
13
  1、升级Wechaty版本到1.x版本,优化部分功能,保持多协议展示形式
package/README.md CHANGED
@@ -118,7 +118,7 @@ $ npm install wechaty-web-panel@latest wechaty@latest --save
118
118
  如果安装长时间没有反应,可以尝试
119
119
 
120
120
  ```
121
- npm install wechaty-web-panel@latest wechaty@latest --save --canvas_binary_host_mirror=https://npm.taobao.org/mirrors/node-canvas-prebuilt/
121
+ npm install wechaty-web-panel@latest wechaty@latest --save
122
122
  ```
123
123
 
124
124
  ### Step 2: 创建机器人并配置插件的`apiKey`和`apiSecret`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wechaty-web-panel",
3
- "version": "1.0.0",
3
+ "version": "1.0.3-beta.0",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -36,32 +36,24 @@
36
36
  "eslint-config-prettier": "^6.11.0",
37
37
  "eslint-plugin-prettier": "^3.1.4",
38
38
  "prettier": "^2.0.5",
39
- "wechaty": "^1.11.42",
39
+ "wechaty": "^1.13.12",
40
40
  "wechaty-puppet-wechat": "^1.11.12"
41
41
  },
42
42
  "readme": "README.md",
43
43
  "dependencies": {
44
44
  "ix": "^4.5.2",
45
- "mcanvas": "^2.0.8",
46
45
  "mqtt": "^4.2.6",
47
46
  "mustache": "^4.2.0",
48
47
  "nedb": "^1.8.0",
49
48
  "node-schedule": "^1.3.2",
49
+ "puppeteer": "^13.3.2",
50
50
  "qrcode-terminal": "^0.12.0",
51
51
  "superagent": "^5.2.2",
52
52
  "tencentcloud-sdk-nodejs": "^4.0.30"
53
53
  },
54
- "peerDependencies": {
55
- "wechaty": "^1.11.42",
56
- "wechaty-puppet-wechat": "^1.11.12"
57
- },
58
54
  "publishConfig": {
59
55
  "registry": " https://registry.npmjs.org/"
60
56
  },
61
- "engines": {
62
- "node": ">=16.0",
63
- "npm": ">=8.1"
64
- },
65
- "_id": "wechaty-web-panel@1.0.0",
66
- "_commitid": "754bf1b"
57
+ "_id": "wechaty-web-panel@1.0.3-beta.0",
58
+ "_commitid": "6714817"
67
59
  }
@@ -42,9 +42,14 @@ async function updateContactInfo(that) {
42
42
  const contactList = await that.Contact.findAll()
43
43
  let res = []
44
44
  const notids = ['filehelper', 'fmessage']
45
- let realContact = hasWeixin ? contactList.filter((item) => item._payload.type == 1 && item._payload.friend && !notids.includes(item._payload.id)) : contactList
45
+ let realContact = hasWeixin
46
+ ? contactList.filter((item) => {
47
+ const payload = item.payload || item._payload
48
+ return payload.type === 1 && payload.friend && !notids.includes(payload.id)
49
+ })
50
+ : contactList
46
51
  for (let i of realContact) {
47
- let contact = i._payload
52
+ let contact = i.payload || i._payload
48
53
  let obj = {
49
54
  robotId: hasWeixin ? contactSelf.weixin : MD5(contactSelf.name),
50
55
  contactId: hasWeixin ? contact.id : MD5(contactSelf.name + contact.name + contact.alias + contact.province + contact.city + contact.gender),
@@ -92,7 +97,7 @@ async function updateRoomInfo(that) {
92
97
  const roomList = await that.Room.findAll()
93
98
  let res = []
94
99
  for (let i of roomList) {
95
- let room = i._payload
100
+ let room = i.payload || i._payload
96
101
  let obj = {
97
102
  robotId: hasWeixin ? contactSelf.weixin : MD5(contactSelf.name),
98
103
  roomId: MD5(room.topic),
@@ -2,7 +2,6 @@ const { delay, MD5 } = require('../lib')
2
2
  const { getConfig, sendRobotInfo, sendError, putqn, setQrCode, updatePanelVersion } = require('../proxy/aibotk')
3
3
  const { addUser } = require('../common/userDb')
4
4
  const { initAllSchedule } = require('../task')
5
- var pjson = require('../../package.json')
6
5
  const { initMqtt } = require('../proxy/mqtt')
7
6
  const { allConfig } = require('../common/configDb')
8
7
 
@@ -19,9 +18,10 @@ async function onLogin(user) {
19
18
  await getConfig() // 获取配置文件
20
19
  const config = await allConfig()
21
20
  const { userId } = config.userInfo
21
+ const payload = user.payload || user._payload
22
22
  const userInfo = {
23
- ...user._payload,
24
- robotId: user._payload.weixin || MD5(user.name()),
23
+ ...payload,
24
+ robotId: payload.weixin || MD5(user.name()),
25
25
  }
26
26
  await addUser(userInfo) // 全局存储登录用户信息
27
27
  const file = await user.avatar()
package/src/lib/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const Crypto = require('crypto')
2
2
  const schedule = require('node-schedule')
3
3
  const fs = require('fs')
4
- const { MCanvas, MImage } = require('mcanvas')
5
4
  const { addRoom, getRoom } = require('../common/roomAvatarDb')
6
5
 
7
6
  /**
@@ -416,7 +415,7 @@ function filterContacts(contacts, query) {
416
415
  let { name, alias, friend, type, gender, province, city, address } = query
417
416
  return contacts.filter((item) => {
418
417
  let arr = []
419
- let { _payload: payload } = item
418
+ let payload = item.payload || item._payload
420
419
  if (friend) {
421
420
  let bool = Number(friend) === 1 ? true : false
422
421
  arr.push(bool === payload.friend)
@@ -440,7 +439,7 @@ function formatContacts(data) {
440
439
  let arr = data.map(function (item) {
441
440
  // const file = await item.avatar()
442
441
  // let avatar = await file.toBase64(file.name, true);
443
- let payload = item._payload
442
+ let payload = item.payload || item._payload
444
443
  return {
445
444
  id: payload.id,
446
445
  name: payload.name,
@@ -517,11 +516,10 @@ async function getRoomAvatarList(room, name) {
517
516
  let res = []
518
517
  console.log('正在努力获取群成员信息...')
519
518
  for (let i of members) {
520
- let member = i._payload
519
+ let member = i.payload || i._payload
521
520
  try {
522
521
  const avatar = await i.avatar()
523
522
  if (avatar._mediaType && avatar._name) {
524
- console.log(avatar)
525
523
  const base64 = member.weixin ? member.avatar : await avatar.toDataURL()
526
524
  let obj = {
527
525
  img: base64,
@@ -584,226 +582,6 @@ async function getRoomAvatar(roomObj, roomName, name) {
584
582
  console.log('getRoomAvatar error', e)
585
583
  }
586
584
  }
587
-
588
- /**
589
- * 头像处理
590
- * @param {*}} list
591
- * @param {*} size
592
- */
593
- async function cropImg(list, size = 74) {
594
- try {
595
- const arr = []
596
- if (list.length >= 100) {
597
- console.log('群成员数量超过100,生成群合影速度可能比较慢,请耐心等待')
598
- }
599
- for (const i of list) {
600
- try {
601
- if (i.img) {
602
- const im = new MImage(i.img)
603
- im.compress({
604
- quality: 1,
605
- width: size,
606
- height: size,
607
- }).crop({
608
- radius: size,
609
- })
610
- const bas64 = await im.draw({ type: 'png' })
611
- arr.push({ img: bas64 })
612
- }
613
- } catch (error) {
614
- console.log('处理头像失败', error)
615
- continue
616
- }
617
- }
618
- return arr
619
- } catch (error) {
620
- console.log('cropImg error', error)
621
- }
622
- }
623
-
624
- /**
625
- *
626
- * @param {*} t 头像数组
627
- * @param {*} param1
628
- */
629
- function __beforePatternCircle(t, { size = 120, space = 24, circleSpace = 24, centerSizeMargin = 0 }) {
630
- let i, r, n, s, o, c, l, p, u, g, m, d, h, f, v, b, w, y, C, _, k, I, x, S, j, q, D, z, P
631
- for (u in ((i = size), (r = space), (n = circleSpace), (s = centerSizeMargin), (o = 1), (c = 0), (l = []), (p = r), t)) {
632
- if (u > 0 && ((g = i / 2), (m = (i + n) * o), (m += s), (d = (2 * Math.PI * m) / (2 * g + r)), (d = Math.floor(d)), u > 0 && c === d - 1 ? (o++, (c = 0), l.push(d)) : c++, (h = t.length - 1 + ''), u === h && ((f = c), (v = c * i), c < d))) {
633
- for (C in ((b = 0), (w = 0), (y = []), l)) {
634
- C < l.length &&
635
- ((_ = l[C] * r),
636
- _ >= i + r &&
637
- ((b += _),
638
- (w += l[C]),
639
- y.push({
640
- level: C,
641
- spaceUnit: _,
642
- })))
643
- }
644
- v <= b && ((k = w + f === 0 ? 0 : (b - v) / (w + f)), (p = k))
645
- }
646
- }
647
- for (j in ((I = 1), (x = 0), (S = 0), t)) j > 0 && ((q = i / 2), (D = (i + n) * I), (D += s), (z = (2 * Math.PI * D) / (2 * q + p)), (z = Math.floor(z)), t.length - j < z && ((P = t.length - 1 + ''), j === P && (S = 360 / (x + 1))), j > 0 && x === z - 1 ? (I++, (x = 0)) : x++)
648
- return {
649
- newSpace: p,
650
- newRate: S,
651
- lastCircleNumber: I,
652
- }
653
- }
654
-
655
- function patternCircle(mc, t, info, i) {
656
- let r, n, s, g, d, h, f, v, b, w, y, C, _, k, I, x, S, j, q
657
- r = i.x
658
- n = i.y
659
- const o = i.centerSize || i.size
660
- s = i.space
661
- const c = o > i.size ? (o - i.size) / 2 : 0
662
- const l = i.backWid
663
- const p = i.backHei
664
- const u = l / 2 // x中间坐标
665
- g = p / 2 // y中间坐标
666
- g = i.marginBottom ? p - i.marginBottom : g
667
- r = u - i.size / 2
668
- n = g - i.size / 2
669
- const m = s
670
- for (w in ((d = info), (s = d.newSpace), (h = d.newRate), (f = d.lastCircleNumber), (v = 1), (b = 0), t)) {
671
- w > 0 &&
672
- ((y = i.size / 2),
673
- (C = (i.size + m) * v),
674
- (C += c),
675
- (_ = (2 * Math.PI * C) / (2 * y + s)),
676
- (_ = Math.floor(_)),
677
- (k = 360 / _),
678
- f === v && (k = h),
679
- (I = ((2 * Math.PI) / 360) * k * b),
680
- (x = u + Math.sin(I + (2 * Math.PI * 270) / 360) * C),
681
- (S = g - Math.cos(I + (2 * Math.PI * 270) / 360) * C),
682
- (r = x - y),
683
- (n = S - y),
684
- w > 0 && b === _ - 1 ? (v++, (b = 0)) : b++),
685
- (j = i.size),
686
- parseInt(w) === 0 && ((j = o), (r = u - o / 2), (n = g - o / 2)),
687
- (q = t[w].img)
688
- mc.add(q, {
689
- width: j,
690
- pos: {
691
- x: r,
692
- y: n,
693
- scale: 1,
694
- },
695
- })
696
- }
697
- return mc
698
- }
699
-
700
- /**
701
- * 绘制标题
702
- * @param {*} mc
703
- * @param {*} title
704
- * @param {*} titleInfo
705
- */
706
-
707
- function drawTitle(mc, title, titleInfo) {
708
- mc.text(title, {
709
- align: 'center',
710
- width: '100%',
711
- normalStyle: {
712
- color: titleInfo.color,
713
- lineHeight: titleInfo.fontSize,
714
- font: `${titleInfo.fontSize}px Microsoft YaHei,sans-serif`,
715
- },
716
- pos: {
717
- x: 0,
718
- y: titleInfo.top,
719
- },
720
- })
721
- }
722
-
723
- async function generateRoomImg(list, options) {
724
- try {
725
- const { sizeInfo, titleInfo, background, roomName } = options
726
- console.log('群合影生成中...')
727
- list = await cropImg(list, sizeInfo.size)
728
- const initOptions = {
729
- title: titleInfo.title || roomName,
730
- centerSize: sizeInfo.centerSize,
731
- space: sizeInfo.space,
732
- circleSpace: sizeInfo.space,
733
- size: sizeInfo.size,
734
- x: 0,
735
- y: 0,
736
- backWid: sizeInfo.width,
737
- backHei: sizeInfo.height,
738
- marginBottom: sizeInfo.bottom, // 距离底部高度
739
- }
740
- const mc = new MCanvas({
741
- width: initOptions.backWid,
742
- height: initOptions.backHei,
743
- backgroundColor: '#ffffff',
744
- })
745
- mc.background(background, {
746
- type: 'contain',
747
- })
748
- const info = __beforePatternCircle(list, initOptions)
749
- patternCircle(mc, list, info, initOptions)
750
- drawTitle(mc, initOptions.title, titleInfo)
751
- const base64 = await mc.draw({ type: 'jpg', quality: 1 })
752
- console.log('群合影生成成功!')
753
- // var base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
754
- // var dataBuffer = Buffer.from(base64Data, 'base64')
755
- // fs.writeFile('image.jpg', dataBuffer, function (err) {
756
- // if (err) {
757
- // console.log(err)
758
- // } else {
759
- // console.log('保存成功!')
760
- // }
761
- // })
762
- return base64
763
- } catch (e) {
764
- console.log('群合影生成失败', e)
765
- }
766
- }
767
-
768
- async function generateAvatar(avatar, coverImg = 'http://image.xkboke.com/hat.png') {
769
- try {
770
- let mc = new MCanvas({
771
- width: 880,
772
- height: 880,
773
- backgroundColor: '#ffffff',
774
- })
775
- mc.add(avatar, {
776
- width: 860,
777
- pos: {
778
- x: 10,
779
- y: 10,
780
- scale: 1,
781
- },
782
- })
783
- mc.add(coverImg, {
784
- width: 880,
785
- pos: {
786
- x: 0,
787
- y: 0,
788
- scale: 1,
789
- },
790
- })
791
- const base64 = await mc.draw({ type: 'jpg', quality: 1 })
792
- // var base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
793
- // var dataBuffer = Buffer.from(base64Data, 'base64')
794
- // fs.writeFile('avatar.jpg', dataBuffer, function (err) {
795
- // if (err) {
796
- // console.log(err)
797
- // } else {
798
- // console.log('保存成功!')
799
- // }
800
- // })
801
- return base64
802
- } catch (e) {
803
- console.log('头像生成失败', e)
804
- }
805
- }
806
-
807
585
  module.exports = {
808
586
  Base64Encode,
809
587
  Base64Decode,
@@ -829,7 +607,5 @@ module.exports = {
829
607
  getAllSchedule,
830
608
  groupArray,
831
609
  getRoomAvatarList,
832
- generateRoomImg,
833
610
  getRoomAvatar,
834
- generateAvatar,
835
611
  }
@@ -0,0 +1,237 @@
1
+ /**
2
+ * 生成群合影
3
+ * @param list
4
+ * @param options
5
+ * @returns {Promise<unknown>}
6
+ */
7
+ async function generateRoomImg({ list, options }) {
8
+ /**
9
+ *
10
+ * @param {*} t 头像数组
11
+ * @param {*} param1
12
+ */
13
+ const __beforePatternCircle = function (t, { size = 120, space = 24, circleSpace = 24, centerSizeMargin = 0 }) {
14
+ let i, r, n, s, o, c, l, p, u, g, m, d, h, f, v, b, w, y, C, _, k, I, x, S, j, q, D, z, P
15
+ for (u in ((i = size), (r = space), (n = circleSpace), (s = centerSizeMargin), (o = 1), (c = 0), (l = []), (p = r), t)) {
16
+ if (u > 0 && ((g = i / 2), (m = (i + n) * o), (m += s), (d = (2 * Math.PI * m) / (2 * g + r)), (d = Math.floor(d)), u > 0 && c === d - 1 ? (o++, (c = 0), l.push(d)) : c++, (h = t.length - 1 + ''), u === h && ((f = c), (v = c * i), c < d))) {
17
+ for (C in ((b = 0), (w = 0), (y = []), l)) {
18
+ C < l.length &&
19
+ ((_ = l[C] * r),
20
+ _ >= i + r &&
21
+ ((b += _),
22
+ (w += l[C]),
23
+ y.push({
24
+ level: C,
25
+ spaceUnit: _,
26
+ })))
27
+ }
28
+ v <= b && ((k = w + f === 0 ? 0 : (b - v) / (w + f)), (p = k))
29
+ }
30
+ }
31
+ for (j in ((I = 1), (x = 0), (S = 0), t)) j > 0 && ((q = i / 2), (D = (i + n) * I), (D += s), (z = (2 * Math.PI * D) / (2 * q + p)), (z = Math.floor(z)), t.length - j < z && ((P = t.length - 1 + ''), j === P && (S = 360 / (x + 1))), j > 0 && x === z - 1 ? (I++, (x = 0)) : x++)
32
+ return {
33
+ newSpace: p,
34
+ newRate: S,
35
+ lastCircleNumber: I,
36
+ }
37
+ }
38
+
39
+ const patternCircle = function (mc, t, info, i) {
40
+ let r, n, s, g, d, h, f, v, b, w, y, C, _, k, I, x, S, j, q
41
+ r = i.x
42
+ n = i.y
43
+ const o = i.centerSize || i.size
44
+ s = i.space
45
+ const c = o > i.size ? (o - i.size) / 2 : 0
46
+ const l = i.backWid
47
+ const p = i.backHei
48
+ const u = l / 2 // x中间坐标
49
+ g = p / 2 // y中间坐标
50
+ g = i.marginBottom ? p - i.marginBottom : g
51
+ r = u - i.size / 2
52
+ n = g - i.size / 2
53
+ const m = s
54
+ for (w in ((d = info), (s = d.newSpace), (h = d.newRate), (f = d.lastCircleNumber), (v = 1), (b = 0), t)) {
55
+ w > 0 &&
56
+ ((y = i.size / 2),
57
+ (C = (i.size + m) * v),
58
+ (C += c),
59
+ (_ = (2 * Math.PI * C) / (2 * y + s)),
60
+ (_ = Math.floor(_)),
61
+ (k = 360 / _),
62
+ f === v && (k = h),
63
+ (I = ((2 * Math.PI) / 360) * k * b),
64
+ (x = u + Math.sin(I + (2 * Math.PI * 270) / 360) * C),
65
+ (S = g - Math.cos(I + (2 * Math.PI * 270) / 360) * C),
66
+ (r = x - y),
67
+ (n = S - y),
68
+ w > 0 && b === _ - 1 ? (v++, (b = 0)) : b++),
69
+ (j = i.size),
70
+ parseInt(w) === 0 && ((j = o), (r = u - o / 2), (n = g - o / 2)),
71
+ (q = t[w].img)
72
+ mc.add(q, {
73
+ width: j,
74
+ pos: {
75
+ x: r,
76
+ y: n,
77
+ scale: 1,
78
+ },
79
+ })
80
+ }
81
+ return mc
82
+ }
83
+
84
+ /**
85
+ * 绘制标题
86
+ * @param {*} mc
87
+ * @param {*} title
88
+ * @param {*} titleInfo
89
+ */
90
+
91
+ const drawTitle = function (mc, title, titleInfo) {
92
+ mc.text(title, {
93
+ align: 'center',
94
+ width: '100%',
95
+ normalStyle: {
96
+ color: titleInfo.color,
97
+ lineHeight: titleInfo.fontSize,
98
+ font: `${titleInfo.fontSize}px Microsoft YaHei,sans-serif`,
99
+ },
100
+ pos: {
101
+ x: 0,
102
+ y: titleInfo.top,
103
+ },
104
+ })
105
+ }
106
+ /**
107
+ * 头像处理
108
+ * @param {*}} list
109
+ * @param {*} size
110
+ */
111
+ const cropImg = async function (list, size = 74, MImage) {
112
+ try {
113
+ const arr = []
114
+ if (list.length >= 100) {
115
+ console.log('群成员数量超过100,生成群合影速度可能比较慢,请耐心等待')
116
+ }
117
+ for (const i of list) {
118
+ try {
119
+ if (i.img) {
120
+ const im = new MImage(i.img)
121
+ im.compress({
122
+ quality: 1,
123
+ width: size,
124
+ height: size,
125
+ }).crop({
126
+ radius: size,
127
+ })
128
+ const bas64 = await im.draw({ type: 'png' })
129
+ arr.push({ img: bas64 })
130
+ }
131
+ } catch (error) {
132
+ console.log('处理头像失败', error)
133
+ continue
134
+ }
135
+ }
136
+ return arr
137
+ } catch (error) {
138
+ console.log('cropImg error', error)
139
+ }
140
+ }
141
+
142
+ return new Promise(async (resolve, reject) => {
143
+ let MCanvas = window.MCanvas.MCanvas
144
+ let MImage = window.MCanvas.MImage
145
+ try {
146
+ const { sizeInfo, titleInfo, background, roomName } = options
147
+ list = await cropImg(list, sizeInfo.size, MImage)
148
+ const initOptions = {
149
+ title: titleInfo.title || roomName,
150
+ centerSize: sizeInfo.centerSize,
151
+ space: sizeInfo.space,
152
+ circleSpace: sizeInfo.space,
153
+ size: sizeInfo.size,
154
+ x: 0,
155
+ y: 0,
156
+ backWid: sizeInfo.width,
157
+ backHei: sizeInfo.height,
158
+ marginBottom: sizeInfo.bottom, // 距离底部高度
159
+ }
160
+ const mc = new MCanvas({
161
+ width: initOptions.backWid,
162
+ height: initOptions.backHei,
163
+ backgroundColor: '#ffffff',
164
+ })
165
+ mc.background(background, {
166
+ type: 'contain',
167
+ })
168
+ const info = __beforePatternCircle(list, initOptions)
169
+ patternCircle(mc, list, info, initOptions)
170
+ drawTitle(mc, initOptions.title, titleInfo)
171
+ const base64 = await mc.draw({ type: 'jpg', quality: 1 })
172
+ console.log('群合影生成成功!')
173
+ // var base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
174
+ // var dataBuffer = Buffer.from(base64Data, 'base64')
175
+ // fs.writeFile('image.jpg', dataBuffer, function (err) {
176
+ // if (err) {
177
+ // console.log(err)
178
+ // } else {
179
+ // console.log('保存成功!')
180
+ // }
181
+ // })
182
+ resolve(base64)
183
+ } catch (e) {
184
+ console.log('群合影生成失败', e)
185
+ reject(`群合影生成失败:${e}`)
186
+ }
187
+ })
188
+ }
189
+
190
+ async function generateAvatar({ avatar, coverImg }) {
191
+ coverImg = coverImg || 'http://image.xkboke.com/hat.png'
192
+ return new Promise(async (resolve, reject) => {
193
+ try {
194
+ let MCanvas = window.MCanvas.MCanvas
195
+ let mc = new MCanvas({
196
+ width: 880,
197
+ height: 880,
198
+ backgroundColor: '#ffffff',
199
+ })
200
+ mc.add(avatar, {
201
+ width: 860,
202
+ pos: {
203
+ x: 10,
204
+ y: 10,
205
+ scale: 1,
206
+ },
207
+ })
208
+ mc.add(coverImg, {
209
+ width: 880,
210
+ pos: {
211
+ x: 0,
212
+ y: 0,
213
+ scale: 1,
214
+ },
215
+ })
216
+ const base64 = await mc.draw({ type: 'jpg', quality: 1 })
217
+ // var base64Data = base64.replace(/^data:image\/\w+;base64,/, '')
218
+ // var dataBuffer = Buffer.from(base64Data, 'base64')
219
+ // fs.writeFile('avatar.jpg', dataBuffer, function (err) {
220
+ // if (err) {
221
+ // console.log(err)
222
+ // } else {
223
+ // console.log('保存成功!')
224
+ // }
225
+ // })
226
+ resolve(base64)
227
+ } catch (e) {
228
+ console.log('头像生成失败', e)
229
+ reject(`头像生成失败:${e}`)
230
+ }
231
+ })
232
+ }
233
+
234
+ module.exports = {
235
+ generateAvatar,
236
+ generateRoomImg,
237
+ }
@@ -0,0 +1,37 @@
1
+ const puppeteer = require('puppeteer')
2
+ const mainConfig = require('./mainConfig')
3
+ class BrowserManage {
4
+ browserDestructionTimeout //清理浏览器实例
5
+ browserInstance //浏览器实例
6
+ browserState = 'closed' //浏览器状态
7
+ /**
8
+ * 用于长时间未进行操作时关闭浏览器实例
9
+ */
10
+ scheduleBrowserForDestruction() {
11
+ clearTimeout(this.browserDestructionTimeout)
12
+ this.browserDestructionTimeout = setTimeout(async () => {
13
+ if (this.browserInstance) {
14
+ this.browserState = 'closed'
15
+ await this.browserInstance.close() //关闭浏览器实例
16
+ }
17
+ }, 5000)
18
+ }
19
+ /**
20
+ * 用于长时间未进行操作时关闭浏览器实例
21
+ */
22
+ async getBrowser() {
23
+ return new Promise(async (resolve, reject) => {
24
+ if (this.browserState === 'closed') {
25
+ this.browserInstance = await puppeteer.launch(mainConfig.config.puppeteer) //开启浏览器实例
26
+ this.browserState = 'open'
27
+ resolve(this.browserInstance)
28
+ }
29
+ if (this.browserState === 'open') {
30
+ if (this.browserInstance) {
31
+ resolve(this.browserInstance)
32
+ }
33
+ }
34
+ })
35
+ }
36
+ }
37
+ module.exports = new BrowserManage()
@@ -0,0 +1,38 @@
1
+ const browserManage = require('./index')
2
+ const drawHelpers = require('./drawHelp')
3
+
4
+ const generateRoomImg = async function (passedOptions) {
5
+ const browser = await browserManage.getBrowser()
6
+ const page = (await browser.pages())[0]
7
+ // 添加 mcanvas cdn地址
8
+ let filePath = 'http://image.xkboke.com/pubilc/cdn/mcanvas.js'
9
+ await page.addScriptTag({
10
+ url: filePath,
11
+ })
12
+ page.on('console', (msg) => console.log(msg.type(), msg.text())) //监听页面console事件
13
+ const base64 = await page.evaluate(drawHelpers.generateRoomImg, passedOptions)
14
+ browserManage.scheduleBrowserForDestruction()
15
+ // await FileBox.fromBase64(base64).toFile('./test.png')
16
+ // const buffer = Buffer.from(base64, 'base64')
17
+ return base64 //返回Base64 编码的图片
18
+ }
19
+
20
+ const generateAvatar = async function (passedOptions) {
21
+ const browser = await browserManage.getBrowser()
22
+ const page = (await browser.pages())[0]
23
+ // 添加 mcanvas cdn地址
24
+ let filePath = 'http://image.xkboke.com/pubilc/cdn/mcanvas.js'
25
+ await page.addScriptTag({
26
+ url: filePath,
27
+ })
28
+ page.on('console', (msg) => console.log(msg.type(), msg.text())) //监听页面console事件
29
+ const base64 = await page.evaluate(drawHelpers.generateAvatar, passedOptions)
30
+ browserManage.scheduleBrowserForDestruction()
31
+ // await FileBox.fromBase64(base64).toFile('./test.png')
32
+ return base64 //返回Base64 编码的图片
33
+ }
34
+
35
+ module.exports = {
36
+ generateAvatar,
37
+ generateRoomImg,
38
+ }
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ config: {
3
+ puppeteer: {
4
+ headless: true,
5
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
6
+ },
7
+ },
8
+ }
@@ -1,6 +1,7 @@
1
1
  const api = require('../proxy/api')
2
- const { getConfig, asyncData, getRoomPhotoConfig, drawRoomPhoto, getMeiNv } = require('../proxy/aibotk')
3
- const { getConstellation, msgArr, getAllSchedule, generateRoomImg, getRoomAvatar, generateAvatar } = require('../lib')
2
+ const { getConfig, getRoomPhotoConfig, getMeiNv } = require('../proxy/aibotk')
3
+ const { getConstellation, msgArr, getAllSchedule, getRoomAvatar } = require('../lib')
4
+ const { generateAvatar, generateRoomImg } = require('../puppeteer-paint/lanuch')
4
5
  const { initTaskLocalSchedule } = require('../task/index')
5
6
  const { updateContactAndRoom, updateContactOnly, updateRoomOnly } = require('../common/index')
6
7
  const { chatTencent } = require('../proxy/tencent')
@@ -51,7 +52,7 @@ async function dispatchEventContent(that, eName, msg, name, id, avatar, room) {
51
52
  if (avatar.mimeType) {
52
53
  // 如果图片类型正确再进行头像处理
53
54
  let base64Text = await avatar.toDataURL()
54
- url = await generateAvatar(base64Text)
55
+ url = await generateAvatar({ avatar: base64Text })
55
56
  type = 3
56
57
  } else {
57
58
  content = '你的头像属于高维世界产物,小助手能力不足,无法解析,待我修炼后为你提供服务'
@@ -80,7 +81,7 @@ async function dispatchEventContent(that, eName, msg, name, id, avatar, room) {
80
81
  } else if (config.authList.length) {
81
82
  if (config.authList.includes(name)) {
82
83
  memberList = await getRoomAvatar(room, roomName, name)
83
- const baseImg = await generateRoomImg(memberList, config)
84
+ const baseImg = await generateRoomImg({ list: memberList, options: config })
84
85
  type = 3
85
86
  url = baseImg
86
87
  } else {
@@ -88,7 +89,7 @@ async function dispatchEventContent(that, eName, msg, name, id, avatar, room) {
88
89
  }
89
90
  } else {
90
91
  memberList = await getRoomAvatar(room, roomName, name)
91
- const baseImg = await generateRoomImg(memberList, config)
92
+ const baseImg = await generateRoomImg({ list: memberList, options: config })
92
93
  type = 3
93
94
  url = baseImg
94
95
  }
@@ -1,6 +1,7 @@
1
1
  const dispatch = require('./event-dispatch-service')
2
2
  const { setSchedule, updateSchedule } = require('../proxy/aibotk')
3
- const { contentDistinguish, setLocalSchedule, isRealDate, generateAvatar } = require('../lib')
3
+ const { contentDistinguish, setLocalSchedule, isRealDate } = require('../lib')
4
+ const { generateAvatar } = require('../puppeteer-paint/lanuch')
4
5
  const { addRoom } = require('../common/index')
5
6
 
6
7
  function emptyMsg() {
@@ -1,9 +1,6 @@
1
- const { log } = require('wechaty')
2
- const { Message } = require('wechaty').types
3
1
  const Mustache = require('mustache')
4
2
 
5
3
  function roomTalker(options) {
6
- log.verbose('WechatyPluginContrib', 'roomTalker(%s)', JSON.stringify(options))
7
4
  if (!options) {
8
5
  return () => undefined
9
6
  }
@@ -12,7 +9,6 @@ function roomTalker(options) {
12
9
  }
13
10
  const optionList = options
14
11
  return async function talkRoom(room, contact, mustacheView) {
15
- log.verbose('WechatyPluginContrib', 'roomTalker() talkRoom(%s, %s, %s)', room, contact || '', mustacheView ? JSON.stringify(mustacheView) : '')
16
12
  for (const option of optionList) {
17
13
  let msg
18
14
  if (option instanceof Function) {
@@ -47,18 +43,13 @@ function roomTalker(options) {
47
43
  }
48
44
 
49
45
  function messageMapper(mapperOptions, one) {
50
- log.verbose('WechatyPluginContrib', 'messageMapper(%s)', typeof mapperOptions === 'function' ? 'function' : JSON.stringify(mapperOptions))
51
-
52
46
  return async function mapMessage(message) {
53
- log.verbose('WechatyPluginContrib', 'mapMessage(%s)', message)
54
-
55
47
  return normalizeMappedMessageList(mapperOptions, message, one)
56
48
  }
57
49
  }
58
50
 
59
51
  async function normalizeMappedMessageList(options, message, one) {
60
52
  try {
61
- log.verbose('WechatyPluginContrib', 'normalizeMappedMessageList(%s, %s)', JSON.stringify(options), message)
62
53
  const msgList = []
63
54
  let optionList
64
55
  if (Array.isArray(options)) {
@@ -88,8 +79,6 @@ async function normalizeMappedMessageList(options, message, one) {
88
79
  }
89
80
 
90
81
  function messageMatcher(matcherOptions) {
91
- log.verbose('WechatyPluginContrib', 'messageMatcher(%s)', JSON.stringify(matcherOptions))
92
-
93
82
  if (!matcherOptions) {
94
83
  return () => Promise.resolve(false)
95
84
  }
@@ -102,7 +91,6 @@ function messageMatcher(matcherOptions) {
102
91
 
103
92
  return async function matchMessage(message) {
104
93
  try {
105
- log.verbose('WechatyPluginContrib', 'messageMatcher() matchMessage(%s)', message)
106
94
  const room = message.room()
107
95
  const roomTopic = await room.topic()
108
96
  const talker = message.talker()
@@ -185,7 +173,7 @@ const bidirectionalMapper = async (message) => {
185
173
  try {
186
174
  const abbrRoomTopicForDevelopersHome = abbrRoomTopicByRegex(/\s*([^\s]*\s*[^\s]+)$/)
187
175
  // Drop all messages if not Text
188
- if (message.type() !== Message.Text) {
176
+ if (message.type() !== 7) {
189
177
  return
190
178
  }
191
179
 
@@ -219,7 +207,7 @@ const unidirectionalMapper = async (message, one) => {
219
207
  const room = message.room()
220
208
  const topic = await room.topic()
221
209
  switch (message.type()) {
222
- case Message.Text:
210
+ case 7:
223
211
  messageList.push(`${prefix}: ${message.text()}`)
224
212
  break
225
213
 
@@ -245,14 +233,11 @@ const unidirectionalMapper = async (message, one) => {
245
233
  }
246
234
 
247
235
  const isMatchConfig = (config) => {
248
- log.verbose('WechatyPluginContrib', 'ManyToManyRoomConnector() isMatchConfig(%s)', JSON.stringify(config))
249
-
250
236
  const matchWhitelist = messageMatcher(config.whitelist)
251
237
  const matchBlacklist = messageMatcher(config.blacklist)
252
238
 
253
239
  return async function isMatch(message) {
254
240
  try {
255
- log.verbose('WechatyPluginContrib', 'ManyToManyRoomConnector() isMatchConfig() isMatch(%s)', message.toString())
256
241
  if (message.self()) {
257
242
  return
258
243
  }
@@ -292,7 +277,6 @@ const isMatchConfig = (config) => {
292
277
  */
293
278
  async function manyToMany(that, config, msg) {
294
279
  try {
295
- log.verbose('WechatyPluginContrib', 'ManyToManyRoomConnector(%s)', JSON.stringify(config))
296
280
  const isMatch = isMatchConfig(config)
297
281
  const mapMessage = messageMapper(config.map)
298
282
  const matchAndForward = async (message, roomList) => {
@@ -339,7 +323,6 @@ async function manyToMany(that, config, msg) {
339
323
 
340
324
  async function manyToOne(that, config, msg) {
341
325
  try {
342
- log.verbose('WechatyPluginContrib', 'ManyToOneRoomConnector(%s)', JSON.stringify(config))
343
326
  const isMatch = isMatchConfig(config)
344
327
  const mapMessage = messageMapper(config.map)
345
328
  const matchAndForward = async (message, room) => {
@@ -352,7 +335,7 @@ async function manyToOne(that, config, msg) {
352
335
  const talkRoom = roomTalker(msgList)
353
336
  await talkRoom(room)
354
337
  } catch (e) {
355
- log.error('WechatyPluginContrib', 'ManyToOneRoomConnector() filterThenToManyRoom(%s, %s) rejection: %s', message, room, e)
338
+ console.log('WechatyPluginContrib, ManyToOneRoomConnector() filterThenToManyRoom(%s, %s) rejection: %s')
356
339
  }
357
340
  }
358
341
  let oneRoom = ''
@@ -374,7 +357,6 @@ async function manyToOne(that, config, msg) {
374
357
  */
375
358
  async function oneToMany(that, config, msg) {
376
359
  try {
377
- log.verbose('WechatyPluginContrib', 'OneToManyRoomConnector(%s)', JSON.stringify(config))
378
360
  const isMatch = isMatchConfig(config)
379
361
  const mapMessage = messageMapper(config.map, config.one)
380
362
  const matchAndForward = async (message, roomList) => {
@@ -393,7 +375,7 @@ async function oneToMany(that, config, msg) {
393
375
  }
394
376
  }
395
377
  } catch (e) {
396
- log.error('WechatyPluginContrib', 'OneToManyRoomConnector() filterThenToManyRoom(%s, %s) rejection: %s', message, roomList.join(','), e)
378
+ console.log('WechatyPluginContrib', 'OneToManyRoomConnector() filterThenToManyRoom(%s, %s) rejection: %s')
397
379
  }
398
380
  }
399
381
  let manyRoomList = []
@@ -422,7 +404,7 @@ async function dispatchAsync(that, msg, list) {
422
404
  const type = msg.type()
423
405
  const content = msg.text()
424
406
  const mentionSelf = content.includes(`@${userSelfName}`)
425
- if (that.Message.Text === type && mentionSelf) {
407
+ if (7 === type && mentionSelf) {
426
408
  // 如果内容中有提及机器人的内容,不进行转发
427
409
  return
428
410
  }
@@ -431,7 +413,7 @@ async function dispatchAsync(that, msg, list) {
431
413
  config.blacklist = [async () => true]
432
414
  if (config.forward === 1) {
433
415
  config.map = bidirectionalMapper
434
- config.whitelist = [async (message) => message.type() === Message.Text]
416
+ config.whitelist = [async (message) => message.type() === 7]
435
417
  } else if (config.forward === 2) {
436
418
  config.blacklist = []
437
419
  config.map = unidirectionalMapper