dsh-qq-tools 0.9.2 → 0.9.5
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/shared.js +44 -21
- package/package.json +4 -1
package/lib/shared.js
CHANGED
|
@@ -450,6 +450,29 @@ export function formatIncomingLine(name, userId, text, messageId) {
|
|
|
450
450
|
return `${who}(${qq}):${text || '(空)'}` + (messageId != null ? `(消息 #${messageId})` : '')
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
+
// 回复风格:群聊要像随手打字,短、分条、不刷屏地 @ 和引用。
|
|
454
|
+
export const CHAT_STYLE = '回复像群友随手打字:想连发几条就**多次调用同一个工具**(一次调用只发一条),每条一句短话(十几二十个字),最多三次调用;'
|
|
455
|
+
+ '不要句号收尾,不要写长段落,不要 Markdown,不要把昵称或 QQ 号写进正文,不要每句都 @ 或引用——'
|
|
456
|
+
+ '只有确实要指代某条消息时才传 reply_to,只有必须提醒某个人时才传 at_user_ids。'
|
|
457
|
+
|
|
458
|
+
const CHAT_BUBBLE_CHARS = 80
|
|
459
|
+
|
|
460
|
+
// 一条消息的清理:压掉复制来的对齐空格和多行,去掉正文开头重复的 @昵称。
|
|
461
|
+
function cleanBubble(value) {
|
|
462
|
+
return String(value || '')
|
|
463
|
+
.replace(/\r/g, '')
|
|
464
|
+
.replace(/[\u3000\t ]+/g, ' ')
|
|
465
|
+
.replace(/\s*\n\s*/g, ' ')
|
|
466
|
+
.trim()
|
|
467
|
+
.replace(/^@[^\s@]{1,40}\s+/, '')
|
|
468
|
+
.slice(0, CHAT_BUBBLE_CHARS)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// 一次工具调用只发一条:想分几条说就让模型多次调用工具。
|
|
472
|
+
export function chatText(args) {
|
|
473
|
+
return cleanBubble(args && args.message)
|
|
474
|
+
}
|
|
475
|
+
|
|
453
476
|
|
|
454
477
|
function renderTool(_args, v) {
|
|
455
478
|
if (!v || !v.ok) return [{ type: 'text', text: 'QQ 工具失败:' + String((v && v.error) || '未知错误') }]
|
|
@@ -924,17 +947,17 @@ export function makeSendTool(onSent) {
|
|
|
924
947
|
return {
|
|
925
948
|
name: 'qq_send_group_message',
|
|
926
949
|
description:
|
|
927
|
-
'
|
|
950
|
+
'在指定群里发一条消息。发之前先确认群号;只发到允许的群。想分几条说就**多次调用本工具**,一次一条。' +
|
|
928
951
|
'reply_to 传消息前面的 #编号 可以引用那条消息;at_user_ids 传 QQ 号可以 @ 人,at_all=true 是 @全体成员。' +
|
|
929
|
-
|
|
952
|
+
CHAT_STYLE +
|
|
930
953
|
'不想说话就不要调用这个工具;调用后不要再重复说自己发了什么。',
|
|
931
954
|
parameters: {
|
|
932
955
|
type: 'object',
|
|
933
956
|
properties: {
|
|
934
957
|
group_id: { type: 'number', description: '群号' },
|
|
935
|
-
message: { type: 'string', description: '
|
|
936
|
-
reply_to: { type: 'string', description: '要引用的消息 ID(消息前面的 #编号),可选' },
|
|
937
|
-
at_user_ids: { type: 'array', items: { type: 'number' }, description: '要 @ 的 QQ 号列表,可选' },
|
|
958
|
+
message: { type: 'string', description: '这一条的文字,一句短话;只 @ 不说话时可以留空' },
|
|
959
|
+
reply_to: { type: 'string', description: '要引用的消息 ID(消息前面的 #编号),可选', },
|
|
960
|
+
at_user_ids: { type: 'array', items: { type: 'number' }, description: '要 @ 的 QQ 号列表,可选', },
|
|
938
961
|
at_all: { type: 'boolean', description: 'true = @全体成员,可选' },
|
|
939
962
|
},
|
|
940
963
|
required: ['group_id'],
|
|
@@ -944,7 +967,7 @@ export function makeSendTool(onSent) {
|
|
|
944
967
|
async execute(args) {
|
|
945
968
|
const cfg = loadCfg()
|
|
946
969
|
const groupId = Number(args && args.group_id)
|
|
947
|
-
const text =
|
|
970
|
+
const text = chatText(args)
|
|
948
971
|
const atIds = [...new Set(
|
|
949
972
|
(Array.isArray(args && args.at_user_ids) ? args.at_user_ids : [])
|
|
950
973
|
.map((x) => Number(x)).filter((x) => Number.isInteger(x) && x > 0),
|
|
@@ -954,12 +977,12 @@ export function makeSendTool(onSent) {
|
|
|
954
977
|
if (!text && !atIds.length && !atAll) return { ok: false, error: '消息是空的' }
|
|
955
978
|
if (!allowedGroup(groupId, cfg)) return { ok: false, error: '这个群不在允许列表里' }
|
|
956
979
|
try {
|
|
957
|
-
const message = []
|
|
958
980
|
const replyTo = args && args.reply_to
|
|
981
|
+
const message = []
|
|
959
982
|
if (replyTo != null && replyTo !== '') message.push({ type: 'reply', data: { id: String(replyTo) } })
|
|
960
983
|
if (atAll) message.push({ type: 'at', data: { qq: 'all' } })
|
|
961
984
|
else for (const qq of atIds) message.push({ type: 'at', data: { qq: String(qq) } })
|
|
962
|
-
if (text) message.push({ type: 'text', data: { text
|
|
985
|
+
if (text) message.push({ type: 'text', data: { text } })
|
|
963
986
|
const data = await onebot('send_group_msg', { group_id: groupId, message }, cfg)
|
|
964
987
|
try { if (typeof onSent === 'function') onSent(groupId, '发群消息') } catch (error) {}
|
|
965
988
|
return {
|
|
@@ -1125,15 +1148,15 @@ export function makeSendPrivateTool() {
|
|
|
1125
1148
|
return {
|
|
1126
1149
|
name: 'qq_send_private_message',
|
|
1127
1150
|
description:
|
|
1128
|
-
'给某个 QQ 好友发一条私聊消息。user_id 从 qq_list_friends
|
|
1151
|
+
'给某个 QQ 好友发一条私聊消息。user_id 从 qq_list_friends 或私聊唤醒提示里拿。想分几条说就多次调用本工具,一次一条。' +
|
|
1129
1152
|
'reply_to 传消息前面的 #编号 可以引用那条消息。' +
|
|
1130
|
-
|
|
1153
|
+
CHAT_STYLE +
|
|
1131
1154
|
'不想说话就不要调用;调用后不要再重复说自己发了什么。',
|
|
1132
1155
|
parameters: {
|
|
1133
1156
|
type: 'object',
|
|
1134
1157
|
properties: {
|
|
1135
1158
|
user_id: { type: 'number', description: '对方的 QQ 号' },
|
|
1136
|
-
message: { type: 'string', description: '
|
|
1159
|
+
message: { type: 'string', description: '这一条的文字,一句短话' },
|
|
1137
1160
|
reply_to: { type: 'string', description: '要引用的消息 ID(消息前面的 #编号),可选' },
|
|
1138
1161
|
group_id: { type: 'number', description: '临时会话的来源群号,可选;普通好友私聊不用传' },
|
|
1139
1162
|
},
|
|
@@ -1144,16 +1167,16 @@ export function makeSendPrivateTool() {
|
|
|
1144
1167
|
async execute(args) {
|
|
1145
1168
|
const cfg = loadCfg()
|
|
1146
1169
|
const userId = Number(args && args.user_id)
|
|
1147
|
-
const text =
|
|
1170
|
+
const text = chatText(args)
|
|
1148
1171
|
const groupId = args && args.group_id != null ? Number(args.group_id) : 0
|
|
1149
1172
|
if (!Number.isFinite(userId)) return { ok: false, error: 'user_id 不是有效 QQ 号' }
|
|
1150
1173
|
if (!text) return { ok: false, error: '消息是空的' }
|
|
1151
1174
|
if (!allowedPrivate(userId, cfg)) return { ok: false, error: '这个 QQ 不在允许列表里' }
|
|
1152
1175
|
try {
|
|
1153
|
-
const message = []
|
|
1154
1176
|
const replyTo = args && args.reply_to
|
|
1177
|
+
const message = []
|
|
1155
1178
|
if (replyTo != null && replyTo !== '') message.push({ type: 'reply', data: { id: String(replyTo) } })
|
|
1156
|
-
message.push({ type: 'text', data: { text
|
|
1179
|
+
message.push({ type: 'text', data: { text } })
|
|
1157
1180
|
const body = { user_id: userId, message }
|
|
1158
1181
|
if (groupId) body.group_id = groupId
|
|
1159
1182
|
const data = await onebot('send_private_msg', body, cfg)
|
|
@@ -3034,13 +3057,13 @@ async function pollMentions(ctx) {
|
|
|
3034
3057
|
if (hasActiveTool('qq_send_face')) sendTools.push('qq_send_face')
|
|
3035
3058
|
const sendHint = sendTools.length ? '想说话可以用 ' + sendTools.join(' / ') + ' 回复;' : ''
|
|
3036
3059
|
const quoteHint = hasActiveTool('qq_send_group_message')
|
|
3037
|
-
? '
|
|
3060
|
+
? '确实要指代某条消息时才传 reply_to=消息编号;必须提醒某个人时才传 at_user_ids=[QQ号];'
|
|
3038
3061
|
: ''
|
|
3039
3062
|
const prompt =
|
|
3040
3063
|
'【群聊 @ 提醒】有人 @ 了你:\n' +
|
|
3041
3064
|
lines.join('\n') +
|
|
3042
3065
|
'\n' + lookHint() + '再决定要不要回复、回哪个群。' + sendHint + quoteHint + '不想说话就安静待着;' +
|
|
3043
|
-
'
|
|
3066
|
+
'不要提工具和私聊内容。' + CHAT_STYLE
|
|
3044
3067
|
const message = {
|
|
3045
3068
|
id: randomUUID(),
|
|
3046
3069
|
role: 'user',
|
|
@@ -3058,9 +3081,9 @@ async function pollMentions(ctx) {
|
|
|
3058
3081
|
'【群聊后续】你刚才在群「' + group + '」里说过话,现在又有人说话:\n' +
|
|
3059
3082
|
lines2.join('\n') +
|
|
3060
3083
|
'\n想接就接;' +
|
|
3061
|
-
(hasActiveTool('qq_send_group_message') ? '想说话用 qq_send_group_message
|
|
3084
|
+
(hasActiveTool('qq_send_group_message') ? '想说话用 qq_send_group_message 发到这个群,确实要指代某条才传 reply_to=消息编号,必须提醒某人才传 at_user_ids=[QQ号];' : '') +
|
|
3062
3085
|
(hasActiveTool('qq_send_face') ? '想回表情用 qq_send_face;' : '') +
|
|
3063
|
-
'
|
|
3086
|
+
'不想接就安静待着。' + CHAT_STYLE
|
|
3064
3087
|
await agent.followup({
|
|
3065
3088
|
id: randomUUID(),
|
|
3066
3089
|
role: 'user',
|
|
@@ -3136,7 +3159,7 @@ async function handlePoke(ctx, ev) {
|
|
|
3136
3159
|
if (hasActiveTool('qq_send_poke')) actions.push(`想戳回去用 qq_send_poke(group_id=${groupId}, user_id=${sender})`)
|
|
3137
3160
|
prompt = `【戳一戳】群「${groupName}」里 ${name}(${sender}) 戳了你一下。`
|
|
3138
3161
|
+ (actions.length ? actions.join(';') + '。不想回就安静待着。' : '不用回复。')
|
|
3139
|
-
+
|
|
3162
|
+
+ CHAT_STYLE
|
|
3140
3163
|
} else {
|
|
3141
3164
|
const actions = []
|
|
3142
3165
|
if (hasActiveTool('qq_send_private_message')) actions.push(`想回就用 qq_send_private_message(user_id=${sender})`)
|
|
@@ -3144,7 +3167,7 @@ async function handlePoke(ctx, ev) {
|
|
|
3144
3167
|
if (hasActiveTool('qq_send_poke')) actions.push(`想戳回去用 qq_send_poke(user_id=${sender})`)
|
|
3145
3168
|
prompt = `【戳一戳】${sender} 在私聊里戳了你一下。`
|
|
3146
3169
|
+ (actions.length ? actions.join(';') + '。不想回就安静待着。' : '不用回复。')
|
|
3147
|
-
+
|
|
3170
|
+
+ CHAT_STYLE
|
|
3148
3171
|
}
|
|
3149
3172
|
const sourceKind = groupId ? 'qq-poke' : 'qq-private'
|
|
3150
3173
|
await agent.followup({
|
|
@@ -3200,7 +3223,7 @@ async function handlePrivateMessage(ctx, ev, admitImages) {
|
|
|
3200
3223
|
const prompt =
|
|
3201
3224
|
'【QQ ' + where + '】' + formatIncomingLine(name, userId, text, ev.message_id) +
|
|
3202
3225
|
'\n' + (actions.length ? actions.join(';') + '。' : '') +
|
|
3203
|
-
'
|
|
3226
|
+
'不想回就安静待着;不要提工具。' + CHAT_STYLE
|
|
3204
3227
|
const content = [{ type: 'text', text: prompt }]
|
|
3205
3228
|
if (typeof admitImages === 'function') {
|
|
3206
3229
|
// 和群/私聊记录一样:先保证最近两张动图能进来,剩下的名额给静图。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-qq-tools",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.5",
|
|
4
4
|
"description": "给大肥鱼会话用的 QQ 群/私聊工具:列群、看群记录、发群消息(可引用/@)、发表情、收藏表情(SnowLuma 收藏夹)、好友/私聊记录、发私聊消息、私聊唤醒、群成员、戳一戳感知/戳回去、更改头像、好友管理(资料/备注/分组/删好友/名片赞/好友申请)、QQ 空间说说(列表/详情/发表/评论/点赞/权限)、挂载会话选择。11 个工具组加 1 个设置组件,可在插件页逐个开关。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -98,3 +98,6 @@
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|