@overlaysymphony/twitch 0.3.3 → 0.3.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@overlaysymphony/twitch",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Twitch module for the OverlaySymphony interactive streaming framework.",
5
5
  "homepage": "https://github.com/OverlaySymphony/overlaysymphony",
6
6
  "repository": {
@@ -18,7 +18,7 @@
18
18
  "./package.json": "./package.json"
19
19
  },
20
20
  "dependencies": {
21
- "@overlaysymphony/core": "0.2.2"
21
+ "@overlaysymphony/core": "0.2.3"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@vitest/coverage-v8": "^2.1.2",
@@ -1,4 +1,4 @@
1
- import { type TwitchUser, getUser } from "../helix/users/index.ts"
1
+ import { type TwitchUser, getCurrentUser } from "../helix/users/index.ts"
2
2
 
3
3
  export interface Authentication {
4
4
  tokenType: "bearer"
@@ -61,7 +61,7 @@ export async function getAuthentication(
61
61
  return undefined
62
62
  }
63
63
 
64
- const user = await getUser(authentication as Authentication)
64
+ const user = await getCurrentUser(authentication as Authentication)
65
65
  if (!user) {
66
66
  return undefined
67
67
  }
package/src/chat/chat.ts CHANGED
@@ -46,6 +46,8 @@ export default async function createChat(
46
46
 
47
47
  const onMessage: ChatMessageSubscriber = (callback) => {
48
48
  return eventsub.on(["channel.chat.message"], (payload) => {
49
+ if (payload.event.broadcaster_user_id !== authentication.user.id) return
50
+
49
51
  callback(payload.event)
50
52
  })
51
53
  }
@@ -1,34 +1,37 @@
1
1
  import { type Authentication } from "../../authentication/index.ts"
2
2
  import { helix } from "../helix.ts"
3
3
 
4
- interface ChannelEmote {
5
- id: string /** An ID that identifies this emote. */
6
- name: string /** The name of the emote. This is the name that viewers type in the chat window to get the emote to appear. */
7
- image: () => string /** A generator for the image URLs for the emote. */
8
- tier: string /** The subscriber tier at which the emote is unlocked. This field contains the tier information only if emote_type is set to subscriptions, otherwise, it's an empty string. */
9
- emote_type: string /** The type of emote. Possible values are:\n* bitstier — A custom Bits tier emote.\n* follower A custom follower emote.\n* subscriptions — A custom subscriber emote. */
10
- emote_set_id: string /** An ID that identifies the emote set that the emote belongs to. */
11
- format: string[] /** The formats that the emote is available in. Possible values are:\n* animated An animated GIF is available for this emote.\n* static A static PNG file is available for this emote. */
12
- scale: string[] /** The sizes that the emote is available in.\n* 1.0 — A small version (28px x 28px) is available.\n* 2.0 — A medium version (56px x 56px) is available.\n* 3.0 — A large version (112px x 112px) is available. */
13
- theme_mode: string[] /** The background themes that the emote is available in. Possible values are:\n* dark\n* light */
4
+ interface Emote {
5
+ /** An ID that identifies this emote. */
6
+ id: string
7
+ /** The name of the emote. This is the name that viewers type in the chat window to get the emote to appear. */
8
+ name: string
9
+ /** A generator for the image URLs for the emote. */
10
+ image: (config?: { format?: string; mode?: string; scale?: string }) => string
11
+ /** The subscriber tier at which the emote is unlocked. This field contains the tier information only if emote_type is set to subscriptions, otherwise, it's an empty string. */
12
+ tier: string
13
+ /** The type of emote. Possible values are:\n* bitstier — A custom Bits tier emote.\n* follower — A custom follower emote.\n* subscriptions — A custom subscriber emote. */
14
+ emote_type: string
15
+ /** An ID that identifies the emote set that the emote belongs to. */
16
+ emote_set_id: string
17
+ /** The ID of the broadcaster who owns the emote. */
18
+ owner_id: string
19
+ /** The formats that the emote is available in. Possible values are:\n* animated — An animated GIF is available for this emote.\n* static — A static PNG file is available for this emote. */
20
+ format: string[]
21
+ /** The sizes that the emote is available in.\n* 1.0 — A small version (28px x 28px) is available.\n* 2.0 — A medium version (56px x 56px) is available.\n* 3.0 — A large version (112px x 112px) is available. */
22
+ scale: string[]
23
+ /** The background themes that the emote is available in. Possible values are:\n* dark\n* light */
24
+ theme_mode: string[]
14
25
  }
15
26
 
16
27
  interface ChannelEmoteResponse {
17
- data: Array<
18
- Omit<ChannelEmote, "image"> & {
19
- images: {
20
- url_1x: string
21
- url_2x: string
22
- url_4x: string
23
- }
24
- }
25
- >
28
+ data: Array<Omit<Emote, "image" | "owner_id">>
26
29
  template: string
27
30
  }
28
31
 
29
32
  export async function getChannelEmotes(
30
33
  authentication: Authentication,
31
- ): Promise<ChannelEmote[]> {
34
+ ): Promise<Emote[]> {
32
35
  const { data: emotes, template } = await helix<
33
36
  ChannelEmoteResponse,
34
37
  {
@@ -42,8 +45,42 @@ export async function getChannelEmotes(
42
45
  },
43
46
  })
44
47
 
45
- return emotes.map(({ images, ...emote }) => ({
48
+ return emotes.map((emote) => ({
49
+ owner_id: authentication.user.id,
50
+ ...emote,
51
+ image: ({ format, mode, scale } = {}) =>
52
+ template
53
+ .replace("{{id}}", emote.id)
54
+ .replace("{{format}}", format ?? emote.format[0])
55
+ .replace("{{theme_mode}}", mode ?? emote.theme_mode[0])
56
+ .replace("{{scale}}", scale ?? emote.scale[emote.scale.length - 1]),
57
+ }))
58
+ }
59
+
60
+ export async function getUserEmotes(
61
+ authentication: Authentication,
62
+ ): Promise<Emote[]> {
63
+ const { data: emotes, template } = await helix<
64
+ ChannelEmoteResponse,
65
+ {
66
+ user_id: string
67
+ }
68
+ >(authentication, {
69
+ method: "GET",
70
+ path: "/chat/emotes/user",
71
+ params: {
72
+ user_id: authentication.user.id,
73
+ },
74
+ })
75
+
76
+ return emotes.map((emote) => ({
77
+ owner_id: authentication.user.id,
46
78
  ...emote,
47
- image: () => template,
79
+ image: ({ format, mode, scale } = {}) =>
80
+ template
81
+ .replace("{{id}}", emote.id)
82
+ .replace("{{format}}", format ?? emote.format[0])
83
+ .replace("{{theme_mode}}", mode ?? emote.theme_mode[0])
84
+ .replace("{{scale}}", scale ?? emote.scale[emote.scale.length - 1]),
48
85
  }))
49
86
  }
@@ -0,0 +1 @@
1
+ export * from "./streams.ts"
@@ -0,0 +1,48 @@
1
+ import { type Authentication } from "../../authentication/index.ts"
2
+ import { helix } from "../helix.ts"
3
+
4
+ export interface TwitchStream {
5
+ id: string
6
+ user_id: string
7
+ user_login: string
8
+ user_name: string
9
+ game_id: string
10
+ game_name: string
11
+ type: string
12
+ title: string
13
+ tags: string[]
14
+ viewer_count: number
15
+ started_at: string
16
+ language: string
17
+ thumbnail_url: string
18
+ }
19
+
20
+ interface StreamsResponse {
21
+ data: TwitchStream[]
22
+ }
23
+
24
+ export async function getStream(
25
+ authentication: Authentication,
26
+ login?: string | string[],
27
+ id?: string | string[],
28
+ ): Promise<TwitchStream | undefined> {
29
+ if (!login && !id) return undefined
30
+
31
+ if (typeof login === "string" && login.startsWith("@")) {
32
+ login = login.slice(1)
33
+ }
34
+
35
+ const { data: streams } = await helix<
36
+ StreamsResponse,
37
+ { user_id?: string | string[]; user_login?: string | string[] }
38
+ >(authentication, {
39
+ method: "GET",
40
+ path: "/users",
41
+ params: {
42
+ user_id: id,
43
+ user_login: login,
44
+ },
45
+ })
46
+
47
+ return streams[0]
48
+ }
@@ -26,6 +26,12 @@ export async function getUser(
26
26
  login?: string | string[],
27
27
  id?: string | string[],
28
28
  ): Promise<TwitchUser | undefined> {
29
+ if (!login && !id) return undefined
30
+
31
+ if (typeof login === "string" && login.startsWith("@")) {
32
+ login = login.slice(1)
33
+ }
34
+
29
35
  const { data: users } = await helix<
30
36
  UsersResponse,
31
37
  { id?: string | string[]; login?: string | string[] }
@@ -45,3 +51,19 @@ export async function getUser(
45
51
  created_at: new Date(created_at),
46
52
  }
47
53
  }
54
+
55
+ export async function getCurrentUser(
56
+ authentication: Authentication,
57
+ ): Promise<TwitchUser | undefined> {
58
+ const { data: users } = await helix<UsersResponse>(authentication, {
59
+ method: "GET",
60
+ path: "/users",
61
+ })
62
+
63
+ const [{ created_at, ...user }] = users
64
+
65
+ return {
66
+ ...user,
67
+ created_at: new Date(created_at),
68
+ }
69
+ }
@@ -47,7 +47,7 @@ export function onAlert(
47
47
 
48
48
  export default function createAlertQueue(
49
49
  eventsub: TwitchEventSub,
50
- handleAlert: (alert: Alert) => void,
50
+ handleAlert: (alert: Alert) => unknown,
51
51
  ): Queue<Alert>["dismiss"] {
52
52
  const queue = createQueue<Alert>()
53
53
  queue.listen(handleAlert)