An implementation of the ATProto statusphere example app but in Go
1package statusphere
2
3import (
4 "fmt"
5 "log/slog"
6 "net/http"
7 "time"
8)
9
10var Availablestatus = []string{
11 "👍",
12 "👎",
13 "💙",
14 "🥹",
15 "😧",
16 "😤",
17 "🙃",
18 "😉",
19 "😎",
20 "🤓",
21 "🤨",
22 "🥳",
23 "😭",
24 "😤",
25 "🤯",
26 "🫡",
27 "💀",
28 "✊",
29 "🤘",
30 "👀",
31 "🧠",
32 "👩💻",
33 "🧑💻",
34 "🥷",
35 "🧌",
36 "🦋",
37 "🚀",
38}
39
40type HomeData struct {
41 DisplayName string
42 AvailableStatus []string
43 UsersStatus []UserStatus
44}
45
46type UserStatus struct {
47 Status string
48 Handle string
49 HandleURL string
50 Date string
51 IsToday bool
52}
53
54func (s *Server) HandleHome(w http.ResponseWriter, r *http.Request) {
55 tmpl := s.getTemplate("home.html")
56 data := HomeData{
57 AvailableStatus: Availablestatus,
58 }
59
60 did, _ := s.currentSessionDID(r)
61 if did != nil {
62 profile, err := s.getUserProfileForDid(did.String())
63 if err != nil {
64 slog.Error("getting logged in users profile", "error", err)
65 }
66 data.DisplayName = profile.DisplayName
67 }
68
69 today := time.Now().Format(time.DateOnly)
70
71 results, err := s.store.GetStatuses(10)
72 if err != nil {
73 slog.Error("get status'", "error", err)
74 }
75
76 for _, status := range results {
77 date := time.UnixMilli(status.CreatedAt).Format(time.DateOnly)
78
79 profile, err := s.getUserProfileForDid(status.Did)
80 if err != nil {
81 slog.Error("getting user profile for status - skipping", "error", err, "did", status.Did)
82 continue
83 }
84
85 data.UsersStatus = append(data.UsersStatus, UserStatus{
86 Status: status.Status,
87 Handle: profile.Handle,
88 HandleURL: fmt.Sprintf("https://bsky.app/profile/%s", status.Did),
89 Date: date,
90 IsToday: date == today,
91 })
92 }
93
94 tmpl.Execute(w, data)
95}
96
97func (s *Server) HandleStatus(w http.ResponseWriter, r *http.Request) {
98 err := r.ParseForm()
99 if err != nil {
100 slog.Error("parsing form", "error", err)
101 http.Error(w, "parsing form", http.StatusBadRequest)
102 return
103 }
104
105 status := r.FormValue("status")
106 if status == "" {
107 http.Error(w, "missing status", http.StatusBadRequest)
108 return
109 }
110
111 did, sessionID := s.currentSessionDID(r)
112 if did == nil {
113 http.Redirect(w, r, "/login", http.StatusFound)
114 return
115 }
116
117 oauthSess, err := s.oauthClient.ResumeSession(r.Context(), *did, sessionID)
118 if err != nil {
119 http.Error(w, "not authenticated", http.StatusUnauthorized)
120 return
121 }
122 c := oauthSess.APIClient()
123
124 createdAt := time.Now()
125
126 bodyReq := map[string]any{
127 "repo": c.AccountDID.String(),
128 "collection": "xyz.statusphere.status",
129 "record": map[string]any{
130 "status": status,
131 "createdAt": createdAt,
132 },
133 }
134 var result CreateRecordResp
135 err = c.Post(r.Context(), "com.atproto.repo.createRecord", bodyReq, &result)
136 if err != nil {
137 slog.Error("failed to create new status", "error", err)
138 http.Redirect(w, r, "/", http.StatusFound)
139 return
140 }
141
142 statusToStore := Status{
143 URI: result.URI,
144 Did: c.AccountDID.String(),
145 Status: status,
146 CreatedAt: createdAt.UnixMilli(),
147 IndexedAt: time.Now().UnixMilli(),
148 }
149
150 err = s.store.CreateStatus(statusToStore)
151 if err != nil {
152 slog.Error("failed to store status that has been created", "error", err)
153 }
154
155 http.Redirect(w, r, "/", http.StatusFound)
156}