Live video on the AT Protocol
1package discord
2
3import (
4 "bytes"
5 "context"
6 "encoding/json"
7 "fmt"
8 "io"
9 "net/http"
10 "strings"
11
12 "stream.place/streamplace/pkg/aqhttp"
13 "stream.place/streamplace/pkg/integrations/discord/discordtypes"
14 "stream.place/streamplace/pkg/log"
15 "stream.place/streamplace/pkg/streamplace"
16)
17
18func SendChat(ctx context.Context, w *discordtypes.Webhook, did string, scm *streamplace.ChatDefs_MessageView) error {
19
20 msg, ok := scm.Record.Val.(*streamplace.ChatMessage)
21 if !ok {
22 return fmt.Errorf("failed to cast chat message to streamplace chat message")
23 }
24
25 avatarURL, err := GetAvatarURL(ctx, did)
26 if err != nil {
27 log.Warn(ctx, "failed to get avatar URL", "err", err)
28 }
29
30 payload := discordtypes.Payload{
31 Username: fmt.Sprintf("@%s", scm.Author.Handle),
32 Content: fmt.Sprintf("%s%s%s", w.Prefix, msg.Text, w.Suffix),
33 }
34 if avatarURL != "" {
35 payload.AvatarURL = avatarURL
36 }
37
38 // apply default anti-ping rewrites
39 payload.Content = strings.ReplaceAll(payload.Content, "@here", "@\u200Bhere")
40 payload.Content = strings.ReplaceAll(payload.Content, "@everyone", "@\u200Beveryone")
41 // and for <@{userid/roleid}>
42 payload.Content = strings.ReplaceAll(payload.Content, "<@", "<@\u200B")
43
44 // then apply custom rewrites, in case user wants to undo the above or do something else
45 for _, rewrite := range w.Rewrite {
46 payload.Content = strings.ReplaceAll(payload.Content, rewrite.From, rewrite.To)
47 }
48
49 jsonPayload, err := json.Marshal(payload)
50 if err != nil {
51 return fmt.Errorf("failed to marshal payload: %w", err)
52 }
53
54 log.Warn(ctx, "sending chat to discord", "payload", string(jsonPayload))
55
56 req, err := http.NewRequestWithContext(ctx, "POST", w.URL, bytes.NewReader(jsonPayload))
57 if err != nil {
58 return fmt.Errorf("failed to create request: %w", err)
59 }
60 req.Header.Set("Content-Type", "application/json")
61
62 resp, err := aqhttp.Do(ctx, req)
63 if err != nil {
64 return fmt.Errorf("failed to send request: %w", err)
65 }
66 defer resp.Body.Close()
67
68 body, err := io.ReadAll(resp.Body)
69 if err != nil {
70 return fmt.Errorf("failed to read response body: %w", err)
71 }
72
73 if resp.StatusCode != 204 {
74 return fmt.Errorf("failed to send chat to discord: %s", string(body))
75 }
76
77 log.Warn(ctx, "chat sent to discord", "payload", string(body))
78
79 return nil
80}