Skip to content

Commit ed24ee1

Browse files
authored
feat(Jackbox): add The Jackbox Survey Scramble (#9338)
* feat(bigsurvery): add game details * feat: support additional player name location * chore: format code for new style * feat: update logo * refactor: remove debug warn log
1 parent 9faa79d commit ed24ee1

File tree

4 files changed

+161
-25
lines changed

4 files changed

+161
-25
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import type { GameCallbackParams } from '../../types.js'
2+
import { Assets } from 'premid'
3+
4+
export const name = 'The Jackbox Survey Scramble'
5+
export const logo = 'https://i.imgur.com/Uq3wXdV.png'
6+
7+
function getGoalString(goal: string) {
8+
switch (goal) {
9+
case 'High':
10+
return 'most popular'
11+
case 'Low':
12+
return 'least popular'
13+
}
14+
}
15+
16+
export async function getPresenceData({
17+
playerState,
18+
}: GameCallbackParams): Promise<PresenceData> {
19+
switch (playerState.kind) {
20+
case 'lobby': {
21+
if (playerState.textEntry) {
22+
const input = document.querySelector<HTMLInputElement>('#input')
23+
let detailsText = playerState.textEntry.prompt
24+
if (input?.value)
25+
detailsText += ` - "${input.value}"`
26+
27+
return {
28+
state: 'Waiting in lobby',
29+
smallImageKey: Assets.Question,
30+
smallImageText: detailsText,
31+
}
32+
}
33+
return { state: 'Waiting in lobby' }
34+
}
35+
case 'postGame': {
36+
return { state: 'Viewing the results' }
37+
}
38+
case 'choices': {
39+
switch (playerState.responseKey) {
40+
case 'objectGuess:3': {
41+
const prompt = playerState.prompt as string
42+
if (prompt?.includes('MORE'))
43+
return { state: `Dare: Guessing ${prompt}` }
44+
45+
return { state: 'High-Low: Guessing the more popular answer' }
46+
}
47+
case 'voteResponse:3': {
48+
return { state: 'Voting for a topic' }
49+
}
50+
}
51+
return { state: 'Making a choice' }
52+
}
53+
case 'teamChoice': {
54+
return { state: 'Choosing their team' }
55+
}
56+
case 'bounce': {
57+
if (playerState.instructions?.includes('Try'))
58+
return { state: 'Bounce: Practicing answers' }
59+
60+
return {
61+
state: 'Bounce: Submitting answers',
62+
}
63+
}
64+
case 'ticTacToe': {
65+
return {
66+
state: `Tic Tac Toe: ${
67+
playerState.instructions?.includes('next') ? 'Suggesting' : 'Guessing'
68+
} prompts`,
69+
smallImageKey: Assets.Question,
70+
smallImageText: document.querySelector('.prompt'),
71+
}
72+
}
73+
case 'speed': {
74+
return {
75+
state: 'Speed: Guessing answers',
76+
smallImageKey: Assets.Question,
77+
smallImageText: document.querySelector('.prompt'),
78+
}
79+
}
80+
case 'highLow': {
81+
return {
82+
state: `High-Low: Guessing the ${getGoalString(
83+
playerState.goal!,
84+
)} answer`,
85+
smallImageKey: Assets.Question,
86+
smallImageText: document.querySelector('.prompt'),
87+
}
88+
}
89+
case 'horseRace': {
90+
return {
91+
state: `Dash: Guessing the ${getGoalString(playerState.goal!)} answer`,
92+
smallImageKey: Assets.Question,
93+
smallImageText: `Between '${playerState.options?.join(', ')}`,
94+
}
95+
}
96+
case 'dare': {
97+
return { state: 'Dare: Choosing opponent\'s difficulty' }
98+
}
99+
case 'dareText': {
100+
if (playerState.successfulGuess)
101+
return { state: 'Dare: Guessing rank of submission' }
102+
103+
return {
104+
state: 'Dare: Providing guess',
105+
smallImageKey: Assets.Question,
106+
smallImageText: document.querySelector('.prompt'),
107+
}
108+
}
109+
default: {
110+
return { state: 'Waiting' }
111+
}
112+
}
113+
}

websites/J/Jackbox/games/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Game } from '../types.js'
2+
import * as bigsurvey from './Non-Pack/bigsurvey.js'
23
import * as drawful2 from './Party Pack 1/drawful2.js'
34
import * as drawful2international from './Party Pack 1/drawful2international.js'
45
import * as drawful from './Party Pack 1/drawful.js'
@@ -109,4 +110,5 @@ export default {
109110
'risky-text': riskytext,
110111
'time-trivia': timetrivia,
111112
'us-them': usthem,
113+
bigsurvey,
112114
} as Record<string, Game>

websites/J/Jackbox/presence.ts

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ let gamePlayerState: GamePlayerState = {
1515
let gamePlayerInfoState: GameInfoState = {
1616
name: null as unknown as string,
1717
}
18-
let game: Game
18+
let game: Game | undefined
1919
let browsingTimestamp = Math.round(Date.now() / 1000)
2020
let gametag: string
2121

2222
if (document.location.hostname === 'jackbox.tv') {
2323
setInterval(async () => {
2424
const playerStateLogs = await presence.getLogs(
25-
/recv <- .*?("key": "(bc:customer|player|info):[a-z0-9-]+",)/s,
25+
/recv <- .*?("key": "(bc:customer|player|info):[a-z0-9-]+",|"opcode":\s*"client\/welcome")/s,
2626
)
2727
if (playerStateLogs.length > 0) {
2828
let updatedMainState = false
@@ -48,18 +48,26 @@ if (document.location.hostname === 'jackbox.tv') {
4848
switch (true) {
4949
case /recv <- .*?"entities": \{\n/s.test(latestLog): {
5050
if (!updatedMainState) {
51-
gamePlayerState = parsedLog.result.entities[
52-
latestLog.match(
53-
/"key": "((?:bc:customer|player):[a-z0-9-]+)",/,
54-
)?.[1] ?? ''
55-
]?.[1].val ?? parsedLog.result.val
56-
updatedMainState = true
51+
const entityKey = latestLog.match(
52+
/"key": "((?:bc:customer|player):[a-z0-9-]+)",/,
53+
)?.[1]
54+
if (entityKey) {
55+
gamePlayerState
56+
= parsedLog.result.entities[
57+
entityKey
58+
]?.[1].val ?? parsedLog.result.val
59+
updatedMainState = true
60+
}
5761
}
5862
if (!updatedInfoState) {
59-
gamePlayerInfoState = parsedLog.result.entities[
60-
latestLog.match(/"key": "(info:\d+)",/)?.[1] ?? ''
61-
]?.[1].val ?? parsedLog.result
62-
updatedInfoState = true
63+
const entityKey = latestLog.match(/"key": "(info:\d+)",/)?.[1]
64+
if (entityKey) {
65+
gamePlayerInfoState
66+
= parsedLog.result.entities[
67+
entityKey
68+
]?.[1].val ?? parsedLog.result
69+
updatedInfoState = true
70+
}
6371
}
6472
break
6573
}
@@ -77,23 +85,30 @@ if (document.location.hostname === 'jackbox.tv') {
7785
gamePlayerInfoState = parsedLog.result.val
7886
updatedInfoState = true
7987
}
88+
break
89+
}
90+
case /recv <- .*?"opcode": "client\/welcome"/s.test(latestLog): {
91+
if (!updatedInfoState) {
92+
gamePlayerInfoState = parsedLog.result
93+
updatedInfoState = true
94+
}
95+
break
8096
}
8197
}
8298
}
8399
}
84100
if (!game) {
85-
interface JackboxStorageLetiable {
86-
tag: string
87-
}
88-
const { tag } = await presence.getPageletiable<JackboxStorageLetiable>(
89-
'tv"]["storage',
90-
)
91-
gametag = tag
92-
if (tag && tag !== '@connect') {
93-
game = games[tag]!
94-
browsingTimestamp = Math.round(Date.now() / 1000)
95-
if (!game)
96-
game = games.unknown!
101+
const { 'tv.storage.tag': tag } = await presence.getPageVariable<
102+
Record<string, string>
103+
>('tv.storage.tag')
104+
if (tag) {
105+
gametag = tag
106+
if (tag !== '@connect') {
107+
game = games[tag]
108+
browsingTimestamp = Math.round(Date.now() / 1000)
109+
if (!game)
110+
game = games.unknown
111+
}
97112
}
98113
}
99114
}, 1000)
@@ -219,7 +234,7 @@ presence.on('UpdateData', async () => {
219234
else {
220235
presenceData.details = 'Browsing'
221236
presenceData.state = document.title.match(
222-
/^(.*?)( - Jackbox Games)?$/,
237+
/^.*?( - Jackbox Games)?$/,
223238
)?.[1]
224239
}
225240
}

websites/J/Jackbox/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ export interface GamePlayerState {
2626
placeholder?: string
2727
choiceType?: string
2828
classes?: string[]
29+
instructions?: string
30+
goal?: string
31+
options?: string[]
32+
textEntry?: {
33+
prompt: string
34+
}
2935
[x: string]: unknown
3036
}
3137

0 commit comments

Comments
 (0)