koishi-plugin-onebot-info-image 0.2.0-alpha.9 → 0.3.1-beta.1

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.
package/lib/index.js CHANGED
@@ -1,6 +1,8 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
6
8
  var __export = (target, all) => {
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
@@ -34,11 +44,15 @@ var import_path2 = require("path");
34
44
  // src/type.ts
35
45
  var IMAGE_STYLES = {
36
46
  SOURCE_HAN_SERIF_SC: "思源宋体SourceHanSerifSC",
37
- LXGW_WENKAI: "落霞孤鹜文楷LXGWWenKai"
47
+ LXGW_WENKAI: "落霞孤鹜文楷LXGWWenKai",
48
+ FLAT_MINIMAL: "扁平化简约FlatMinimal"
38
49
  };
50
+ var IMAGE_STYLE_KEY_ARR = Object.keys(IMAGE_STYLES);
39
51
  var FONT_FILES = {
40
52
  [IMAGE_STYLES.SOURCE_HAN_SERIF_SC]: "SourceHanSerifSC-Medium.otf",
41
- [IMAGE_STYLES.LXGW_WENKAI]: "LXGWWenKaiMono-Regular.ttf"
53
+ [IMAGE_STYLES.LXGW_WENKAI]: "LXGWWenKaiMono-Regular.ttf",
54
+ [IMAGE_STYLES.FLAT_MINIMAL]: "LXGWWenKaiMono-Regular.ttf"
55
+ // 扁平化样式使用文楷字体
42
56
  };
43
57
  var IMAGE_TYPES = {
44
58
  PNG: "png",
@@ -71,8 +85,13 @@ function convertToUnifiedUserInfo(userInfo, onebotImplName) {
71
85
  unfriendly: userInfo.unfriendly || false,
72
86
  card_changeable: userInfo.card_changeable || false,
73
87
  // Lagrange 特有字段
74
- sign: userInfo.sign || "",
75
- q_id: userInfo.q_id || "",
88
+ sign: userInfo.sign || userInfo.longNick || userInfo.long_nick || "",
89
+ q_id: userInfo.q_id || userInfo.qid || "",
90
+ // 同时支持 q_id 和 qid 两种格式
91
+ qid: userInfo.qid || userInfo.q_id || "",
92
+ // 同时支持 qid 和 q_id 两种格式
93
+ longNick: userInfo.longNick || userInfo.long_nick || "",
94
+ long_nick: userInfo.long_nick || userInfo.longNick || "",
76
95
  RegisterTime: userInfo.RegisterTime || "",
77
96
  Business: userInfo.Business || [],
78
97
  status: userInfo.status || {},
@@ -348,7 +367,7 @@ var formatMsTimestamp = /* @__PURE__ */ __name((timestamp) => {
348
367
  const date = new Date(timestamp);
349
368
  return date.toLocaleString("zh-CN");
350
369
  }, "formatMsTimestamp");
351
- var getSourceHanSerifSCStyleUserInfoHtmlStr = /* @__PURE__ */ __name(async (userInfo, contextInfo, avatarBase64, groupAvatarBase64, fontBase64, enableDarkMode) => {
370
+ var getSourceHanSerifSCStyleUserInfoHtmlStr = /* @__PURE__ */ __name(async (userInfo, contextInfo, avatarBase64, groupAvatarBase64, fontBase64, enableDarkMode, hidePhoneNumber = true) => {
352
371
  const timestamp = generateTimestamp();
353
372
  const backgroundStyle = avatarBase64 ? `background-image: url(data:image/jpeg;base64,${avatarBase64});` : `background-color: #f0f2f5;`;
354
373
  const getValue = /* @__PURE__ */ __name((value, fallback = '<span class="unknown">未知</span>') => value && value !== "-" ? value : fallback, "getValue");
@@ -397,11 +416,11 @@ var getSourceHanSerifSCStyleUserInfoHtmlStr = /* @__PURE__ */ __name(async (user
397
416
  getInfoItem("性别", getSex(userInfo.sex)),
398
417
  getInfoItem("年龄", getValue(userInfo.age)),
399
418
  getInfoItem("QQ等级", getValue(userInfo.qq_level || userInfo.level)),
400
- getInfoItem("QID", getValue(userInfo.q_id)),
419
+ getInfoItem("QID", getValue(userInfo.q_id || userInfo.qid)),
401
420
  getInfoItem("注册时间", formatMsTimestamp(userInfo.RegisterTime)),
402
- getInfoItem("个性签名", getValue(userInfo.sign || userInfo.longNick), true),
421
+ getInfoItem("个性签名", getValue(userInfo.sign || userInfo.longNick || userInfo.long_nick), true),
403
422
  getInfoItem("邮箱", getValue(userInfo.eMail || userInfo.email)),
404
- getInfoItem("电话", getValue(userInfo.phoneNum || userInfo.phone)),
423
+ getInfoItem("电话", hidePhoneNumber ? '<span class="unknown">已隐藏</span>' : getValue(userInfo.phoneNum || userInfo.phone)),
405
424
  getInfoItem("地址信息", getLocationString(userInfo), true),
406
425
  `
407
426
  <div class="two-column-row">
@@ -528,7 +547,441 @@ var getSourceHanSerifSCStyleUserInfoHtmlStr = /* @__PURE__ */ __name(async (user
528
547
  </body>
529
548
  </html>`;
530
549
  }, "getSourceHanSerifSCStyleUserInfoHtmlStr");
531
- var getLXGWWenKaiUserInfoHtmlStr = /* @__PURE__ */ __name(async (userInfo, contextInfo, avatarBase64, groupAvatarBase64, fontBase64, enableDarkMode) => {
550
+ var getFlatMinimalUserInfoHtmlStr = /* @__PURE__ */ __name(async (userInfo, contextInfo, avatarBase64, groupAvatarBase64, fontBase64, enableDarkMode, hidePhoneNumber = true) => {
551
+ const isGroup = contextInfo.isGroup;
552
+ const isDarkMode = enableDarkMode;
553
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN", {
554
+ year: "numeric",
555
+ month: "2-digit",
556
+ day: "2-digit",
557
+ hour: "2-digit",
558
+ minute: "2-digit",
559
+ second: "2-digit",
560
+ hour12: false
561
+ });
562
+ const colors = isDarkMode ? {
563
+ // 黑色背景配色:亮蓝、亮绿、亮橙
564
+ background: "#0a0a0a",
565
+ cardBackground: "#1a1a1a",
566
+ textPrimary: "#ffffff",
567
+ textSecondary: "#b0b0b0",
568
+ primary: "#00d4ff",
569
+ // 亮蓝色
570
+ secondary: "#00ff88",
571
+ // 亮绿色
572
+ accent: "#ff8800",
573
+ // 亮橙色
574
+ border: "#333333",
575
+ hover: "#2a2a2a"
576
+ } : {
577
+ // 白色背景配色:黑、蓝、灰
578
+ background: "#f5f7fa",
579
+ cardBackground: "#ffffff",
580
+ textPrimary: "#2c3e50",
581
+ // 深蓝灰
582
+ textSecondary: "#6c757d",
583
+ // 中性灰
584
+ primary: "#007bff",
585
+ // 蓝色
586
+ secondary: "#34495e",
587
+ // 深灰蓝
588
+ accent: "#6c757d",
589
+ // 灰色
590
+ border: "#e0e6ed",
591
+ hover: "#f8f9fa"
592
+ };
593
+ const getShengXiao = /* @__PURE__ */ __name((num) => ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"][num] || "", "getShengXiao");
594
+ const getConstellation = /* @__PURE__ */ __name((num) => ["摩羯座", "水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座"][num - 1] || "", "getConstellation");
595
+ const getBloodType = /* @__PURE__ */ __name((num) => ["O", "A", "B", "AB"][num] || "", "getBloodType");
596
+ const formatAddress = /* @__PURE__ */ __name((user) => {
597
+ const parts = [user.country, user.province, user.city, user.postCode].filter((part) => part && part !== "0" && part !== "-");
598
+ let locationStr = parts.length > 0 ? parts.join("-") : "";
599
+ if (user.address && user.address !== locationStr) {
600
+ locationStr = locationStr ? `${locationStr} ${user.address}` : user.address;
601
+ }
602
+ return locationStr || '<span class="unknown">未知</span>';
603
+ }, "formatAddress");
604
+ const getGroupRole = /* @__PURE__ */ __name((role) => {
605
+ switch (role) {
606
+ case "owner":
607
+ return "群主";
608
+ case "admin":
609
+ return "管理员";
610
+ case "member":
611
+ return "成员";
612
+ default:
613
+ return '<span class="unknown">未知</span>';
614
+ }
615
+ }, "getGroupRole");
616
+ return `<!DOCTYPE html><html><head><style>
617
+ ${fontBase64 ? `@font-face{font-family:'CustomFont';src:url('data:font/truetype;charset=utf-8;base64,${fontBase64}') format('truetype');font-weight:400;font-style:normal;font-display:swap;}` : ""}
618
+ * { box-sizing: border-box; margin: 0; padding: 0; }
619
+ body {
620
+ font-family: ${fontBase64 ? "'CustomFont'," : ""} -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;
621
+ background: ${colors.background};
622
+ color: ${colors.textPrimary};
623
+ width: 999px;
624
+ height: 999px;
625
+ display: flex;
626
+ align-items: center;
627
+ justify-content: center;
628
+ padding: 16px;
629
+ overflow: hidden;
630
+ }
631
+ .main-container {
632
+ width: 100%;
633
+ max-width: 970px;
634
+ height: 100%;
635
+ display: flex;
636
+ flex-direction: column;
637
+ gap: 10px;
638
+ }
639
+ .header {
640
+ background: ${colors.cardBackground};
641
+ border: 2px solid ${colors.border};
642
+ border-radius: 18px;
643
+ padding: 14px 20px;
644
+ text-align: center;
645
+ box-shadow: 0 4px 16px rgba(0,0,0,${isDarkMode ? "0.4" : "0.08"});
646
+ }
647
+ .title {
648
+ font-size: 34px;
649
+ font-weight: 700;
650
+ color: ${colors.primary};
651
+ margin-bottom: 4px;
652
+ letter-spacing: 2px;
653
+ }
654
+ .subtitle {
655
+ font-size: 19px;
656
+ color: ${colors.textSecondary};
657
+ font-weight: 500;
658
+ }
659
+ .content {
660
+ display: flex;
661
+ gap: 10px;
662
+ flex: 1;
663
+ min-height: 0;
664
+ }
665
+ .left-panel {
666
+ flex: 0 0 270px;
667
+ display: flex;
668
+ flex-direction: column;
669
+ gap: 10px;
670
+ }
671
+ .avatar-card {
672
+ background: ${colors.cardBackground};
673
+ border: 2px solid ${colors.border};
674
+ border-radius: 18px;
675
+ padding: 16px 14px;
676
+ text-align: center;
677
+ box-shadow: 0 4px 16px rgba(0,0,0,${isDarkMode ? "0.4" : "0.08"});
678
+ flex: 1;
679
+ display: flex;
680
+ flex-direction: column;
681
+ justify-content: center;
682
+ align-items: center;
683
+ }
684
+ .avatar {
685
+ width: 130px;
686
+ height: 130px;
687
+ border-radius: 65px;
688
+ object-fit: cover;
689
+ border: 4px solid ${colors.primary};
690
+ margin-bottom: 14px;
691
+ display: block;
692
+ box-shadow: 0 8px 24px rgba(0,0,0,${isDarkMode ? "0.5" : "0.15"});
693
+ }
694
+ .avatar-placeholder {
695
+ width: 130px;
696
+ height: 130px;
697
+ border-radius: 65px;
698
+ background: linear-gradient(135deg, ${colors.primary}, ${colors.secondary});
699
+ margin-bottom: 14px;
700
+ }
701
+ .user-name {
702
+ font-size: 26px;
703
+ font-weight: 700;
704
+ color: ${colors.textPrimary};
705
+ margin-bottom: 8px;
706
+ letter-spacing: 1px;
707
+ }
708
+ .user-id {
709
+ font-size: 17px;
710
+ color: ${colors.textSecondary};
711
+ background: ${colors.primary}25;
712
+ padding: 6px 12px;
713
+ border-radius: 12px;
714
+ display: inline-block;
715
+ font-weight: 600;
716
+ border: 1px solid ${colors.primary}40;
717
+ }
718
+ .group-card {
719
+ background: ${colors.cardBackground};
720
+ border: 2px solid ${colors.border};
721
+ border-radius: 18px;
722
+ padding: 12px;
723
+ box-shadow: 0 4px 16px rgba(0,0,0,${isDarkMode ? "0.4" : "0.08"});
724
+ flex: 0.618;
725
+ }
726
+ .group-header {
727
+ display: flex;
728
+ align-items: center;
729
+ gap: 12px;
730
+ margin-bottom: 12px;
731
+ padding-bottom: 10px;
732
+ border-bottom: 1px solid ${colors.border};
733
+ }
734
+ .group-avatar {
735
+ width: 58px;
736
+ height: 58px;
737
+ border-radius: 14px;
738
+ object-fit: cover;
739
+ border: 3px solid ${colors.secondary};
740
+ box-shadow: 0 4px 12px rgba(0,0,0,${isDarkMode ? "0.4" : "0.1"});
741
+ }
742
+ .group-info {
743
+ flex: 1;
744
+ }
745
+ .group-name {
746
+ font-size: 18px;
747
+ font-weight: 700;
748
+ color: ${colors.textPrimary};
749
+ margin-bottom: 3px;
750
+ }
751
+ .group-id {
752
+ font-size: 14px;
753
+ color: ${colors.textSecondary};
754
+ font-weight: 500;
755
+ }
756
+ .group-member-count {
757
+ font-size: 13px;
758
+ color: ${colors.accent};
759
+ margin-top: 3px;
760
+ font-weight: 600;
761
+ }
762
+ .group-details {
763
+ display: grid;
764
+ grid-template-columns: 1fr 1fr;
765
+ gap: 6px;
766
+ }
767
+ .group-detail-item {
768
+ text-align: center;
769
+ background: ${colors.background};
770
+ padding: 6px 4px;
771
+ border-radius: 10px;
772
+ border: 1px solid ${colors.border};
773
+ }
774
+ .group-detail-label {
775
+ font-size: 11px;
776
+ color: ${colors.textSecondary};
777
+ margin-bottom: 2px;
778
+ font-weight: 500;
779
+ }
780
+ .group-detail-value {
781
+ font-size: 14px;
782
+ color: ${colors.textPrimary};
783
+ font-weight: 600;
784
+ word-break: break-all;
785
+ }
786
+ .right-panel {
787
+ flex: 1;
788
+ display: flex;
789
+ flex-direction: column;
790
+ }
791
+ .info-card {
792
+ background: ${colors.cardBackground};
793
+ border: 2px solid ${colors.border};
794
+ border-radius: 18px;
795
+ padding: 16px;
796
+ flex: 1;
797
+ box-shadow: 0 4px 16px rgba(0,0,0,${isDarkMode ? "0.4" : "0.08"});
798
+ overflow-y: auto;
799
+ display: flex;
800
+ flex-direction: column;
801
+ }
802
+ .info-grid {
803
+ display: grid;
804
+ grid-template-columns: repeat(2, 1fr);
805
+ gap: 10px;
806
+ flex: 1;
807
+ align-content: start;
808
+ }
809
+ .info-item {
810
+ background: ${colors.background};
811
+ border: 1px solid ${colors.border};
812
+ border-radius: 10px;
813
+ padding: 10px 12px;
814
+ transition: all 0.25s ease;
815
+ display: flex;
816
+ flex-direction: column;
817
+ justify-content: center;
818
+ min-height: 52px;
819
+ }
820
+ .info-item:hover {
821
+ background: ${colors.hover};
822
+ transform: translateY(-2px);
823
+ box-shadow: 0 4px 16px rgba(0,0,0,${isDarkMode ? "0.5" : "0.12"});
824
+ border-color: ${colors.primary}50;
825
+ }
826
+ .info-item.full-width {
827
+ grid-column: 1 / -1;
828
+ }
829
+ .info-label {
830
+ font-size: 13px;
831
+ color: ${colors.textSecondary};
832
+ font-weight: 600;
833
+ margin-bottom: 4px;
834
+ text-transform: uppercase;
835
+ letter-spacing: 0.5px;
836
+ }
837
+ .info-value {
838
+ font-size: 18px;
839
+ color: ${colors.textPrimary};
840
+ font-weight: 600;
841
+ line-height: 1.35;
842
+ word-break: break-all;
843
+ }
844
+ .unknown {
845
+ color: ${colors.textSecondary};
846
+ font-style: italic;
847
+ opacity: 0.7;
848
+ }
849
+ .timestamp {
850
+ position: fixed;
851
+ top: 12px;
852
+ left: 12px;
853
+ font-size: 13px;
854
+ color: ${colors.textSecondary};
855
+ opacity: 0.5;
856
+ font-family: 'Courier New', monospace;
857
+ z-index: 1000;
858
+ }
859
+ .primary-accent { color: ${colors.primary}; }
860
+ .secondary-accent { color: ${colors.secondary}; }
861
+ .accent-color { color: ${colors.accent}; }
862
+ </style></head><body>
863
+ <div class="main-container">
864
+ <div class="header">
865
+ <div class="title">${isGroup ? "群员信息" : "用户信息"}</div>
866
+ <div class="subtitle">详细资料</div>
867
+ </div>
868
+
869
+ <div class="content">
870
+ <div class="left-panel">
871
+ <div class="avatar-card">
872
+ ${avatarBase64 ? `<img class="avatar" src="data:image/jpeg;base64,${avatarBase64}" alt="用户头像">` : '<div class="avatar-placeholder"></div>'}
873
+ <div class="user-name">${userInfo.nickname || "未知昵称"}</div>
874
+ <div class="user-id">QQ: ${userInfo.user_id}</div>
875
+ </div>
876
+
877
+ ${isGroup ? `
878
+ <div class="group-card">
879
+ <div class="group-header">
880
+ ${groupAvatarBase64 ? `<img class="group-avatar" src="data:image/jpeg;base64,${groupAvatarBase64}" alt="群头像">` : ""}
881
+ <div class="group-info">
882
+ <div class="group-name">${contextInfo.groupName || "未知群名"}</div>
883
+ <div class="group-id">群号: ${contextInfo.groupId}</div>
884
+ ${contextInfo.memberCount ? `<div class="group-member-count">成员: ${contextInfo.memberCount}${contextInfo.maxMemberCount ? `/${contextInfo.maxMemberCount}` : ""}</div>` : ""}
885
+ </div>
886
+ </div>
887
+ <div class="group-details">
888
+ <div class="group-detail-item">
889
+ <div class="group-detail-label">群名片</div>
890
+ <div class="group-detail-value">${userInfo.card || "未设置"}</div>
891
+ </div>
892
+ <div class="group-detail-item">
893
+ <div class="group-detail-label">群角色</div>
894
+ <div class="group-detail-value primary-accent">${getGroupRole(userInfo.role)}</div>
895
+ </div>
896
+ <div class="group-detail-item">
897
+ <div class="group-detail-label">群等级</div>
898
+ <div class="group-detail-value secondary-accent">${userInfo.group_level || "未知"}</div>
899
+ </div>
900
+ <div class="group-detail-item">
901
+ <div class="group-detail-label">专属头衔</div>
902
+ <div class="group-detail-value accent-color">${userInfo.title || "无"}</div>
903
+ </div>
904
+ <div class="group-detail-item">
905
+ <div class="group-detail-label">加群时间</div>
906
+ <div class="group-detail-value">${userInfo.join_time ? `${new Date(userInfo.join_time).toLocaleDateString("zh-CN")}<br>${new Date(userInfo.join_time).toLocaleTimeString("zh-CN")}` : '<span class="unknown">未知</span>'}</div>
907
+ </div>
908
+ <div class="group-detail-item">
909
+ <div class="group-detail-label">最后发言</div>
910
+ <div class="group-detail-value">${userInfo.last_sent_time ? `${new Date(userInfo.last_sent_time).toLocaleDateString("zh-CN")}<br>${new Date(userInfo.last_sent_time).toLocaleTimeString("zh-CN")}` : '<span class="unknown">未知</span>'}</div>
911
+ </div>
912
+ </div>
913
+ </div>
914
+ ` : ""}
915
+ </div>
916
+
917
+ <div class="right-panel">
918
+ <div class="info-card">
919
+ <div class="info-grid">
920
+ <div class="info-item">
921
+ <div class="info-label">性别</div>
922
+ <div class="info-value">${userInfo.sex === "male" ? "男" : userInfo.sex === "female" ? "女" : '<span class="unknown">未知</span>'}</div>
923
+ </div>
924
+ <div class="info-item">
925
+ <div class="info-label">年龄</div>
926
+ <div class="info-value">${userInfo.age || '<span class="unknown">未知</span>'}</div>
927
+ </div>
928
+ <div class="info-item">
929
+ <div class="info-label">QQ等级</div>
930
+ <div class="info-value primary-accent">${userInfo.qq_level || userInfo.level || '<span class="unknown">未知</span>'}</div>
931
+ </div>
932
+ <div class="info-item">
933
+ <div class="info-label">QID</div>
934
+ <div class="info-value">${userInfo.q_id || userInfo.qid || '<span class="unknown">未知</span>'}</div>
935
+ </div>
936
+ ${userInfo.sign || userInfo.longNick || userInfo.long_nick ? `
937
+ <div class="info-item full-width">
938
+ <div class="info-label">个性签名</div>
939
+ <div class="info-value">${userInfo.sign || userInfo.longNick || userInfo.long_nick}</div>
940
+ </div>
941
+ ` : ""}
942
+ ${userInfo.RegisterTime ? `
943
+ <div class="info-item full-width">
944
+ <div class="info-label">注册时间</div>
945
+ <div class="info-value">${new Date(userInfo.RegisterTime).toLocaleString("zh-CN")}</div>
946
+ </div>
947
+ ` : ""}
948
+ <div class="info-item full-width">
949
+ <div class="info-label">邮箱</div>
950
+ <div class="info-value">${(userInfo.eMail || userInfo.email) && userInfo.eMail !== "-" ? userInfo.eMail || userInfo.email : '<span class="unknown">未知</span>'}</div>
951
+ </div>
952
+ <div class="info-item full-width">
953
+ <div class="info-label">电话</div>
954
+ <div class="info-value">${hidePhoneNumber ? '<span class="unknown">已隐藏</span>' : userInfo.phoneNum && userInfo.phoneNum !== "-" ? userInfo.phoneNum : '<span class="unknown">未知</span>'}</div>
955
+ </div>
956
+ <div class="info-item full-width">
957
+ <div class="info-label">地址信息</div>
958
+ <div class="info-value">${formatAddress(userInfo)}</div>
959
+ </div>
960
+ <div class="info-item full-width">
961
+ <div class="info-label">个人特征</div>
962
+ <div class="info-value">生肖: ${getShengXiao(userInfo.shengXiao) || '<span class="unknown">未知</span>'} | 星座: ${getConstellation(userInfo.constellation) || '<span class="unknown">未知</span>'} | 血型: ${getBloodType(userInfo.kBloodType) ? `${getBloodType(userInfo.kBloodType)}型` : '<span class="unknown">未知</span>'}</div>
963
+ </div>
964
+ <div class="info-item full-width">
965
+ <div class="info-label">生日</div>
966
+ <div class="info-value">${userInfo.birthday_year && userInfo.birthday_month && userInfo.birthday_day ? `${userInfo.birthday_year}年${userInfo.birthday_month}月${userInfo.birthday_day}日` : '<span class="unknown">未知</span>'}</div>
967
+ </div>
968
+ <div class="info-item full-width">
969
+ <div class="info-label">VIP信息</div>
970
+ <div class="info-value">VIP: ${userInfo.is_vip ? "是" : "否"} | 年费VIP: ${userInfo.is_years_vip ? "是" : "否"} | VIP等级: ${userInfo.vip_level || 0}</div>
971
+ </div>
972
+ <div class="info-item full-width">
973
+ <div class="info-label">状态</div>
974
+ <div class="info-value">${userInfo.status && userInfo.status.message || '<span class="unknown">未知</span>'}</div>
975
+ </div>
976
+ </div>
977
+ </div>
978
+ </div>
979
+ </div>
980
+ </div>
981
+ <div class="timestamp">${timestamp}</div>
982
+ </body></html>`;
983
+ }, "getFlatMinimalUserInfoHtmlStr");
984
+ var getLXGWWenKaiUserInfoHtmlStr = /* @__PURE__ */ __name(async (userInfo, contextInfo, avatarBase64, groupAvatarBase64, fontBase64, enableDarkMode, hidePhoneNumber = true) => {
532
985
  const isGroup = contextInfo.isGroup;
533
986
  const isDarkMode = enableDarkMode;
534
987
  const timestamp = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN", {
@@ -650,11 +1103,11 @@ ${contextInfo.memberCount ? `<div class="group-member-count">群人数: ${contex
650
1103
  <div class="info-card"><div class="info-label">性别</div><div class="info-value">${userInfo.sex === "male" ? "男" : userInfo.sex === "female" ? "女" : "未知"}</div></div>
651
1104
  <div class="info-card"><div class="info-label">年龄</div><div class="info-value">${userInfo.age || '<span class="unknown">未知</span>'}</div></div>
652
1105
  <div class="info-card"><div class="info-label">QQ等级</div><div class="info-value">${userInfo.qq_level || userInfo.level || '<span class="unknown">未知</span>'}</div></div>
653
- <div class="info-card"><div class="info-label">QID</div><div class="info-value">${userInfo.q_id || '<span class="unknown">未知</span>'}</div></div>
654
- ${userInfo.sign || userInfo.longNick ? `<div class="info-card full-width"><div class="info-label">个性签名</div><div class="info-value">${userInfo.sign || userInfo.longNick}</div></div>` : ""}
1106
+ <div class="info-card"><div class="info-label">QID</div><div class="info-value">${userInfo.q_id || userInfo.qid || '<span class="unknown">未知</span>'}</div></div>
1107
+ ${userInfo.sign || userInfo.longNick || userInfo.long_nick ? `<div class="info-card full-width"><div class="info-label">个性签名</div><div class="info-value">${userInfo.sign || userInfo.longNick || userInfo.long_nick}</div></div>` : ""}
655
1108
  ${userInfo.RegisterTime ? `<div class="info-card full-width"><div class="info-label">注册时间</div><div class="info-value">${new Date(userInfo.RegisterTime).toLocaleString("zh-CN")}</div></div>` : ""}
656
1109
  <div class="info-card"><div class="info-label">邮箱</div><div class="info-value">${(userInfo.eMail || userInfo.email) && userInfo.eMail !== "-" ? userInfo.eMail || userInfo.email : '<span class="unknown">未知</span>'}</div></div>
657
- <div class="info-card"><div class="info-label">电话</div><div class="info-value">${userInfo.phoneNum && userInfo.phoneNum !== "-" ? userInfo.phoneNum : '<span class="unknown">未知</span>'}</div></div>
1110
+ <div class="info-card"><div class="info-label">电话</div><div class="info-value">${hidePhoneNumber ? '<span class="unknown">已隐藏</span>' : userInfo.phoneNum && userInfo.phoneNum !== "-" ? userInfo.phoneNum : '<span class="unknown">未知</span>'}</div></div>
658
1111
  <div class="info-card full-width"><div class="info-label">地址信息</div><div class="info-value">${formatAddress(userInfo)}</div></div>
659
1112
  <div class="info-card full-width"><div class="info-label">个人特征</div><div class="info-value multi-info-row">
660
1113
  <div class="multi-info-item"><div class="info-label">生肖</div><div class="info-value">${getShengXiao(userInfo.shengXiao) || '<span class="unknown">未知</span>'}</div></div>
@@ -675,7 +1128,7 @@ ${userInfo.RegisterTime ? `<div class="info-card full-width"><div class="info-la
675
1128
  <div class="timestamp-watermark">${timestamp}</div>
676
1129
  </body></html>`;
677
1130
  }, "getLXGWWenKaiUserInfoHtmlStr");
678
- async function renderUserInfo(ctx, userInfo, contextInfo, imageStyle, enableDarkMode, imageType, screenshotQuality) {
1131
+ async function renderUserInfo(ctx, userInfo, contextInfo, imageStyle, enableDarkMode, imageType, screenshotQuality, hidePhoneNumber = true) {
679
1132
  const browserPage = await ctx.puppeteer.page();
680
1133
  let avatarBase64;
681
1134
  let groupAvatarBase64;
@@ -699,9 +1152,11 @@ async function renderUserInfo(ctx, userInfo, contextInfo, imageStyle, enableDark
699
1152
  }
700
1153
  let htmlContent;
701
1154
  if (imageStyle === IMAGE_STYLES.SOURCE_HAN_SERIF_SC) {
702
- htmlContent = await getSourceHanSerifSCStyleUserInfoHtmlStr(userInfo, contextInfo, avatarBase64 || "", groupAvatarBase64 || "", fontBase64 || "", enableDarkMode);
1155
+ htmlContent = await getSourceHanSerifSCStyleUserInfoHtmlStr(userInfo, contextInfo, avatarBase64 || "", groupAvatarBase64 || "", fontBase64 || "", enableDarkMode, hidePhoneNumber);
703
1156
  } else if (imageStyle === IMAGE_STYLES.LXGW_WENKAI) {
704
- htmlContent = await getLXGWWenKaiUserInfoHtmlStr(userInfo, contextInfo, avatarBase64 || "", groupAvatarBase64 || "", fontBase64 || "", enableDarkMode);
1157
+ htmlContent = await getLXGWWenKaiUserInfoHtmlStr(userInfo, contextInfo, avatarBase64 || "", groupAvatarBase64 || "", fontBase64 || "", enableDarkMode, hidePhoneNumber);
1158
+ } else if (imageStyle === IMAGE_STYLES.FLAT_MINIMAL) {
1159
+ htmlContent = await getFlatMinimalUserInfoHtmlStr(userInfo, contextInfo, avatarBase64 || "", groupAvatarBase64 || "", fontBase64 || "", enableDarkMode, hidePhoneNumber);
705
1160
  }
706
1161
  await browserPage.setViewport({
707
1162
  width: 999,
@@ -714,8 +1169,8 @@ async function renderUserInfo(ctx, userInfo, contextInfo, imageStyle, enableDark
714
1169
  const images = Array.from(document.querySelectorAll("img"));
715
1170
  await Promise.all(images.map((img) => {
716
1171
  if (img.complete) return;
717
- return new Promise((resolve2, reject) => {
718
- img.addEventListener("load", resolve2);
1172
+ return new Promise((resolve3, reject) => {
1173
+ img.addEventListener("load", resolve3);
719
1174
  img.addEventListener("error", reject);
720
1175
  });
721
1176
  }));
@@ -1041,6 +1496,325 @@ var getLXGWWenKaiAdminListHtmlStr = /* @__PURE__ */ __name(async (admins, contex
1041
1496
  </body>
1042
1497
  </html>`;
1043
1498
  }, "getLXGWWenKaiAdminListHtmlStr");
1499
+ var getFlatMinimalAdminListHtmlStr = /* @__PURE__ */ __name(async (admins, contextInfo, groupAvatarBase64, fontBase64, enableDarkMode) => {
1500
+ const isDarkMode = enableDarkMode;
1501
+ const timestamp = generateTimestamp();
1502
+ const colors = isDarkMode ? {
1503
+ // 深色模式:亮蓝、灰色系
1504
+ background: "#000000",
1505
+ cardBackground: "#1a1a1a",
1506
+ textPrimary: "#ffffff",
1507
+ textSecondary: "#b0b0b0",
1508
+ primary: "#00d4ff",
1509
+ // 亮蓝色
1510
+ secondary: "#6c757d",
1511
+ // 灰色
1512
+ accent: "#00ff88",
1513
+ // 亮绿色
1514
+ border: "#333333",
1515
+ hover: "#2a2a2a",
1516
+ ownerBg: "rgba(255,140,0,0.2)",
1517
+ ownerText: "#ffa07a",
1518
+ adminBg: "rgba(0,212,255,0.2)",
1519
+ adminText: "#4da6ff"
1520
+ } : {
1521
+ // 浅色模式:蓝色、黑白灰
1522
+ background: "#f5f7fa",
1523
+ cardBackground: "#ffffff",
1524
+ textPrimary: "#2c3e50",
1525
+ textSecondary: "#6c757d",
1526
+ primary: "#007bff",
1527
+ // 蓝色
1528
+ secondary: "#34495e",
1529
+ // 深灰蓝
1530
+ accent: "#28a745",
1531
+ // 绿色
1532
+ border: "#dee2e6",
1533
+ hover: "#f8f9fa",
1534
+ ownerBg: "rgba(255,140,0,0.1)",
1535
+ ownerText: "#ff8c00",
1536
+ adminBg: "rgba(0,123,255,0.1)",
1537
+ adminText: "#007bff"
1538
+ };
1539
+ const flatAdminListItems = admins.map((admin, index) => `
1540
+ <div class="admin-item">
1541
+ <div class="admin-number">${(index + 1).toString().padStart(2, "0")}</div>
1542
+ <div class="admin-avatar-wrapper">
1543
+ <img src="${admin.avatar || `https://q1.qlogo.cn/g?b=qq&nk=${admin.user_id}&s=640`}" alt="头像" class="admin-avatar" />
1544
+ </div>
1545
+ <div class="admin-info">
1546
+ <div class="admin-name">${admin.nickname || "未知"}</div>
1547
+ <div class="admin-id">
1548
+ <span class="admin-id-label">QQ:</span>
1549
+ <span class="admin-id-value">${admin.user_id}</span>
1550
+ </div>
1551
+ ${admin.card ? `<div class="admin-card"><span class="admin-card-label">群昵称:</span>${admin.card}</div>` : '<div class="admin-card-empty">无群昵称</div>'}
1552
+ </div>
1553
+ <div class="admin-role ${admin.role}">${admin.role === "owner" ? "群 主" : "管理员"}</div>
1554
+ </div>
1555
+ `).join("");
1556
+ return `<!DOCTYPE html>
1557
+ <html>
1558
+ <head>
1559
+ <style>
1560
+ ${fontBase64 ? `@font-face { font-family: 'CustomFont'; src: url('data:font/opentype;charset=utf-8;base64,${fontBase64}') format('opentype'); font-weight: normal; font-style: normal; font-display: swap; }` : ""}
1561
+
1562
+ * { box-sizing: border-box; margin: 0; padding: 0; }
1563
+
1564
+ html, body { margin: 0; padding: 0; width: 100%; height: auto; }
1565
+
1566
+ body {
1567
+ font-family: ${fontBase64 ? "'CustomFont'," : ""} -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;
1568
+ width: 800px;
1569
+ min-height: 100vh;
1570
+ background: ${colors.background};
1571
+ color: ${colors.textPrimary};
1572
+ padding: 40px;
1573
+ display: flex;
1574
+ align-items: center;
1575
+ justify-content: center;
1576
+ }
1577
+
1578
+ .container {
1579
+ width: 100%;
1580
+ max-width: 720px;
1581
+ }
1582
+
1583
+ .header {
1584
+ background: ${colors.cardBackground};
1585
+ border: 2px solid ${colors.border};
1586
+ border-radius: 20px;
1587
+ padding: 32px;
1588
+ margin-bottom: 24px;
1589
+ box-shadow: 0 4px 16px rgba(0,0,0,${isDarkMode ? "0.3" : "0.08"});
1590
+ }
1591
+
1592
+ .group-info-wrapper {
1593
+ display: flex;
1594
+ align-items: center;
1595
+ gap: 24px;
1596
+ margin-bottom: 24px;
1597
+ }
1598
+
1599
+ .group-avatar {
1600
+ width: 90px;
1601
+ height: 90px;
1602
+ border-radius: 16px;
1603
+ object-fit: cover;
1604
+ border: 3px solid ${colors.primary};
1605
+ box-shadow: 0 4px 12px rgba(0,0,0,${isDarkMode ? "0.3" : "0.1"});
1606
+ }
1607
+
1608
+ .group-details {
1609
+ flex: 1;
1610
+ }
1611
+
1612
+ .group-name {
1613
+ font-size: 26px;
1614
+ font-weight: 700;
1615
+ color: ${colors.textPrimary};
1616
+ margin-bottom: 8px;
1617
+ }
1618
+
1619
+ .group-meta {
1620
+ font-size: 16px;
1621
+ color: ${colors.textSecondary};
1622
+ line-height: 1.6;
1623
+ }
1624
+
1625
+ .group-meta-item {
1626
+ display: inline-block;
1627
+ margin-right: 16px;
1628
+ }
1629
+
1630
+ .group-meta-label {
1631
+ color: ${colors.textSecondary};
1632
+ }
1633
+
1634
+ .group-meta-value {
1635
+ color: ${colors.primary};
1636
+ font-weight: 600;
1637
+ }
1638
+
1639
+ .title {
1640
+ font-size: 32px;
1641
+ font-weight: 700;
1642
+ color: ${colors.primary};
1643
+ text-align: center;
1644
+ padding-bottom: 16px;
1645
+ border-bottom: 2px solid ${colors.border};
1646
+ }
1647
+
1648
+ .title-count {
1649
+ color: ${colors.accent};
1650
+ font-size: 24px;
1651
+ }
1652
+
1653
+ .admin-list {
1654
+ display: flex;
1655
+ flex-direction: column;
1656
+ gap: 16px;
1657
+ }
1658
+
1659
+ .admin-item {
1660
+ background: ${colors.cardBackground};
1661
+ border: 2px solid ${colors.border};
1662
+ border-radius: 16px;
1663
+ padding: 20px;
1664
+ display: flex;
1665
+ align-items: center;
1666
+ gap: 20px;
1667
+ transition: all 0.3s ease;
1668
+ box-shadow: 0 2px 8px rgba(0,0,0,${isDarkMode ? "0.2" : "0.05"});
1669
+ }
1670
+
1671
+ .admin-item:hover {
1672
+ transform: translateY(-2px);
1673
+ box-shadow: 0 6px 20px rgba(0,0,0,${isDarkMode ? "0.4" : "0.12"});
1674
+ border-color: ${colors.primary};
1675
+ }
1676
+
1677
+ .admin-number {
1678
+ font-size: 32px;
1679
+ font-weight: 700;
1680
+ color: ${colors.textSecondary};
1681
+ font-family: 'Courier New', monospace;
1682
+ min-width: 50px;
1683
+ text-align: center;
1684
+ }
1685
+
1686
+ .admin-avatar-wrapper {
1687
+ position: relative;
1688
+ }
1689
+
1690
+ .admin-avatar {
1691
+ width: 80px;
1692
+ height: 80px;
1693
+ border-radius: 50%;
1694
+ object-fit: cover;
1695
+ border: 3px solid ${colors.border};
1696
+ transition: border-color 0.3s ease;
1697
+ }
1698
+
1699
+ .admin-item:hover .admin-avatar {
1700
+ border-color: ${colors.primary};
1701
+ }
1702
+
1703
+ .admin-info {
1704
+ flex: 1;
1705
+ display: flex;
1706
+ flex-direction: column;
1707
+ gap: 8px;
1708
+ }
1709
+
1710
+ .admin-name {
1711
+ font-size: 20px;
1712
+ font-weight: 600;
1713
+ color: ${colors.textPrimary};
1714
+ }
1715
+
1716
+ .admin-id {
1717
+ font-size: 14px;
1718
+ color: ${colors.textSecondary};
1719
+ font-family: 'Courier New', monospace;
1720
+ }
1721
+
1722
+ .admin-id-label {
1723
+ color: ${colors.textSecondary};
1724
+ font-weight: 500;
1725
+ }
1726
+
1727
+ .admin-id-value {
1728
+ color: ${colors.primary};
1729
+ font-weight: 600;
1730
+ }
1731
+
1732
+ .admin-card {
1733
+ font-size: 15px;
1734
+ color: ${colors.textPrimary};
1735
+ padding: 6px 12px;
1736
+ background: ${colors.hover};
1737
+ border-radius: 8px;
1738
+ display: inline-block;
1739
+ }
1740
+
1741
+ .admin-card-label {
1742
+ color: ${colors.textSecondary};
1743
+ font-weight: 500;
1744
+ margin-right: 8px;
1745
+ }
1746
+
1747
+ .admin-card-empty {
1748
+ font-size: 14px;
1749
+ color: ${colors.textSecondary};
1750
+ font-style: italic;
1751
+ }
1752
+
1753
+ .admin-role {
1754
+ font-size: 16px;
1755
+ font-weight: 700;
1756
+ padding: 10px 20px;
1757
+ border-radius: 12px;
1758
+ text-align: center;
1759
+ min-width: 90px;
1760
+ }
1761
+
1762
+ .admin-role.owner {
1763
+ background: ${colors.ownerBg};
1764
+ color: ${colors.ownerText};
1765
+ border: 2px solid ${colors.ownerText};
1766
+ }
1767
+
1768
+ .admin-role.admin {
1769
+ background: ${colors.adminBg};
1770
+ color: ${colors.adminText};
1771
+ border: 2px solid ${colors.adminText};
1772
+ }
1773
+
1774
+ .timestamp {
1775
+ position: fixed;
1776
+ top: 8px;
1777
+ left: 8px;
1778
+ font-size: 12px;
1779
+ color: ${colors.textSecondary};
1780
+ opacity: 0.6;
1781
+ font-family: 'Courier New', monospace;
1782
+ z-index: 1000;
1783
+ }
1784
+ </style>
1785
+ </head>
1786
+ <body>
1787
+ <div class="container">
1788
+ <div class="header">
1789
+ <div class="group-info-wrapper">
1790
+ <img src="data:image/jpeg;base64,${groupAvatarBase64}" alt="群头像" class="group-avatar" />
1791
+ <div class="group-details">
1792
+ <div class="group-name">${contextInfo.groupName || "未知群聊"}</div>
1793
+ <div class="group-meta">
1794
+ <span class="group-meta-item">
1795
+ <span class="group-meta-label">群号:</span>
1796
+ <span class="group-meta-value">${contextInfo.groupId}</span>
1797
+ </span>
1798
+ <span class="group-meta-item">
1799
+ <span class="group-meta-label">成员:</span>
1800
+ <span class="group-meta-value">${contextInfo.memberCount}/${contextInfo.maxMemberCount}</span>
1801
+ </span>
1802
+ </div>
1803
+ </div>
1804
+ </div>
1805
+ <div class="title">
1806
+ 群管理员列表 <span class="title-count">(${admins.length}人)</span>
1807
+ </div>
1808
+ </div>
1809
+
1810
+ <div class="admin-list">
1811
+ ${flatAdminListItems}
1812
+ </div>
1813
+ </div>
1814
+ <div class="timestamp">${timestamp}</div>
1815
+ </body>
1816
+ </html>`;
1817
+ }, "getFlatMinimalAdminListHtmlStr");
1044
1818
  async function renderAdminList(ctx, admins, contextInfo, imageStyle, enableDarkMode, imageType, screenshotQuality) {
1045
1819
  const browserPage = await ctx.puppeteer.page();
1046
1820
  admins.sort((a, b) => {
@@ -1070,6 +1844,14 @@ async function renderAdminList(ctx, admins, contextInfo, imageStyle, enableDarkM
1070
1844
  fontBase64,
1071
1845
  enableDarkMode
1072
1846
  );
1847
+ } else if (imageStyle === IMAGE_STYLES.FLAT_MINIMAL) {
1848
+ htmlContent = await getFlatMinimalAdminListHtmlStr(
1849
+ admins,
1850
+ contextInfo,
1851
+ groupAvatarBase64,
1852
+ fontBase64,
1853
+ enableDarkMode
1854
+ );
1073
1855
  } else {
1074
1856
  throw new Error(`不支持的图片样式: ${imageStyle}`);
1075
1857
  }
@@ -1077,8 +1859,8 @@ async function renderAdminList(ctx, admins, contextInfo, imageStyle, enableDarkM
1077
1859
  await browserPage.waitForSelector("body", { timeout: 15e3 });
1078
1860
  await browserPage.evaluate(() => {
1079
1861
  const images = Array.from(document.querySelectorAll("img"));
1080
- return Promise.all(images.filter((img) => !img.complete).map((img) => new Promise((resolve2) => {
1081
- img.onload = img.onerror = resolve2;
1862
+ return Promise.all(images.filter((img) => !img.complete).map((img) => new Promise((resolve3) => {
1863
+ img.onload = img.onerror = resolve3;
1082
1864
  })));
1083
1865
  });
1084
1866
  const bodyElement = await browserPage.$("body");
@@ -1103,6 +1885,144 @@ async function renderAdminList(ctx, admins, contextInfo, imageStyle, enableDarkM
1103
1885
  }
1104
1886
  __name(renderAdminList, "renderAdminList");
1105
1887
 
1888
+ // src/data_server.ts
1889
+ var import_plugin_console = require("@koishijs/plugin-console");
1890
+ var path = __toESM(require("path"));
1891
+ var OnebotInfoImageDataServer = class extends import_plugin_console.DataService {
1892
+ static {
1893
+ __name(this, "OnebotInfoImageDataServer");
1894
+ }
1895
+ fontsBase64 = {
1896
+ sourceHanSerif: "",
1897
+ lxgwWenKai: ""
1898
+ };
1899
+ fontLoaded = false;
1900
+ currentTemplate = "sourceHanSerif";
1901
+ currentFont = "sourceHanSerif";
1902
+ currentDarkMode = true;
1903
+ static inject = ["console"];
1904
+ constructor(ctx) {
1905
+ super(ctx, "onebot-info-image", { immediate: true });
1906
+ this.loadFonts();
1907
+ ctx.console.addEntry({
1908
+ dev: path.resolve(__dirname, "../client/index.ts"),
1909
+ prod: path.resolve(__dirname, "../dist")
1910
+ });
1911
+ ctx.console.addListener("onebot-info-image/refresh", async () => {
1912
+ await this.refresh();
1913
+ }, { authority: 0 });
1914
+ ctx.console.addListener("onebot-info-image/setTemplate", async (template) => {
1915
+ this.currentTemplate = template;
1916
+ await this.refresh();
1917
+ }, { authority: 0 });
1918
+ ctx.console.addListener("onebot-info-image/setFont", async (font) => {
1919
+ this.currentFont = font;
1920
+ await this.refresh();
1921
+ }, { authority: 0 });
1922
+ ctx.console.addListener("onebot-info-image/setDarkMode", async (darkMode) => {
1923
+ this.currentDarkMode = darkMode;
1924
+ await this.refresh();
1925
+ }, { authority: 0 });
1926
+ ctx.on("bot-status-updated", async () => {
1927
+ await this.refresh();
1928
+ });
1929
+ }
1930
+ async loadFonts() {
1931
+ try {
1932
+ await validateFonts(this.ctx);
1933
+ this.fontsBase64.sourceHanSerif = await getFontBase64(this.ctx, IMAGE_STYLES.SOURCE_HAN_SERIF_SC);
1934
+ this.fontsBase64.lxgwWenKai = await getFontBase64(this.ctx, IMAGE_STYLES.LXGW_WENKAI);
1935
+ this.fontLoaded = true;
1936
+ this.ctx.logger("onebot-info-image").info("字体加载完成");
1937
+ await this.refresh();
1938
+ } catch (e) {
1939
+ this.ctx.logger("onebot-info-image").warn("加载字体失败:", e);
1940
+ this.fontLoaded = true;
1941
+ await this.refresh();
1942
+ }
1943
+ }
1944
+ getHtmlGenerator(template) {
1945
+ switch (template) {
1946
+ case "sourceHanSerif":
1947
+ return getSourceHanSerifSCStyleUserInfoHtmlStr;
1948
+ case "flatMinimal":
1949
+ return getFlatMinimalUserInfoHtmlStr;
1950
+ case "lxgwWenKai":
1951
+ return getLXGWWenKaiUserInfoHtmlStr;
1952
+ default:
1953
+ return getSourceHanSerifSCStyleUserInfoHtmlStr;
1954
+ }
1955
+ }
1956
+ async get() {
1957
+ try {
1958
+ if (!this.fontLoaded) {
1959
+ return {
1960
+ status: "loading",
1961
+ msg: "正在加载字体..."
1962
+ };
1963
+ }
1964
+ const onebotBot = this.ctx.bots.find((b) => b.platform === "onebot");
1965
+ if (!onebotBot) {
1966
+ return {
1967
+ status: "no_bot",
1968
+ msg: "未找到 OneBot 平台的机器人"
1969
+ };
1970
+ }
1971
+ const selfId = onebotBot.selfId;
1972
+ const loginInfo = await onebotBot.internal.getLoginInfo();
1973
+ const strangerInfo = await onebotBot.internal.getStrangerInfo(Number(selfId));
1974
+ const avatarUrl = `https://q.qlogo.cn/headimg_dl?dst_uin=${selfId}&spec=640&img_type=jpg`;
1975
+ let avatarBase64 = "";
1976
+ try {
1977
+ const response = await this.ctx.http.get(avatarUrl, { responseType: "arraybuffer" });
1978
+ avatarBase64 = Buffer.from(response).toString("base64");
1979
+ } catch (e) {
1980
+ this.ctx.logger("onebot-info-image").warn("获取头像失败:", e);
1981
+ }
1982
+ const userInfo = {
1983
+ user_id: selfId,
1984
+ nickname: loginInfo.nickname || strangerInfo.nickname,
1985
+ sex: strangerInfo.sex || "",
1986
+ age: strangerInfo.age || 0,
1987
+ sign: strangerInfo.sign,
1988
+ level: strangerInfo.level,
1989
+ login_days: strangerInfo.login_days,
1990
+ qid: strangerInfo.qid
1991
+ };
1992
+ const contextInfo = {
1993
+ isGroup: false
1994
+ };
1995
+ const fontBase64 = this.fontsBase64[this.currentFont] || "";
1996
+ const htmlGenerator = this.getHtmlGenerator(this.currentTemplate);
1997
+ const htmlContent = await htmlGenerator(
1998
+ userInfo,
1999
+ contextInfo,
2000
+ avatarBase64,
2001
+ "",
2002
+ fontBase64,
2003
+ this.currentDarkMode,
2004
+ // 使用当前选择的主题模式
2005
+ true
2006
+ // hidePhoneNumber: 预览中默认隐藏手机号
2007
+ );
2008
+ return {
2009
+ status: "loaded",
2010
+ msg: "加载成功",
2011
+ htmlContent,
2012
+ currentTemplate: this.currentTemplate,
2013
+ currentFont: this.currentFont,
2014
+ currentDarkMode: this.currentDarkMode
2015
+ };
2016
+ } catch (e) {
2017
+ this.ctx.logger("onebot-info-image").error("获取 Bot 信息失败:", e);
2018
+ return {
2019
+ status: "error",
2020
+ msg: `获取失败: ${e.message || e}`
2021
+ };
2022
+ }
2023
+ }
2024
+ };
2025
+
1106
2026
  // src/index.ts
1107
2027
  var name = "onebot-info-image";
1108
2028
  var inject = {
@@ -1113,12 +2033,11 @@ var pkg = JSON.parse(
1113
2033
  );
1114
2034
  var usage = `
1115
2035
  <h1>Koishi 插件:onebot-info-image 获取群员信息 渲染成图像</h1>
1116
- <h2>🎯 插件版本:v${pkg.version}</h2>
1117
- <p>插件使用问题 / Bug反馈 / 插件开发交流,欢迎加入QQ群:<b>259248174</b></p>
2036
+ <h2>🎯 插件版本:<span style="color: #ff6b6b; font-weight: bold;">v${pkg.version}</span></h2>
2037
+ <p>插件使用问题 / Bug反馈 / 插件开发交流,欢迎加入QQ群:<b style="color: #50c878;">259248174</b></p>
1118
2038
 
1119
- 目前仅仅适配了Lagrange 和 Napcat 协议
1120
- <br>
1121
- Napcat能拿到的东西更多, 为了更好的使用体验,推荐使用Napcat
2039
+ <p>目前仅仅适配了 <b>Lagrange</b><b>Napcat</b> 协议</p>
2040
+ <p style="color: #f39c12;">Napcat能拿到的东西更多, 为了更好的使用体验,推荐使用 Napcat</p>
1122
2041
 
1123
2042
  <hr>
1124
2043
 
@@ -1130,20 +2049,20 @@ Napcat能拿到的东西更多, 为了更好的使用体验,推荐使用Napc
1130
2049
 
1131
2050
  <hr>
1132
2051
 
1133
- <h3>字体使用声明</h3>
2052
+ <h3 style="color: #27ae60;">字体使用声明</h3>
1134
2053
  <p>本插件使用以下开源字体进行图像渲染:</p>
1135
2054
  <ul>
1136
- <li><b>思源宋体(Source Han Serif SC)</b> - 由 Adobe 与 Google 联合开发,遵循 <a href="https://openfontlicense.org">SIL Open Font License 1.1</a> 协议。</li>
1137
- <li><b>霞鹜文楷(LXGW WenKai)</b> - 由 LXGW 开发并维护,遵循 <a href="https://openfontlicense.org">SIL Open Font License 1.1</a> 协议。</li>
2055
+ <li><b style="color: #3498db;">思源宋体(Source Han Serif SC)</b> - 由 Adobe 与 Google 联合开发,遵循 <a href="https://openfontlicense.org">SIL Open Font License 1.1</a> 协议。</li>
2056
+ <li><b style="color: #3498db;">霞鹜文楷(LXGW WenKai)</b> - 由 LXGW 开发并维护,遵循 <a href="https://openfontlicense.org">SIL Open Font License 1.1</a> 协议。</li>
1138
2057
  </ul>
1139
2058
  <p>两者均为自由字体,可在本项目中自由使用、修改与发布。若你也在开发相关插件或项目,欢迎一同使用这些优秀的字体。</p>
1140
2059
 
1141
2060
  <hr>
1142
2061
 
1143
- <h3>插件许可声明</h3>
2062
+ <h3 style="color: #e67e22;">插件许可声明</h3>
1144
2063
  <p>本插件为开源免费项目,基于 MIT 协议开放。欢迎修改、分发、二创。</p>
1145
- <p>如果你觉得插件好用,欢迎在 GitHub 上 Star 或通过其他方式给予支持(例如提供服务器、API Key 或直接赞助)!</p>
1146
- <p>感谢所有开源字体与项目的贡献者 ❤️</p>
2064
+ <p>如果你觉得插件好用,欢迎在 GitHub 上 Star 或通过其他方式给予支持(例如提供服务器、API Key 或直接赞助)!</p>
2065
+ <p style="color: #e91e63;">感谢所有开源字体与项目的贡献者 ❤️</p>
1147
2066
  `;
1148
2067
  var Config = import_koishi.Schema.intersect([
1149
2068
  import_koishi.Schema.object({
@@ -1156,8 +2075,10 @@ var Config = import_koishi.Schema.intersect([
1156
2075
  import_koishi.Schema.object({
1157
2076
  enableUserInfoCommand: import_koishi.Schema.boolean().default(true).description("ℹ️ 是否启用用户信息命令。"),
1158
2077
  userinfoCommandName: import_koishi.Schema.string().default("用户信息").description("🔍 用户信息命令名称。"),
2078
+ hidePhoneNumber: import_koishi.Schema.boolean().default(true).experimental().description("📱 是否隐藏手机号。开启后手机号将显示为【已隐藏】。</br> <i> 保护隐私捏 </i>"),
1159
2079
  enableGroupAdminListCommand: import_koishi.Schema.boolean().default(false).description("👥 是否启用群管理员列表命令。"),
1160
- groupAdminListCommandName: import_koishi.Schema.string().default("群管理列表").description("👥 群管理员列表命令名称。")
2080
+ groupAdminListCommandName: import_koishi.Schema.string().default("群管理列表").description("👥 群管理员列表命令名称。"),
2081
+ inspectStyleCommandName: import_koishi.Schema.string().default("查看图片样式").description("🎨 查看图片样式列表命令名称。")
1161
2082
  }).description("基础配置 ⚙️"),
1162
2083
  import_koishi.Schema.object({
1163
2084
  sendText: import_koishi.Schema.boolean().default(false).description("💬 是否启用文本回复。"),
@@ -1166,11 +2087,37 @@ var Config = import_koishi.Schema.intersect([
1166
2087
  import_koishi.Schema.object({
1167
2088
  sendImage: import_koishi.Schema.boolean().default(true).description("🖼️ 是否启用 Puppeteer 渲染图片。"),
1168
2089
  enableQuoteWithImage: import_koishi.Schema.boolean().default(false).description("📸 回复图片的时候,是否带引用触发指令的消息。"),
1169
- imageStyle: import_koishi.Schema.union([
1170
- import_koishi.Schema.const(IMAGE_STYLES.SOURCE_HAN_SERIF_SC).description("✨ 现代风格,使用SourceHanSerifSC 思源宋体"),
1171
- import_koishi.Schema.const(IMAGE_STYLES.LXGW_WENKAI).description("📜 简洁古风,使用LXGWWenKai 字体")
1172
- ]).role("radio").default(IMAGE_STYLES.SOURCE_HAN_SERIF_SC).description("🎨 渲染图片的风格与字体。"),
1173
- enableDarkMode: import_koishi.Schema.boolean().default(false).description("🌙 是否启用暗黑模式。"),
2090
+ imageStyleDetails: import_koishi.Schema.array(
2091
+ import_koishi.Schema.object({
2092
+ styleKey: import_koishi.Schema.union(IMAGE_STYLE_KEY_ARR.map((key) => import_koishi.Schema.const(key).description(IMAGE_STYLES[key]))).role("radio").description("🎨 图片样式"),
2093
+ darkMode: import_koishi.Schema.boolean().description("🌙 启用深色模式")
2094
+ })
2095
+ ).role("table").default([
2096
+ {
2097
+ styleKey: IMAGE_STYLE_KEY_ARR[0],
2098
+ darkMode: false
2099
+ },
2100
+ {
2101
+ styleKey: IMAGE_STYLE_KEY_ARR[0],
2102
+ darkMode: true
2103
+ },
2104
+ {
2105
+ styleKey: IMAGE_STYLE_KEY_ARR[1],
2106
+ darkMode: false
2107
+ },
2108
+ {
2109
+ styleKey: IMAGE_STYLE_KEY_ARR[1],
2110
+ darkMode: true
2111
+ },
2112
+ {
2113
+ styleKey: IMAGE_STYLE_KEY_ARR[2],
2114
+ darkMode: false
2115
+ },
2116
+ {
2117
+ styleKey: IMAGE_STYLE_KEY_ARR[2],
2118
+ darkMode: true
2119
+ }
2120
+ ]).description("� 图片样式配置。第一行是默认使用的样式,指定样式请使用 -i 参数"),
1174
2121
  imageType: import_koishi.Schema.union([
1175
2122
  import_koishi.Schema.const(IMAGE_TYPES.PNG).description(`🖼️ ${IMAGE_TYPES.PNG}, ❌ 不支持调整quality`),
1176
2123
  import_koishi.Schema.const(IMAGE_TYPES.JPEG).description(`🌄 ${IMAGE_TYPES.JPEG}, ✅ 支持调整quality`),
@@ -1183,27 +2130,70 @@ var Config = import_koishi.Schema.intersect([
1183
2130
  }).description("发送 onebot转发消息 配置 ✉️"),
1184
2131
  import_koishi.Schema.object({
1185
2132
  verboseSessionOutput: import_koishi.Schema.boolean().default(false).description("🗣️ 是否在会话中输出详细信息。(生产环境别开,东西很多)"),
1186
- verboseConsoleOutput: import_koishi.Schema.boolean().default(false).description("💻 是否在控制台输出详细信息。")
2133
+ verboseConsoleOutput: import_koishi.Schema.boolean().default(false).description("💻 是否在控制台输出详细信息。"),
2134
+ verboseFileOutput: import_koishi.Schema.boolean().default(false).description("📄 是否在文件中输出详细信息。(生产环境不要开)")
1187
2135
  }).description("调试 (Debug) 配置 🐞")
1188
2136
  ]);
1189
2137
  function apply(ctx, config) {
1190
2138
  validateFonts(ctx).catch((error) => {
1191
2139
  ctx.logger.error(`字体文件验证失败: ${error.message}`);
1192
2140
  });
2141
+ ctx.plugin(OnebotInfoImageDataServer);
1193
2142
  const responseHint = [
1194
2143
  config.sendText && "文本消息",
1195
2144
  config.sendImage && "图片消息",
1196
2145
  config.sendForward && "合并转发消息"
1197
2146
  ].filter(Boolean).join("、");
2147
+ ctx.command(config.inspectStyleCommandName, "查看图片样式列表").alias("ais").alias("awa_inspect_style").action(async ({ session }) => {
2148
+ let msg = "用户信息图片样式列表:\n";
2149
+ for (let i = 0; i < config.imageStyleDetails.length; i++) {
2150
+ const o = config.imageStyleDetails[i];
2151
+ msg += ` 【${i}】: ${IMAGE_STYLES[o.styleKey]} ${o.darkMode ? "深色模式" : "浅色模式"} (${o.styleKey})
2152
+ `;
2153
+ }
2154
+ await session.send(msg);
2155
+ });
1198
2156
  if (config.enableUserInfoCommand)
1199
- ctx.command(config.userinfoCommandName, `获取用户信息, 发送${responseHint}`).alias("aui").alias("awa_user_info").action(async ({ session }) => {
2157
+ ctx.command(`${config.userinfoCommandName} [qqId:string]`, `获取用户信息, 发送${responseHint}`).alias("aui").alias("awa_user_info").option("imageStyleIdx", "-i, --idx, --index <idx:number> 图片样式索引").action(async ({ session, options }, qqId) => {
1200
2158
  if (!session.onebot)
1201
2159
  return session.send("[error]当前会话不支持onebot协议。");
2160
+ const IMAGE_STYLE_VALUES = Object.values(IMAGE_STYLES);
2161
+ const defaultStyleDetailObj = config.imageStyleDetails.length > 0 ? config.imageStyleDetails[0] : { styleKey: IMAGE_STYLE_KEY_ARR[0], darkMode: false };
2162
+ let selectedStyleDetailObj = defaultStyleDetailObj;
2163
+ if (options.imageStyleIdx !== void 0) {
2164
+ const isIdxValid = options.imageStyleIdx >= 0 && options.imageStyleIdx < config.imageStyleDetails.length;
2165
+ if (!isIdxValid) {
2166
+ let idxInvalidMsgArr = [
2167
+ `图片样式索引不合法。`,
2168
+ ` 合法范围:[0, ${config.imageStyleDetails.length - 1}]双闭区间。`,
2169
+ ` 当前输入:${options.imageStyleIdx}`,
2170
+ `
2171
+ `,
2172
+ `输入指令 ${config.inspectStyleCommandName} 查看图片样式列表。`
2173
+ ];
2174
+ return await session.send(idxInvalidMsgArr.join("\n"));
2175
+ }
2176
+ selectedStyleDetailObj = config.imageStyleDetails[options.imageStyleIdx];
2177
+ }
1202
2178
  let targetUserId = session.userId;
1203
- for (const e of session.event.message.elements) {
1204
- if (e.type === "at") {
1205
- targetUserId = e.attrs.id;
1206
- break;
2179
+ let isDirectQuery = false;
2180
+ if (qqId) {
2181
+ const userIdRegex = /<at id="([^"]+)"(?: name="[^"]*")?\/>/;
2182
+ const match = qqId.match(userIdRegex);
2183
+ if (match) {
2184
+ targetUserId = match[1];
2185
+ isDirectQuery = true;
2186
+ } else {
2187
+ targetUserId = qqId;
2188
+ isDirectQuery = true;
2189
+ }
2190
+ }
2191
+ if (!isDirectQuery) {
2192
+ for (const e of session.event.message.elements) {
2193
+ if (e.type === "at") {
2194
+ targetUserId = e.attrs.id;
2195
+ break;
2196
+ }
1207
2197
  }
1208
2198
  }
1209
2199
  const userObj = await session.bot.getUser(targetUserId);
@@ -1227,7 +2217,7 @@ function apply(ctx, config) {
1227
2217
  ${JSON.stringify(strangerInfoObj)}`;
1228
2218
  if (config.verboseSessionOutput) await session.send(strangerInfoObjMsg);
1229
2219
  if (config.verboseConsoleOutput) ctx.logger.info(strangerInfoObjMsg);
1230
- if (session.guildId) {
2220
+ if (session.guildId && !isDirectQuery) {
1231
2221
  const groupMemberInfoObj = await session.onebot.getGroupMemberInfo(session.guildId, targetUserId);
1232
2222
  let groupMemberInfoObjMsg = `groupMemberInfoObj =
1233
2223
  ${JSON.stringify(groupMemberInfoObj)}`;
@@ -1282,9 +2272,10 @@ function apply(ctx, config) {
1282
2272
  if (config.onebotImplName === ONEBOT_IMPL_NAME.LAGRNAGE) {
1283
2273
  } else if (config.onebotImplName === ONEBOT_IMPL_NAME.NAPCAT) {
1284
2274
  const ncUserStatusObj = await session.onebot._request("nc_get_user_status", { user_id: targetUserId });
2275
+ const napcatStatusData = ncUserStatusObj?.data ?? null;
1285
2276
  userInfoArg.status = {
1286
2277
  napcat_origin: ncUserStatusObj,
1287
- message: getNapcatQQStatusText(ncUserStatusObj?.data.status, ncUserStatusObj?.data.ext_status)
2278
+ message: getNapcatQQStatusText(napcatStatusData?.status, napcatStatusData?.ext_status)
1288
2279
  };
1289
2280
  }
1290
2281
  let userInfoArgMsg = `userInfoArg =
@@ -1320,7 +2311,20 @@ function apply(ctx, config) {
1320
2311
  }
1321
2312
  if (config.sendImage) {
1322
2313
  const waitTipMsgId = await session.send(`${import_koishi.h.quote(session.messageId)}🔄正在渲染用户信息图片,请稍候⏳...`);
1323
- const userInfoimageBase64 = await renderUserInfo(ctx, unifiedUserInfo, unifiedContextInfo, config.imageStyle, config.enableDarkMode, config.imageType, config.screenshotQuality);
2314
+ const selectedImageStyle = IMAGE_STYLES[selectedStyleDetailObj.styleKey];
2315
+ const selectedDarkMode = selectedStyleDetailObj.darkMode;
2316
+ const userInfoimageBase64 = await renderUserInfo(ctx, unifiedUserInfo, unifiedContextInfo, selectedImageStyle, selectedDarkMode, config.imageType, config.screenshotQuality, config.hidePhoneNumber);
2317
+ if (config.verboseFileOutput) {
2318
+ try {
2319
+ const tmpDir = (0, import_path2.resolve)(__dirname, "../tmp");
2320
+ (0, import_fs2.mkdirSync)(tmpDir, { recursive: true });
2321
+ const outputPath = (0, import_path2.resolve)(tmpDir, "image_base64.txt");
2322
+ (0, import_fs2.writeFileSync)(outputPath, userInfoimageBase64, "utf-8");
2323
+ ctx.logger.info(`图片 base64 已输出到: ${outputPath}`);
2324
+ } catch (error) {
2325
+ ctx.logger.error(`写入 base64 文件失败: ${error.message}`);
2326
+ }
2327
+ }
1324
2328
  await session.send(`${config.enableQuoteWithImage ? import_koishi.h.quote(session.messageId) : ""}${import_koishi.h.image(`data:image/png;base64,${userInfoimageBase64}`)}`);
1325
2329
  await session.bot.deleteMessage(session.guildId, String(waitTipMsgId));
1326
2330
  }
@@ -1337,11 +2341,29 @@ function apply(ctx, config) {
1337
2341
  }
1338
2342
  });
1339
2343
  if (config.enableGroupAdminListCommand)
1340
- ctx.command(config.groupAdminListCommandName, `获取群管理员列表, 发送${responseHint}`).alias("al").alias("awa_group_admin_list").action(async ({ session, options }) => {
2344
+ ctx.command(config.groupAdminListCommandName, `获取群管理员列表, 发送${responseHint}`).alias("al").alias("awa_group_admin_list").option("imageStyleIdx", "-i, --idx, --index <idx:number> 图片样式索引").action(async ({ session, options }) => {
1341
2345
  if (!session.onebot)
1342
2346
  return session.send("[error]当前会话不支持onebot协议。");
1343
2347
  if (!session.guildId)
1344
2348
  return session.send("[error]当前会话不在群聊中。");
2349
+ const IMAGE_STYLE_VALUES = Object.values(IMAGE_STYLES);
2350
+ const defaultStyleDetailObj = config.imageStyleDetails.length > 0 ? config.imageStyleDetails[0] : { styleKey: IMAGE_STYLE_KEY_ARR[0], darkMode: false };
2351
+ let selectedStyleDetailObj = defaultStyleDetailObj;
2352
+ if (options.imageStyleIdx !== void 0) {
2353
+ const isIdxValid = options.imageStyleIdx >= 0 && options.imageStyleIdx < config.imageStyleDetails.length;
2354
+ if (!isIdxValid) {
2355
+ let idxInvalidMsgArr = [
2356
+ `图片样式索引不合法。`,
2357
+ ` 合法范围:[0, ${config.imageStyleDetails.length - 1}]双闭区间。`,
2358
+ ` 当前输入:${options.imageStyleIdx}`,
2359
+ `
2360
+ `,
2361
+ `输入指令 ${config.inspectStyleCommandName} 查看图片样式列表。`
2362
+ ];
2363
+ return await session.send(idxInvalidMsgArr.join("\n"));
2364
+ }
2365
+ selectedStyleDetailObj = config.imageStyleDetails[options.imageStyleIdx];
2366
+ }
1345
2367
  try {
1346
2368
  const groupMemberListObj = await session.onebot.getGroupMemberList(session.guildId);
1347
2369
  const groupInfoObj = await session.onebot.getGroupInfo(session.guildId);
@@ -1397,7 +2419,9 @@ function apply(ctx, config) {
1397
2419
  ctx.logger.info(`context info = ${JSON.stringify(contextInfo)}`);
1398
2420
  const waitTipMsgId = await session.send(`${import_koishi.h.quote(session.messageId)}🔄正在渲染群管理员列表图片,请稍候⏳...`);
1399
2421
  const unifiedContextInfo = convertToUnifiedContextInfo(contextInfo, config.onebotImplName);
1400
- const adminListImageBase64 = await renderAdminList(ctx, adminListArg, unifiedContextInfo, config.imageStyle, config.enableDarkMode, config.imageType, config.screenshotQuality);
2422
+ const selectedImageStyle = IMAGE_STYLES[selectedStyleDetailObj.styleKey];
2423
+ const selectedDarkMode = selectedStyleDetailObj.darkMode;
2424
+ const adminListImageBase64 = await renderAdminList(ctx, adminListArg, unifiedContextInfo, selectedImageStyle, selectedDarkMode, config.imageType, config.screenshotQuality);
1401
2425
  await session.send(`${config.enableQuoteWithImage ? import_koishi.h.quote(session.messageId) : ""}${import_koishi.h.image(`data:image/png;base64,${adminListImageBase64}`)}`);
1402
2426
  await session.bot.deleteMessage(session.guildId, String(waitTipMsgId));
1403
2427
  }