koishi-plugin-dew-bot 1.0.250 → 1.0.252
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/README.md +0 -4
- package/lib/index.js +226 -88
- package/lib/plugin/serverHandel/qqCommandMessage.d.ts +5 -0
- package/lib/plugin/serverHandel/qqPageMessage.d.ts +5 -0
- package/lib/plugin/serverHandel/qqStoryMessage.d.ts +6 -0
- package/lib/shared/face/index.d.ts +4 -0
- package/package.json +1 -1
- package/src/index.ts +0 -4
- package/src/plugin/serverHandel/image.ts +11 -4
- package/src/plugin/serverHandel/qqCommandMessage.ts +36 -0
- package/src/plugin/serverHandel/qqPageMessage.ts +61 -23
- package/src/plugin/serverHandel/qqStoryMessage.ts +56 -0
- package/src/plugin/serverHandel/qqTransactionMessage.ts +6 -7
- package/src/plugin/serverHandel/text.ts +13 -5
- package/src/shared/bot/serviceProto.ts +18 -2
- package/src/shared/face/index.ts +4 -0
package/README.md
CHANGED
package/lib/index.js
CHANGED
|
@@ -42,40 +42,6 @@ var import_koishi2 = require("koishi");
|
|
|
42
42
|
// src/plugin/serverHandel/image.ts
|
|
43
43
|
var import_koishi = require("koishi");
|
|
44
44
|
|
|
45
|
-
// src/shared/bot/analysisMsg.ts
|
|
46
|
-
var AnalysisMsg = class {
|
|
47
|
-
static {
|
|
48
|
-
__name(this, "AnalysisMsg");
|
|
49
|
-
}
|
|
50
|
-
parse(data) {
|
|
51
|
-
let msg = "";
|
|
52
|
-
let haveTitle = false;
|
|
53
|
-
for (const item of data.line) {
|
|
54
|
-
switch (item.type) {
|
|
55
|
-
case 0 /* title */:
|
|
56
|
-
haveTitle = true;
|
|
57
|
-
msg += `┄━═${item.content}═━┄
|
|
58
|
-
`;
|
|
59
|
-
break;
|
|
60
|
-
case 1 /* line */:
|
|
61
|
-
msg += `${item.content}
|
|
62
|
-
`;
|
|
63
|
-
break;
|
|
64
|
-
case 2 /* error */:
|
|
65
|
-
msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者
|
|
66
|
-
`;
|
|
67
|
-
break;
|
|
68
|
-
default:
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (haveTitle) {
|
|
73
|
-
msg += "┄━════════━┄";
|
|
74
|
-
}
|
|
75
|
-
return msg;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
45
|
// src/plugin/appearanceCardTemplate.ts
|
|
80
46
|
var escape = /* @__PURE__ */ __name((value) => String(value ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]), "escape");
|
|
81
47
|
function appearanceCardHtml(card, artwork, paper) {
|
|
@@ -689,28 +655,37 @@ var handel_base = class {
|
|
|
689
655
|
}
|
|
690
656
|
};
|
|
691
657
|
|
|
692
|
-
// src/
|
|
693
|
-
var
|
|
658
|
+
// src/shared/bot/analysisMsg.ts
|
|
659
|
+
var AnalysisMsg = class {
|
|
694
660
|
static {
|
|
695
|
-
__name(this, "
|
|
661
|
+
__name(this, "AnalysisMsg");
|
|
696
662
|
}
|
|
697
|
-
|
|
698
|
-
let
|
|
699
|
-
let
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
663
|
+
parse(data) {
|
|
664
|
+
let msg = "";
|
|
665
|
+
let haveTitle = false;
|
|
666
|
+
for (const item of data.line) {
|
|
667
|
+
switch (item.type) {
|
|
668
|
+
case 0 /* title */:
|
|
669
|
+
haveTitle = true;
|
|
670
|
+
msg += `┄━═${item.content}═━┄
|
|
671
|
+
`;
|
|
672
|
+
break;
|
|
673
|
+
case 1 /* line */:
|
|
674
|
+
msg += `${item.content}
|
|
675
|
+
`;
|
|
676
|
+
break;
|
|
677
|
+
case 2 /* error */:
|
|
678
|
+
msg += `❌出错了!错误原因:${item.content},请反馈至联系开发者
|
|
679
|
+
`;
|
|
680
|
+
break;
|
|
681
|
+
default:
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if (haveTitle) {
|
|
686
|
+
msg += "┄━════════━┄";
|
|
712
687
|
}
|
|
713
|
-
|
|
688
|
+
return msg;
|
|
714
689
|
}
|
|
715
690
|
};
|
|
716
691
|
|
|
@@ -723,6 +698,12 @@ function compactDisplayText(value) {
|
|
|
723
698
|
return value.trim().replace(/[ \t\u3000]*([·×+|])[ \t\u3000]*/g, "$1").replace(/([\p{Script=Han}\p{Extended_Pictographic}\p{Emoji_Modifier}\p{Regional_Indicator}\uFE0F])[ \t\u3000]+/gu, "$1").replace(/[ \t\u3000]+(?=[\p{Script=Han}\p{Extended_Pictographic}\p{Regional_Indicator}])/gu, "");
|
|
724
699
|
}
|
|
725
700
|
__name(compactDisplayText, "compactDisplayText");
|
|
701
|
+
function renderQQTitle(content) {
|
|
702
|
+
return `**${escapeMarkdown(compactDisplayText(content))}**
|
|
703
|
+
|
|
704
|
+
***`;
|
|
705
|
+
}
|
|
706
|
+
__name(renderQQTitle, "renderQQTitle");
|
|
726
707
|
function compactListLabel(value) {
|
|
727
708
|
const label = compactDisplayText(value);
|
|
728
709
|
return /[📖📚]/u.test(label) ? label.replace(/技能书(?=$|×\d+(?:\.\d+)?(?:||$)||)/g, "") : label;
|
|
@@ -733,6 +714,42 @@ function inlineCommandUrl(command) {
|
|
|
733
714
|
return `mqqapi://aio/inlinecmd?command=${encoded}&enter=true&reply=false`;
|
|
734
715
|
}
|
|
735
716
|
__name(inlineCommandUrl, "inlineCommandUrl");
|
|
717
|
+
function inlineCommandSpans(content) {
|
|
718
|
+
const spans = [];
|
|
719
|
+
let depth = 0, start = 0, labelStart = 0;
|
|
720
|
+
for (let index = 0; index < content.length; index++) {
|
|
721
|
+
if (content[index] === "[") {
|
|
722
|
+
if (depth === 0) {
|
|
723
|
+
start = index > 0 && content[index - 1] === "\\" ? index - 1 : index;
|
|
724
|
+
labelStart = index + 1;
|
|
725
|
+
}
|
|
726
|
+
depth++;
|
|
727
|
+
} else if (content[index] === "]" && depth > 0 && --depth === 0) {
|
|
728
|
+
const label = content.slice(labelStart, index).replace(/\\$/, "");
|
|
729
|
+
if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue;
|
|
730
|
+
const command = label.split(/[++]/, 1)[0].trim();
|
|
731
|
+
if (command) spans.push({ start, end: index + 1, label, command });
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return spans;
|
|
735
|
+
}
|
|
736
|
+
__name(inlineCommandSpans, "inlineCommandSpans");
|
|
737
|
+
function hasInlineCommands(content) {
|
|
738
|
+
return inlineCommandSpans(content).length > 0;
|
|
739
|
+
}
|
|
740
|
+
__name(hasInlineCommands, "hasInlineCommands");
|
|
741
|
+
function renderInlineCommands(content) {
|
|
742
|
+
const parts = [];
|
|
743
|
+
let offset = 0;
|
|
744
|
+
for (const span of inlineCommandSpans(content)) {
|
|
745
|
+
parts.push(escapeMarkdown(content.slice(offset, span.start)));
|
|
746
|
+
parts.push("[" + escapeMarkdown(span.label) + "](" + inlineCommandUrl(span.command) + ")");
|
|
747
|
+
offset = span.end;
|
|
748
|
+
}
|
|
749
|
+
parts.push(escapeMarkdown(content.slice(offset)));
|
|
750
|
+
return parts.join("");
|
|
751
|
+
}
|
|
752
|
+
__name(renderInlineCommands, "renderInlineCommands");
|
|
736
753
|
function pageCommandLines(nav) {
|
|
737
754
|
return [
|
|
738
755
|
...nav.previousCommand ? [nav.previousCommand] : [],
|
|
@@ -746,11 +763,6 @@ function renderQQPageMarkdown(data, showGreeting) {
|
|
|
746
763
|
const listPattern = /^\[(\d+)\]\s*([^\r\n]+)$/;
|
|
747
764
|
const pagePattern = /^第\s*\d+\s*\/\s*\d+\s*页(?:\s*[||]\s*共\s*(\d+)\s*([项条]))?$/;
|
|
748
765
|
const title = data.line.find((item) => item.type === 0 /* title */);
|
|
749
|
-
const summaryLine = data.line.find((item) => item.type === 1 /* line */ && pagePattern.test(item.content));
|
|
750
|
-
const summary = summaryLine && pagePattern.exec(summaryLine.content);
|
|
751
|
-
const listCount = data.line.filter((item) => item.type === 1 /* line */ && listPattern.test(item.content)).length;
|
|
752
|
-
const count = summary?.[1] ?? (nav?.total === 1 ? String(listCount) : void 0);
|
|
753
|
-
const countUnit = summary?.[2] ?? "项";
|
|
754
766
|
const isLeaderboardDirectory = !!nav?.entries?.length && /^(?:🏆)?排行榜$/u.test(compactDisplayText(title?.content ?? ""));
|
|
755
767
|
const sections = [];
|
|
756
768
|
const bodyLines = [];
|
|
@@ -773,20 +785,16 @@ function renderQQPageMarkdown(data, showGreeting) {
|
|
|
773
785
|
switch (item.type) {
|
|
774
786
|
case 0 /* title */:
|
|
775
787
|
flush();
|
|
776
|
-
|
|
777
|
-
sections.push(`## ${item.content.includes("🏆") ? "" : "🏆"}${escapeMarkdown(compactDisplayText(item.content))}`);
|
|
778
|
-
sections.push(`> 共${nav.entries.length}个榜单·点击榜名查看`);
|
|
779
|
-
} else {
|
|
780
|
-
sections.push(`**${escapeMarkdown(compactDisplayText(item.content))}${count !== void 0 ? `·${count}${countUnit}` : ""}**`);
|
|
781
|
-
}
|
|
788
|
+
sections.push(renderQQTitle(`${isLeaderboardDirectory && !item.content.includes("🏆") ? "🏆" : ""}${item.content}`));
|
|
782
789
|
break;
|
|
783
790
|
case 1 /* line */: {
|
|
784
791
|
const listEntry = listPattern.exec(item.content);
|
|
785
792
|
if (listEntry) {
|
|
786
793
|
flushBody();
|
|
787
794
|
const command2 = entryCommands.get(lineIndex);
|
|
788
|
-
const
|
|
789
|
-
const
|
|
795
|
+
const content = compactListLabel(listEntry[2]);
|
|
796
|
+
const label = escapeMarkdown(content);
|
|
797
|
+
const name2 = command2 ? `[${label}](${inlineCommandUrl(command2)})` : renderInlineCommands(content);
|
|
790
798
|
listLines.push(`${listEntry[1]}. ${name2}`);
|
|
791
799
|
break;
|
|
792
800
|
}
|
|
@@ -803,31 +811,28 @@ function renderQQPageMarkdown(data, showGreeting) {
|
|
|
803
811
|
const page = pagePattern.exec(item.content);
|
|
804
812
|
if (page && nav) {
|
|
805
813
|
flushBody();
|
|
806
|
-
pageFooter = `**第${nav.current}/${nav.total}
|
|
814
|
+
pageFooter = `**第${nav.current}/${nav.total}页**`;
|
|
807
815
|
hasPageSummary = true;
|
|
808
816
|
break;
|
|
809
817
|
}
|
|
810
818
|
const command = entryCommands.get(lineIndex);
|
|
811
|
-
bodyLines.push(command ? `[${escapeMarkdown(
|
|
819
|
+
bodyLines.push(command ? `[${escapeMarkdown(compactListLabel(item.content))}](${inlineCommandUrl(command)})` : renderInlineCommands(item.content));
|
|
812
820
|
break;
|
|
813
821
|
}
|
|
814
822
|
case 2 /* error */:
|
|
815
823
|
flush();
|
|
816
|
-
sections.push(`❌ 出错了!错误原因:${
|
|
824
|
+
sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`);
|
|
817
825
|
break;
|
|
818
826
|
}
|
|
819
827
|
}
|
|
820
828
|
flush();
|
|
821
829
|
if (nav && !hasPageSummary && (nav.total > 1 || nav.previousCommand || nav.nextCommand))
|
|
822
830
|
pageFooter = `**第${nav.current}/${nav.total}页**`;
|
|
823
|
-
const previousLink = nav?.previousCommand ? `[上一页](${inlineCommandUrl(nav.previousCommand)})` : void 0;
|
|
824
|
-
const nextLink = nav?.nextCommand ? `[下一页](${inlineCommandUrl(nav.nextCommand)})` : void 0;
|
|
831
|
+
const previousLink = nav?.previousCommand ? `[${nav.previousCommand === "上一个" ? "上一个" : "上一页"}](${inlineCommandUrl(nav.previousCommand)})` : void 0;
|
|
832
|
+
const nextLink = nav?.nextCommand ? `[${nav.nextCommand === "下一个" ? "下一个" : "下一页"}](${inlineCommandUrl(nav.nextCommand)})` : void 0;
|
|
825
833
|
const helpLink = hasHelp ? `[指令](${inlineCommandUrl("指令")})` : void 0;
|
|
826
834
|
const footer = pageFooter ? [pageFooter, previousLink, nextLink, helpLink].filter(Boolean).join("·") : helpLink ? `更多操作:${helpLink}` : void 0;
|
|
827
|
-
if (footer)
|
|
828
|
-
if (isLeaderboardDirectory) sections.push("***");
|
|
829
|
-
sections.push(footer);
|
|
830
|
-
}
|
|
835
|
+
if (footer) sections.push(footer);
|
|
831
836
|
return sections.join("\n\n") || " ";
|
|
832
837
|
}
|
|
833
838
|
__name(renderQQPageMarkdown, "renderQQPageMarkdown");
|
|
@@ -883,19 +888,19 @@ function renderQQTransactionMarkdown(data, showGreeting) {
|
|
|
883
888
|
for (const item of data.line) {
|
|
884
889
|
if (item.type === 0 /* title */) {
|
|
885
890
|
flush();
|
|
886
|
-
sections.push(
|
|
891
|
+
sections.push(renderQQTitle(item.content));
|
|
887
892
|
continue;
|
|
888
893
|
}
|
|
889
894
|
if (item.type === 2 /* error */) {
|
|
890
895
|
flush();
|
|
891
|
-
sections.push(`❌ 出错了!错误原因:${
|
|
896
|
+
sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`);
|
|
892
897
|
continue;
|
|
893
898
|
}
|
|
894
899
|
if (item.type !== 1 /* line */) continue;
|
|
895
900
|
const action = data.transaction && (item.content === "[是]确认交易" ? { command: "是", label: "确认交易" } : item.content === "[否]取消交易" ? { command: "否", label: "取消交易" } : void 0);
|
|
896
901
|
if (action) {
|
|
897
902
|
flushBody();
|
|
898
|
-
actions.push(`[${action.
|
|
903
|
+
actions.push(`[${action.label}](${inlineCommandUrl(action.command)})`);
|
|
899
904
|
continue;
|
|
900
905
|
}
|
|
901
906
|
flushActions();
|
|
@@ -908,7 +913,7 @@ function renderQQTransactionMarkdown(data, showGreeting) {
|
|
|
908
913
|
flushBody();
|
|
909
914
|
sections.push(`**${escapeMarkdown(section?.[1] ?? "还缺")}**`);
|
|
910
915
|
} else {
|
|
911
|
-
bodyLines.push(
|
|
916
|
+
bodyLines.push(renderInlineCommands(item.content));
|
|
912
917
|
}
|
|
913
918
|
}
|
|
914
919
|
flush();
|
|
@@ -920,6 +925,90 @@ async function sendQQTransaction(session, data) {
|
|
|
920
925
|
}
|
|
921
926
|
__name(sendQQTransaction, "sendQQTransaction");
|
|
922
927
|
|
|
928
|
+
// src/plugin/serverHandel/qqCommandMessage.ts
|
|
929
|
+
function renderQQCommandMarkdown(data, showGreeting) {
|
|
930
|
+
const sections = [];
|
|
931
|
+
const bodyLines = [];
|
|
932
|
+
const flushBody = /* @__PURE__ */ __name(() => {
|
|
933
|
+
if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
|
|
934
|
+
}, "flushBody");
|
|
935
|
+
if (showGreeting) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
|
|
936
|
+
for (const item of data.line) {
|
|
937
|
+
switch (item.type) {
|
|
938
|
+
case 0 /* title */:
|
|
939
|
+
flushBody();
|
|
940
|
+
sections.push(renderQQTitle(item.content));
|
|
941
|
+
break;
|
|
942
|
+
case 1 /* line */:
|
|
943
|
+
if (!item.content.trim()) flushBody();
|
|
944
|
+
else bodyLines.push(renderInlineCommands(item.content));
|
|
945
|
+
break;
|
|
946
|
+
case 2 /* error */:
|
|
947
|
+
flushBody();
|
|
948
|
+
sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`);
|
|
949
|
+
break;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
flushBody();
|
|
953
|
+
return sections.join("\n\n") || " ";
|
|
954
|
+
}
|
|
955
|
+
__name(renderQQCommandMarkdown, "renderQQCommandMarkdown");
|
|
956
|
+
async function sendQQCommand(session, data) {
|
|
957
|
+
await sendQQMarkdown(session, renderQQCommandMarkdown(data, session.event?.channel?.type === 0));
|
|
958
|
+
}
|
|
959
|
+
__name(sendQQCommand, "sendQQCommand");
|
|
960
|
+
|
|
961
|
+
// src/plugin/serverHandel/qqStoryMessage.ts
|
|
962
|
+
function hasStoryCodeBlock(data) {
|
|
963
|
+
return data.line.some((item) => item.type === 1 /* line */ && item.codeBlock === true);
|
|
964
|
+
}
|
|
965
|
+
__name(hasStoryCodeBlock, "hasStoryCodeBlock");
|
|
966
|
+
function fencedStory(content) {
|
|
967
|
+
const story = content.replace(/\r\n?/g, "\n");
|
|
968
|
+
const longestRun = Math.max(0, ...Array.from(story.matchAll(/`+/g), (match) => match[0].length));
|
|
969
|
+
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
970
|
+
return fence + "\n" + story + (story.endsWith("\n") ? "" : "\n") + fence;
|
|
971
|
+
}
|
|
972
|
+
__name(fencedStory, "fencedStory");
|
|
973
|
+
function renderQQStoryMarkdown(data, showGreeting) {
|
|
974
|
+
const sections = [];
|
|
975
|
+
const bodyLines = [];
|
|
976
|
+
const flushBody = /* @__PURE__ */ __name(() => {
|
|
977
|
+
if (bodyLines.length) sections.push(bodyLines.splice(0).join(" \n"));
|
|
978
|
+
}, "flushBody");
|
|
979
|
+
if (showGreeting) sections.push(escapeMarkdown("Ciallo~(∠・ω< )"));
|
|
980
|
+
for (const item of data.line) {
|
|
981
|
+
switch (item.type) {
|
|
982
|
+
case 0 /* title */:
|
|
983
|
+
flushBody();
|
|
984
|
+
sections.push(renderQQTitle(item.content));
|
|
985
|
+
break;
|
|
986
|
+
case 1 /* line */:
|
|
987
|
+
if (item.codeBlock) {
|
|
988
|
+
flushBody();
|
|
989
|
+
sections.push(fencedStory(item.content));
|
|
990
|
+
} else {
|
|
991
|
+
for (const line of item.content.replace(/\r\n?/g, "\n").split("\n")) {
|
|
992
|
+
if (!line.trim()) flushBody();
|
|
993
|
+
else bodyLines.push(renderInlineCommands(line));
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
break;
|
|
997
|
+
case 2 /* error */:
|
|
998
|
+
flushBody();
|
|
999
|
+
sections.push("❌ 出错了!错误原因:" + renderInlineCommands(item.content) + ",请反馈至联系开发者");
|
|
1000
|
+
break;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
flushBody();
|
|
1004
|
+
return sections.join("\n\n") || " ";
|
|
1005
|
+
}
|
|
1006
|
+
__name(renderQQStoryMarkdown, "renderQQStoryMarkdown");
|
|
1007
|
+
async function sendQQStory(session, data) {
|
|
1008
|
+
await sendQQMarkdown(session, renderQQStoryMarkdown(data, session.event?.channel?.type === 0));
|
|
1009
|
+
}
|
|
1010
|
+
__name(sendQQStory, "sendQQStory");
|
|
1011
|
+
|
|
923
1012
|
// src/plugin/serverHandel/text.ts
|
|
924
1013
|
var handelr_text = class extends handel_base {
|
|
925
1014
|
static {
|
|
@@ -928,11 +1017,16 @@ var handelr_text = class extends handel_base {
|
|
|
928
1017
|
async start(data) {
|
|
929
1018
|
const content = new AnalysisMsg().parse(data);
|
|
930
1019
|
const session = sessions_default.get(sessionKey(data.botId, data.userId, data.channelId));
|
|
931
|
-
|
|
1020
|
+
const inlineCommands = data.line.some((item) => (item.type === 1 /* line */ || item.type === 2 /* error */) && hasInlineCommands(item.content));
|
|
1021
|
+
const story = hasStoryCodeBlock(data);
|
|
1022
|
+
if ((story || data.commandHelp || data.transaction || data.pageNav || inlineCommands) && canSendQQPage(session, data)) {
|
|
932
1023
|
const sendStartedAt = this.timing ? performance.now() : 0;
|
|
933
1024
|
try {
|
|
934
|
-
if (
|
|
935
|
-
else await
|
|
1025
|
+
if (story) await sendQQStory(session, data);
|
|
1026
|
+
else if (data.commandHelp) await sendQQCommand(session, data);
|
|
1027
|
+
else if (data.transaction) await sendQQTransaction(session, data);
|
|
1028
|
+
else if (data.pageNav) await sendQQPage(session, data);
|
|
1029
|
+
else await sendQQCommand(session, data);
|
|
936
1030
|
if (this.timing) {
|
|
937
1031
|
this.timing.delivery = "qq-markdown";
|
|
938
1032
|
this.timing.sendMs = performance.now() - sendStartedAt;
|
|
@@ -941,7 +1035,7 @@ var handelr_text = class extends handel_base {
|
|
|
941
1035
|
} catch (error) {
|
|
942
1036
|
this.ctx.logger.warn("QQ Markdown 发送失败,改用普通文字", error instanceof Error ? error.message : error);
|
|
943
1037
|
}
|
|
944
|
-
const commands = !data.transaction && data.pageNav ? pageCommandLines(data.pageNav) : [];
|
|
1038
|
+
const commands = !story && !data.commandHelp && !data.transaction && data.pageNav ? pageCommandLines(data.pageNav) : [];
|
|
945
1039
|
await this.send(data, commands.length ? `${content.trimEnd()}
|
|
946
1040
|
${commands.join("\n")}` : content);
|
|
947
1041
|
return;
|
|
@@ -950,6 +1044,38 @@ ${commands.join("\n")}` : content);
|
|
|
950
1044
|
}
|
|
951
1045
|
};
|
|
952
1046
|
|
|
1047
|
+
// src/plugin/serverHandel/image.ts
|
|
1048
|
+
var ImageHandler = class extends handel_base {
|
|
1049
|
+
static {
|
|
1050
|
+
__name(this, "ImageHandler");
|
|
1051
|
+
}
|
|
1052
|
+
async start(data) {
|
|
1053
|
+
let content = "";
|
|
1054
|
+
let rendered = false;
|
|
1055
|
+
const renderStartedAt = this.timing ? performance.now() : 0;
|
|
1056
|
+
try {
|
|
1057
|
+
if (!data.characterCard && !data.itemCard && !data.card && !data.leaderboard && !data.equipmentCard) throw new Error("缺少卡片数据");
|
|
1058
|
+
const image = await renderSkillCard(this.ctx, data.card, data.leaderboard, data.itemCard, data.characterCard, data.equipmentCard);
|
|
1059
|
+
if (this.timing) this.timing.imageBytes = image.length;
|
|
1060
|
+
content = import_koishi.h.image(image, "image/jpeg").toString();
|
|
1061
|
+
rendered = true;
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
this.ctx.logger.warn("卡片绘制失败,使用文字详情", error instanceof Error ? error.message : "unknown");
|
|
1064
|
+
} finally {
|
|
1065
|
+
if (this.timing) this.timing.renderMs = performance.now() - renderStartedAt;
|
|
1066
|
+
}
|
|
1067
|
+
if (!rendered) {
|
|
1068
|
+
const fallback = new handelr_text(data);
|
|
1069
|
+
fallback.set(this.ctx);
|
|
1070
|
+
if (this.timing) fallback.setTiming(this.timing);
|
|
1071
|
+
fallback.send = (message, text, type = "text") => this.send(message, text, type);
|
|
1072
|
+
await fallback.start(data);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
await this.send(data, content, "image");
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
|
|
953
1079
|
// src/plugin/messageQueue.ts
|
|
954
1080
|
var handlers = {
|
|
955
1081
|
["text" /* text */]: handelr_text,
|
|
@@ -1014,7 +1140,7 @@ var import_tsrpc = require("tsrpc");
|
|
|
1014
1140
|
|
|
1015
1141
|
// src/shared/bot/serviceProto.ts
|
|
1016
1142
|
var serviceProto = {
|
|
1017
|
-
"version":
|
|
1143
|
+
"version": 14,
|
|
1018
1144
|
"services": [
|
|
1019
1145
|
{
|
|
1020
1146
|
"id": 0,
|
|
@@ -1157,6 +1283,14 @@ var serviceProto = {
|
|
|
1157
1283
|
},
|
|
1158
1284
|
"optional": true
|
|
1159
1285
|
},
|
|
1286
|
+
{
|
|
1287
|
+
"id": 9,
|
|
1288
|
+
"name": "commandHelp",
|
|
1289
|
+
"type": {
|
|
1290
|
+
"type": "Boolean"
|
|
1291
|
+
},
|
|
1292
|
+
"optional": true
|
|
1293
|
+
},
|
|
1160
1294
|
{
|
|
1161
1295
|
"id": 1,
|
|
1162
1296
|
"name": "line",
|
|
@@ -1893,6 +2027,14 @@ var serviceProto = {
|
|
|
1893
2027
|
"type": {
|
|
1894
2028
|
"type": "String"
|
|
1895
2029
|
}
|
|
2030
|
+
},
|
|
2031
|
+
{
|
|
2032
|
+
"id": 2,
|
|
2033
|
+
"name": "codeBlock",
|
|
2034
|
+
"type": {
|
|
2035
|
+
"type": "Boolean"
|
|
2036
|
+
},
|
|
2037
|
+
"optional": true
|
|
1896
2038
|
}
|
|
1897
2039
|
]
|
|
1898
2040
|
},
|
|
@@ -2224,10 +2366,6 @@ var usage = `
|
|
|
2224
2366
|
踏入主神空间,成为轮回者。与伙伴迎接未知的试炼,在生死之间积蓄力量,书写属于你的无限冒险。
|
|
2225
2367
|
|
|
2226
2368
|
无论你熟悉原作,还是初次踏入无限流世界,这场轮回,都有你的位置。
|
|
2227
|
-
|
|
2228
|
-
---
|
|
2229
|
-
|
|
2230
|
-
连接 DEW 游戏服后,在聊天中发送 **帮助**,开启第一场冒险。
|
|
2231
2369
|
`;
|
|
2232
2370
|
var Config = import_koishi2.Schema.object({
|
|
2233
2371
|
服务器地址: import_koishi2.Schema.string().default("ws://dew-v7-test:3021").description("DEW 游戏服的 WebSocket 地址;同机部署可填写 ws://127.0.0.1:3021")
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Session } from 'koishi';
|
|
2
|
+
import type { MsgMessage } from '../../shared/bot/MsgMessage';
|
|
3
|
+
/** Render command help and ordinary prose with clickable bracketed commands. */
|
|
4
|
+
export declare function renderQQCommandMarkdown(data: MsgMessage, showGreeting: boolean): string;
|
|
5
|
+
export declare function sendQQCommand(session: Session, data: MsgMessage): Promise<void>;
|
|
@@ -3,7 +3,12 @@ import { type PageNavData } from '../../shared/face';
|
|
|
3
3
|
import type { MsgMessage } from '../../shared/bot/MsgMessage';
|
|
4
4
|
/** Escape server-provided lines before embedding them in native QQ Markdown. */
|
|
5
5
|
export declare function escapeMarkdown(value: string): string;
|
|
6
|
+
/** Keep native QQ Markdown titles consistent across message types. */
|
|
7
|
+
export declare function renderQQTitle(content: string): string;
|
|
6
8
|
export declare function inlineCommandUrl(command: string): string;
|
|
9
|
+
export declare function hasInlineCommands(content: string): boolean;
|
|
10
|
+
/** Escape surrounding prose while rendering boxed commands as inline links. */
|
|
11
|
+
export declare function renderInlineCommands(content: string): string;
|
|
7
12
|
export declare function pageCommandLines(nav: PageNavData): string[];
|
|
8
13
|
export declare function renderQQPageMarkdown(data: MsgMessage, showGreeting: boolean): string;
|
|
9
14
|
/** Returns false if this is not the matching QQ session or the adapter has no native API. */
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Session } from 'koishi';
|
|
2
|
+
import type { MsgMessage } from '../../shared/bot/MsgMessage';
|
|
3
|
+
export declare function hasStoryCodeBlock(data: Pick<MsgMessage, 'line'>): boolean;
|
|
4
|
+
/** Only marked story lines become code blocks; result summaries remain prose. */
|
|
5
|
+
export declare function renderQQStoryMarkdown(data: MsgMessage, showGreeting: boolean): string;
|
|
6
|
+
export declare function sendQQStory(session: Session, data: MsgMessage): Promise<void>;
|
|
@@ -101,6 +101,8 @@ export interface ITitleMessage {
|
|
|
101
101
|
export interface ILineMessage {
|
|
102
102
|
type: MessageType.line;
|
|
103
103
|
content: string;
|
|
104
|
+
/** 原文长故事:支持的平台使用 Markdown 代码块,其余平台按普通文字显示。 */
|
|
105
|
+
codeBlock?: boolean;
|
|
104
106
|
}
|
|
105
107
|
/** 错误消息项 **/
|
|
106
108
|
export interface IErrorMessage {
|
|
@@ -132,6 +134,8 @@ export interface IMessage {
|
|
|
132
134
|
pageNav?: PageNavData;
|
|
133
135
|
/** 通用交易确认、结果与失败消息;客户端可使用交易 Markdown 展示。 */
|
|
134
136
|
transaction?: boolean;
|
|
137
|
+
/** 指令系统与指令提示;客户端可用 Markdown 展示可点击的方框指令。 */
|
|
138
|
+
commandHelp?: boolean;
|
|
135
139
|
/** 消息项列表 **/
|
|
136
140
|
line: IMessageItem[];
|
|
137
141
|
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { h } from 'koishi'
|
|
2
2
|
import { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
3
|
-
import { AnalysisMsg } from '../../shared/bot/analysisMsg'
|
|
4
3
|
import { renderSkillCard } from '../skillCard'
|
|
5
4
|
import handel_base from './handel_base'
|
|
5
|
+
import TextHandler from './text'
|
|
6
6
|
export default class ImageHandler extends handel_base {
|
|
7
7
|
async start(data: MsgMessage) {
|
|
8
|
-
let content
|
|
8
|
+
let content = ''
|
|
9
9
|
let rendered = false
|
|
10
10
|
const renderStartedAt = this.timing ? performance.now() : 0
|
|
11
11
|
try {
|
|
@@ -16,10 +16,17 @@ export default class ImageHandler extends handel_base {
|
|
|
16
16
|
rendered = true
|
|
17
17
|
} catch (error) {
|
|
18
18
|
this.ctx.logger.warn('卡片绘制失败,使用文字详情', error instanceof Error ? error.message : 'unknown')
|
|
19
|
-
content = new AnalysisMsg().parse(data)
|
|
20
19
|
} finally {
|
|
21
20
|
if (this.timing) this.timing.renderMs = performance.now() - renderStartedAt
|
|
22
21
|
}
|
|
23
|
-
|
|
22
|
+
if (!rendered) {
|
|
23
|
+
const fallback = new TextHandler(data)
|
|
24
|
+
fallback.set(this.ctx)
|
|
25
|
+
if (this.timing) fallback.setTiming(this.timing)
|
|
26
|
+
fallback.send = (message, text, type = 'text') => this.send(message, text, type)
|
|
27
|
+
await fallback.start(data)
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
await this.send(data, content, 'image')
|
|
24
31
|
}
|
|
25
32
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Session } from 'koishi'
|
|
2
|
+
import { MessageType } from '../../shared/face'
|
|
3
|
+
import type { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
4
|
+
import { escapeMarkdown, renderInlineCommands, renderQQTitle, sendQQMarkdown } from './qqPageMessage'
|
|
5
|
+
|
|
6
|
+
/** Render command help and ordinary prose with clickable bracketed commands. */
|
|
7
|
+
export function renderQQCommandMarkdown(data: MsgMessage, showGreeting: boolean): string {
|
|
8
|
+
const sections: string[] = []
|
|
9
|
+
const bodyLines: string[] = []
|
|
10
|
+
const flushBody = () => {
|
|
11
|
+
if (bodyLines.length) sections.push(bodyLines.splice(0).join(' \n'))
|
|
12
|
+
}
|
|
13
|
+
if (showGreeting) sections.push(escapeMarkdown('Ciallo~(∠・ω< )'))
|
|
14
|
+
for (const item of data.line) {
|
|
15
|
+
switch (item.type) {
|
|
16
|
+
case MessageType.title:
|
|
17
|
+
flushBody()
|
|
18
|
+
sections.push(renderQQTitle(item.content))
|
|
19
|
+
break
|
|
20
|
+
case MessageType.line:
|
|
21
|
+
if (!item.content.trim()) flushBody()
|
|
22
|
+
else bodyLines.push(renderInlineCommands(item.content))
|
|
23
|
+
break
|
|
24
|
+
case MessageType.error:
|
|
25
|
+
flushBody()
|
|
26
|
+
sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`)
|
|
27
|
+
break
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
flushBody()
|
|
31
|
+
return sections.join('\n\n') || ' '
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function sendQQCommand(session: Session, data: MsgMessage): Promise<void> {
|
|
35
|
+
await sendQQMarkdown(session, renderQQCommandMarkdown(data, session.event?.channel?.type === 0))
|
|
36
|
+
}
|
|
@@ -35,6 +35,11 @@ function compactDisplayText(value: string): string {
|
|
|
35
35
|
.replace(/[ \t\u3000]+(?=[\p{Script=Han}\p{Extended_Pictographic}\p{Regional_Indicator}])/gu, '')
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/** Keep native QQ Markdown titles consistent across message types. */
|
|
39
|
+
export function renderQQTitle(content: string): string {
|
|
40
|
+
return `**${escapeMarkdown(compactDisplayText(content))}**\n\n***`
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
function compactListLabel(value: string): string {
|
|
39
44
|
const label = compactDisplayText(value)
|
|
40
45
|
// The book icon already identifies skill books. Only shorten the displayed
|
|
@@ -52,6 +57,47 @@ export function inlineCommandUrl(command: string): string {
|
|
|
52
57
|
return `mqqapi://aio/inlinecmd?command=${encoded}&enter=true&reply=false`
|
|
53
58
|
}
|
|
54
59
|
|
|
60
|
+
|
|
61
|
+
type InlineCommandSpan = { start: number; end: number; label: string; command: string }
|
|
62
|
+
|
|
63
|
+
/** Bracketed commands may appear anywhere in prose, including escaped brackets. */
|
|
64
|
+
function inlineCommandSpans(content: string): InlineCommandSpan[] {
|
|
65
|
+
const spans: InlineCommandSpan[] = []
|
|
66
|
+
let depth = 0, start = 0, labelStart = 0
|
|
67
|
+
for (let index = 0; index < content.length; index++) {
|
|
68
|
+
if (content[index] === '[') {
|
|
69
|
+
if (depth === 0) {
|
|
70
|
+
start = index > 0 && content[index - 1] === '\\' ? index - 1 : index
|
|
71
|
+
labelStart = index + 1
|
|
72
|
+
}
|
|
73
|
+
depth++
|
|
74
|
+
} else if (content[index] === ']' && depth > 0 && --depth === 0) {
|
|
75
|
+
const label = content.slice(labelStart, index).replace(/\\$/, '')
|
|
76
|
+
if (!label.trim() || /[\[\]\r\n]/.test(label) || /^\d+$/.test(label.trim())) continue
|
|
77
|
+
const command = label.split(/[++]/, 1)[0].trim()
|
|
78
|
+
if (command) spans.push({ start, end: index + 1, label, command })
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return spans
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function hasInlineCommands(content: string): boolean {
|
|
85
|
+
return inlineCommandSpans(content).length > 0
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Escape surrounding prose while rendering boxed commands as inline links. */
|
|
89
|
+
export function renderInlineCommands(content: string): string {
|
|
90
|
+
const parts: string[] = []
|
|
91
|
+
let offset = 0
|
|
92
|
+
for (const span of inlineCommandSpans(content)) {
|
|
93
|
+
parts.push(escapeMarkdown(content.slice(offset, span.start)))
|
|
94
|
+
parts.push('[' + escapeMarkdown(span.label) + '](' + inlineCommandUrl(span.command) + ')')
|
|
95
|
+
offset = span.end
|
|
96
|
+
}
|
|
97
|
+
parts.push(escapeMarkdown(content.slice(offset)))
|
|
98
|
+
return parts.join('')
|
|
99
|
+
}
|
|
100
|
+
|
|
55
101
|
export function pageCommandLines(nav: PageNavData): string[] {
|
|
56
102
|
return [
|
|
57
103
|
...(nav.previousCommand ? [nav.previousCommand] : []),
|
|
@@ -65,11 +111,6 @@ export function renderQQPageMarkdown(data: MsgMessage, showGreeting: boolean): s
|
|
|
65
111
|
const listPattern = /^\[(\d+)\]\s*([^\r\n]+)$/
|
|
66
112
|
const pagePattern = /^第\s*\d+\s*\/\s*\d+\s*页(?:\s*[||]\s*共\s*(\d+)\s*([项条]))?$/
|
|
67
113
|
const title = data.line.find(item => item.type === MessageType.title)
|
|
68
|
-
const summaryLine = data.line.find(item => item.type === MessageType.line && pagePattern.test(item.content))
|
|
69
|
-
const summary = summaryLine && pagePattern.exec(summaryLine.content)
|
|
70
|
-
const listCount = data.line.filter(item => item.type === MessageType.line && listPattern.test(item.content)).length
|
|
71
|
-
const count = summary?.[1] ?? (nav?.total === 1 ? String(listCount) : undefined)
|
|
72
|
-
const countUnit = summary?.[2] ?? '项'
|
|
73
114
|
const isLeaderboardDirectory = !!nav?.entries?.length
|
|
74
115
|
&& /^(?:🏆)?排行榜$/u.test(compactDisplayText(title?.content ?? ''))
|
|
75
116
|
const sections: string[] = []
|
|
@@ -90,22 +131,18 @@ export function renderQQPageMarkdown(data: MsgMessage, showGreeting: boolean): s
|
|
|
90
131
|
switch (item.type) {
|
|
91
132
|
case MessageType.title:
|
|
92
133
|
flush()
|
|
93
|
-
|
|
94
|
-
sections.push(`## ${item.content.includes('🏆') ? '' : '🏆'}${escapeMarkdown(compactDisplayText(item.content))}`)
|
|
95
|
-
sections.push(`> 共${nav!.entries!.length}个榜单·点击榜名查看`)
|
|
96
|
-
} else {
|
|
97
|
-
sections.push(`**${escapeMarkdown(compactDisplayText(item.content))}${count !== undefined ? `·${count}${countUnit}` : ''}**`)
|
|
98
|
-
}
|
|
134
|
+
sections.push(renderQQTitle(`${isLeaderboardDirectory && !item.content.includes('🏆') ? '🏆' : ''}${item.content}`))
|
|
99
135
|
break
|
|
100
136
|
case MessageType.line: {
|
|
101
137
|
const listEntry = listPattern.exec(item.content)
|
|
102
138
|
if (listEntry) {
|
|
103
139
|
flushBody()
|
|
104
140
|
const command = entryCommands.get(lineIndex)
|
|
105
|
-
const
|
|
141
|
+
const content = compactListLabel(listEntry[2])
|
|
142
|
+
const label = escapeMarkdown(content)
|
|
106
143
|
const name = command
|
|
107
144
|
? `[${label}](${inlineCommandUrl(command)})`
|
|
108
|
-
:
|
|
145
|
+
: renderInlineCommands(content)
|
|
109
146
|
// Keep the numeric marker as Markdown syntax; escape only the item text.
|
|
110
147
|
listLines.push(`${listEntry[1]}. ${name}`)
|
|
111
148
|
break
|
|
@@ -125,35 +162,36 @@ export function renderQQPageMarkdown(data: MsgMessage, showGreeting: boolean): s
|
|
|
125
162
|
const page = pagePattern.exec(item.content)
|
|
126
163
|
if (page && nav) {
|
|
127
164
|
flushBody()
|
|
128
|
-
pageFooter = `**第${nav.current}/${nav.total}
|
|
165
|
+
pageFooter = `**第${nav.current}/${nav.total}页**`
|
|
129
166
|
hasPageSummary = true
|
|
130
167
|
break
|
|
131
168
|
}
|
|
132
169
|
const command = entryCommands.get(lineIndex)
|
|
133
170
|
bodyLines.push(command
|
|
134
|
-
? `[${escapeMarkdown(
|
|
135
|
-
:
|
|
171
|
+
? `[${escapeMarkdown(compactListLabel(item.content))}](${inlineCommandUrl(command)})`
|
|
172
|
+
: renderInlineCommands(item.content))
|
|
136
173
|
break
|
|
137
174
|
}
|
|
138
175
|
case MessageType.error:
|
|
139
176
|
flush()
|
|
140
|
-
sections.push(`❌ 出错了!错误原因:${
|
|
177
|
+
sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`)
|
|
141
178
|
break
|
|
142
179
|
}
|
|
143
180
|
}
|
|
144
181
|
flush()
|
|
145
182
|
if (nav && !hasPageSummary && (nav.total > 1 || nav.previousCommand || nav.nextCommand))
|
|
146
183
|
pageFooter = `**第${nav.current}/${nav.total}页**`
|
|
147
|
-
const previousLink = nav?.previousCommand
|
|
148
|
-
|
|
184
|
+
const previousLink = nav?.previousCommand
|
|
185
|
+
? `[${nav.previousCommand === '上一个' ? '上一个' : '上一页'}](${inlineCommandUrl(nav.previousCommand)})`
|
|
186
|
+
: undefined
|
|
187
|
+
const nextLink = nav?.nextCommand
|
|
188
|
+
? `[${nav.nextCommand === '下一个' ? '下一个' : '下一页'}](${inlineCommandUrl(nav.nextCommand)})`
|
|
189
|
+
: undefined
|
|
149
190
|
const helpLink = hasHelp ? `[指令](${inlineCommandUrl('指令')})` : undefined
|
|
150
191
|
const footer = pageFooter
|
|
151
192
|
? [pageFooter, previousLink, nextLink, helpLink].filter(Boolean).join('·')
|
|
152
193
|
: helpLink ? `更多操作:${helpLink}` : undefined
|
|
153
|
-
if (footer)
|
|
154
|
-
if (isLeaderboardDirectory) sections.push('***')
|
|
155
|
-
sections.push(footer)
|
|
156
|
-
}
|
|
194
|
+
if (footer) sections.push(footer)
|
|
157
195
|
return sections.join('\n\n') || ' '
|
|
158
196
|
}
|
|
159
197
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Session } from 'koishi'
|
|
2
|
+
import { MessageType } from '../../shared/face'
|
|
3
|
+
import type { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
4
|
+
import { escapeMarkdown, renderInlineCommands, renderQQTitle, sendQQMarkdown } from './qqPageMessage'
|
|
5
|
+
|
|
6
|
+
export function hasStoryCodeBlock(data: Pick<MsgMessage, 'line'>): boolean {
|
|
7
|
+
return data.line.some(item => item.type === MessageType.line && item.codeBlock === true)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function fencedStory(content: string): string {
|
|
11
|
+
const story = content.replace(/\r\n?/g, '\n')
|
|
12
|
+
// A story may itself contain backticks. A longer fence keeps its entire text
|
|
13
|
+
// inside the same block without altering the source used by plain fallback.
|
|
14
|
+
const longestRun = Math.max(0, ...Array.from(story.matchAll(/`+/g), match => match[0].length))
|
|
15
|
+
const fence = '`'.repeat(Math.max(3, longestRun + 1))
|
|
16
|
+
return fence + '\n' + story + (story.endsWith('\n') ? '' : '\n') + fence
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Only marked story lines become code blocks; result summaries remain prose. */
|
|
20
|
+
export function renderQQStoryMarkdown(data: MsgMessage, showGreeting: boolean): string {
|
|
21
|
+
const sections: string[] = []
|
|
22
|
+
const bodyLines: string[] = []
|
|
23
|
+
const flushBody = () => {
|
|
24
|
+
if (bodyLines.length) sections.push(bodyLines.splice(0).join(' \n'))
|
|
25
|
+
}
|
|
26
|
+
if (showGreeting) sections.push(escapeMarkdown('Ciallo~(∠・ω< )'))
|
|
27
|
+
for (const item of data.line) {
|
|
28
|
+
switch (item.type) {
|
|
29
|
+
case MessageType.title:
|
|
30
|
+
flushBody()
|
|
31
|
+
sections.push(renderQQTitle(item.content))
|
|
32
|
+
break
|
|
33
|
+
case MessageType.line:
|
|
34
|
+
if (item.codeBlock) {
|
|
35
|
+
flushBody()
|
|
36
|
+
sections.push(fencedStory(item.content))
|
|
37
|
+
} else {
|
|
38
|
+
for (const line of item.content.replace(/\r\n?/g, '\n').split('\n')) {
|
|
39
|
+
if (!line.trim()) flushBody()
|
|
40
|
+
else bodyLines.push(renderInlineCommands(line))
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
break
|
|
44
|
+
case MessageType.error:
|
|
45
|
+
flushBody()
|
|
46
|
+
sections.push('❌ 出错了!错误原因:' + renderInlineCommands(item.content) + ',请反馈至联系开发者')
|
|
47
|
+
break
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
flushBody()
|
|
51
|
+
return sections.join('\n\n') || ' '
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function sendQQStory(session: Session, data: MsgMessage): Promise<void> {
|
|
55
|
+
await sendQQMarkdown(session, renderQQStoryMarkdown(data, session.event?.channel?.type === 0))
|
|
56
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Session } from 'koishi'
|
|
2
2
|
import { MessageType } from '../../shared/face'
|
|
3
3
|
import type { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
4
|
-
import { escapeMarkdown, inlineCommandUrl, sendQQMarkdown } from './qqPageMessage'
|
|
4
|
+
import { escapeMarkdown, inlineCommandUrl, renderInlineCommands, renderQQTitle, sendQQMarkdown } from './qqPageMessage'
|
|
5
5
|
|
|
6
6
|
/** Render only explicitly marked transactions; line content never enables this channel. */
|
|
7
7
|
export function renderQQTransactionMarkdown(data: MsgMessage, showGreeting: boolean): string {
|
|
@@ -19,23 +19,22 @@ export function renderQQTransactionMarkdown(data: MsgMessage, showGreeting: bool
|
|
|
19
19
|
for (const item of data.line) {
|
|
20
20
|
if (item.type === MessageType.title) {
|
|
21
21
|
flush()
|
|
22
|
-
sections.push(
|
|
22
|
+
sections.push(renderQQTitle(item.content))
|
|
23
23
|
continue
|
|
24
24
|
}
|
|
25
25
|
if (item.type === MessageType.error) {
|
|
26
26
|
flush()
|
|
27
|
-
sections.push(`❌ 出错了!错误原因:${
|
|
27
|
+
sections.push(`❌ 出错了!错误原因:${renderInlineCommands(item.content)},请反馈至联系开发者`)
|
|
28
28
|
continue
|
|
29
29
|
}
|
|
30
30
|
if (item.type !== MessageType.line) continue
|
|
31
|
-
//
|
|
32
|
-
// material text remain escaped display text rather than executable commands.
|
|
31
|
+
// Keep the common trade actions grouped; other bracketed commands render inline.
|
|
33
32
|
const action = data.transaction && (item.content === '[是]确认交易'
|
|
34
33
|
? { command: '是', label: '确认交易' }
|
|
35
34
|
: item.content === '[否]取消交易' ? { command: '否', label: '取消交易' } : undefined)
|
|
36
35
|
if (action) {
|
|
37
36
|
flushBody()
|
|
38
|
-
actions.push(`[${action.
|
|
37
|
+
actions.push(`[${action.label}](${inlineCommandUrl(action.command)})`)
|
|
39
38
|
continue
|
|
40
39
|
}
|
|
41
40
|
flushActions()
|
|
@@ -50,7 +49,7 @@ export function renderQQTransactionMarkdown(data: MsgMessage, showGreeting: bool
|
|
|
50
49
|
} else {
|
|
51
50
|
// Preserve the server's quoted amounts, names and descriptions verbatim
|
|
52
51
|
// apart from Markdown escaping; rendering never recalculates payments.
|
|
53
|
-
bodyLines.push(
|
|
52
|
+
bodyLines.push(renderInlineCommands(item.content))
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
flush()
|
|
@@ -1,19 +1,27 @@
|
|
|
1
|
+
import { MessageType } from '../../shared/face'
|
|
1
2
|
import { AnalysisMsg } from '../../shared/bot/analysisMsg'
|
|
2
3
|
import { MsgMessage } from '../../shared/bot/MsgMessage'
|
|
3
4
|
import sessions, { sessionKey } from '../sessions'
|
|
4
5
|
import handel_base from './handel_base'
|
|
5
|
-
import { canSendQQPage, pageCommandLines, sendQQPage } from './qqPageMessage'
|
|
6
|
+
import { canSendQQPage, hasInlineCommands, pageCommandLines, sendQQPage } from './qqPageMessage'
|
|
6
7
|
import { sendQQTransaction } from './qqTransactionMessage'
|
|
8
|
+
import { sendQQCommand } from './qqCommandMessage'
|
|
9
|
+
import { hasStoryCodeBlock, sendQQStory } from './qqStoryMessage'
|
|
7
10
|
|
|
8
11
|
export default class handelr_text extends handel_base {
|
|
9
12
|
async start(data: MsgMessage) {
|
|
10
13
|
const content = new AnalysisMsg().parse(data)
|
|
11
14
|
const session = sessions.get(sessionKey(data.botId, data.userId, data.channelId))
|
|
12
|
-
|
|
15
|
+
const inlineCommands = data.line.some(item => (item.type === MessageType.line || item.type === MessageType.error) && hasInlineCommands(item.content))
|
|
16
|
+
const story = hasStoryCodeBlock(data)
|
|
17
|
+
if ((story || data.commandHelp || data.transaction || data.pageNav || inlineCommands) && canSendQQPage(session, data)) {
|
|
13
18
|
const sendStartedAt = this.timing ? performance.now() : 0
|
|
14
19
|
try {
|
|
15
|
-
if (
|
|
16
|
-
else await
|
|
20
|
+
if (story) await sendQQStory(session, data)
|
|
21
|
+
else if (data.commandHelp) await sendQQCommand(session, data)
|
|
22
|
+
else if (data.transaction) await sendQQTransaction(session, data)
|
|
23
|
+
else if (data.pageNav) await sendQQPage(session, data)
|
|
24
|
+
else await sendQQCommand(session, data)
|
|
17
25
|
if (this.timing) {
|
|
18
26
|
this.timing.delivery = 'qq-markdown'
|
|
19
27
|
this.timing.sendMs = performance.now() - sendStartedAt
|
|
@@ -22,7 +30,7 @@ export default class handelr_text extends handel_base {
|
|
|
22
30
|
} catch (error) {
|
|
23
31
|
this.ctx.logger.warn('QQ Markdown 发送失败,改用普通文字', error instanceof Error ? error.message : error)
|
|
24
32
|
}
|
|
25
|
-
const commands = !data.transaction && data.pageNav ? pageCommandLines(data.pageNav) : []
|
|
33
|
+
const commands = !story && !data.commandHelp && !data.transaction && data.pageNav ? pageCommandLines(data.pageNav) : []
|
|
26
34
|
await this.send(data, commands.length ? `${content.trimEnd()}\n${commands.join('\n')}` : content)
|
|
27
35
|
return
|
|
28
36
|
}
|
|
@@ -20,7 +20,7 @@ export interface ServiceType {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const serviceProto: ServiceProto<ServiceType> = {
|
|
23
|
-
"version":
|
|
23
|
+
"version": 14,
|
|
24
24
|
"services": [
|
|
25
25
|
{
|
|
26
26
|
"id": 0,
|
|
@@ -163,6 +163,14 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
|
|
163
163
|
},
|
|
164
164
|
"optional": true
|
|
165
165
|
},
|
|
166
|
+
{
|
|
167
|
+
"id": 9,
|
|
168
|
+
"name": "commandHelp",
|
|
169
|
+
"type": {
|
|
170
|
+
"type": "Boolean"
|
|
171
|
+
},
|
|
172
|
+
"optional": true
|
|
173
|
+
},
|
|
166
174
|
{
|
|
167
175
|
"id": 1,
|
|
168
176
|
"name": "line",
|
|
@@ -899,6 +907,14 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
|
|
899
907
|
"type": {
|
|
900
908
|
"type": "String"
|
|
901
909
|
}
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
"id": 2,
|
|
913
|
+
"name": "codeBlock",
|
|
914
|
+
"type": {
|
|
915
|
+
"type": "Boolean"
|
|
916
|
+
},
|
|
917
|
+
"optional": true
|
|
902
918
|
}
|
|
903
919
|
]
|
|
904
920
|
},
|
|
@@ -1058,4 +1074,4 @@ export const serviceProto: ServiceProto<ServiceType> = {
|
|
|
1058
1074
|
]
|
|
1059
1075
|
}
|
|
1060
1076
|
}
|
|
1061
|
-
};
|
|
1077
|
+
};
|
package/src/shared/face/index.ts
CHANGED
|
@@ -73,6 +73,8 @@ export interface ITitleMessage {
|
|
|
73
73
|
export interface ILineMessage {
|
|
74
74
|
type: MessageType.line;
|
|
75
75
|
content: string;
|
|
76
|
+
/** 原文长故事:支持的平台使用 Markdown 代码块,其余平台按普通文字显示。 */
|
|
77
|
+
codeBlock?: boolean;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
/** 错误消息项 **/
|
|
@@ -105,6 +107,8 @@ export interface IMessage {
|
|
|
105
107
|
pageNav?: PageNavData;
|
|
106
108
|
/** 通用交易确认、结果与失败消息;客户端可使用交易 Markdown 展示。 */
|
|
107
109
|
transaction?: boolean;
|
|
110
|
+
/** 指令系统与指令提示;客户端可用 Markdown 展示可点击的方框指令。 */
|
|
111
|
+
commandHelp?: boolean;
|
|
108
112
|
/** 消息项列表 **/
|
|
109
113
|
line: IMessageItem[];
|
|
110
114
|
}
|