tinyrelayfile
tinyrelay/internal/daemon/chat_activity.go/

internal/daemon/chat_activity.go raw
1package daemon
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "time"
8
9 "github.com/FelineStateMachine/tinyrelay/internal/event"
10 "github.com/FelineStateMachine/tinyrelay/internal/storage"
11)
12
13func (t *Tenant) browseChatActivity(ctx context.Context, actor string, params []json.RawMessage) (any, error) {
14 var q struct {
15 Room string `json:"room"`
16 Root string `json:"root"`
17 }
18 if len(params) != 1 || json.Unmarshal(params[0], &q) != nil || q.Room == "" {
19 return nil, errors.New("invalid: chat activity room")
20 }
21 if _, _, err := t.roomFor(ctx, actor, q.Room); err != nil {
22 return nil, err
23 }
24 if q.Root != "" {
25 root, err := t.roomThreadRoot(ctx, actor, q.Room, q.Root)
26 if err != nil {
27 return nil, err
28 }
29 q.Root = root.ID
30 }
31 rows := []event.Event{}
32 for _, filter := range []event.Filter{
33 {Kinds: approvalKinds, Tags: map[string][]string{"h": {q.Room}}},
34 {Kinds: jobRequestKinds, Tags: map[string][]string{"h": {q.Room}}},
35 } {
36 page, err := t.store.Query(ctx, filter, storage.QueryOptions{Access: storage.Access{PubKeys: []string{actor}}, Limit: 1000})
37 if err != nil {
38 return nil, err
39 }
40 rows = append(rows, page.Events...)
41 }
42 session := browseSession(t, actor)
43 jobs, approvals := []jobItem{}, []approvalItem{}
44 now := time.Now().Unix()
45 roots := map[string]string{}
46 for _, row := range rows {
47 _, isApproval := approvalRequest(row)
48 if !event.IsJobRequest(row.Kind) && !isApproval {
49 continue
50 }
51 if !t.gate.CanSee(ctx, row, session, nil) {
52 continue
53 }
54 if q.Root != "" && !t.activityInThread(ctx, actor, q.Root, row, roots) {
55 continue
56 }
57 if event.IsJobRequest(row.Kind) && t.Policy().Features.Jobs && (event.Expiration(row) == 0 || event.Expiration(row) > now) {
58 jobs = append(jobs, jobItemFrom(row))
59 } else if kind, ok := approvalRequest(row); ok && (row.PubKey == actor || containsString(approvalAsked(row), actor)) {
60 approvals = append(approvals, approvalItemFrom(row, kind, t.publicURL))
61 }
62 }
63 // Keep the inline panel bounded even when a busy room has accumulated a
64 // large amount of historical task and approval traffic. The store returns
65 // each filter newest first, so retaining the first 50 preserves the useful
66 // recent context while the dedicated views remain available for history.
67 if len(jobs) > 50 {
68 jobs = jobs[:50]
69 }
70 if len(approvals) > 50 {
71 approvals = approvals[:50]
72 }
73 answers, err := t.jobAnswers(ctx, jobs, session, now)
74 if err != nil {
75 return nil, err
76 }
77 for i := range jobs {
78 jobSettle(&jobs[i], answers[jobs[i].ID])
79 }
80 decisions, err := t.approvalAnswers(ctx, approvals, now)
81 if err != nil {
82 return nil, err
83 }
84 for i := range approvals {
85 approvalSettle(&approvals[i], decisions[approvals[i].ID], now)
86 }
87 return map[string]any{"jobs": jobs, "approvals": approvals}, nil
88}
89
90func (b backend) ReadChatActivity(ctx context.Context, actor, room, root string) (any, error) {
91 raw, err := json.Marshal(map[string]string{"room": room, "root": root})
92 if err != nil {
93 return nil, err
94 }
95 return b.tenant.Execute(ctx, actor, "browsechatactivity", []json.RawMessage{raw})
96}
97
98// Resolve parent-only references once per parent while keeping unrelated tasks
99// out of the thread. roomThreadRoot also enforces room and reader access.
100func (t *Tenant) activityInThread(ctx context.Context, actor, root string, row event.Event, roots map[string]string) bool {
101 if row.ID == root {
102 return true
103 }
104 reference := event.RoomReplyRoot(row)
105 if reference == "" && event.IsJobRequest(row.Kind) {
106 reference = event.Tag(row, "e")
107 }
108 if reference == root {
109 return true
110 }
111 if reference == "" {
112 return false
113 }
114 canonical, known := roots[reference]
115 if !known {
116 parent, err := t.roomThreadRoot(ctx, actor, event.Tag(row, "h"), reference)
117 if err == nil {
118 canonical = parent.ID
119 }
120 roots[reference] = canonical
121 }
122 return canonical == root
123}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs