@folotoy/folotoy-openclaw-plugin 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  "id": "folotoy-openclaw-plugin",
3
3
  "name": "FoloToy",
4
4
  "description": "Empower your FoloToy with OpenClaw AI capabilities.",
5
- "version": "0.2.0",
5
+ "version": "0.2.1",
6
6
  "channels": ["folotoy"],
7
7
  "configSchema": {
8
8
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@folotoy/folotoy-openclaw-plugin",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Empower your FoloToy with OpenClaw AI capabilities.",
5
5
  "keywords": [
6
6
  "folotoy",
package/src/config.ts CHANGED
@@ -31,7 +31,7 @@ export type FlatChannelConfig = {
31
31
  }
32
32
 
33
33
  export const DEFAULT_API_URL = 'https://api.folotoy.cn'
34
- export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? '198.19.249.25'
34
+ export const DEFAULT_MQTT_HOST = process.env.FOLOTOY_MQTT_HOST ?? 'f.qrc92.cn'
35
35
  export const DEFAULT_MQTT_PORT = 1883
36
36
 
37
37
  export function flatToPluginConfig(flat: FlatChannelConfig): PluginConfig {
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
 
@@ -81,6 +111,7 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
81
111
  }
82
112
 
83
113
  const mqttConfig = flatToPluginConfig(account)
114
+ log?.info?.(`Connecting to MQTT broker ${mqttConfig.mqtt.host}:${mqttConfig.mqtt.port}...`)
84
115
  const credentials = await resolveCredentials(mqttConfig)
85
116
  const client = await createMqttClient(mqttConfig, credentials)
86
117
  const inboundTopic = buildInboundTopic(credentials.toy_sn)
@@ -105,6 +136,15 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
105
136
  const { msgId, inputParams: { text, recording_id } } = msg
106
137
  let order = 0
107
138
 
139
+ // Send a quick soothing acknowledgment before AI processing
140
+ const notificationTopic = buildNotificationTopic(credentials.toy_sn)
141
+ const soothingMsg: NotificationMessage = {
142
+ msgId,
143
+ identifier: 'send_notification',
144
+ outParams: { text: pickSoothingReply(text) },
145
+ }
146
+ client.publish(notificationTopic, JSON.stringify(soothingMsg))
147
+
108
148
  const inboundCtx = channelRuntime.reply.finalizeInboundContext({
109
149
  Body: text,
110
150
  From: credentials.toy_sn,
@@ -171,9 +211,9 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
171
211
 
172
212
  const outboundTopic = buildOutboundTopic(entry.toy_sn)
173
213
  const msgId = entry.nextMsgId++
174
- const outMsg: OutboundMessage = {
214
+ const outMsg = {
175
215
  msgId,
176
- identifier: 'chat_output',
216
+ identifier: 'chat_output' as const,
177
217
  outParams: { content: text },
178
218
  }
179
219
  entry.client.publish(outboundTopic, JSON.stringify(outMsg))
@@ -182,6 +222,22 @@ const folotoyChannel: ChannelPlugin<FlatChannelConfig> = {
182
222
  },
183
223
  }
184
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
+
185
241
  export default (api: OpenClawPluginApi) => {
186
242
  api.registerChannel({ plugin: folotoyChannel })
187
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