···22export { default as Header } from "./header";
33export { default as InformationWidget } from "./information-widget";
44export { default as ModActions } from "./mod-actions";
55-export { default as Problems } from "./problems";
55+export { default as Problems, ProblemsWrapperRef } from "./problems";
···110110 "type": "string",
111111 "description": "A description of what this webhook is used for.",
112112 "maxLength": 500
113113+ },
114114+ "muteWords": {
115115+ "type": "array",
116116+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded.",
117117+ "items": {
118118+ "type": "string",
119119+ "maxLength": 100
120120+ }
113121 }
114122 },
115123 "required": ["url", "events"]
···465473 "type": "string",
466474 "description": "A description of what this webhook is used for.",
467475 "maxLength": 500
476476+ },
477477+ "muteWords": {
478478+ "type": "array",
479479+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded.",
480480+ "items": {
481481+ "type": "string",
482482+ "maxLength": 100
483483+ }
468484 }
469485 },
470486 "required": ["id"]
···16551671 "errorCount": {
16561672 "type": "integer",
16571673 "description": "Number of consecutive errors for this webhook."
16741674+ },
16751675+ "muteWords": {
16761676+ "type": "array",
16771677+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded.",
16781678+ "items": {
16791679+ "type": "string",
16801680+ "maxLength": 100
16811681+ }
16581682 }
16591683 },
16601684 "required": ["id", "url", "events", "active", "createdAt"]
···24242525**Schema Type:** `object`
26262727-| Name | Type | Req'd | Description | Constraints |
2828-| ------------- | ------------------------------------------------------------------------------------------------------ | ----- | ---------------------------------------------------- | ---------------- |
2929-| `url` | `string` | ✅ | The webhook URL where events will be sent. | Format: `uri` |
3030-| `events` | Array of `string` | ✅ | The types of events this webhook should receive. | |
3131-| `active` | `boolean` | ❌ | Whether this webhook should be active upon creation. | Default: `false` |
3232-| `prefix` | `string` | ❌ | Text to prepend to webhook messages. | Max Length: 100 |
3333-| `suffix` | `string` | ❌ | Text to append to webhook messages. | Max Length: 100 |
3434-| `rewrite` | Array of [`place.stream.server.defs#rewriteRule`](/lex-reference/place-stream-server-defs#rewriterule) | ❌ | Text replacement rules for webhook messages. | |
3535-| `name` | `string` | ❌ | A user-friendly name for this webhook. | Max Length: 100 |
3636-| `description` | `string` | ❌ | A description of what this webhook is used for. | Max Length: 500 |
2727+| Name | Type | Req'd | Description | Constraints |
2828+| ------------- | ------------------------------------------------------------------------------------------------------ | ----- | ----------------------------------------------------------------------------------------------------- | ---------------- |
2929+| `url` | `string` | ✅ | The webhook URL where events will be sent. | Format: `uri` |
3030+| `events` | Array of `string` | ✅ | The types of events this webhook should receive. | |
3131+| `active` | `boolean` | ❌ | Whether this webhook should be active upon creation. | Default: `false` |
3232+| `prefix` | `string` | ❌ | Text to prepend to webhook messages. | Max Length: 100 |
3333+| `suffix` | `string` | ❌ | Text to append to webhook messages. | Max Length: 100 |
3434+| `rewrite` | Array of [`place.stream.server.defs#rewriteRule`](/lex-reference/place-stream-server-defs#rewriterule) | ❌ | Text replacement rules for webhook messages. | |
3535+| `name` | `string` | ❌ | A user-friendly name for this webhook. | Max Length: 100 |
3636+| `description` | `string` | ❌ | A description of what this webhook is used for. | Max Length: 500 |
3737+| `muteWords` | Array of `string` | ❌ | Words to filter out from chat messages. Messages containing any of these words will not be forwarded. | |
37383839**Output:**
3940···115116 "type": "string",
116117 "maxLength": 500,
117118 "description": "A description of what this webhook is used for."
119119+ },
120120+ "muteWords": {
121121+ "type": "array",
122122+ "items": {
123123+ "type": "string",
124124+ "maxLength": 100
125125+ },
126126+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded."
118127 }
119128 }
120129 }
···17171818**Properties:**
19192020-| Name | Type | Req'd | Description | Constraints |
2121-| --------------- | --------------------------------------- | ----- | ------------------------------------------------ | ------------------ |
2222-| `id` | `string` | ✅ | Unique identifier for this webhook. | |
2323-| `url` | `string` | ✅ | The webhook URL where events will be sent. | Format: `uri` |
2424-| `events` | Array of `string` | ✅ | The types of events this webhook should receive. | |
2525-| `active` | `boolean` | ✅ | Whether this webhook is currently active. | |
2626-| `prefix` | `string` | ❌ | Text to prepend to webhook messages. | Max Length: 100 |
2727-| `suffix` | `string` | ❌ | Text to append to webhook messages. | Max Length: 100 |
2828-| `rewrite` | Array of [`#rewriteRule`](#rewriterule) | ❌ | Text replacement rules for webhook messages. | |
2929-| `createdAt` | `string` | ✅ | When this webhook was created. | Format: `datetime` |
3030-| `updatedAt` | `string` | ❌ | When this webhook was last updated. | Format: `datetime` |
3131-| `name` | `string` | ❌ | A user-friendly name for this webhook. | Max Length: 100 |
3232-| `description` | `string` | ❌ | A description of what this webhook is used for. | Max Length: 500 |
3333-| `lastTriggered` | `string` | ❌ | When this webhook was last triggered. | Format: `datetime` |
3434-| `errorCount` | `integer` | ❌ | Number of consecutive errors for this webhook. | |
2020+| Name | Type | Req'd | Description | Constraints |
2121+| --------------- | --------------------------------------- | ----- | ----------------------------------------------------------------------------------------------------- | ------------------ |
2222+| `id` | `string` | ✅ | Unique identifier for this webhook. | |
2323+| `url` | `string` | ✅ | The webhook URL where events will be sent. | Format: `uri` |
2424+| `events` | Array of `string` | ✅ | The types of events this webhook should receive. | |
2525+| `active` | `boolean` | ✅ | Whether this webhook is currently active. | |
2626+| `prefix` | `string` | ❌ | Text to prepend to webhook messages. | Max Length: 100 |
2727+| `suffix` | `string` | ❌ | Text to append to webhook messages. | Max Length: 100 |
2828+| `rewrite` | Array of [`#rewriteRule`](#rewriterule) | ❌ | Text replacement rules for webhook messages. | |
2929+| `createdAt` | `string` | ✅ | When this webhook was created. | Format: `datetime` |
3030+| `updatedAt` | `string` | ❌ | When this webhook was last updated. | Format: `datetime` |
3131+| `name` | `string` | ❌ | A user-friendly name for this webhook. | Max Length: 100 |
3232+| `description` | `string` | ❌ | A description of what this webhook is used for. | Max Length: 500 |
3333+| `lastTriggered` | `string` | ❌ | When this webhook was last triggered. | Format: `datetime` |
3434+| `errorCount` | `integer` | ❌ | Number of consecutive errors for this webhook. | |
3535+| `muteWords` | Array of `string` | ❌ | Words to filter out from chat messages. Messages containing any of these words will not be forwarded. | |
35363637---
3738···129130 "errorCount": {
130131 "type": "integer",
131132 "description": "Number of consecutive errors for this webhook."
133133+ },
134134+ "muteWords": {
135135+ "type": "array",
136136+ "items": {
137137+ "type": "string",
138138+ "maxLength": 100
139139+ },
140140+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded."
132141 }
133142 }
134143 },
···24242525**Schema Type:** `object`
26262727-| Name | Type | Req'd | Description | Constraints |
2828-| ------------- | ------------------------------------------------------------------------------------------------------ | ----- | ------------------------------------------------ | --------------- |
2929-| `id` | `string` | ✅ | The ID of the webhook to update. | |
3030-| `url` | `string` | ❌ | The webhook URL where events will be sent. | Format: `uri` |
3131-| `events` | Array of `string` | ❌ | The types of events this webhook should receive. | |
3232-| `active` | `boolean` | ❌ | Whether this webhook should be active. | |
3333-| `prefix` | `string` | ❌ | Text to prepend to webhook messages. | Max Length: 100 |
3434-| `suffix` | `string` | ❌ | Text to append to webhook messages. | Max Length: 100 |
3535-| `rewrite` | Array of [`place.stream.server.defs#rewriteRule`](/lex-reference/place-stream-server-defs#rewriterule) | ❌ | Text replacement rules for webhook messages. | |
3636-| `name` | `string` | ❌ | A user-friendly name for this webhook. | Max Length: 100 |
3737-| `description` | `string` | ❌ | A description of what this webhook is used for. | Max Length: 500 |
2727+| Name | Type | Req'd | Description | Constraints |
2828+| ------------- | ------------------------------------------------------------------------------------------------------ | ----- | ----------------------------------------------------------------------------------------------------- | --------------- |
2929+| `id` | `string` | ✅ | The ID of the webhook to update. | |
3030+| `url` | `string` | ❌ | The webhook URL where events will be sent. | Format: `uri` |
3131+| `events` | Array of `string` | ❌ | The types of events this webhook should receive. | |
3232+| `active` | `boolean` | ❌ | Whether this webhook should be active. | |
3333+| `prefix` | `string` | ❌ | Text to prepend to webhook messages. | Max Length: 100 |
3434+| `suffix` | `string` | ❌ | Text to append to webhook messages. | Max Length: 100 |
3535+| `rewrite` | Array of [`place.stream.server.defs#rewriteRule`](/lex-reference/place-stream-server-defs#rewriterule) | ❌ | Text replacement rules for webhook messages. | |
3636+| `name` | `string` | ❌ | A user-friendly name for this webhook. | Max Length: 100 |
3737+| `description` | `string` | ❌ | A description of what this webhook is used for. | Max Length: 500 |
3838+| `muteWords` | Array of `string` | ❌ | Words to filter out from chat messages. Messages containing any of these words will not be forwarded. | |
38393940**Output:**
4041···120121 "type": "string",
121122 "maxLength": 500,
122123 "description": "A description of what this webhook is used for."
124124+ },
125125+ "muteWords": {
126126+ "type": "array",
127127+ "items": {
128128+ "type": "string",
129129+ "maxLength": 100
130130+ },
131131+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded."
123132 }
124133 }
125134 }
···5757 "type": "string",
5858 "maxLength": 500,
5959 "description": "A description of what this webhook is used for."
6060+ },
6161+ "muteWords": {
6262+ "type": "array",
6363+ "items": {
6464+ "type": "string",
6565+ "maxLength": 100
6666+ },
6767+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded."
6068 }
6169 }
6270 }
+8
lexicons/place/stream/server/defs.json
···7171 "errorCount": {
7272 "type": "integer",
7373 "description": "Number of consecutive errors for this webhook."
7474+ },
7575+ "muteWords": {
7676+ "type": "array",
7777+ "items": {
7878+ "type": "string",
7979+ "maxLength": 100
8080+ },
8181+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded."
7482 }
7583 }
7684 },
+8
lexicons/place/stream/server/updateWebhook.json
···6060 "type": "string",
6161 "maxLength": 500,
6262 "description": "A description of what this webhook is used for."
6363+ },
6464+ "muteWords": {
6565+ "type": "array",
6666+ "items": {
6767+ "type": "string",
6868+ "maxLength": 100
6969+ },
7070+ "description": "Words to filter out from chat messages. Messages containing any of these words will not be forwarded."
6371 }
6472 }
6573 }
···9090 Redirects []string
9191 TestStream bool
9292 FrontendProxy string
9393+ PublicOAuth bool
9394 AppBundleID string
9495 NoFirehose bool
9596 PrintChat bool
···154155 fs.StringVar(&cli.AppBundleID, "app-bundle-id", "", "bundle id of an app that we facilitate oauth login for")
155156 fs.StringVar(&cli.StreamerName, "streamer-name", "", "name of the person streaming from this streamplace node")
156157 fs.StringVar(&cli.FrontendProxy, "dev-frontend-proxy", "", "(FOR DEVELOPMENT ONLY) proxy frontend requests to this address instead of using the bundled frontend")
158158+ fs.BoolVar(&cli.PublicOAuth, "dev-public-oauth", false, "(FOR DEVELOPMENT ONLY) enable public oauth login for http://127.0.0.1 development")
157159 fs.StringVar(&cli.LivepeerGatewayURL, "livepeer-gateway-url", "", "URL of the Livepeer Gateway to use for transcoding")
158160 fs.BoolVar(&cli.LivepeerGateway, "livepeer-gateway", false, "enable embedded Livepeer Gateway")
159161 fs.BoolVar(&cli.WideOpen, "wide-open", false, "allow ALL streams to be uploaded to this node (not recommended for production)")
···211213212214var StreamplaceSchemePrefix = "streamplace://"
213215216216+func (cli *CLI) OwnPublicURL() string {
217217+ // No errors because we know it's valid from AddrFlag
218218+ host, port, _ := net.SplitHostPort(cli.HTTPAddr)
219219+220220+ ip := net.ParseIP(host)
221221+ if host == "" || ip.IsUnspecified() {
222222+ host = "127.0.0.1"
223223+ }
224224+ addr := net.JoinHostPort(host, port)
225225+ return fmt.Sprintf("http://%s", addr)
226226+}
227227+214228func (cli *CLI) OwnInternalURL() string {
215229 // No errors because we know it's valid from AddrFlag
216230 host, port, _ := net.SplitHostPort(cli.HTTPInternalAddr)
···280294 return fmt.Errorf("defining both livepeer-gateway and livepeer-gateway-url doesn't make sense. do you want an embedded gateway or an external one?")
281295 }
282296 if cli.LivepeerGateway {
297297+ log.MonkeypatchStderr()
283298 gatewayPath := cli.DataFilePath([]string{"livepeer", "gateway"})
284299 err = fs.Set("livepeer.rtmp-addr", "127.0.0.1:0")
285300 if err != nil {
···323338 }
324339 if cli.ServerHost == "" && cli.BroadcasterHost != "" {
325340 cli.ServerHost = cli.BroadcasterHost
341341+ }
342342+ if cli.PublicOAuth {
343343+ log.Warn(context.Background(), "--dev-public-oauth is set, this is not recommended for production")
326344 }
327345 return nil
328346}
+14
pkg/integrations/webhook/manager.go
···33import (
44 "context"
55 "fmt"
66+ "strings"
6778 "github.com/bluesky-social/indigo/api/bsky"
89 "stream.place/streamplace/pkg/integrations/discord"
···12131314// SendChatWebhook sends chat message to a specific webhook
1415func SendChatWebhook(ctx context.Context, webhook *streamplace.ServerDefs_Webhook, authorDID string, scm *streamplace.ChatDefs_MessageView) error {
1616+ // Check if message should be muted
1717+ if msg, ok := scm.Record.Val.(*streamplace.ChatMessage); ok {
1818+ if len(webhook.MuteWords) > 0 {
1919+ messageText := strings.ToLower(msg.Text)
2020+ for _, muteWord := range webhook.MuteWords {
2121+ if strings.Contains(messageText, strings.ToLower(muteWord)) {
2222+ // Message contains a mute word, skip forwarding
2323+ return nil
2424+ }
2525+ }
2626+ }
2727+ }
2828+1529 discordWebhook, err := webhookToDiscordWebhook(webhook)
1630 if err != nil {
1731 return fmt.Errorf("failed to convert webhook: %w", err)
···6262 message.ChatProfile = scp
6363 } else {
6464 // If no chat profile exists, create a default one with a color based on the user's DID
6565- defaultColor := defaultColors[hashString(m.RepoDID)%len(defaultColors)]
6565+ defaultColor := DefaultColors[hashString(m.RepoDID)%len(DefaultColors)]
6666 message.ChatProfile = &streamplace.ChatProfile{
6767 Color: defaultColor,
6868 }
···1818 Description *string `json:"description,omitempty" cborgen:"description,omitempty"`
1919 // events: The types of events this webhook should receive.
2020 Events []string `json:"events" cborgen:"events"`
2121+ // muteWords: Words to filter out from chat messages. Messages containing any of these words will not be forwarded.
2222+ MuteWords []string `json:"muteWords,omitempty" cborgen:"muteWords,omitempty"`
2123 // name: A user-friendly name for this webhook.
2224 Name *string `json:"name,omitempty" cborgen:"name,omitempty"`
2325 // prefix: Text to prepend to webhook messages.
+2
pkg/streamplace/serverdefs.go
···3030 Id string `json:"id" cborgen:"id"`
3131 // lastTriggered: When this webhook was last triggered.
3232 LastTriggered *string `json:"lastTriggered,omitempty" cborgen:"lastTriggered,omitempty"`
3333+ // muteWords: Words to filter out from chat messages. Messages containing any of these words will not be forwarded.
3434+ MuteWords []string `json:"muteWords,omitempty" cborgen:"muteWords,omitempty"`
3335 // name: A user-friendly name for this webhook.
3436 Name *string `json:"name,omitempty" cborgen:"name,omitempty"`
3537 // prefix: Text to prepend to webhook messages.
+2
pkg/streamplace/serverupdateWebhook.go
···2020 Events []string `json:"events,omitempty" cborgen:"events,omitempty"`
2121 // id: The ID of the webhook to update.
2222 Id string `json:"id" cborgen:"id"`
2323+ // muteWords: Words to filter out from chat messages. Messages containing any of these words will not be forwarded.
2424+ MuteWords []string `json:"muteWords,omitempty" cborgen:"muteWords,omitempty"`
2325 // name: A user-friendly name for this webhook.
2426 Name *string `json:"name,omitempty" cborgen:"name,omitempty"`
2527 // prefix: Text to prepend to webhook messages.