@folotoy/folotoy-openclaw-plugin 0.2.1 → 0.3.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/package.json +1 -1
- package/src/index.ts +59 -4
- package/src/mqtt.ts +4 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { OpenClawPluginApi, ChannelPlugin } from 'openclaw/plugin-sdk/core'
|
|
2
|
-
import { resolveCredentials, createMqttClient, buildInboundTopic, buildOutboundTopic } from './mqtt.js'
|
|
2
|
+
import { resolveCredentials, createMqttClient, buildInboundTopic, buildOutboundTopic, buildNotificationTopic } from './mqtt.js'
|
|
3
3
|
import { DEFAULT_MQTT_HOST, DEFAULT_MQTT_PORT, flatToPluginConfig } from './config.js'
|
|
4
4
|
import type { FlatChannelConfig } from './config.js'
|
|
5
5
|
import type { MqttClient } from 'mqtt'
|
|
@@ -16,6 +16,36 @@ type OutboundMessage = {
|
|
|
16
16
|
outParams: { content: string; recording_id: number; order: number; is_finished: boolean }
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
type NotificationMessage = {
|
|
20
|
+
msgId: number
|
|
21
|
+
identifier: 'send_notification'
|
|
22
|
+
outParams: { text: string }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Pick a soothing acknowledgment that loosely matches the input. */
|
|
26
|
+
function pickSoothingReply(text: string): string {
|
|
27
|
+
const t = text.toLowerCase()
|
|
28
|
+
|
|
29
|
+
if (/难过|伤心|哭|不开心|sad|upset|cry/.test(t))
|
|
30
|
+
return '抱抱你,我在听呢,让我想想怎么帮你。'
|
|
31
|
+
if (/害怕|恐惧|怕|scared|afraid/.test(t))
|
|
32
|
+
return '别怕,有我在呢,让我想一想。'
|
|
33
|
+
if (/生气|愤怒|烦|angry|mad/.test(t))
|
|
34
|
+
return '我理解你的感受,让我来帮你想想办法。'
|
|
35
|
+
if (/累|疲|困|tired|exhausted/.test(t))
|
|
36
|
+
return '辛苦了,休息一下,我来帮你想。'
|
|
37
|
+
if (/无聊|没意思|bored/.test(t))
|
|
38
|
+
return '我来陪你聊聊吧,让我想想。'
|
|
39
|
+
if (/谢|感谢|thank/.test(t))
|
|
40
|
+
return '不客气呀,让我想想还能帮你什么。'
|
|
41
|
+
if (/你好|嗨|hello|hi|hey/.test(t))
|
|
42
|
+
return '你好呀!让我想想怎么回答你。'
|
|
43
|
+
if (/帮|help|怎么办/.test(t))
|
|
44
|
+
return '没问题,让我帮你想想办法。'
|
|
45
|
+
|
|
46
|
+
return '好的,让我想一想,马上回复你。'
|
|
47
|
+
}
|
|
48
|
+
|
|
19
49
|
// Per-account MQTT clients and msgId counters
|
|
20
50
|
const activeClients = new Map<string, { client: MqttClient; toy_sn: string; nextMsgId: number }>()
|
|
21
51
|
|
|
@@ -104,7 +134,16 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
104
134
|
if (msg.identifier !== 'chat_input' || typeof msg.inputParams?.text !== 'string') return
|
|
105
135
|
|
|
106
136
|
const { msgId, inputParams: { text, recording_id } } = msg
|
|
107
|
-
let order =
|
|
137
|
+
let order = 1
|
|
138
|
+
|
|
139
|
+
// Send a quick soothing acknowledgment before AI processing (order=1).
|
|
140
|
+
// AI replies continue from order=2.
|
|
141
|
+
const ackMsg: OutboundMessage = {
|
|
142
|
+
msgId,
|
|
143
|
+
identifier: 'chat_output',
|
|
144
|
+
outParams: { content: pickSoothingReply(text), recording_id, order, is_finished: false },
|
|
145
|
+
}
|
|
146
|
+
client.publish(outboundTopic, JSON.stringify(ackMsg))
|
|
108
147
|
|
|
109
148
|
const inboundCtx = channelRuntime.reply.finalizeInboundContext({
|
|
110
149
|
Body: text,
|
|
@@ -172,9 +211,9 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
172
211
|
|
|
173
212
|
const outboundTopic = buildOutboundTopic(entry.toy_sn)
|
|
174
213
|
const msgId = entry.nextMsgId++
|
|
175
|
-
const outMsg
|
|
214
|
+
const outMsg = {
|
|
176
215
|
msgId,
|
|
177
|
-
identifier: 'chat_output',
|
|
216
|
+
identifier: 'chat_output' as const,
|
|
178
217
|
outParams: { content: text },
|
|
179
218
|
}
|
|
180
219
|
entry.client.publish(outboundTopic, JSON.stringify(outMsg))
|
|
@@ -183,6 +222,22 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
|
|
|
183
222
|
},
|
|
184
223
|
}
|
|
185
224
|
|
|
225
|
+
export function sendNotification({ text, accountId }: { text: string; accountId?: string }) {
|
|
226
|
+
const key = accountId ?? 'default'
|
|
227
|
+
const entry = activeClients.get(key)
|
|
228
|
+
if (!entry) throw new Error(`No active MQTT client for account "${key}"`)
|
|
229
|
+
|
|
230
|
+
const notificationTopic = buildNotificationTopic(entry.toy_sn)
|
|
231
|
+
const msgId = entry.nextMsgId++
|
|
232
|
+
const notifMsg: NotificationMessage = {
|
|
233
|
+
msgId,
|
|
234
|
+
identifier: 'send_notification',
|
|
235
|
+
outParams: { text },
|
|
236
|
+
}
|
|
237
|
+
entry.client.publish(notificationTopic, JSON.stringify(notifMsg))
|
|
238
|
+
return { channel: 'folotoy', messageId: String(msgId) }
|
|
239
|
+
}
|
|
240
|
+
|
|
186
241
|
export default (api: OpenClawPluginApi) => {
|
|
187
242
|
api.registerChannel({ plugin: folotoyChannel })
|
|
188
243
|
}
|
package/src/mqtt.ts
CHANGED
|
@@ -52,6 +52,10 @@ export function buildOutboundTopic(toy_sn: string): string {
|
|
|
52
52
|
return `/openapi/folotoy/${toy_sn}/thing/command/callAck`
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
export function buildNotificationTopic(toy_sn: string): string {
|
|
56
|
+
return `/openapi/folotoy/${toy_sn}/thing/event/post`
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
export async function createMqttClient(config: PluginConfig, credentials: MqttCredentials): Promise<MqttClient> {
|
|
56
60
|
const { host, port } = config.mqtt
|
|
57
61
|
const { username, password } = credentials
|