mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 17:58:55 +00:00
WIP Twitch Event Rules List
This commit is contained in:
1 parent
c959ca9fe6
commit
7af2c8ee46
12 files changed
+220
-2
No files matched your search
@@ -20,6 +20,7 @@ enum Domain {
|
||||
JungleHorse,
|
||||
Vocodes,
|
||||
FakeYou,
|
||||
Storyteller,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@@ -47,10 +48,14 @@ class ApiConfig {
|
||||
domain = Domain.Vocodes;
|
||||
} else if (document.location.host.includes("fakeyou.com")) {
|
||||
domain = Domain.FakeYou;
|
||||
} else if (document.location.host.includes("storyteller.io")) {
|
||||
domain = Domain.Storyteller;
|
||||
}
|
||||
|
||||
let v2ApiHost = "api.fakeyou.com";
|
||||
if (!useSsl && (domain === Domain.Localhost || domain === Domain.JungleHorse)) {
|
||||
if (domain === Domain.Storyteller) {
|
||||
v2ApiHost = "api.storyteller.io";
|
||||
} else if (!useSsl && (domain === Domain.Localhost || domain === Domain.JungleHorse)) {
|
||||
// NB: Lack of SSL means use local development.
|
||||
v2ApiHost = "api.jungle.horse";
|
||||
}
|
||||
@@ -342,6 +347,24 @@ class ApiConfig {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/detect_locale`;
|
||||
}
|
||||
|
||||
// =============== Storyteller-specific ===============
|
||||
|
||||
listTwitchEventRules() : string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/twitch/event_rule/list`;
|
||||
}
|
||||
|
||||
createTwitchEventRule() : string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/twitch/event_rule/create`;
|
||||
}
|
||||
|
||||
deleteTwitchEventRule(eventRuleToken: string) : string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/twitch/event_rule/delete/${eventRuleToken}`;
|
||||
}
|
||||
|
||||
editTwitchEventRule(eventRuleToken: string) : string {
|
||||
return `${this.getScheme()}://${this.getNewApiHost()}/twitch/event_rule/update/${eventRuleToken}`;
|
||||
}
|
||||
|
||||
obsEventsWebsocket(twitchUsername: string) : string {
|
||||
return `ws://localhost:54321/obs/${twitchUsername}`;
|
||||
//return `wss://obs.storyteller.io/obs/${twitchUsername}`;
|
||||
|
||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { ApiConfig } from "../../ApiConfig";
|
||||
import { EventMatchPredicate } from "./shared/EventMatchPredicate";
|
||||
import { EventResponse } from "./shared/EventResponse";
|
||||
import { TwitchEventCategory } from "./shared/TwitchEventCategory";
|
||||
|
||||
export interface ListTwitchEventRulesSuccessResponse {
|
||||
success: boolean,
|
||||
twitch_event_rules: Array<TwitchEventRule>
|
||||
}
|
||||
|
||||
export interface TwitchEventRule{
|
||||
token: string,
|
||||
event_category: TwitchEventCategory,
|
||||
event_match_predicate: EventMatchPredicate,
|
||||
event_response: EventResponse,
|
||||
user_specified_rule_order: number,
|
||||
rule_is_disabled: boolean,
|
||||
created_at: Date,
|
||||
updated_at: Date,
|
||||
}
|
||||
|
||||
export interface ListTwitchEventRulesErrorResponse {
|
||||
success: boolean,
|
||||
}
|
||||
|
||||
type ListTwitchEventRulesResponse = ListTwitchEventRulesSuccessResponse | ListTwitchEventRulesErrorResponse;
|
||||
|
||||
export function ListTwitchEventRulesIsOk(response: ListTwitchEventRulesResponse): response is ListTwitchEventRulesSuccessResponse {
|
||||
return response?.success === true;
|
||||
}
|
||||
|
||||
export function ListTwitchEventRulesIsError(response: ListTwitchEventRulesResponse): response is ListTwitchEventRulesErrorResponse {
|
||||
return response?.success === false;
|
||||
}
|
||||
|
||||
export async function ListTwitchEventRules() : Promise<ListTwitchEventRulesResponse>
|
||||
{
|
||||
const endpoint = new ApiConfig().listTwitchEventRules();
|
||||
|
||||
return await fetch(endpoint, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
credentials: 'include',
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(res => {
|
||||
if (!res) {
|
||||
return { success : false }; // TODO: This loses error semantics and is deprecated
|
||||
}
|
||||
|
||||
if (res && 'success' in res) {
|
||||
return res;
|
||||
} else {
|
||||
return { success : false };
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
return { success : false };
|
||||
});
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
|
||||
export interface NotSet {
|
||||
};
|
||||
|
||||
export interface BitsCheermoteNameExactMatch {
|
||||
cheermote_name: string,
|
||||
};
|
||||
|
||||
export interface BitsCheermotePrefixSpendThreshold {
|
||||
cheermote_name_prefix: string,
|
||||
minimum_bits_spent: number,
|
||||
};
|
||||
|
||||
export interface BitsSpendThreshold {
|
||||
minimum_bits_spent: number,
|
||||
}
|
||||
|
||||
export interface ChannelPointsRewardNameExactMatch {
|
||||
reward_name: string,
|
||||
}
|
||||
|
||||
// The predicate will have one field set exclusively.
|
||||
export interface EventMatchPredicate {
|
||||
// Empty
|
||||
not_set?: NotSet,
|
||||
|
||||
// Bits
|
||||
bits_cheermote_name_exact_match?: BitsCheermoteNameExactMatch,
|
||||
bits_spend_threshold?: BitsSpendThreshold,
|
||||
bits_cheermote_prefix_spend_threshold?: BitsCheermotePrefixSpendThreshold,
|
||||
|
||||
// Channel Points
|
||||
channel_points_reward_name_exact_match?: ChannelPointsRewardNameExactMatch,
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
export interface NotSet {
|
||||
};
|
||||
|
||||
export interface TtsSingleVoice {
|
||||
tts_model_token: string,
|
||||
};
|
||||
|
||||
export interface TtsRandomVoice {
|
||||
tts_model_tokens: string[],
|
||||
};
|
||||
|
||||
|
||||
// The response will have one field set exclusively.
|
||||
export interface EventResponse {
|
||||
// Empty
|
||||
not_set?: NotSet,
|
||||
|
||||
// Respond with TTS
|
||||
tts_single_voice?: TtsSingleVoice,
|
||||
tts_random_voice?: TtsRandomVoice,
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
|
||||
export enum TwitchEventCategory {
|
||||
Bits = "bits",
|
||||
ChannelPoints = "channel_points",
|
||||
ChatCommand = "chat_command",
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import i18n from 'i18next';
|
||||
import { ComingSoonPage } from './pages/coming-soon/ComingSoonPage';
|
||||
import { LoginPage } from './pages/login/LoginPage';
|
||||
import { SignupPage } from './pages/signup/SignupPage';
|
||||
import { TtsConfigsIndexPage } from './pages/tts_configs/TtsConfigsIndexPage';
|
||||
import { DetectLocale, DetectLocaleIsOk } from '@storyteller/components/src/api/locale/DetectLocale';
|
||||
import { IndexPage } from './pages/index/IndexPage';
|
||||
import { ObsLayerPage } from './pages/obs-layer/ObsLayerPage';
|
||||
@@ -15,6 +16,7 @@ import { STORYTELLER_MERGED_TRANSLATIONS } from './_i18n/StorytellerTranslations
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { TopNav } from './layout/TopNav';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import { TtsConfigsCreateRulePage } from './pages/tts_configs/TtsConfigsCreateRulePage';
|
||||
|
||||
i18n
|
||||
.use(initReactI18next) // passes i18n down to react-i18next
|
||||
@@ -97,6 +99,16 @@ class App extends React.Component<Props, State> {
|
||||
querySessionAction={this.querySession}
|
||||
/>
|
||||
</Route>
|
||||
<Route exact={true} path="/tts_configs/create">
|
||||
<TtsConfigsCreateRulePage
|
||||
sessionWrapper={this.state.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
<Route exact={true} path="/tts_configs">
|
||||
<TtsConfigsIndexPage
|
||||
sessionWrapper={this.state.sessionWrapper}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="/obs/:username">
|
||||
<ObsLayerPage />
|
||||
</Route>
|
||||
|
||||
@@ -45,7 +45,12 @@ function TopNav(props: Props) {
|
||||
<Link to="/stream"
|
||||
className="navbar-item"
|
||||
onClick={() => closeHamburger()}
|
||||
>Stream TTS</Link>
|
||||
>Stream TTS (OLD)</Link>
|
||||
|
||||
<Link to="/tts_configs"
|
||||
className="navbar-item"
|
||||
onClick={() => closeHamburger()}
|
||||
>TTS Configs (NEW)</Link>
|
||||
|
||||
<Link to="/coming-soon"
|
||||
className="navbar-item"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
}
|
||||
|
||||
function TtsConfigsCreateRulePage(props: Props) {
|
||||
return (
|
||||
<div>
|
||||
<h1> TTS : Create Rule </h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { TtsConfigsCreateRulePage }
|
||||
@@ -0,0 +1,37 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { SessionWrapper } from '@storyteller/components/src/session/SessionWrapper';
|
||||
import { ListTwitchEventRules, ListTwitchEventRulesIsError, ListTwitchEventRulesIsOk, TwitchEventRule } from '@storyteller/components/src/api/storyteller/twitch_event_rules/ListTwitchEventRules';
|
||||
|
||||
interface Props {
|
||||
sessionWrapper: SessionWrapper,
|
||||
}
|
||||
|
||||
function TtsConfigsIndexPage(props: Props) {
|
||||
const [twitchEventRules, setTwitchEventRules] = useState<TwitchEventRule[]>([]);
|
||||
|
||||
const listTwitchEventRules = useCallback(async () => {
|
||||
const response = await ListTwitchEventRules();
|
||||
|
||||
if (ListTwitchEventRulesIsOk(response)) {
|
||||
setTwitchEventRules(response.twitch_event_rules);
|
||||
} else if (ListTwitchEventRulesIsError(response)) {
|
||||
// TODO
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
listTwitchEventRules();
|
||||
}, [listTwitchEventRules]);
|
||||
|
||||
if (!props.sessionWrapper.isLoggedIn()) {
|
||||
return <h1>Must Log In</h1>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1> TTS Setup </h1>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { TtsConfigsIndexPage }
|
||||
Reference in new issue
Block a user