social-mcp 2.1.0 → 2.3.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.
Files changed (103) hide show
  1. package/README.md +129 -3
  2. package/dist/index.js +64 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/config.d.ts +34 -0
  5. package/dist/lib/config.js +34 -0
  6. package/dist/lib/config.js.map +1 -1
  7. package/dist/services/beehiiv-service.d.ts +69 -0
  8. package/dist/services/beehiiv-service.js +97 -0
  9. package/dist/services/beehiiv-service.js.map +1 -0
  10. package/dist/services/devto-service.d.ts +99 -0
  11. package/dist/services/devto-service.js +92 -0
  12. package/dist/services/devto-service.js.map +1 -0
  13. package/dist/services/email-service.d.ts +5 -1
  14. package/dist/services/email-service.js +76 -4
  15. package/dist/services/email-service.js.map +1 -1
  16. package/dist/services/ghost-service.d.ts +80 -0
  17. package/dist/services/ghost-service.js +123 -0
  18. package/dist/services/ghost-service.js.map +1 -0
  19. package/dist/services/hashnode-service.d.ts +86 -0
  20. package/dist/services/hashnode-service.js +161 -0
  21. package/dist/services/hashnode-service.js.map +1 -0
  22. package/dist/services/tumblr-service.d.ts +73 -0
  23. package/dist/services/tumblr-service.js +113 -0
  24. package/dist/services/tumblr-service.js.map +1 -0
  25. package/dist/services/twitch-service.d.ts +85 -0
  26. package/dist/services/twitch-service.js +135 -0
  27. package/dist/services/twitch-service.js.map +1 -0
  28. package/dist/tools/beehiiv-tools/create-post.d.ts +40 -0
  29. package/dist/tools/beehiiv-tools/create-post.js +49 -0
  30. package/dist/tools/beehiiv-tools/create-post.js.map +1 -0
  31. package/dist/tools/beehiiv-tools/get-posts.d.ts +20 -0
  32. package/dist/tools/beehiiv-tools/get-posts.js +51 -0
  33. package/dist/tools/beehiiv-tools/get-posts.js.map +1 -0
  34. package/dist/tools/beehiiv-tools/get-subscribers.d.ts +20 -0
  35. package/dist/tools/beehiiv-tools/get-subscribers.js +48 -0
  36. package/dist/tools/beehiiv-tools/get-subscribers.js.map +1 -0
  37. package/dist/tools/devto-tools/create-article.d.ts +30 -0
  38. package/dist/tools/devto-tools/create-article.js +47 -0
  39. package/dist/tools/devto-tools/create-article.js.map +1 -0
  40. package/dist/tools/devto-tools/get-article.d.ts +16 -0
  41. package/dist/tools/devto-tools/get-article.js +40 -0
  42. package/dist/tools/devto-tools/get-article.js.map +1 -0
  43. package/dist/tools/devto-tools/get-articles.d.ts +18 -0
  44. package/dist/tools/devto-tools/get-articles.js +41 -0
  45. package/dist/tools/devto-tools/get-articles.js.map +1 -0
  46. package/dist/tools/devto-tools/update-article.d.ts +28 -0
  47. package/dist/tools/devto-tools/update-article.js +51 -0
  48. package/dist/tools/devto-tools/update-article.js.map +1 -0
  49. package/dist/tools/email-tools/send-bulk-email.d.ts +8 -0
  50. package/dist/tools/email-tools/send-bulk-email.js +17 -1
  51. package/dist/tools/email-tools/send-bulk-email.js.map +1 -1
  52. package/dist/tools/email-tools/send-email.d.ts +8 -0
  53. package/dist/tools/email-tools/send-email.js +17 -1
  54. package/dist/tools/email-tools/send-email.js.map +1 -1
  55. package/dist/tools/ghost-tools/create-post.d.ts +36 -0
  56. package/dist/tools/ghost-tools/create-post.js +51 -0
  57. package/dist/tools/ghost-tools/create-post.js.map +1 -0
  58. package/dist/tools/ghost-tools/delete-post.d.ts +18 -0
  59. package/dist/tools/ghost-tools/delete-post.js +28 -0
  60. package/dist/tools/ghost-tools/delete-post.js.map +1 -0
  61. package/dist/tools/ghost-tools/get-posts.d.ts +30 -0
  62. package/dist/tools/ghost-tools/get-posts.js +51 -0
  63. package/dist/tools/ghost-tools/get-posts.js.map +1 -0
  64. package/dist/tools/ghost-tools/update-post.d.ts +36 -0
  65. package/dist/tools/ghost-tools/update-post.js +58 -0
  66. package/dist/tools/ghost-tools/update-post.js.map +1 -0
  67. package/dist/tools/hashnode-tools/create-post.d.ts +34 -0
  68. package/dist/tools/hashnode-tools/create-post.js +46 -0
  69. package/dist/tools/hashnode-tools/create-post.js.map +1 -0
  70. package/dist/tools/hashnode-tools/get-posts.d.ts +18 -0
  71. package/dist/tools/hashnode-tools/get-posts.js +48 -0
  72. package/dist/tools/hashnode-tools/get-posts.js.map +1 -0
  73. package/dist/tools/hashnode-tools/get-publication.d.ts +16 -0
  74. package/dist/tools/hashnode-tools/get-publication.js +36 -0
  75. package/dist/tools/hashnode-tools/get-publication.js.map +1 -0
  76. package/dist/tools/tumblr-tools/create-post.d.ts +34 -0
  77. package/dist/tools/tumblr-tools/create-post.js +45 -0
  78. package/dist/tools/tumblr-tools/create-post.js.map +1 -0
  79. package/dist/tools/tumblr-tools/delete-post.d.ts +18 -0
  80. package/dist/tools/tumblr-tools/delete-post.js +28 -0
  81. package/dist/tools/tumblr-tools/delete-post.js.map +1 -0
  82. package/dist/tools/tumblr-tools/get-blog-info.d.ts +16 -0
  83. package/dist/tools/tumblr-tools/get-blog-info.js +38 -0
  84. package/dist/tools/tumblr-tools/get-blog-info.js.map +1 -0
  85. package/dist/tools/tumblr-tools/get-posts.d.ts +40 -0
  86. package/dist/tools/tumblr-tools/get-posts.js +53 -0
  87. package/dist/tools/tumblr-tools/get-posts.js.map +1 -0
  88. package/dist/tools/twitch-tools/get-channel-info.d.ts +18 -0
  89. package/dist/tools/twitch-tools/get-channel-info.js +38 -0
  90. package/dist/tools/twitch-tools/get-channel-info.js.map +1 -0
  91. package/dist/tools/twitch-tools/get-streams.d.ts +22 -0
  92. package/dist/tools/twitch-tools/get-streams.js +50 -0
  93. package/dist/tools/twitch-tools/get-streams.js.map +1 -0
  94. package/dist/tools/twitch-tools/get-user.d.ts +18 -0
  95. package/dist/tools/twitch-tools/get-user.js +39 -0
  96. package/dist/tools/twitch-tools/get-user.js.map +1 -0
  97. package/dist/tools/twitch-tools/search-channels.d.ts +22 -0
  98. package/dist/tools/twitch-tools/search-channels.js +41 -0
  99. package/dist/tools/twitch-tools/search-channels.js.map +1 -0
  100. package/dist/tools/twitch-tools/send-chat-message.d.ts +24 -0
  101. package/dist/tools/twitch-tools/send-chat-message.js +39 -0
  102. package/dist/tools/twitch-tools/send-chat-message.js.map +1 -0
  103. package/package.json +1 -1
@@ -0,0 +1,86 @@
1
+ export interface HashnodeCredentials {
2
+ accessToken: string;
3
+ publicationId?: string;
4
+ }
5
+ export declare class HashnodeService {
6
+ private baseUrl;
7
+ private accessToken;
8
+ private publicationId;
9
+ constructor(credentials?: HashnodeCredentials);
10
+ private gql;
11
+ getPublication(publicationId?: string): Promise<{
12
+ id: string;
13
+ title?: string | undefined;
14
+ url?: string | undefined;
15
+ displayTitle?: string | undefined;
16
+ posts?: {
17
+ edges: {
18
+ node: {
19
+ id: string;
20
+ title: string;
21
+ slug?: string | undefined;
22
+ url?: string | undefined;
23
+ brief?: string | undefined;
24
+ publishedAt?: string | undefined;
25
+ tags?: {
26
+ name: string;
27
+ slug: string;
28
+ }[] | undefined;
29
+ reactionCount?: number | undefined;
30
+ responseCount?: number | undefined;
31
+ };
32
+ }[];
33
+ pageInfo?: {
34
+ hasNextPage?: boolean | undefined;
35
+ endCursor?: string | null | undefined;
36
+ } | undefined;
37
+ } | undefined;
38
+ } | null | undefined>;
39
+ getPosts(publicationId?: string, first?: number): Promise<{
40
+ id: string;
41
+ title?: string | undefined;
42
+ url?: string | undefined;
43
+ displayTitle?: string | undefined;
44
+ posts?: {
45
+ edges: {
46
+ node: {
47
+ id: string;
48
+ title: string;
49
+ slug?: string | undefined;
50
+ url?: string | undefined;
51
+ brief?: string | undefined;
52
+ publishedAt?: string | undefined;
53
+ tags?: {
54
+ name: string;
55
+ slug: string;
56
+ }[] | undefined;
57
+ reactionCount?: number | undefined;
58
+ responseCount?: number | undefined;
59
+ };
60
+ }[];
61
+ pageInfo?: {
62
+ hasNextPage?: boolean | undefined;
63
+ endCursor?: string | null | undefined;
64
+ } | undefined;
65
+ } | undefined;
66
+ } | null | undefined>;
67
+ createPost(title: string, contentMarkdown: string, tags?: {
68
+ name: string;
69
+ slug: string;
70
+ }[], publicationId?: string, subtitle?: string, coverImageUrl?: string, disableComments?: boolean): Promise<{
71
+ data?: {
72
+ publishPost: {
73
+ post: {
74
+ id: string;
75
+ title: string;
76
+ slug?: string | undefined;
77
+ url?: string | undefined;
78
+ };
79
+ };
80
+ } | undefined;
81
+ errors?: {
82
+ message: string;
83
+ }[] | undefined;
84
+ }>;
85
+ }
86
+ export declare function getHashnodeService(): HashnodeService;
@@ -0,0 +1,161 @@
1
+ import { z } from "zod";
2
+ import { config } from "../lib/config.js";
3
+ import { CredentialsError } from "../lib/errors.js";
4
+ const publicationSchema = z.object({
5
+ id: z.string(),
6
+ title: z.string().optional(),
7
+ url: z.string().optional(),
8
+ displayTitle: z.string().optional(),
9
+ });
10
+ const postSchema = z.object({
11
+ id: z.string(),
12
+ title: z.string(),
13
+ slug: z.string().optional(),
14
+ url: z.string().optional(),
15
+ brief: z.string().optional(),
16
+ publishedAt: z.string().optional(),
17
+ tags: z.array(z.object({ name: z.string(), slug: z.string() })).optional(),
18
+ reactionCount: z.number().optional(),
19
+ responseCount: z.number().optional(),
20
+ });
21
+ const publishPostResponseSchema = z.object({
22
+ data: z.object({
23
+ publishPost: z.object({
24
+ post: z.object({
25
+ id: z.string(),
26
+ title: z.string(),
27
+ slug: z.string().optional(),
28
+ url: z.string().optional(),
29
+ }),
30
+ }),
31
+ }).optional(),
32
+ errors: z.array(z.object({ message: z.string() })).optional(),
33
+ });
34
+ const publicationQuerySchema = z.object({
35
+ data: z.object({
36
+ publication: z.object({
37
+ id: z.string(),
38
+ title: z.string().optional(),
39
+ url: z.string().optional(),
40
+ displayTitle: z.string().optional(),
41
+ posts: z.object({
42
+ edges: z.array(z.object({ node: postSchema })),
43
+ pageInfo: z.object({
44
+ hasNextPage: z.boolean().optional(),
45
+ endCursor: z.string().nullable().optional(),
46
+ }).optional(),
47
+ }).optional(),
48
+ }).nullable(),
49
+ }).optional(),
50
+ errors: z.array(z.object({ message: z.string() })).optional(),
51
+ });
52
+ export class HashnodeService {
53
+ baseUrl = config.hashnode.baseUrl;
54
+ accessToken;
55
+ publicationId;
56
+ constructor(credentials) {
57
+ const accessToken = credentials?.accessToken ?? config.hashnode.accessToken;
58
+ if (!accessToken) {
59
+ throw new CredentialsError("Hashnode", ["HASHNODE_ACCESS_TOKEN"]);
60
+ }
61
+ this.accessToken = accessToken;
62
+ this.publicationId = credentials?.publicationId ?? config.hashnode.publicationId;
63
+ }
64
+ async gql(query, variables, schema) {
65
+ const response = await fetch(this.baseUrl, {
66
+ method: "POST",
67
+ headers: {
68
+ "Content-Type": "application/json",
69
+ Authorization: this.accessToken,
70
+ },
71
+ body: JSON.stringify({ query, variables }),
72
+ });
73
+ if (!response.ok) {
74
+ throw new Error(`Hashnode API error ${response.status}: ${response.statusText}`);
75
+ }
76
+ const data = await response.json();
77
+ if (data.errors?.length) {
78
+ throw new Error(`Hashnode GraphQL error: ${data.errors.map((e) => e.message).join("; ")}`);
79
+ }
80
+ return schema.parse(data);
81
+ }
82
+ async getPublication(publicationId) {
83
+ const id = publicationId ?? this.publicationId;
84
+ if (!id)
85
+ throw new Error("publicationId is required. Set HASHNODE_PUBLICATION_ID or pass it explicitly.");
86
+ const query = `query GetPublication($id: ObjectId!) {
87
+ publication(id: $id) {
88
+ id
89
+ title
90
+ url
91
+ displayTitle
92
+ }
93
+ }`;
94
+ const result = await this.gql(query, { id }, publicationQuerySchema);
95
+ return result.data?.publication;
96
+ }
97
+ async getPosts(publicationId, first = 10) {
98
+ const id = publicationId ?? this.publicationId;
99
+ if (!id)
100
+ throw new Error("publicationId is required. Set HASHNODE_PUBLICATION_ID or pass it explicitly.");
101
+ const query = `query GetPosts($id: ObjectId!, $first: Int!) {
102
+ publication(id: $id) {
103
+ id
104
+ title
105
+ url
106
+ posts(first: $first) {
107
+ edges {
108
+ node {
109
+ id
110
+ title
111
+ slug
112
+ url
113
+ brief
114
+ publishedAt
115
+ reactionCount
116
+ responseCount
117
+ tags { name slug }
118
+ }
119
+ }
120
+ pageInfo { hasNextPage endCursor }
121
+ }
122
+ }
123
+ }`;
124
+ const result = await this.gql(query, { id, first }, publicationQuerySchema);
125
+ return result.data?.publication;
126
+ }
127
+ async createPost(title, contentMarkdown, tags = [], publicationId, subtitle, coverImageUrl, disableComments = false) {
128
+ const id = publicationId ?? this.publicationId;
129
+ if (!id)
130
+ throw new Error("publicationId is required. Set HASHNODE_PUBLICATION_ID or pass it explicitly.");
131
+ const mutation = `mutation PublishPost($input: PublishPostInput!) {
132
+ publishPost(input: $input) {
133
+ post {
134
+ id
135
+ title
136
+ slug
137
+ url
138
+ }
139
+ }
140
+ }`;
141
+ const input = {
142
+ title,
143
+ contentMarkdown,
144
+ tags,
145
+ publicationId: id,
146
+ disableComments,
147
+ };
148
+ if (subtitle)
149
+ input.subtitle = subtitle;
150
+ if (coverImageUrl)
151
+ input.coverImageOptions = { coverImageURL: coverImageUrl };
152
+ return this.gql(mutation, { input }, publishPostResponseSchema);
153
+ }
154
+ }
155
+ let _instance;
156
+ export function getHashnodeService() {
157
+ if (!_instance)
158
+ _instance = new HashnodeService();
159
+ return _instance;
160
+ }
161
+ //# sourceMappingURL=hashnode-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashnode-service.js","sourceRoot":"","sources":["../../src/services/hashnode-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ export interface TumblrCredentials {
2
+ accessToken: string;
3
+ blogIdentifier?: string;
4
+ }
5
+ export declare class TumblrService {
6
+ private baseUrl;
7
+ private headers;
8
+ private blogIdentifier;
9
+ constructor(credentials?: TumblrCredentials);
10
+ private getBlog;
11
+ getBlogInfo(blogIdentifier?: string): Promise<{
12
+ response: {
13
+ blog: {
14
+ name: string;
15
+ title?: string | undefined;
16
+ description?: string | undefined;
17
+ url?: string | undefined;
18
+ posts?: number | undefined;
19
+ followers?: number | undefined;
20
+ is_nsfw?: boolean | undefined;
21
+ updated?: number | undefined;
22
+ };
23
+ };
24
+ }>;
25
+ getPosts(blogIdentifier?: string, type?: string, offset?: number, limit?: number): Promise<{
26
+ response: {
27
+ posts: {
28
+ id: string | number;
29
+ id_string?: string | undefined;
30
+ blog_name?: string | undefined;
31
+ post_url?: string | undefined;
32
+ type?: string | undefined;
33
+ timestamp?: number | undefined;
34
+ date?: string | undefined;
35
+ format?: string | undefined;
36
+ tags?: string[] | undefined;
37
+ note_count?: number | undefined;
38
+ content?: {
39
+ type: string;
40
+ text?: string | undefined;
41
+ subtype?: string | undefined;
42
+ url?: string | undefined;
43
+ media?: {
44
+ url: string;
45
+ type?: string | undefined;
46
+ }[] | undefined;
47
+ }[] | undefined;
48
+ summary?: string | undefined;
49
+ title?: string | undefined;
50
+ body?: string | undefined;
51
+ }[];
52
+ total_posts?: number | undefined;
53
+ };
54
+ }>;
55
+ createPost(blogIdentifier: string | undefined, content: Array<{
56
+ type: string;
57
+ text?: string;
58
+ subtype?: string;
59
+ }>, tags?: string[], state?: "published" | "draft" | "queue" | "private", title?: string, nativeInlineImages?: boolean): Promise<{
60
+ response: {
61
+ id: string | number;
62
+ id_string?: string | undefined;
63
+ state?: string | undefined;
64
+ post_url?: string | undefined;
65
+ };
66
+ meta?: {
67
+ status: number;
68
+ msg: string;
69
+ } | undefined;
70
+ }>;
71
+ deletePost(postId: string, blogIdentifier?: string): Promise<void>;
72
+ }
73
+ export declare function getTumblrService(): TumblrService;
@@ -0,0 +1,113 @@
1
+ import { z } from "zod";
2
+ import { config } from "../lib/config.js";
3
+ import { CredentialsError } from "../lib/errors.js";
4
+ import { fetchJson } from "../lib/http.js";
5
+ const blogInfoSchema = z.object({
6
+ response: z.object({
7
+ blog: z.object({
8
+ name: z.string(),
9
+ title: z.string().optional(),
10
+ description: z.string().optional(),
11
+ url: z.string().optional(),
12
+ posts: z.number().optional(),
13
+ followers: z.number().optional(),
14
+ is_nsfw: z.boolean().optional(),
15
+ updated: z.number().optional(),
16
+ }),
17
+ }),
18
+ });
19
+ const npfContentBlock = z.object({
20
+ type: z.string(),
21
+ text: z.string().optional(),
22
+ subtype: z.string().optional(),
23
+ url: z.string().optional(),
24
+ media: z.array(z.object({ url: z.string(), type: z.string().optional() })).optional(),
25
+ });
26
+ const postSchema = z.object({
27
+ id: z.union([z.string(), z.number()]),
28
+ id_string: z.string().optional(),
29
+ blog_name: z.string().optional(),
30
+ post_url: z.string().optional(),
31
+ type: z.string().optional(),
32
+ timestamp: z.number().optional(),
33
+ date: z.string().optional(),
34
+ format: z.string().optional(),
35
+ tags: z.array(z.string()).optional(),
36
+ note_count: z.number().optional(),
37
+ content: z.array(npfContentBlock).optional(),
38
+ summary: z.string().optional(),
39
+ title: z.string().optional(),
40
+ body: z.string().optional(),
41
+ });
42
+ const postsResponseSchema = z.object({
43
+ response: z.object({
44
+ posts: z.array(postSchema),
45
+ total_posts: z.number().optional(),
46
+ }),
47
+ });
48
+ const createPostResponseSchema = z.object({
49
+ meta: z.object({ status: z.number(), msg: z.string() }).optional(),
50
+ response: z.object({
51
+ id: z.union([z.string(), z.number()]),
52
+ id_string: z.string().optional(),
53
+ state: z.string().optional(),
54
+ post_url: z.string().optional(),
55
+ }),
56
+ });
57
+ export class TumblrService {
58
+ baseUrl = config.tumblr.baseUrl;
59
+ headers;
60
+ blogIdentifier;
61
+ constructor(credentials) {
62
+ const accessToken = credentials?.accessToken ?? config.tumblr.accessToken;
63
+ if (!accessToken) {
64
+ throw new CredentialsError("Tumblr", ["TUMBLR_ACCESS_TOKEN"]);
65
+ }
66
+ this.blogIdentifier = credentials?.blogIdentifier ?? config.tumblr.blogIdentifier;
67
+ this.headers = {
68
+ Authorization: `Bearer ${accessToken}`,
69
+ "Content-Type": "application/json",
70
+ };
71
+ }
72
+ getBlog(override) {
73
+ const blog = override ?? this.blogIdentifier;
74
+ if (!blog)
75
+ throw new Error("blogIdentifier is required. Set TUMBLR_BLOG_IDENTIFIER or pass it explicitly.");
76
+ return blog;
77
+ }
78
+ async getBlogInfo(blogIdentifier) {
79
+ const blog = this.getBlog(blogIdentifier);
80
+ return fetchJson(`${this.baseUrl}/blog/${blog}/info`, { method: "GET", headers: this.headers }, blogInfoSchema);
81
+ }
82
+ async getPosts(blogIdentifier, type, offset = 0, limit = 10) {
83
+ const blog = this.getBlog(blogIdentifier);
84
+ const params = new URLSearchParams({ offset: String(offset), limit: String(limit), npf: "false" });
85
+ if (type)
86
+ params.set("type", type);
87
+ return fetchJson(`${this.baseUrl}/blog/${blog}/posts?${params}`, { method: "GET", headers: this.headers }, postsResponseSchema);
88
+ }
89
+ async createPost(blogIdentifier, content, tags = [], state = "published", title, nativeInlineImages = false) {
90
+ const blog = this.getBlog(blogIdentifier);
91
+ const body = { content, tags, state, native_inline_images: nativeInlineImages };
92
+ if (title)
93
+ body.title = title;
94
+ return fetchJson(`${this.baseUrl}/blog/${blog}/posts`, { method: "POST", headers: this.headers, body: JSON.stringify(body) }, createPostResponseSchema);
95
+ }
96
+ async deletePost(postId, blogIdentifier) {
97
+ const blog = this.getBlog(blogIdentifier);
98
+ const response = await fetch(`${this.baseUrl}/blog/${blog}/posts/${postId}`, {
99
+ method: "DELETE",
100
+ headers: this.headers,
101
+ });
102
+ if (!response.ok) {
103
+ throw new Error(`Tumblr API error ${response.status}: ${response.statusText}`);
104
+ }
105
+ }
106
+ }
107
+ let _instance;
108
+ export function getTumblrService() {
109
+ if (!_instance)
110
+ _instance = new TumblrService();
111
+ return _instance;
112
+ }
113
+ //# sourceMappingURL=tumblr-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tumblr-service.js","sourceRoot":"","sources":["../../src/services/tumblr-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ export interface TwitchCredentials {
2
+ clientId: string;
3
+ clientSecret: string;
4
+ accessToken?: string;
5
+ }
6
+ export declare class TwitchService {
7
+ private baseUrl;
8
+ private clientId;
9
+ private clientSecret;
10
+ private _accessToken;
11
+ private _appToken;
12
+ constructor(credentials?: TwitchCredentials);
13
+ private getAppToken;
14
+ private headers;
15
+ getUser(login?: string): Promise<{
16
+ data: {
17
+ id: string;
18
+ login: string;
19
+ display_name: string;
20
+ description?: string | undefined;
21
+ profile_image_url?: string | undefined;
22
+ view_count?: number | undefined;
23
+ created_at?: string | undefined;
24
+ broadcaster_type?: string | undefined;
25
+ }[];
26
+ }>;
27
+ getStreams(opts?: {
28
+ userLogins?: string[];
29
+ gameId?: string;
30
+ first?: number;
31
+ }): Promise<{
32
+ data: {
33
+ id: string;
34
+ user_id: string;
35
+ user_login: string;
36
+ user_name: string;
37
+ game_name?: string | undefined;
38
+ title?: string | undefined;
39
+ viewer_count?: number | undefined;
40
+ started_at?: string | undefined;
41
+ language?: string | undefined;
42
+ thumbnail_url?: string | undefined;
43
+ is_mature?: boolean | undefined;
44
+ }[];
45
+ pagination?: {
46
+ cursor?: string | undefined;
47
+ } | undefined;
48
+ }>;
49
+ getChannelInfo(broadcasterId: string): Promise<{
50
+ data: {
51
+ broadcaster_id: string;
52
+ broadcaster_login: string;
53
+ broadcaster_name: string;
54
+ game_name?: string | undefined;
55
+ game_id?: string | undefined;
56
+ title?: string | undefined;
57
+ delay?: number | undefined;
58
+ tags?: string[] | undefined;
59
+ content_classification_labels?: string[] | undefined;
60
+ }[];
61
+ }>;
62
+ searchChannels(query: string, liveOnly?: boolean, first?: number): Promise<{
63
+ data: {
64
+ id: string;
65
+ display_name: string;
66
+ broadcaster_login: string;
67
+ game_name?: string | undefined;
68
+ title?: string | undefined;
69
+ is_live?: boolean | undefined;
70
+ started_at?: string | undefined;
71
+ thumbnail_url?: string | undefined;
72
+ tags?: string[] | undefined;
73
+ }[];
74
+ pagination?: {
75
+ cursor?: string | undefined;
76
+ } | undefined;
77
+ }>;
78
+ sendChatMessage(broadcasterId: string, senderId: string, message: string): Promise<{
79
+ data: Array<{
80
+ message_id: string;
81
+ is_sent: boolean;
82
+ }>;
83
+ }>;
84
+ }
85
+ export declare function getTwitchService(): TwitchService;
@@ -0,0 +1,135 @@
1
+ import { z } from "zod";
2
+ import { config } from "../lib/config.js";
3
+ import { CredentialsError } from "../lib/errors.js";
4
+ import { fetchJson } from "../lib/http.js";
5
+ const tokenSchema = z.object({
6
+ access_token: z.string(),
7
+ token_type: z.string(),
8
+ expires_in: z.number().optional(),
9
+ });
10
+ const userSchema = z.object({
11
+ id: z.string(),
12
+ login: z.string(),
13
+ display_name: z.string(),
14
+ description: z.string().optional(),
15
+ profile_image_url: z.string().optional(),
16
+ view_count: z.number().optional(),
17
+ created_at: z.string().optional(),
18
+ broadcaster_type: z.string().optional(),
19
+ });
20
+ const streamSchema = z.object({
21
+ id: z.string(),
22
+ user_id: z.string(),
23
+ user_login: z.string(),
24
+ user_name: z.string(),
25
+ game_name: z.string().optional(),
26
+ title: z.string().optional(),
27
+ viewer_count: z.number().optional(),
28
+ started_at: z.string().optional(),
29
+ language: z.string().optional(),
30
+ thumbnail_url: z.string().optional(),
31
+ is_mature: z.boolean().optional(),
32
+ });
33
+ const channelSchema = z.object({
34
+ broadcaster_id: z.string(),
35
+ broadcaster_login: z.string(),
36
+ broadcaster_name: z.string(),
37
+ game_name: z.string().optional(),
38
+ game_id: z.string().optional(),
39
+ title: z.string().optional(),
40
+ delay: z.number().optional(),
41
+ tags: z.array(z.string()).optional(),
42
+ content_classification_labels: z.array(z.string()).optional(),
43
+ });
44
+ const searchChannelSchema = z.object({
45
+ id: z.string(),
46
+ display_name: z.string(),
47
+ broadcaster_login: z.string(),
48
+ game_name: z.string().optional(),
49
+ title: z.string().optional(),
50
+ is_live: z.boolean().optional(),
51
+ started_at: z.string().optional(),
52
+ thumbnail_url: z.string().optional(),
53
+ tags: z.array(z.string()).optional(),
54
+ });
55
+ export class TwitchService {
56
+ baseUrl = config.twitch.baseUrl;
57
+ clientId;
58
+ clientSecret;
59
+ _accessToken;
60
+ _appToken = null;
61
+ constructor(credentials) {
62
+ const clientId = credentials?.clientId ?? config.twitch.clientId;
63
+ const clientSecret = credentials?.clientSecret ?? config.twitch.clientSecret;
64
+ if (!clientId || !clientSecret) {
65
+ throw new CredentialsError("Twitch", ["TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET"]);
66
+ }
67
+ this.clientId = clientId;
68
+ this.clientSecret = clientSecret;
69
+ this._accessToken = credentials?.accessToken ?? config.twitch.accessToken;
70
+ }
71
+ async getAppToken() {
72
+ if (this._appToken)
73
+ return this._appToken;
74
+ const response = await fetch(`https://id.twitch.tv/oauth2/token?client_id=${this.clientId}&client_secret=${this.clientSecret}&grant_type=client_credentials`, { method: "POST" });
75
+ if (!response.ok) {
76
+ throw new Error(`Twitch auth failed: ${response.status} ${await response.text()}`);
77
+ }
78
+ const data = tokenSchema.parse(await response.json());
79
+ this._appToken = data.access_token;
80
+ return this._appToken;
81
+ }
82
+ async headers(useUserToken = false) {
83
+ const token = useUserToken && this._accessToken ? this._accessToken : await this.getAppToken();
84
+ return {
85
+ Authorization: `Bearer ${token}`,
86
+ "Client-Id": this.clientId,
87
+ "Content-Type": "application/json",
88
+ };
89
+ }
90
+ async getUser(login) {
91
+ const hdrs = await this.headers();
92
+ const url = login
93
+ ? `${this.baseUrl}/users?login=${login}`
94
+ : `${this.baseUrl}/users`;
95
+ return fetchJson(url, { method: "GET", headers: hdrs }, z.object({ data: z.array(userSchema) }));
96
+ }
97
+ async getStreams(opts = {}) {
98
+ const hdrs = await this.headers();
99
+ const params = new URLSearchParams();
100
+ for (const login of opts.userLogins ?? [])
101
+ params.append("user_login", login);
102
+ if (opts.gameId)
103
+ params.set("game_id", opts.gameId);
104
+ params.set("first", String(opts.first ?? 10));
105
+ return fetchJson(`${this.baseUrl}/streams?${params}`, { method: "GET", headers: hdrs }, z.object({ data: z.array(streamSchema), pagination: z.object({ cursor: z.string().optional() }).optional() }));
106
+ }
107
+ async getChannelInfo(broadcasterId) {
108
+ const hdrs = await this.headers();
109
+ return fetchJson(`${this.baseUrl}/channels?broadcaster_id=${broadcasterId}`, { method: "GET", headers: hdrs }, z.object({ data: z.array(channelSchema) }));
110
+ }
111
+ async searchChannels(query, liveOnly = false, first = 10) {
112
+ const hdrs = await this.headers();
113
+ const params = new URLSearchParams({ query, live_only: String(liveOnly), first: String(first) });
114
+ return fetchJson(`${this.baseUrl}/search/channels?${params}`, { method: "GET", headers: hdrs }, z.object({ data: z.array(searchChannelSchema), pagination: z.object({ cursor: z.string().optional() }).optional() }));
115
+ }
116
+ async sendChatMessage(broadcasterId, senderId, message) {
117
+ const hdrs = await this.headers(true);
118
+ const response = await fetch(`${this.baseUrl}/chat/messages`, {
119
+ method: "POST",
120
+ headers: hdrs,
121
+ body: JSON.stringify({ broadcaster_id: broadcasterId, sender_id: senderId, message }),
122
+ });
123
+ if (!response.ok) {
124
+ throw new Error(`Twitch API error ${response.status}: ${await response.text()}`);
125
+ }
126
+ return response.json();
127
+ }
128
+ }
129
+ let _instance;
130
+ export function getTwitchService() {
131
+ if (!_instance)
132
+ _instance = new TwitchService();
133
+ return _instance;
134
+ }
135
+ //# sourceMappingURL=twitch-service.js.map