feedback_actions.gno
5.23 Kb · 111 lines
1package memba_weighted_host
2
3import (
4 "chain"
5 "strconv"
6 "strings"
7 "gno.land/p/samcrew/memba_weighted_policy"
8)
9
10const FeedbackPath = "gno.land/r/samcrew/memba_feedback_v2"
11
12type FeedbackActionKind string
13
14const (
15 FeedbackAcceptOwner FeedbackActionKind = "accept-owner"
16 FeedbackReturnOwner FeedbackActionKind = "return-owner"
17 FeedbackAbortReturn FeedbackActionKind = "abort-return"
18 FeedbackAddMember FeedbackActionKind = "add-member"
19 FeedbackRemoveMember FeedbackActionKind = "remove-member"
20 FeedbackSetRoles FeedbackActionKind = "set-roles"
21 FeedbackCreateText FeedbackActionKind = "create-text-channel"
22 FeedbackUnpause FeedbackActionKind = "unpause"
23)
24
25type FeedbackPolicy struct{ dao, successor address }
26
27func NewFeedbackPolicy(dao, successor address) FeedbackPolicy {
28 if !dao.IsValid() || dao != chain.PackageAddress("gno.land/r/samcrew/memba_dao") {
29 panic("feedback policy requires the founding DAO address")
30 }
31 if !successor.IsValid() || successor == dao || successor == chain.PackageAddress(FeedbackPath) {
32 panic("feedback policy requires a valid distinct successor")
33 }
34 return FeedbackPolicy{dao: dao, successor: successor}
35}
36func (p FeedbackPolicy) DAO() address { return p.dao }
37func (p FeedbackPolicy) Successor() address { return p.successor }
38
39type FeedbackAction struct {
40 kind FeedbackActionKind
41 recipient, subject address
42 roles string
43 name, description string
44}
45func (p FeedbackPolicy) Accept() FeedbackAction { return FeedbackAction{kind: FeedbackAcceptOwner} }
46func (p FeedbackPolicy) Return() FeedbackAction { return FeedbackAction{kind: FeedbackReturnOwner, recipient: p.successor} }
47func (p FeedbackPolicy) AbortReturn() FeedbackAction { return FeedbackAction{kind: FeedbackAbortReturn, recipient: p.successor} }
48func (p FeedbackPolicy) AddMember(who address, roles string) FeedbackAction { a:=FeedbackAction{kind: FeedbackAddMember, subject:who, roles:roles}; a.Bytes(); return a }
49func (p FeedbackPolicy) RemoveMember(who address) FeedbackAction { a:=FeedbackAction{kind: FeedbackRemoveMember, subject:who}; a.Bytes(); return a }
50func (p FeedbackPolicy) SetRoles(who address, roles string) FeedbackAction { a:=FeedbackAction{kind: FeedbackSetRoles, subject:who, roles:roles}; a.Bytes(); return a }
51func (p FeedbackPolicy) CreateText(name, description string) FeedbackAction { a:=FeedbackAction{kind: FeedbackCreateText, name:name, description:description}; a.Bytes(); return a }
52func FeedbackResume() FeedbackAction { return FeedbackAction{kind: FeedbackUnpause} }
53func (a FeedbackAction) Kind() FeedbackActionKind { return a.kind }
54func (a FeedbackAction) Recipient() address { return a.recipient }
55func (a FeedbackAction) Subject() address { return a.subject }
56func (a FeedbackAction) Roles() string { return a.roles }
57func (a FeedbackAction) Name() string { return a.name }
58func (a FeedbackAction) Description() string { return a.description }
59
60func validFeedbackName(name string) bool {
61 if len(name)<1 || len(name)>50 { return false }
62 for _,c:=range name { if !((c>='a'&&c<='z')||(c>='0'&&c<='9')||c=='-') { return false } }
63 return true
64}
65func validFeedbackDescription(description string) bool {
66 if len(description)<1 || len(description)>200 { return false }
67 for _,c:=range description { if c<32 || c==127 { return false } }
68 return true
69}
70
71// Only the four target ACL roles are representable; every role list has a
72// canonical order, so a voted commitment cannot alias another role grant.
73func canonicalFeedbackRoles(roles string) bool {
74 if roles == "" { return false }
75 parts:=strings.Split(roles, ",")
76 if len(parts)>4 { return false }
77 previous:=-1
78 for _,part:=range parts {
79 index:=-1
80 switch part { case "admin": index=0; case "dev": index=1; case "ops": index=2; case "member": index=3 }
81 if index<=previous { return false }
82 previous=index
83 }
84 return true
85}
86func (a FeedbackAction) Bytes() string {
87 base:="feedback/v1|"+FeedbackPath+"|"+string(a.kind)
88 switch a.kind {
89 case FeedbackAcceptOwner, FeedbackUnpause:
90 if a.recipient!="" || a.subject!="" || a.roles!="" || a.name!="" || a.description!="" { panic("malformed feedback action") }
91 return base
92 case FeedbackReturnOwner, FeedbackAbortReturn:
93 if !a.recipient.IsValid() || a.subject!="" || a.roles!="" || a.name!="" || a.description!="" { panic("malformed feedback return action") }
94 return base+"|"+string(a.recipient)
95 case FeedbackAddMember, FeedbackSetRoles:
96 if !a.subject.IsValid() || a.recipient!="" || a.name!="" || a.description!="" || !canonicalFeedbackRoles(a.roles) { panic("invalid feedback member action") }
97 return base+"|"+string(a.subject)+"|"+strconv.Itoa(len(a.roles))+":"+a.roles
98 case FeedbackRemoveMember:
99 if !a.subject.IsValid() || a.recipient!="" || a.roles!="" || a.name!="" || a.description!="" { panic("invalid feedback member action") }
100 return base+"|"+string(a.subject)
101 case FeedbackCreateText:
102 if a.recipient!="" || a.subject!="" || a.roles!="" || !validFeedbackName(a.name) || !validFeedbackDescription(a.description) { panic("invalid feedback text creation") }
103 return base+"|"+a.name+"|"+strconv.Itoa(len(a.description))+":"+a.description
104 default: panic("unsupported feedback action")
105 }
106}
107func (a FeedbackAction) Category() memba_weighted_policy.Category {
108 a.Bytes()
109 if a.kind==FeedbackUnpause { return memba_weighted_policy.Financial }
110 return memba_weighted_policy.Critical
111}