@streamorchard/grapevine 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROTOCOL.md +18 -9
- package/README.md +36 -7
- package/fixtures/drops.farming/1/invalid/image-not-https.json +11 -0
- package/fixtures/drops.farming/1/invalid/next-without-name.json +11 -0
- package/fixtures/drops.farming/1/invalid/reward-without-state.json +16 -0
- package/fixtures/drops.farming/1/valid/farming-plain.json +12 -0
- package/fixtures/drops.farming/1/valid/farming.json +31 -2
- package/fixtures/drops.farming/1/valid/show-result.json +1 -0
- package/package.json +1 -1
- package/schemas/common.json +52 -10
- package/schemas/drops.farming/1.json +205 -11
- package/types/capabilities.d.ts +47 -1
package/PROTOCOL.md
CHANGED
|
@@ -181,7 +181,8 @@ A capability is a named feature one app provides and others use, defined by a JS
|
|
|
181
181
|
| ----------- | ----------------------------------------------------------------- |
|
|
182
182
|
| `Timestamp` | ISO 8601, in UTC |
|
|
183
183
|
| `Platform` | A streaming site: `kick`, `twitch`, `youtube`, or one added later |
|
|
184
|
-
| `Channel` | `{ platform, slug, name? }`
|
|
184
|
+
| `Channel` | `{ platform, slug, name?, avatar? }` |
|
|
185
|
+
| `Image` | A picture's `https` address, as the site gives it |
|
|
185
186
|
| `Account` | `{ platform, user }`: the username only, never a token or cookie |
|
|
186
187
|
| `Progress` | `{ done, total, unit }`, where `unit` is `minutes` for now |
|
|
187
188
|
|
|
@@ -194,14 +195,22 @@ A capability is a named feature one app provides and others use, defined by a JS
|
|
|
194
195
|
|
|
195
196
|
### `drops.farming` 1.0
|
|
196
197
|
|
|
197
|
-
What a drops app is farming. Provided by Pear Drops.
|
|
198
|
-
|
|
199
|
-
| Field
|
|
200
|
-
|
|
|
201
|
-
| `state`
|
|
202
|
-
| `account`
|
|
203
|
-
| `channel`
|
|
204
|
-
| `
|
|
198
|
+
What a drops app is farming, with enough for another app to show it, in a top-bar widget say. Provided by Pear Drops.
|
|
199
|
+
|
|
200
|
+
| Field | |
|
|
201
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
202
|
+
| `state` | `farming` (its player is watching a stream for drop credit), `waiting`, `paused`, `done`, `signed-out`, or one added later |
|
|
203
|
+
| `account` | The account it farms for, or `null` |
|
|
204
|
+
| `channel` | The channel it's watching while `farming` (with its `avatar`, optional), else `null` |
|
|
205
|
+
| `stream` | Optional, while farming: `{ title?, viewers? }` |
|
|
206
|
+
| `drop` | While farming, `{ campaign, category, progress }`, else `null`. Optional extras below |
|
|
207
|
+
| `drop.image` | The category's picture |
|
|
208
|
+
| `drop.endsAt` | When the drop ends |
|
|
209
|
+
| `drop.rewards` | `{ claimed, total, items? }`. `items` lists every reward in the order they unlock (up to 20): `{ name, image?, required, state, unlocksAt? }`, where `state` is `claimed`, `ready` (unlocked, waiting to be claimed), `locked`, or one added later, and a locked one's `unlocksAt` is when it should unlock at the current rate |
|
|
210
|
+
| `drop.next` | The reward being worked towards: `{ name, image?, required, unlocksAt }`, where `required` is minutes and `unlocksAt` is when it should unlock at the current rate |
|
|
211
|
+
| `drop.doneAt` | When every reward should be earned at the current rate, or `null` |
|
|
212
|
+
|
|
213
|
+
`drops.farming.show {}` asks the drops app to bring its window to the front, for a click on that widget. It replies `{ shown }`.
|
|
205
214
|
|
|
206
215
|
### `players` 1.0
|
|
207
216
|
|
package/README.md
CHANGED
|
@@ -53,12 +53,37 @@ const link = createLink({
|
|
|
53
53
|
|
|
54
54
|
await link.start();
|
|
55
55
|
|
|
56
|
-
// Tell every app that subscribed. seq and at are added for you
|
|
56
|
+
// Tell every app that subscribed. seq and at are added for you, and the last one also answers
|
|
57
|
+
// drops.farming.get. Everything past progress is optional: it's what a widget would show.
|
|
57
58
|
link.publish('drops.farming.changed', {
|
|
58
59
|
state: 'farming',
|
|
59
60
|
account: { platform: 'kick', user: 'pearfan' },
|
|
60
|
-
channel: { platform: 'kick', slug: 'pixelpear' },
|
|
61
|
-
|
|
61
|
+
channel: { platform: 'kick', slug: 'pixelpear', name: 'PixelPear', avatar: 'https://files.kick.com/…/pixelpear.webp' },
|
|
62
|
+
stream: { title: 'Neon Rush ranked, chill vibes', viewers: 1240 },
|
|
63
|
+
drop: {
|
|
64
|
+
campaign: 'Neon Rush Launch Week',
|
|
65
|
+
category: 'Neon Rush',
|
|
66
|
+
progress: { done: 48, total: 120, unit: 'minutes' },
|
|
67
|
+
image: 'https://files.kick.com/…/neon-rush.webp',
|
|
68
|
+
endsAt: '2026-10-21T18:00:00Z',
|
|
69
|
+
rewards: {
|
|
70
|
+
claimed: 1,
|
|
71
|
+
total: 3,
|
|
72
|
+
items: [
|
|
73
|
+
{ name: 'Pear Badge', required: 30, state: 'claimed' },
|
|
74
|
+
{ name: 'Hype Rocket Emote', required: 60, state: 'locked', unlocksAt: '2026-10-18T09:24:00Z' },
|
|
75
|
+
{ name: 'Neon Crown', required: 120, state: 'locked', unlocksAt: '2026-10-18T10:24:00Z' },
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
next: { name: 'Hype Rocket Emote', required: 60, unlocksAt: '2026-10-18T09:24:00Z' },
|
|
79
|
+
doneAt: '2026-10-18T10:24:00Z',
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// A click on another app's widget asks to see Pear Drops.
|
|
84
|
+
link.handle('drops.farming.show', () => {
|
|
85
|
+
mainWindow.show();
|
|
86
|
+
return { shown: true };
|
|
62
87
|
});
|
|
63
88
|
|
|
64
89
|
// Hear what the other apps provide.
|
|
@@ -69,6 +94,10 @@ link.on('event', (fromAppId, name, params) => {
|
|
|
69
94
|
// Ask one of them something.
|
|
70
95
|
const players = await link.request('com.glowingpear.streammultiview', 'players.get');
|
|
71
96
|
|
|
97
|
+
// On the other side, an app showing a farming widget (Stream Multiview, say):
|
|
98
|
+
// link.on('event', (from, name, f) => name === 'drops.farming.changed' && drawWidget(f));
|
|
99
|
+
// widget.onclick = () => link.request('com.streamorchard.peardrops', 'drops.farming.show');
|
|
100
|
+
|
|
72
101
|
// When a Connected apps switch changes:
|
|
73
102
|
link.refresh();
|
|
74
103
|
|
|
@@ -123,10 +152,10 @@ Example messages for every capability version, good and bad, are in `@streamorch
|
|
|
123
152
|
|
|
124
153
|
## Capabilities
|
|
125
154
|
|
|
126
|
-
| Capability | Provided by | What it says
|
|
127
|
-
| ------------------- | ---------------- |
|
|
128
|
-
| `drops.farming` 1.0 | Pear Drops | Whether it's farming
|
|
129
|
-
| `players` 1.0 | Stream Multiview | Its players by site: signed in or not, and which channels are open
|
|
155
|
+
| Capability | Provided by | What it says |
|
|
156
|
+
| ------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
157
|
+
| `drops.farming` 1.0 | Pear Drops | Whether it's farming: the channel, stream and drop, with pictures and every reward's state and when it unlocks, enough for a widget. Also `show`, to bring Pear Drops to the front |
|
|
158
|
+
| `players` 1.0 | Stream Multiview | Its players by site: signed in or not, and which channels are open |
|
|
130
159
|
|
|
131
160
|
To add one:
|
|
132
161
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"event": "drops.farming.changed",
|
|
3
|
+
"params": {
|
|
4
|
+
"seq": 1,
|
|
5
|
+
"at": "2026-10-18T09:12:04Z",
|
|
6
|
+
"state": "farming",
|
|
7
|
+
"account": null,
|
|
8
|
+
"channel": null,
|
|
9
|
+
"drop": { "campaign": "c", "category": "c", "progress": { "done": 1, "total": 2, "unit": "minutes" }, "next": { "required": 60 } }
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"event": "drops.farming.changed",
|
|
3
|
+
"params": {
|
|
4
|
+
"seq": 1,
|
|
5
|
+
"at": "2026-10-18T09:12:04Z",
|
|
6
|
+
"state": "farming",
|
|
7
|
+
"account": null,
|
|
8
|
+
"channel": null,
|
|
9
|
+
"drop": {
|
|
10
|
+
"campaign": "c",
|
|
11
|
+
"category": "c",
|
|
12
|
+
"progress": { "done": 1, "total": 2, "unit": "minutes" },
|
|
13
|
+
"rewards": { "claimed": 0, "total": 1, "items": [{ "name": "Pear Badge", "required": 30 }] }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"event": "drops.farming.changed",
|
|
3
|
+
"note": "Only the required fields: the rest are optional",
|
|
4
|
+
"params": {
|
|
5
|
+
"seq": 41,
|
|
6
|
+
"at": "2026-10-18T09:10:00Z",
|
|
7
|
+
"state": "farming",
|
|
8
|
+
"account": null,
|
|
9
|
+
"channel": { "platform": "kick", "slug": "pixelpear" },
|
|
10
|
+
"drop": { "campaign": "Neon Rush Launch Week", "category": "Neon Rush", "progress": { "done": 46, "total": 120, "unit": "minutes" } }
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -5,7 +5,36 @@
|
|
|
5
5
|
"at": "2026-10-18T09:12:04.120Z",
|
|
6
6
|
"state": "farming",
|
|
7
7
|
"account": { "platform": "kick", "user": "pearfan" },
|
|
8
|
-
"channel": { "platform": "kick", "slug": "pixelpear", "name": "PixelPear" },
|
|
9
|
-
"
|
|
8
|
+
"channel": { "platform": "kick", "slug": "pixelpear", "name": "PixelPear", "avatar": "https://files.kick.com/images/user/1/profile_image/pixelpear.webp" },
|
|
9
|
+
"stream": { "title": "Neon Rush ranked, chill vibes", "viewers": 1240 },
|
|
10
|
+
"drop": {
|
|
11
|
+
"campaign": "Neon Rush Launch Week",
|
|
12
|
+
"category": "Neon Rush",
|
|
13
|
+
"progress": { "done": 48, "total": 120, "unit": "minutes" },
|
|
14
|
+
"image": "https://files.kick.com/images/subcategories/1/banner/neon-rush.webp",
|
|
15
|
+
"endsAt": "2026-10-21T18:00:00Z",
|
|
16
|
+
"doneAt": "2026-10-18T10:24:00Z",
|
|
17
|
+
"rewards": {
|
|
18
|
+
"claimed": 1,
|
|
19
|
+
"total": 3,
|
|
20
|
+
"items": [
|
|
21
|
+
{ "name": "Pear Badge", "image": "https://files.kick.com/drops/reward/pear-badge.png", "required": 30, "state": "claimed" },
|
|
22
|
+
{
|
|
23
|
+
"name": "Hype Rocket Emote",
|
|
24
|
+
"image": "https://files.kick.com/drops/reward/hype-rocket.png",
|
|
25
|
+
"required": 60,
|
|
26
|
+
"state": "locked",
|
|
27
|
+
"unlocksAt": "2026-10-18T09:24:00Z"
|
|
28
|
+
},
|
|
29
|
+
{ "name": "Neon Crown", "required": 120, "state": "locked", "unlocksAt": "2026-10-18T10:24:00Z" }
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"next": {
|
|
33
|
+
"name": "Hype Rocket Emote",
|
|
34
|
+
"image": "https://files.kick.com/drops/reward/hype-rocket.png",
|
|
35
|
+
"required": 60,
|
|
36
|
+
"unlocksAt": "2026-10-18T09:24:00Z"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
10
39
|
}
|
|
11
40
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "result": "drops.farming.show", "params": { "shown": true } }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamorchard/grapevine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Lets desktop apps on the same computer find each other and talk, privately: local sockets, JSON-RPC, pairing and versioned capabilities.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"author": "StreamOrchard",
|
package/schemas/common.json
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"description": "Types every capability shares. Lists marked x-open may gain values: an app must accept a value it doesn't know.",
|
|
3
3
|
"defs": {
|
|
4
|
-
"Timestamp": {
|
|
4
|
+
"Timestamp": {
|
|
5
|
+
"type": "string",
|
|
6
|
+
"format": "date-time",
|
|
7
|
+
"description": "ISO 8601, in UTC"
|
|
8
|
+
},
|
|
9
|
+
"Image": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"pattern": "^https://",
|
|
12
|
+
"maxLength": 2000,
|
|
13
|
+
"description": "A picture's https address, as the site gives it"
|
|
14
|
+
},
|
|
5
15
|
"Platform": {
|
|
6
16
|
"type": "string",
|
|
7
17
|
"pattern": "^[a-z0-9-]{1,32}$",
|
|
@@ -12,9 +22,24 @@
|
|
|
12
22
|
"type": "object",
|
|
13
23
|
"required": ["platform", "slug"],
|
|
14
24
|
"properties": {
|
|
15
|
-
"platform": {
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
"platform": {
|
|
26
|
+
"$ref": "common:Platform"
|
|
27
|
+
},
|
|
28
|
+
"slug": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"minLength": 1,
|
|
31
|
+
"maxLength": 100,
|
|
32
|
+
"description": "As in the channel's address"
|
|
33
|
+
},
|
|
34
|
+
"name": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"maxLength": 200,
|
|
37
|
+
"description": "As shown on the site"
|
|
38
|
+
},
|
|
39
|
+
"avatar": {
|
|
40
|
+
"$ref": "common:Image",
|
|
41
|
+
"description": "The channel's picture"
|
|
42
|
+
}
|
|
18
43
|
}
|
|
19
44
|
},
|
|
20
45
|
"Account": {
|
|
@@ -22,19 +47,36 @@
|
|
|
22
47
|
"required": ["platform", "user"],
|
|
23
48
|
"description": "Who an app is signed in as: the username only, never a token or cookie",
|
|
24
49
|
"properties": {
|
|
25
|
-
"platform": {
|
|
26
|
-
|
|
50
|
+
"platform": {
|
|
51
|
+
"$ref": "common:Platform"
|
|
52
|
+
},
|
|
53
|
+
"user": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"minLength": 1,
|
|
56
|
+
"maxLength": 100
|
|
57
|
+
}
|
|
27
58
|
}
|
|
28
59
|
},
|
|
29
60
|
"Progress": {
|
|
30
61
|
"type": "object",
|
|
31
62
|
"required": ["done", "total", "unit"],
|
|
32
63
|
"properties": {
|
|
33
|
-
"done": {
|
|
34
|
-
|
|
35
|
-
|
|
64
|
+
"done": {
|
|
65
|
+
"type": "number",
|
|
66
|
+
"minimum": 0
|
|
67
|
+
},
|
|
68
|
+
"total": {
|
|
69
|
+
"type": "number",
|
|
70
|
+
"minimum": 0
|
|
71
|
+
},
|
|
72
|
+
"unit": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"x-open": ["minutes"]
|
|
75
|
+
}
|
|
36
76
|
}
|
|
37
77
|
},
|
|
38
|
-
"Empty": {
|
|
78
|
+
"Empty": {
|
|
79
|
+
"type": "object"
|
|
80
|
+
}
|
|
39
81
|
}
|
|
40
82
|
}
|
|
@@ -2,20 +2,50 @@
|
|
|
2
2
|
"capability": "drops.farming",
|
|
3
3
|
"major": 1,
|
|
4
4
|
"minor": 0,
|
|
5
|
-
"description": "What a drops app is farming. Provided by Pear Drops.",
|
|
5
|
+
"description": "What a drops app is farming, with enough to show it (a top-bar widget, say). Provided by Pear Drops.",
|
|
6
6
|
"events": {
|
|
7
|
-
"drops.farming.changed": {
|
|
7
|
+
"drops.farming.changed": {
|
|
8
|
+
"$ref": "#State"
|
|
9
|
+
}
|
|
8
10
|
},
|
|
9
11
|
"methods": {
|
|
10
|
-
"drops.farming.get": {
|
|
12
|
+
"drops.farming.get": {
|
|
13
|
+
"params": {
|
|
14
|
+
"$ref": "common:Empty"
|
|
15
|
+
},
|
|
16
|
+
"result": {
|
|
17
|
+
"$ref": "#State"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"drops.farming.show": {
|
|
21
|
+
"params": {
|
|
22
|
+
"$ref": "common:Empty"
|
|
23
|
+
},
|
|
24
|
+
"result": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"required": ["shown"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"shown": {
|
|
29
|
+
"type": "boolean",
|
|
30
|
+
"description": "Whether its window came to the front"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
11
35
|
},
|
|
12
36
|
"defs": {
|
|
13
37
|
"State": {
|
|
14
38
|
"type": "object",
|
|
15
39
|
"required": ["seq", "at", "state", "account", "channel", "drop"],
|
|
16
40
|
"properties": {
|
|
17
|
-
"seq": {
|
|
18
|
-
|
|
41
|
+
"seq": {
|
|
42
|
+
"type": "integer",
|
|
43
|
+
"minimum": 0,
|
|
44
|
+
"description": "Counts up with each change; 0 for a reply to drops.farming.get"
|
|
45
|
+
},
|
|
46
|
+
"at": {
|
|
47
|
+
"$ref": "common:Timestamp"
|
|
48
|
+
},
|
|
19
49
|
"state": {
|
|
20
50
|
"type": "string",
|
|
21
51
|
"minLength": 1,
|
|
@@ -23,21 +53,185 @@
|
|
|
23
53
|
"x-open": ["farming", "waiting", "paused", "done", "signed-out"],
|
|
24
54
|
"description": "farming: its player is watching a stream for drop credit. Any other value, known or not, means it isn't."
|
|
25
55
|
},
|
|
26
|
-
"account": {
|
|
27
|
-
|
|
56
|
+
"account": {
|
|
57
|
+
"anyOf": [
|
|
58
|
+
{
|
|
59
|
+
"$ref": "common:Account"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"type": "null"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
"channel": {
|
|
67
|
+
"anyOf": [
|
|
68
|
+
{
|
|
69
|
+
"$ref": "common:Channel"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"type": "null"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"description": "The stream being watched, while farming"
|
|
76
|
+
},
|
|
28
77
|
"drop": {
|
|
29
78
|
"anyOf": [
|
|
30
79
|
{
|
|
31
80
|
"type": "object",
|
|
32
81
|
"required": ["campaign", "category", "progress"],
|
|
33
82
|
"properties": {
|
|
34
|
-
"campaign": {
|
|
35
|
-
|
|
36
|
-
|
|
83
|
+
"campaign": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"maxLength": 200
|
|
86
|
+
},
|
|
87
|
+
"category": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"maxLength": 200
|
|
90
|
+
},
|
|
91
|
+
"progress": {
|
|
92
|
+
"$ref": "common:Progress"
|
|
93
|
+
},
|
|
94
|
+
"image": {
|
|
95
|
+
"$ref": "common:Image",
|
|
96
|
+
"description": "The category's picture"
|
|
97
|
+
},
|
|
98
|
+
"endsAt": {
|
|
99
|
+
"$ref": "common:Timestamp",
|
|
100
|
+
"description": "When the drop ends"
|
|
101
|
+
},
|
|
102
|
+
"doneAt": {
|
|
103
|
+
"anyOf": [
|
|
104
|
+
{
|
|
105
|
+
"$ref": "common:Timestamp"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"type": "null"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"description": "When every reward should be earned, at the current rate"
|
|
112
|
+
},
|
|
113
|
+
"rewards": {
|
|
114
|
+
"type": "object",
|
|
115
|
+
"required": ["claimed", "total"],
|
|
116
|
+
"properties": {
|
|
117
|
+
"claimed": {
|
|
118
|
+
"type": "integer",
|
|
119
|
+
"minimum": 0
|
|
120
|
+
},
|
|
121
|
+
"total": {
|
|
122
|
+
"type": "integer",
|
|
123
|
+
"minimum": 0
|
|
124
|
+
},
|
|
125
|
+
"items": {
|
|
126
|
+
"type": "array",
|
|
127
|
+
"maxItems": 20,
|
|
128
|
+
"items": {
|
|
129
|
+
"$ref": "#Reward"
|
|
130
|
+
},
|
|
131
|
+
"description": "Every reward of the drop, in the order they unlock"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"next": {
|
|
136
|
+
"anyOf": [
|
|
137
|
+
{
|
|
138
|
+
"type": "object",
|
|
139
|
+
"required": ["name", "required"],
|
|
140
|
+
"properties": {
|
|
141
|
+
"name": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"maxLength": 200
|
|
144
|
+
},
|
|
145
|
+
"image": {
|
|
146
|
+
"$ref": "common:Image"
|
|
147
|
+
},
|
|
148
|
+
"required": {
|
|
149
|
+
"type": "number",
|
|
150
|
+
"minimum": 0,
|
|
151
|
+
"description": "Minutes of watching it unlocks at"
|
|
152
|
+
},
|
|
153
|
+
"unlocksAt": {
|
|
154
|
+
"anyOf": [
|
|
155
|
+
{
|
|
156
|
+
"$ref": "common:Timestamp"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"type": "null"
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
"description": "When it should unlock, at the current rate"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"type": "null"
|
|
168
|
+
}
|
|
169
|
+
],
|
|
170
|
+
"description": "The reward being worked towards"
|
|
171
|
+
}
|
|
37
172
|
}
|
|
38
173
|
},
|
|
39
|
-
{
|
|
174
|
+
{
|
|
175
|
+
"type": "null"
|
|
176
|
+
}
|
|
40
177
|
]
|
|
178
|
+
},
|
|
179
|
+
"stream": {
|
|
180
|
+
"anyOf": [
|
|
181
|
+
{
|
|
182
|
+
"type": "object",
|
|
183
|
+
"properties": {
|
|
184
|
+
"title": {
|
|
185
|
+
"type": "string",
|
|
186
|
+
"maxLength": 300
|
|
187
|
+
},
|
|
188
|
+
"viewers": {
|
|
189
|
+
"type": "integer",
|
|
190
|
+
"minimum": 0
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"type": "null"
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"description": "The stream being watched, while farming: its title and viewers"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"Reward": {
|
|
203
|
+
"type": "object",
|
|
204
|
+
"required": ["name", "required", "state"],
|
|
205
|
+
"properties": {
|
|
206
|
+
"name": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"maxLength": 200
|
|
209
|
+
},
|
|
210
|
+
"image": {
|
|
211
|
+
"$ref": "common:Image"
|
|
212
|
+
},
|
|
213
|
+
"required": {
|
|
214
|
+
"type": "number",
|
|
215
|
+
"minimum": 0,
|
|
216
|
+
"description": "Minutes of watching it unlocks at"
|
|
217
|
+
},
|
|
218
|
+
"state": {
|
|
219
|
+
"type": "string",
|
|
220
|
+
"minLength": 1,
|
|
221
|
+
"maxLength": 32,
|
|
222
|
+
"x-open": ["claimed", "ready", "locked"],
|
|
223
|
+
"description": "ready: unlocked, waiting to be claimed"
|
|
224
|
+
},
|
|
225
|
+
"unlocksAt": {
|
|
226
|
+
"anyOf": [
|
|
227
|
+
{
|
|
228
|
+
"$ref": "common:Timestamp"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"type": "null"
|
|
232
|
+
}
|
|
233
|
+
],
|
|
234
|
+
"description": "For a locked one: when it should unlock, at the current rate"
|
|
41
235
|
}
|
|
42
236
|
}
|
|
43
237
|
}
|
package/types/capabilities.d.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
/** ISO 8601, in UTC */
|
|
4
4
|
export type Timestamp = string;
|
|
5
5
|
|
|
6
|
+
/** A picture's https address, as the site gives it */
|
|
7
|
+
export type Image = string;
|
|
8
|
+
|
|
6
9
|
/** A streaming site */
|
|
7
10
|
export type Platform = "kick" | "twitch" | "youtube" | (string & {});
|
|
8
11
|
|
|
@@ -12,6 +15,8 @@ export type Channel = {
|
|
|
12
15
|
slug: string;
|
|
13
16
|
/** As shown on the site */
|
|
14
17
|
name?: string;
|
|
18
|
+
/** The channel's picture */
|
|
19
|
+
avatar?: Image;
|
|
15
20
|
};
|
|
16
21
|
|
|
17
22
|
/** Who an app is signed in as: the username only, never a token or cookie */
|
|
@@ -28,7 +33,7 @@ export type Progress = {
|
|
|
28
33
|
|
|
29
34
|
export type Empty = Record<string, unknown>;
|
|
30
35
|
|
|
31
|
-
// drops.farming 1.0: What a drops app is farming. Provided by Pear Drops.
|
|
36
|
+
// drops.farming 1.0: What a drops app is farming, with enough to show it (a top-bar widget, say). Provided by Pear Drops.
|
|
32
37
|
export type DropsFarmingV1State = {
|
|
33
38
|
/** Counts up with each change; 0 for a reply to drops.farming.get */
|
|
34
39
|
seq: number;
|
|
@@ -42,9 +47,46 @@ export type DropsFarmingV1State = {
|
|
|
42
47
|
campaign: string;
|
|
43
48
|
category: string;
|
|
44
49
|
progress: Progress;
|
|
50
|
+
/** The category's picture */
|
|
51
|
+
image?: Image;
|
|
52
|
+
/** When the drop ends */
|
|
53
|
+
endsAt?: Timestamp;
|
|
54
|
+
/** When every reward should be earned, at the current rate */
|
|
55
|
+
doneAt?: Timestamp | null;
|
|
56
|
+
rewards?: {
|
|
57
|
+
claimed: number;
|
|
58
|
+
total: number;
|
|
59
|
+
/** Every reward of the drop, in the order they unlock */
|
|
60
|
+
items?: Array<DropsFarmingV1Reward>;
|
|
61
|
+
};
|
|
62
|
+
/** The reward being worked towards */
|
|
63
|
+
next?: {
|
|
64
|
+
name: string;
|
|
65
|
+
image?: Image;
|
|
66
|
+
/** Minutes of watching it unlocks at */
|
|
67
|
+
required: number;
|
|
68
|
+
/** When it should unlock, at the current rate */
|
|
69
|
+
unlocksAt?: Timestamp | null;
|
|
70
|
+
} | null;
|
|
71
|
+
} | null;
|
|
72
|
+
/** The stream being watched, while farming: its title and viewers */
|
|
73
|
+
stream?: {
|
|
74
|
+
title?: string;
|
|
75
|
+
viewers?: number;
|
|
45
76
|
} | null;
|
|
46
77
|
};
|
|
47
78
|
|
|
79
|
+
export type DropsFarmingV1Reward = {
|
|
80
|
+
name: string;
|
|
81
|
+
image?: Image;
|
|
82
|
+
/** Minutes of watching it unlocks at */
|
|
83
|
+
required: number;
|
|
84
|
+
/** ready: unlocked, waiting to be claimed */
|
|
85
|
+
state: "claimed" | "ready" | "locked" | (string & {});
|
|
86
|
+
/** For a locked one: when it should unlock, at the current rate */
|
|
87
|
+
unlocksAt?: Timestamp | null;
|
|
88
|
+
};
|
|
89
|
+
|
|
48
90
|
// players 1.0: An app's stream players, by site: whether they're signed in, and which channels are open. Provided by Stream Multiview.
|
|
49
91
|
export type PlayersV1State = {
|
|
50
92
|
seq: number;
|
|
@@ -73,5 +115,9 @@ export interface Events {
|
|
|
73
115
|
/** Every method, by name: its params and result. */
|
|
74
116
|
export interface Methods {
|
|
75
117
|
"drops.farming.get": { params: Empty; result: DropsFarmingV1State };
|
|
118
|
+
"drops.farming.show": { params: Empty; result: {
|
|
119
|
+
/** Whether its window came to the front */
|
|
120
|
+
shown: boolean;
|
|
121
|
+
} };
|
|
76
122
|
"players.get": { params: Empty; result: PlayersV1State };
|
|
77
123
|
}
|