| | @@ -240,17 +240,22 @@ |
| 240 | 240 | |
| 241 | 241 | var msgID string |
| 242 | 242 | if id, ok := e.Tags.Get("msgid"); ok { |
| 243 | 243 | msgID = id |
| 244 | 244 | } |
| 245 | | - b.dispatch(Message{ |
| 245 | + msg := Message{ |
| 246 | 246 | At: e.Timestamp, |
| 247 | 247 | Channel: channel, |
| 248 | 248 | Nick: nick, |
| 249 | 249 | Text: e.Last(), |
| 250 | 250 | MsgID: msgID, |
| 251 | | - }) |
| 251 | + } |
| 252 | + // Read meta-type from IRCv3 client tags if present. |
| 253 | + if metaType, ok := e.Tags.Get("+scuttlebot/meta-type"); ok && metaType != "" { |
| 254 | + msg.Meta = &Meta{Type: metaType} |
| 255 | + } |
| 256 | + b.dispatch(msg) |
| 252 | 257 | }) |
| 253 | 258 | |
| 254 | 259 | b.client = c |
| 255 | 260 | |
| 256 | 261 | errCh := make(chan error, 1) |
| | @@ -360,26 +365,38 @@ |
| 360 | 365 | } |
| 361 | 366 | |
| 362 | 367 | // SendWithMeta sends a message to channel with optional structured metadata. |
| 363 | 368 | // IRC receives only the plain text; SSE subscribers receive the full message |
| 364 | 369 | // including meta for rich rendering in the web UI. |
| 370 | +// |
| 371 | +// When meta is present, key fields are attached as IRCv3 client-only tags |
| 372 | +// (+scuttlebot/meta-type) so any IRCv3 client can read them. |
| 365 | 373 | // |
| 366 | 374 | // When the server supports RELAYMSG (IRCv3), messages are attributed natively |
| 367 | 375 | // so other clients see the real sender nick. Falls back to [nick] prefix. |
| 368 | 376 | func (b *Bot) SendWithMeta(ctx context.Context, channel, text, senderNick string, meta *Meta) error { |
| 369 | 377 | if b.client == nil { |
| 370 | 378 | return fmt.Errorf("bridge: not connected") |
| 371 | 379 | } |
| 380 | + // Build optional IRCv3 tag prefix for meta-type. |
| 381 | + tagPrefix := "" |
| 382 | + if meta != nil && meta.Type != "" { |
| 383 | + tagPrefix = "@+scuttlebot/meta-type=" + meta.Type + " " |
| 384 | + } |
| 372 | 385 | if senderNick != "" && b.relaySep != "" { |
| 373 | 386 | // Use RELAYMSG for native attribution. |
| 374 | | - b.client.Cmd.SendRawf("RELAYMSG %s %s :%s", channel, senderNick, text) |
| 387 | + b.client.Cmd.SendRawf("%sRELAYMSG %s %s :%s", tagPrefix, channel, senderNick, text) |
| 375 | 388 | } else { |
| 376 | 389 | ircText := text |
| 377 | 390 | if senderNick != "" { |
| 378 | 391 | ircText = "[" + senderNick + "] " + text |
| 379 | 392 | } |
| 380 | | - b.client.Cmd.Message(channel, ircText) |
| 393 | + if tagPrefix != "" { |
| 394 | + b.client.Cmd.SendRawf("%sPRIVMSG %s :%s", tagPrefix, channel, ircText) |
| 395 | + } else { |
| 396 | + b.client.Cmd.Message(channel, ircText) |
| 397 | + } |
| 381 | 398 | } |
| 382 | 399 | |
| 383 | 400 | if senderNick != "" { |
| 384 | 401 | b.TouchUser(channel, senderNick) |
| 385 | 402 | } |
| 386 | 403 | |