feat(telegram): trigger-words, channel posts, message edit & topic posting (#1383)

- Fix image_generation nil-pointer crash on codex agents: the sentinel now
  carries a name-only Function so the many sites reading td.Function.Name never
  nil-deref; codex_build still branches on Type.
- Agent-declared trigger words in IDENTITY.md wake the bot in groups without an
  @mention (whole-word, Cyrillic-aware; text + caption), cached per-agent 60s.
- channel_post support with a synthetic sender + a recover() guard so a
  malformed update can't crash the gateway.
- message tool action=edit (editMessageText + editMessageCaption fallback),
  targeting the replied-to message via reply_to_message_id.
- message tool topic=<name> posts into a named forum topic; topics learned from
  forum_topic_created into channel_contacts and resolved by name.

Co-authored-by: skensel <skensel@MacBook-Pro-skensel.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-07-09 12:28:18 +07:00
1 parent 51ed7845e2
commit ba621391c2
19 files changed
+1062 -28

No files matched your search

+29
View File
@@ -718,6 +718,35 @@ func runGateway() {
if cs, ok := t.(tools.ChannelSenderAware); ok {
cs.SetChannelSender(channelMgr.SendToChannel)
}
if ce, ok := t.(tools.ChannelEditorAware); ok {
ce.SetChannelEditor(channelMgr.EditChannelMessage)
}
if tr, ok := t.(tools.TopicResolverAware); ok && pgStores != nil && pgStores.Contacts != nil {
contacts := pgStores.Contacts
tr.SetTopicResolver(func(ctx context.Context, channel, chatID, topicName string) (string, bool) {
list, err := contacts.ListContacts(ctx, store.ContactListOpts{
ChannelInstance: channel,
ContactType: "topic",
Limit: 500,
})
if err != nil {
return "", false
}
want := strings.ToLower(strings.TrimSpace(topicName))
for _, c := range list {
if c.SenderID != chatID || c.ThreadID == nil || c.DisplayName == nil {
continue
}
if strings.ToLower(strings.TrimSpace(*c.DisplayName)) == want {
return *c.ThreadID, true
}
}
return "", false
})
}
if tp, ok := t.(tools.TopicPosterAware); ok {
tp.SetTopicPoster(channelMgr.PostToTopic)
}
if tc, ok := t.(tools.ChannelTenantCheckerAware); ok {
tc.SetChannelTenantChecker(channelMgr.ChannelTenantID)
}