feat: move to a multi-key system for events

This commit is contained in:
Ash Keel 2024-02-29 21:44:17 +01:00
parent f35f3f0458
commit bcdecf50c0
No known key found for this signature in database
GPG Key ID: 53A9E9A6035DD109
8 changed files with 67 additions and 22 deletions

View File

@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- The windows can now shrink no more than 480x300 but the UI will now better adapt to small window sizes
- A new part of the dashboard will now inform the user if any configuration problems have been detected.
### Changed
- The required set of permissions has changed. Existing users must re-authenticate their users to the app connected to strimertül.
- The `twitch/ev/eventsub-event` and `twitch/eventsub-history` keys have been replaced by a set of keys in the format `twitch/ev/eventsub-event/<event-id>` and `twitch/eventsub-history/<event-id>`. Users of the old system will have to adjust their logic. A simple trick is to change from get/subscribing from a single key to the entire prefix. The data structure is the same.
## 3.3.1 - 2023-11-12

View File

@ -147,7 +147,7 @@ function TwitchEvent({ data }: { data: EventSubNotification }) {
const client = useAppSelector((state) => state.api.client);
const replay = () => {
void client.putJSON('twitch/ev/eventsub-event', {
void client.putJSON(`twitch/ev/eventsub-event/${data.subscription.type}`, {
...data,
subscription: {
...data.subscription,
@ -430,10 +430,33 @@ function TwitchStreamStatus({ info }: { info: StreamInfo }) {
function TwitchSection() {
const { t } = useTranslation();
const twitchInfo = useLiveKey<StreamInfo[]>('twitch/stream-info');
// const twitchActivity = useLiveKey<StreamInfo[]>('twitch/chat-activity');
const twitchEvents = useLiveKey<EventSubNotification[]>(
'twitch/eventsub-history',
);
const kv = useAppSelector((state) => state.api.client);
const [twitchEvents, setTwitchEvents] = useState<EventSubNotification[]>([]);
const loadRecentEvents = async () => {
const keymap = await kv.getKeysByPrefix('twitch/eventsub-history/');
const events = Object.values(keymap)
.map((value) => JSON.parse(value) as EventSubNotification[])
.flat()
.sort((a, b) => Date.parse(b.date) - Date.parse(a.date));
setTwitchEvents(events);
};
useEffect(() => {
void loadRecentEvents();
const onKeyChange = (value: string) => {
const event = JSON.parse(value) as EventSubNotification;
void setTwitchEvents((prev) => [event, ...prev]);
};
void kv.subscribePrefix('twitch/ev/eventsub-event/', onKeyChange);
return () => {
void kv.unsubscribePrefix('twitch/ev/eventsub-event/', onKeyChange);
};
}, []);
return (
<>

View File

@ -412,12 +412,13 @@ function TwitchEventSubSettings() {
const sendFakeEvent = async (event: keyof typeof eventsubTests) => {
const data = eventsubTests[event];
await kv.putJSON('twitch/ev/eventsub-event', {
await kv.putJSON(`twitch/ev/eventsub-event/${event}`, {
...data,
subscription: {
...data.subscription,
created_at: new Date().toISOString(),
},
date: new Date().toISOString(),
});
};

View File

@ -133,7 +133,7 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
bot.logger.Error("Could not set-up bot alert reload subscription", zap.Error(err))
}
mod.cancelTwitchEventSub, err = bot.api.db.SubscribeKey(EventSubEventKey, mod.onEventSubEvent)
mod.cancelTwitchEventSub, err = bot.api.db.SubscribePrefix(mod.onEventSubEvent, EventSubEventKeyPrefix)
if err != nil {
bot.logger.Error("Could not setup twitch alert subscription", zap.Error(err))
}
@ -143,7 +143,7 @@ func SetupAlerts(bot *Bot) *BotAlertsModule {
return mod
}
func (m *BotAlertsModule) onEventSubEvent(value string) {
func (m *BotAlertsModule) onEventSubEvent(_ string, value string) {
var ev eventSubNotification
err := json.UnmarshalFromString(value, &ev)
if err != nil {

View File

@ -31,10 +31,10 @@ var scopes = []string{
"moderator:manage:announcements",
"moderator:read:chatters",
"moderator:read:followers",
"user_read",
"user:bot",
"user:manage:whispers",
"user:read:chat",
"user_read",
"whispers:edit",
"whispers:read",
}
@ -61,6 +61,7 @@ func (c *Client) CheckScopes() (bool, error) {
// Sort scopes for comparison
slices.Sort(authResp.Scope)
slices.Sort(scopes)
return slices.Equal(scopes, authResp.Scope), nil
}

View File

@ -143,13 +143,16 @@ func (c *Client) processEvent(message EventSubWebsocketMessage) {
}
notificationData.Date = time.Now()
err = c.db.PutJSON(EventSubEventKey, notificationData)
eventKey := fmt.Sprintf("%s%s", EventSubEventKeyPrefix, notificationData.Subscription.Type)
historyKey := fmt.Sprintf("%s%s", EventSubHistoryKeyPrefix, notificationData.Subscription.Type)
err = c.db.PutJSON(eventKey, notificationData)
c.logger.Info("Stored event", zap.String("key", eventKey), zap.String("notification-type", notificationData.Subscription.Type))
if err != nil {
c.logger.Error("Error storing event to database", zap.String("key", EventSubEventKey), zap.Error(err))
c.logger.Error("Error storing event to database", zap.String("key", eventKey), zap.Error(err))
}
var archive []NotificationMessagePayload
err = c.db.GetJSON(EventSubHistoryKey, &archive)
err = c.db.GetJSON(historyKey, &archive)
if err != nil {
archive = []NotificationMessagePayload{}
}
@ -157,9 +160,9 @@ func (c *Client) processEvent(message EventSubWebsocketMessage) {
if len(archive) > EventSubHistorySize {
archive = archive[len(archive)-EventSubHistorySize:]
}
err = c.db.PutJSON(EventSubHistoryKey, archive)
err = c.db.PutJSON(historyKey, archive)
if err != nil {
c.logger.Error("Error storing event to database", zap.String("key", EventSubHistoryKey), zap.Error(err))
c.logger.Error("Error storing event to database", zap.String("key", historyKey), zap.Error(err))
}
}
@ -195,15 +198,24 @@ func (c *Client) addSubscriptionsForSession(userClient *helix.Client, session st
func topicCondition(topic string, id string) helix.EventSubCondition {
switch topic {
case "channel.raid":
case helix.EventSubTypeChannelRaid:
return helix.EventSubCondition{
ToBroadcasterUserID: id,
}
case "channel.follow":
case helix.EventSubTypeChannelFollow:
return helix.EventSubCondition{
BroadcasterUserID: id,
ModeratorUserID: id,
}
case
helix.EventSubTypeChannelChatMessage,
helix.EventSubTypeChannelChatNotification:
{
return helix.EventSubCondition{
BroadcasterUserID: id,
UserID: id,
}
}
default:
return helix.EventSubCondition{
BroadcasterUserID: id,
@ -248,6 +260,8 @@ var subscriptionVersions = map[string]string{
helix.EventSubTypeChannelSubscriptionMessage: "1",
helix.EventSubTypeChannelCheer: "1",
helix.EventSubTypeChannelRaid: "1",
helix.EventSubTypeChannelChatMessage: "1",
helix.EventSubTypeChannelChatNotification: "1",
helix.EventSubTypeChannelPollBegin: "1",
helix.EventSubTypeChannelPollProgress: "1",
helix.EventSubTypeChannelPollEnd: "1",

View File

@ -97,8 +97,8 @@ const BotCounterPrefix = "twitch/bot-counters/"
const AuthKey = "twitch/auth-keys"
const (
EventSubEventKey = "twitch/ev/eventsub-event"
EventSubHistoryKey = "twitch/eventsub-history"
EventSubEventKeyPrefix = "twitch/ev/eventsub-event/"
EventSubHistoryKeyPrefix = "twitch/eventsub-history/"
)
const EventSubHistorySize = 100

View File

@ -45,13 +45,13 @@ var Keys = interfaces.KeyMap{
Description: "User access token for the twitch subsystem",
Type: reflect.TypeOf(AuthResponse{}),
},
EventSubEventKey: interfaces.KeyDef{
Description: "On Eventsub event received",
EventSubEventKeyPrefix + "[event-name]": interfaces.KeyDef{
Description: "On Eventsub event [event-name] received",
Type: reflect.TypeOf(NotificationMessagePayload{}),
Tags: []interfaces.KeyTag{interfaces.TagEvent},
},
EventSubHistoryKey: interfaces.KeyDef{
Description: "Last eventsub notifications received",
EventSubHistoryKeyPrefix + "[event-name]": interfaces.KeyDef{
Description: "Last eventsub notifications received for [event-name]",
Type: reflect.TypeOf([]NotificationMessagePayload{}),
Tags: []interfaces.KeyTag{interfaces.TagHistory},
},