@hardlydifficult/social 1.0.59 → 1.0.61

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/README.md CHANGED
@@ -1,16 +1,13 @@
1
1
  # @hardlydifficult/social
2
2
 
3
- Provider-agnostic social read client with an X implementation in this version.
3
+ Opinionated social read client for X.
4
4
 
5
- ## Current scope (read-first)
5
+ This package is intentionally narrow:
6
6
 
7
- This package is intentionally read-only for now:
8
-
9
- - Read timeline content
10
- - Read liked content
11
- - Watch for newly liked posts
12
-
13
- Write operations (posting, liking, reposting) are intentionally not included yet.
7
+ - One factory: `createSocial()`
8
+ - One authenticated account: `me`
9
+ - Small resource tree: `posts.get()`, `me.timeline()`, `me.likes()`
10
+ - Like watching as an async stream
14
11
 
15
12
  ## Installation
16
13
 
@@ -20,212 +17,121 @@ npm install @hardlydifficult/social
20
17
 
21
18
  ## Quick Start
22
19
 
23
- ```typescript
24
- import { createXClient } from '@hardlydifficult/social';
20
+ ```ts
21
+ import { createSocial } from "@hardlydifficult/social";
25
22
 
26
- const client = createXClient({
27
- bearerToken: process.env.X_BEARER_TOKEN!,
23
+ const social = createSocial({
24
+ token: process.env.X_BEARER_TOKEN,
28
25
  });
29
26
 
30
- const timeline = await client.getTimeline();
31
- console.log(`Retrieved ${timeline.length} posts`);
32
- ```
33
-
34
- ## Core Client
35
-
36
- ### Social Client
37
-
38
- The `SocialClient` wraps a `SocialProviderClient` implementation to provide a platform-agnostic interface for social operations.
39
-
40
- ```typescript
41
- import { createXClient } from '@hardlydifficult/social';
42
-
43
- const client = createXClient({ bearerToken: 'your-token' });
44
-
45
- // Get timeline posts
46
- const posts = await client.getTimeline();
47
-
48
- // Get posts where the user was liked (via polling-like interface)
49
- const likedPosts = await client.getLikedPosts();
50
- ```
51
-
52
- ### Social Provider Interface
53
-
54
- All provider implementations must implement `SocialProviderClient`, which requires:
55
-
56
- - `getTimeline()`: Fetch recent posts
57
- - `getLikedPosts()`: Fetch posts the authenticated user has liked
58
- - `getPost(postId: string)`: Fetch a specific post
59
-
60
- ```typescript
61
- import type { SocialProviderClient } from '@hardlydifficult/social';
62
-
63
- // Example interface contract
64
- interface MyProvider implements SocialProviderClient {
65
- getTimeline(): Promise<SocialPost[]>;
66
- getLikedPosts(): Promise<SocialPost[]>;
67
- getPost(id: string): Promise<SocialPost | undefined>;
68
- }
69
- ```
70
-
71
- ## Types
72
-
73
- ### SocialPost
74
-
75
- Standardized post type used across platforms:
76
-
77
- ```typescript
78
- import type { SocialPost } from '@hardlydifficult/social';
79
-
80
- interface SocialPost {
81
- id: string;
82
- text: string;
83
- createdAt: Date;
84
- author: {
85
- username: string;
86
- displayName?: string;
87
- avatarUrl?: string;
88
- };
89
- media?: { url: string; type: 'image' | 'video' }[];
90
- }
27
+ const timeline = await social.me.timeline({ limit: 10 });
28
+ const liked = await social.me.likes();
29
+ const post = await social.posts.get("1234567890");
91
30
  ```
92
31
 
93
- ## X (Twitter) Client
32
+ If you omit `token`, the client reads `X_BEARER_TOKEN` from the environment.
33
+ `bearerToken` is accepted as a compatibility alias.
94
34
 
95
- The `XSocialClient` implements the provider interface against the X/Twitter API.
35
+ ## API
96
36
 
97
- ### Constructor Options
37
+ ### `createSocial(options?)`
98
38
 
99
- | Option | Type | Required | Description |
100
- |--------|------|----------|-------------|
101
- | `bearerToken` | `string` | ✅ | Twitter API bearer token |
102
- | `userId` | `string` | ❌ | User ID; falls back to `/2/users/me` if not provided |
39
+ Creates the social client.
103
40
 
104
- ### Example Usage
105
-
106
- ```typescript
107
- import { createXClient } from '@hardlydifficult/social';
108
-
109
- const client = createXClient({
110
- bearerToken: process.env.X_BEARER_TOKEN!,
41
+ ```ts
42
+ const social = createSocial({
43
+ token: process.env.X_BEARER_TOKEN,
44
+ defaultLimit: 25,
111
45
  });
112
-
113
- // Fetch timeline
114
- const posts = await client.getTimeline();
115
-
116
- // Fetch liked posts for this user
117
- const likedPosts = await client.getLikedPosts();
118
46
  ```
119
47
 
120
- ## Like Watching
121
-
122
- `SocialLikeWatcher` polls the provider for new liked posts and emits events when new ones are detected.
123
-
124
- ### Constructor Options
125
-
126
- | Option | Type | Required | Default | Description |
127
- |--------|------|----------|---------|-------------|
128
- | `client` | `SocialProviderClient` | ✅ | — | Provider client instance |
129
- | `pollIntervalMs` | `number` | ❌ | `60_000` | Polling interval in milliseconds |
130
- | `initialLikeIds` | `string[]` | ❌ | `[]` | Pre-known liked post IDs (for resume) |
48
+ Options:
131
49
 
132
- ### Event Emitter API
50
+ - `token?: string`
51
+ - `bearerToken?: string`
52
+ - `defaultLimit?: number`
53
+ - `limit?: number`
54
+ - `maxResults?: number`
55
+ - `type?: "x"`
133
56
 
134
- ```typescript
135
- import { createXClient, SocialLikeWatcher } from '@hardlydifficult/social';
57
+ `bearerToken`, `limit`, and `maxResults` are accepted as compatibility aliases.
58
+ `type` is optional because X is the only supported provider.
136
59
 
137
- const client = createXClient({ bearerToken: process.env.X_BEARER_TOKEN! });
138
- const watcher = new SocialLikeWatcher(client, { pollIntervalMs: 30_000 });
60
+ ### `social.posts.get(id)`
139
61
 
140
- watcher.on('newLike', (post) => {
141
- console.log(`New like detected: ${post.text}`);
142
- });
143
-
144
- watcher.on('error', (err) => {
145
- console.error('Watcher error:', err);
146
- });
62
+ Fetch a single post.
147
63
 
148
- watcher.start();
149
- // Wait for likes...
150
- // watcher.stop();
64
+ ```ts
65
+ const post = await social.posts.get("123");
151
66
  ```
152
67
 
153
- ## Factory Functions
154
-
155
- The package exports convenience factory functions to construct platform-specific clients:
68
+ ### `social.me.timeline(options?)`
156
69
 
157
- | Function | Returns | Description |
158
- |---------|---------|-------------|
159
- | `createXClient(options)` | `SocialClient` | Constructs X/Twitter client with default implementation |
160
- | `createMastodonClient(options)` | `never` | Currently throws "not implemented" error |
70
+ Fetch the authenticated user timeline.
161
71
 
162
- ```typescript
163
- import { createXClient } from '@hardlydifficult/social';
164
-
165
- // X client
166
- const xClient = createXClient({ bearerToken: '...' });
72
+ ```ts
73
+ const recent = await social.me.timeline();
74
+ const firstTen = await social.me.timeline({ limit: 10 });
167
75
  ```
168
76
 
169
- ## Usage with High-Level API
170
-
171
- For a more opinionated interface, use `createSocial`:
77
+ ### `social.me.likes(options?)`
172
78
 
173
- ```typescript
174
- import { createSocial } from "@hardlydifficult/social";
79
+ Fetch posts liked by the authenticated user.
175
80
 
176
- const social = createSocial(); // defaults to "x"
177
-
178
- const timeline = await social.timeline({ maxResults: 10 });
179
- const liked = await social.likedPosts({ maxResults: 10 });
180
-
181
- const watcher = social.watchLikes({
182
- pollIntervalMs: 30_000,
183
- onLike: ({ post }) => {
184
- console.log(`New like: ${post.url}`);
185
- },
186
- });
187
-
188
- watcher.start();
81
+ ```ts
82
+ const liked = await social.me.likes();
83
+ const latestLiked = await social.me.likes({ limit: 20 });
189
84
  ```
190
85
 
191
- ### X configuration
86
+ ### `social.me.watchLikes(options?)`
192
87
 
193
- Set environment variables, or pass explicit config through `createSocialClient`.
88
+ Returns an async stream of newly liked posts. The first poll seeds the current
89
+ likes and emits nothing, so iteration only yields likes discovered after the
90
+ stream starts.
194
91
 
195
- - `X_BEARER_TOKEN` (required)
196
- - Get a bearer token from the X Developer Portal: https://developer.x.com/en/docs/authentication/oauth-2-0/bearer-tokens
92
+ ```ts
93
+ const controller = new AbortController();
197
94
 
198
- ```typescript
199
- import { createSocialClient } from "@hardlydifficult/social";
200
-
201
- const social = createSocialClient({
202
- type: "x",
203
- bearerToken: process.env.X_BEARER_TOKEN,
204
- });
95
+ for await (const like of social.me.watchLikes({
96
+ everyMs: 30_000,
97
+ signal: controller.signal,
98
+ })) {
99
+ console.log(`[${like.seenAt}] ${like.post.url}`);
100
+ }
205
101
  ```
206
102
 
207
- ## Testing
103
+ Options:
208
104
 
209
- Unit tests cover:
105
+ - `everyMs?: number`
106
+ - `pollIntervalMs?: number`
107
+ - `signal?: AbortSignal`
210
108
 
211
- - Factory initialization
212
- - Timeline retrieval via provider-agnostic interface
213
- - Like watcher polling and stateful detection
109
+ `pollIntervalMs` is accepted as a compatibility alias for `everyMs`.
214
110
 
215
- Run tests with:
111
+ ## Types
216
112
 
217
- ```bash
218
- npm test
219
- ```
113
+ Core types exported by the package:
220
114
 
221
- ## Appendix
115
+ - `CreateSocialOptions`
116
+ - `Provider`
117
+ - `XConfig`
118
+ - `SocialOptions`
119
+ - `SocialConfig`
120
+ - `Social`
121
+ - `SocialListOptions`
122
+ - `SocialPost`
123
+ - `SocialAuthor`
124
+ - `SocialPostMetrics`
125
+ - `LikeNotification`
126
+ - `WatchLikesOptions`
127
+ - `LikeWatcherOptions`
222
128
 
223
- ### Platform Support
129
+ ## Scope
224
130
 
225
- | Feature | X (Twitter) | Mastodon |
226
- |---------|-------------|----------|
227
- | Timeline | ✅ | ❌ (stub) |
228
- | Liked Posts | ✅ | ❌ (stub) |
229
- | Like Watching | | ❌ (stub) |
131
+ This package is read-only for now:
132
+
133
+ - Read timeline content
134
+ - Read liked posts
135
+ - Watch for newly liked posts
230
136
 
231
- Mastodon support is currently unimplemented and will throw errors on usage.
137
+ Posting, liking, and reposting are intentionally out of scope.
package/dist/index.d.ts CHANGED
@@ -1,17 +1,8 @@
1
- export type { Provider, XConfig, SocialConfig, SocialPost, SocialAuthor, SocialPostMetrics, LikeWatcherOptions, LikeNotification, } from "./types.js";
2
- export type { SocialProviderClient } from "./SocialProviderClient.js";
3
- export { SocialClient } from "./SocialClient.js";
4
- export { SocialLikeWatcher, type SocialLikeWatcherOptions, } from "./SocialLikeWatcher.js";
5
- export { XSocialClient } from "./x";
6
- export { MastodonSocialClient } from "./mastodon/MastodonSocialClient.js";
7
- import { SocialClient } from "./SocialClient.js";
8
- import type { SocialConfig } from "./types.js";
1
+ export type { CreateSocialOptions, LikeNotification, LikeWatcherOptions, Provider, Social, SocialAuthor, SocialConfig, SocialListOptions, SocialOptions, SocialPost, SocialPostMetrics, WatchLikesOptions, XConfig, } from "./types.js";
2
+ import type { CreateSocialOptions, Social } from "./types.js";
9
3
  /**
10
- * Create a social client from explicit provider configuration.
4
+ * Create the opinionated social client. X is the only supported provider and
5
+ * token lookup falls back to X_BEARER_TOKEN automatically.
11
6
  */
12
- export declare function createSocialClient(config: SocialConfig): SocialClient;
13
- /**
14
- * Create a social client using default provider configuration.
15
- */
16
- export declare function createSocial(): SocialClient;
7
+ export declare function createSocial(config?: CreateSocialOptions): Social;
17
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAErE;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,YAAY,CAE3C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,OAAO,GACR,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAG9D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,mBAAwB,GAAG,MAAM,CAErE"}
package/dist/index.js CHANGED
@@ -1,28 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MastodonSocialClient = exports.XSocialClient = exports.SocialLikeWatcher = exports.SocialClient = void 0;
4
- exports.createSocialClient = createSocialClient;
5
3
  exports.createSocial = createSocial;
6
- var SocialClient_js_1 = require("./SocialClient.js");
7
- Object.defineProperty(exports, "SocialClient", { enumerable: true, get: function () { return SocialClient_js_1.SocialClient; } });
8
- var SocialLikeWatcher_js_1 = require("./SocialLikeWatcher.js");
9
- Object.defineProperty(exports, "SocialLikeWatcher", { enumerable: true, get: function () { return SocialLikeWatcher_js_1.SocialLikeWatcher; } });
10
- var x_1 = require("./x");
11
- Object.defineProperty(exports, "XSocialClient", { enumerable: true, get: function () { return x_1.XSocialClient; } });
12
- var MastodonSocialClient_js_1 = require("./mastodon/MastodonSocialClient.js");
13
- Object.defineProperty(exports, "MastodonSocialClient", { enumerable: true, get: function () { return MastodonSocialClient_js_1.MastodonSocialClient; } });
14
- const SocialClient_js_2 = require("./SocialClient.js");
15
- const x_2 = require("./x");
4
+ const createXSocial_js_1 = require("./x/createXSocial.js");
16
5
  /**
17
- * Create a social client from explicit provider configuration.
6
+ * Create the opinionated social client. X is the only supported provider and
7
+ * token lookup falls back to X_BEARER_TOKEN automatically.
18
8
  */
19
- function createSocialClient(config) {
20
- return new SocialClient_js_2.SocialClient(new x_2.XSocialClient(config));
21
- }
22
- /**
23
- * Create a social client using default provider configuration.
24
- */
25
- function createSocial() {
26
- return createSocialClient({ type: "x" });
9
+ function createSocial(config = {}) {
10
+ return (0, createXSocial_js_1.createXSocial)(config);
27
11
  }
28
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AA6BA,gDAEC;AAKD,oCAEC;AAzBD,qDAAiD;AAAxC,+GAAA,YAAY,OAAA;AACrB,+DAGgC;AAF9B,yHAAA,iBAAiB,OAAA;AAInB,yBAAoC;AAA3B,kGAAA,aAAa,OAAA;AACtB,8EAA0E;AAAjE,+HAAA,oBAAoB,OAAA;AAE7B,uDAAiD;AAEjD,2BAAoC;AAEpC;;GAEG;AACH,SAAgB,kBAAkB,CAAC,MAAoB;IACrD,OAAO,IAAI,8BAAY,CAAC,IAAI,iBAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY;IAC1B,OAAO,kBAAkB,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AAC3C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAuBA,oCAEC;AARD,2DAAqD;AAErD;;;GAGG;AACH,SAAgB,YAAY,CAAC,SAA8B,EAAE;IAC3D,OAAO,IAAA,gCAAa,EAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,10 +1,18 @@
1
- export type Provider = "x";
2
- export interface XConfig {
3
- readonly type: "x";
1
+ export interface CreateSocialOptions {
2
+ readonly type?: "x";
3
+ readonly token?: string;
4
4
  readonly bearerToken?: string;
5
+ readonly defaultLimit?: number;
6
+ readonly limit?: number;
5
7
  readonly maxResults?: number;
6
8
  }
7
- export type SocialConfig = XConfig;
9
+ export type Provider = "x";
10
+ export type SocialOptions = CreateSocialOptions;
11
+ export type XConfig = CreateSocialOptions;
12
+ export type SocialConfig = CreateSocialOptions;
13
+ export interface SocialListOptions {
14
+ readonly limit?: number;
15
+ }
8
16
  export interface SocialAuthor {
9
17
  readonly id: string;
10
18
  readonly username: string;
@@ -27,9 +35,20 @@ export interface LikeNotification {
27
35
  readonly post: SocialPost;
28
36
  readonly seenAt: string;
29
37
  }
30
- export interface LikeWatcherOptions {
38
+ export interface WatchLikesOptions {
39
+ readonly everyMs?: number;
31
40
  readonly pollIntervalMs?: number;
32
- readonly onLike: (notification: LikeNotification) => void;
33
- readonly onError?: (error: Error) => void;
41
+ readonly signal?: AbortSignal;
42
+ }
43
+ export interface Social {
44
+ readonly posts: {
45
+ get(postId: string): Promise<SocialPost | null>;
46
+ };
47
+ readonly me: {
48
+ timeline(options?: SocialListOptions): Promise<readonly SocialPost[]>;
49
+ likes(options?: SocialListOptions): Promise<readonly SocialPost[]>;
50
+ watchLikes(options?: WatchLikesOptions): AsyncIterable<LikeNotification>;
51
+ };
34
52
  }
53
+ export type LikeWatcherOptions = WatchLikesOptions;
35
54
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC;AAE3B,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;IACnB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC;AAEnC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,CAAC,YAAY,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC3C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC;AAC3B,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAChD,MAAM,MAAM,OAAO,GAAG,mBAAmB,CAAC;AAC1C,MAAM,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE;QACd,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;KACjD,CAAC;IACF,QAAQ,CAAC,EAAE,EAAE;QACX,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;QACtE,KAAK,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;QACnE,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAC;KAC1E,CAAC;CACH;AAED,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { CreateSocialOptions, Social } from "../types.js";
2
+ /** Creates the X-backed social read client with token and limit fallbacks. */
3
+ export declare function createXSocial(options?: CreateSocialOptions): Social;
4
+ //# sourceMappingURL=createXSocial.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createXSocial.d.ts","sourceRoot":"","sources":["../../src/x/createXSocial.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EAEnB,MAAM,EAIP,MAAM,aAAa,CAAC;AA8BrB,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CA8EvE"}
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createXSocial = createXSocial;
4
+ const date_time_1 = require("@hardlydifficult/date-time");
5
+ const DEFAULT_LIMIT = 25;
6
+ const DEFAULT_WATCH_INTERVAL_MS = (0, date_time_1.duration)({ minutes: 1 });
7
+ /** Creates the X-backed social read client with token and limit fallbacks. */
8
+ function createXSocial(options = {}) {
9
+ const token = options.token ?? options.bearerToken ?? process.env.X_BEARER_TOKEN ?? "";
10
+ const defaultLimit = options.defaultLimit ??
11
+ options.limit ??
12
+ options.maxResults ??
13
+ DEFAULT_LIMIT;
14
+ if (token.length === 0) {
15
+ throw new Error("X bearer token is required. Set X_BEARER_TOKEN.");
16
+ }
17
+ async function request(path) {
18
+ const response = await fetch(`https://api.x.com/2${path}`, {
19
+ headers: {
20
+ Authorization: `Bearer ${token}`,
21
+ "Content-Type": "application/json",
22
+ },
23
+ });
24
+ if (!response.ok) {
25
+ const status = String(response.status);
26
+ throw new Error(`X API request failed: ${status} ${response.statusText}`);
27
+ }
28
+ return (await response.json());
29
+ }
30
+ async function getPost(postId) {
31
+ const response = await request(`/tweets/${postId}${buildPostQuery()}`);
32
+ const tweet = readSingleTweet(response.data);
33
+ if (!tweet) {
34
+ return null;
35
+ }
36
+ return normalizeTweet(tweet, response.includes?.users ?? []);
37
+ }
38
+ async function listTimeline(options) {
39
+ const response = await request(`/users/me/timelines/reverse_chronological${buildListQuery(resolveLimit(options, defaultLimit))}`);
40
+ return normalizeTweets(response);
41
+ }
42
+ async function listLikes(options) {
43
+ const response = await request(`/users/me/liked_tweets${buildListQuery(resolveLimit(options, defaultLimit))}`);
44
+ return normalizeTweets(response);
45
+ }
46
+ return {
47
+ posts: {
48
+ get: getPost,
49
+ },
50
+ me: {
51
+ timeline: listTimeline,
52
+ likes: listLikes,
53
+ watchLikes(watchOptions = {}) {
54
+ return watchLikesStream({
55
+ everyMs: watchOptions.everyMs ??
56
+ watchOptions.pollIntervalMs ??
57
+ DEFAULT_WATCH_INTERVAL_MS,
58
+ signal: watchOptions.signal,
59
+ listLikes,
60
+ });
61
+ },
62
+ },
63
+ };
64
+ }
65
+ function resolveLimit(options, defaultLimit) {
66
+ return options?.limit ?? defaultLimit;
67
+ }
68
+ function buildPostQuery() {
69
+ const params = new URLSearchParams({
70
+ expansions: "author_id",
71
+ "tweet.fields": "created_at,public_metrics",
72
+ "user.fields": "name,username",
73
+ });
74
+ return `?${params.toString()}`;
75
+ }
76
+ function buildListQuery(limit) {
77
+ const params = new URLSearchParams({
78
+ expansions: "author_id",
79
+ "tweet.fields": "created_at,public_metrics",
80
+ "user.fields": "name,username",
81
+ max_results: String(limit),
82
+ });
83
+ return `?${params.toString()}`;
84
+ }
85
+ function normalizeTweets(response) {
86
+ return readTweetList(response.data).map((tweet) => normalizeTweet(tweet, response.includes?.users ?? []));
87
+ }
88
+ function readSingleTweet(data) {
89
+ if (!isTweet(data)) {
90
+ return null;
91
+ }
92
+ return data;
93
+ }
94
+ function readTweetList(data) {
95
+ if (!Array.isArray(data)) {
96
+ return [];
97
+ }
98
+ return data.filter((item) => isTweet(item));
99
+ }
100
+ function isTweet(value) {
101
+ if (typeof value !== "object" || value === null) {
102
+ return false;
103
+ }
104
+ const candidate = value;
105
+ return typeof candidate.id === "string" && typeof candidate.text === "string";
106
+ }
107
+ function normalizeTweet(tweet, users) {
108
+ const author = users.find((user) => user.id === tweet.author_id);
109
+ return {
110
+ id: tweet.id,
111
+ text: tweet.text,
112
+ createdAt: tweet.created_at ?? new Date(0).toISOString(),
113
+ url: `https://x.com/${author?.username ?? "i"}/status/${tweet.id}`,
114
+ author: {
115
+ id: author?.id ?? tweet.author_id ?? "unknown",
116
+ username: author?.username ?? "unknown",
117
+ displayName: author?.name ?? author?.username ?? "Unknown",
118
+ },
119
+ metrics: {
120
+ likes: tweet.public_metrics?.like_count ?? 0,
121
+ replies: tweet.public_metrics?.reply_count ?? 0,
122
+ reposts: tweet.public_metrics?.retweet_count ?? 0,
123
+ },
124
+ };
125
+ }
126
+ async function* watchLikesStream({ everyMs, signal, listLikes, }) {
127
+ const knownLikeIds = new Set();
128
+ let seeded = false;
129
+ while (signal?.aborted !== true) {
130
+ const liked = await listLikes();
131
+ const seenAt = new Date().toISOString();
132
+ if (!seeded) {
133
+ for (const post of liked) {
134
+ knownLikeIds.add(post.id);
135
+ }
136
+ seeded = true;
137
+ }
138
+ else {
139
+ const notifications = [];
140
+ for (const post of liked) {
141
+ if (!knownLikeIds.has(post.id)) {
142
+ knownLikeIds.add(post.id);
143
+ notifications.push({ post, seenAt });
144
+ }
145
+ }
146
+ for (const notification of notifications.reverse()) {
147
+ yield notification;
148
+ }
149
+ }
150
+ if (!(await sleep(everyMs, signal))) {
151
+ return;
152
+ }
153
+ }
154
+ }
155
+ function sleep(ms, signal) {
156
+ if (signal?.aborted === true) {
157
+ return Promise.resolve(false);
158
+ }
159
+ if (signal === undefined) {
160
+ return new Promise((resolve) => {
161
+ const timer = setTimeout(() => {
162
+ resolve(true);
163
+ }, ms);
164
+ timer.unref();
165
+ });
166
+ }
167
+ const abortSignal = signal;
168
+ return new Promise((resolve) => {
169
+ const timer = setTimeout(onTimeout, ms);
170
+ timer.unref();
171
+ abortSignal.addEventListener("abort", onAbort, { once: true });
172
+ function onTimeout() {
173
+ abortSignal.removeEventListener("abort", onAbort);
174
+ resolve(true);
175
+ }
176
+ function onAbort() {
177
+ clearTimeout(timer);
178
+ abortSignal.removeEventListener("abort", onAbort);
179
+ resolve(false);
180
+ }
181
+ });
182
+ }
183
+ //# sourceMappingURL=createXSocial.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createXSocial.js","sourceRoot":"","sources":["../../src/x/createXSocial.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardlydifficult/social",
3
- "version": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -33,6 +33,6 @@
33
33
  }
34
34
  },
35
35
  "dependencies": {
36
- "@hardlydifficult/date-time": "1.0.24"
36
+ "@hardlydifficult/date-time": "1.0.25"
37
37
  }
38
38
  }
@@ -1,19 +0,0 @@
1
- import { SocialLikeWatcher, type SocialLikeWatcherOptions } from "./SocialLikeWatcher.js";
2
- import type { SocialProviderClient } from "./SocialProviderClient.js";
3
- import type { SocialPost } from "./types.js";
4
- /**
5
- *
6
- */
7
- export declare class SocialClient {
8
- private readonly provider;
9
- constructor(provider: SocialProviderClient);
10
- getPost(postId: string): Promise<SocialPost | null>;
11
- timeline(options?: {
12
- maxResults?: number;
13
- }): Promise<readonly SocialPost[]>;
14
- likedPosts(options?: {
15
- maxResults?: number;
16
- }): Promise<readonly SocialPost[]>;
17
- watchLikes(options: SocialLikeWatcherOptions): SocialLikeWatcher;
18
- }
19
- //# sourceMappingURL=SocialClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialClient.d.ts","sourceRoot":"","sources":["../src/SocialClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;GAEG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,oBAAoB;IAE3D,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAInD,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAI3E,UAAU,CAAC,OAAO,CAAC,EAAE;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAIlC,UAAU,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB;CAGjE"}
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SocialClient = void 0;
4
- const SocialLikeWatcher_js_1 = require("./SocialLikeWatcher.js");
5
- /**
6
- *
7
- */
8
- class SocialClient {
9
- provider;
10
- constructor(provider) {
11
- this.provider = provider;
12
- }
13
- getPost(postId) {
14
- return this.provider.getPost(postId);
15
- }
16
- timeline(options) {
17
- return this.provider.getTimeline(options);
18
- }
19
- likedPosts(options) {
20
- return this.provider.getLikedPosts(options);
21
- }
22
- watchLikes(options) {
23
- return SocialLikeWatcher_js_1.SocialLikeWatcher.create(this.provider, options);
24
- }
25
- }
26
- exports.SocialClient = SocialClient;
27
- //# sourceMappingURL=SocialClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialClient.js","sourceRoot":"","sources":["../src/SocialClient.ts"],"names":[],"mappings":";;;AAAA,iEAGgC;AAIhC;;GAEG;AACH,MAAa,YAAY;IACM;IAA7B,YAA6B,QAA8B;QAA9B,aAAQ,GAAR,QAAQ,CAAsB;IAAG,CAAC;IAE/D,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,QAAQ,CAAC,OAAiC;QACxC,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,UAAU,CAAC,OAEV;QACC,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,UAAU,CAAC,OAAiC;QAC1C,OAAO,wCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC1D,CAAC;CACF;AApBD,oCAoBC"}
@@ -1,20 +0,0 @@
1
- import type { SocialProviderClient } from "./SocialProviderClient.js";
2
- import type { LikeWatcherOptions } from "./types.js";
3
- export type SocialLikeWatcherOptions = LikeWatcherOptions;
4
- /**
5
- *
6
- */
7
- export declare class SocialLikeWatcher {
8
- private readonly provider;
9
- private readonly options;
10
- private timer;
11
- private polling;
12
- private seeded;
13
- private readonly knownLikeIds;
14
- constructor(provider: SocialProviderClient, options: Required<Pick<LikeWatcherOptions, "pollIntervalMs">> & Pick<LikeWatcherOptions, "onLike" | "onError">);
15
- static create(provider: SocialProviderClient, options: LikeWatcherOptions): SocialLikeWatcher;
16
- start(): void;
17
- stop(): void;
18
- poll(): Promise<void>;
19
- }
20
- //# sourceMappingURL=SocialLikeWatcher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialLikeWatcher.d.ts","sourceRoot":"","sources":["../src/SocialLikeWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAoB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,MAAM,wBAAwB,GAAG,kBAAkB,CAAC;AAE1D;;GAEG;AACH,qBAAa,iBAAiB;IAO1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;gBAG/B,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,QAAQ,CAChC,IAAI,CAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAC3C,GACC,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,SAAS,CAAC;IAGlD,MAAM,CAAC,MAAM,CACX,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,kBAAkB,GAC1B,iBAAiB;IAOpB,KAAK,IAAI,IAAI;IAab,IAAI,IAAI,IAAI;IAON,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CA6C5B"}
@@ -1,80 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SocialLikeWatcher = void 0;
4
- const date_time_1 = require("@hardlydifficult/date-time");
5
- /**
6
- *
7
- */
8
- class SocialLikeWatcher {
9
- provider;
10
- options;
11
- timer = null;
12
- polling = false;
13
- seeded = false;
14
- knownLikeIds = new Set();
15
- constructor(provider, options) {
16
- this.provider = provider;
17
- this.options = options;
18
- }
19
- static create(provider, options) {
20
- return new SocialLikeWatcher(provider, {
21
- ...options,
22
- pollIntervalMs: options.pollIntervalMs ?? date_time_1.MILLISECONDS_PER_MINUTE,
23
- });
24
- }
25
- start() {
26
- if (this.timer !== null) {
27
- return;
28
- }
29
- void this.poll();
30
- this.timer = setInterval(() => void this.poll(), this.options.pollIntervalMs);
31
- this.timer.unref();
32
- }
33
- stop() {
34
- if (this.timer !== null) {
35
- clearInterval(this.timer);
36
- this.timer = null;
37
- }
38
- }
39
- async poll() {
40
- if (this.polling) {
41
- return;
42
- }
43
- this.polling = true;
44
- try {
45
- const liked = await this.provider.getLikedPosts();
46
- const now = new Date().toISOString();
47
- if (!this.seeded) {
48
- for (const post of liked) {
49
- this.knownLikeIds.add(post.id);
50
- }
51
- this.seeded = true;
52
- return;
53
- }
54
- const notifications = [];
55
- for (const post of liked) {
56
- if (!this.knownLikeIds.has(post.id)) {
57
- this.knownLikeIds.add(post.id);
58
- notifications.push({ post, seenAt: now });
59
- }
60
- }
61
- for (const notification of notifications.reverse()) {
62
- this.options.onLike(notification);
63
- }
64
- }
65
- catch (error) {
66
- const normalizedError = error instanceof Error ? error : new Error(String(error));
67
- if (this.options.onError) {
68
- this.options.onError(normalizedError);
69
- }
70
- else {
71
- console.error("SocialLikeWatcher: poll failed:", normalizedError.message);
72
- }
73
- }
74
- finally {
75
- this.polling = false;
76
- }
77
- }
78
- }
79
- exports.SocialLikeWatcher = SocialLikeWatcher;
80
- //# sourceMappingURL=SocialLikeWatcher.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialLikeWatcher.js","sourceRoot":"","sources":["../src/SocialLikeWatcher.ts"],"names":[],"mappings":"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"}
@@ -1,11 +0,0 @@
1
- import type { SocialPost } from "./types.js";
2
- export interface SocialProviderClient {
3
- getPost(postId: string): Promise<SocialPost | null>;
4
- getTimeline(options?: {
5
- maxResults?: number;
6
- }): Promise<readonly SocialPost[]>;
7
- getLikedPosts(options?: {
8
- maxResults?: number;
9
- }): Promise<readonly SocialPost[]>;
10
- }
11
- //# sourceMappingURL=SocialProviderClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialProviderClient.d.ts","sourceRoot":"","sources":["../src/SocialProviderClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACpD,WAAW,CAAC,OAAO,CAAC,EAAE;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IACnC,aAAa,CAAC,OAAO,CAAC,EAAE;QACtB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;CACpC"}
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=SocialProviderClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialProviderClient.js","sourceRoot":"","sources":["../src/SocialProviderClient.ts"],"names":[],"mappings":""}
@@ -1,16 +0,0 @@
1
- import type { SocialProviderClient } from "../SocialProviderClient.js";
2
- import type { SocialPost } from "../types.js";
3
- /**
4
- *
5
- */
6
- export declare class MastodonSocialClient implements SocialProviderClient {
7
- private readonly message;
8
- getPost(_postId: string): Promise<SocialPost | null>;
9
- getTimeline(_options?: {
10
- maxResults?: number;
11
- }): Promise<readonly SocialPost[]>;
12
- getLikedPosts(_options?: {
13
- maxResults?: number;
14
- }): Promise<readonly SocialPost[]>;
15
- }
16
- //# sourceMappingURL=MastodonSocialClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MastodonSocialClient.d.ts","sourceRoot":"","sources":["../../src/mastodon/MastodonSocialClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;GAEG;AACH,qBAAa,oBAAqB,YAAW,oBAAoB;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACiF;IAEzG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAIpD,WAAW,CAAC,QAAQ,CAAC,EAAE;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAIlC,aAAa,CAAC,QAAQ,CAAC,EAAE;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;CAGnC"}
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MastodonSocialClient = void 0;
4
- /**
5
- *
6
- */
7
- class MastodonSocialClient {
8
- message = 'MastodonSocialClient is not yet supported in @hardlydifficult/social. Use provider type "x" for now.';
9
- getPost(_postId) {
10
- throw new Error(this.message);
11
- }
12
- getTimeline(_options) {
13
- throw new Error(this.message);
14
- }
15
- getLikedPosts(_options) {
16
- throw new Error(this.message);
17
- }
18
- }
19
- exports.MastodonSocialClient = MastodonSocialClient;
20
- //# sourceMappingURL=MastodonSocialClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MastodonSocialClient.js","sourceRoot":"","sources":["../../src/mastodon/MastodonSocialClient.ts"],"names":[],"mappings":";;;AAGA;;GAEG;AACH,MAAa,oBAAoB;IACd,OAAO,GACtB,sGAAsG,CAAC;IAEzG,OAAO,CAAC,OAAe;QACrB,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,WAAW,CAAC,QAEX;QACC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,aAAa,CAAC,QAEb;QACC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;CACF;AAnBD,oDAmBC"}
@@ -1,24 +0,0 @@
1
- import type { SocialProviderClient } from "../SocialProviderClient.js";
2
- import type { SocialPost, XConfig } from "../types.js";
3
- /**
4
- *
5
- */
6
- export declare class XSocialClient implements SocialProviderClient {
7
- private readonly bearerToken;
8
- private readonly maxResults;
9
- constructor(config: XConfig);
10
- getPost(postId: string): Promise<SocialPost | null>;
11
- getTimeline(options?: {
12
- maxResults?: number;
13
- }): Promise<readonly SocialPost[]>;
14
- getLikedPosts(options?: {
15
- maxResults?: number;
16
- }): Promise<readonly SocialPost[]>;
17
- private buildQuery;
18
- private request;
19
- private readSingleTweet;
20
- private readTweetList;
21
- private isTweet;
22
- private normalizeTweet;
23
- }
24
- //# sourceMappingURL=XSocialClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"XSocialClient.d.ts","sourceRoot":"","sources":["../../src/x/XSocialClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA2BvD;;GAEG;AACH,qBAAa,aAAc,YAAW,oBAAoB;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,MAAM,EAAE,OAAO;IASrB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAYnD,WAAW,CAAC,OAAO,CAAC,EAAE;QAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAU5B,aAAa,CAAC,OAAO,CAAC,EAAE;QAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAUlC,OAAO,CAAC,UAAU;YAWJ,OAAO;IAgBrB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,cAAc;CAoBvB"}
@@ -1,95 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.XSocialClient = void 0;
4
- /**
5
- *
6
- */
7
- class XSocialClient {
8
- bearerToken;
9
- maxResults;
10
- constructor(config) {
11
- this.bearerToken = config.bearerToken ?? process.env.X_BEARER_TOKEN ?? "";
12
- this.maxResults = config.maxResults ?? 25;
13
- if (this.bearerToken.length === 0) {
14
- throw new Error("X bearer token is required. Set X_BEARER_TOKEN.");
15
- }
16
- }
17
- async getPost(postId) {
18
- const response = await this.request(`/tweets/${postId}${this.buildQuery()}`);
19
- const tweet = this.readSingleTweet(response.data);
20
- if (!tweet) {
21
- return null;
22
- }
23
- return this.normalizeTweet(tweet, response.includes?.users ?? []);
24
- }
25
- async getTimeline(options) {
26
- const response = await this.request(`/users/me/timelines/reverse_chronological${this.buildQuery(options?.maxResults)}`);
27
- return this.readTweetList(response.data).map((tweet) => this.normalizeTweet(tweet, response.includes?.users ?? []));
28
- }
29
- async getLikedPosts(options) {
30
- const response = await this.request(`/users/me/liked_tweets${this.buildQuery(options?.maxResults)}`);
31
- return this.readTweetList(response.data).map((tweet) => this.normalizeTweet(tweet, response.includes?.users ?? []));
32
- }
33
- buildQuery(maxResults) {
34
- const params = new URLSearchParams({
35
- expansions: "author_id",
36
- "tweet.fields": "created_at,public_metrics",
37
- "user.fields": "name,username",
38
- max_results: String(maxResults ?? this.maxResults),
39
- });
40
- return `?${params.toString()}`;
41
- }
42
- async request(path) {
43
- const response = await fetch(`https://api.x.com/2${path}`, {
44
- headers: {
45
- Authorization: `Bearer ${this.bearerToken}`,
46
- "Content-Type": "application/json",
47
- },
48
- });
49
- if (!response.ok) {
50
- const status = String(response.status);
51
- throw new Error(`X API request failed: ${status} ${response.statusText}`);
52
- }
53
- return (await response.json());
54
- }
55
- readSingleTweet(data) {
56
- if (!this.isTweet(data)) {
57
- return null;
58
- }
59
- return data;
60
- }
61
- readTweetList(data) {
62
- if (!Array.isArray(data)) {
63
- return [];
64
- }
65
- return data.filter((item) => this.isTweet(item));
66
- }
67
- isTweet(value) {
68
- if (typeof value !== "object" || value === null) {
69
- return false;
70
- }
71
- const candidate = value;
72
- return (typeof candidate.id === "string" && typeof candidate.text === "string");
73
- }
74
- normalizeTweet(tweet, users) {
75
- const author = users.find((user) => user.id === tweet.author_id);
76
- return {
77
- id: tweet.id,
78
- text: tweet.text,
79
- createdAt: tweet.created_at ?? new Date(0).toISOString(),
80
- url: `https://x.com/${author?.username ?? "i"}/status/${tweet.id}`,
81
- author: {
82
- id: author?.id ?? tweet.author_id ?? "unknown",
83
- username: author?.username ?? "unknown",
84
- displayName: author?.name ?? author?.username ?? "Unknown",
85
- },
86
- metrics: {
87
- likes: tweet.public_metrics?.like_count ?? 0,
88
- replies: tweet.public_metrics?.reply_count ?? 0,
89
- reposts: tweet.public_metrics?.retweet_count ?? 0,
90
- },
91
- };
92
- }
93
- }
94
- exports.XSocialClient = XSocialClient;
95
- //# sourceMappingURL=XSocialClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"XSocialClient.js","sourceRoot":"","sources":["../../src/x/XSocialClient.ts"],"names":[],"mappings":";;;AA4BA;;GAEG;AACH,MAAa,aAAa;IACP,WAAW,CAAS;IACpB,UAAU,CAAS;IAEpC,YAAY,MAAe;QACzB,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;QAC1E,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;QAE1C,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAc;QAC1B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CACjC,WAAW,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE,CACxC,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAEjB;QACC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CACjC,4CAA4C,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CACnF,CAAC;QAEF,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACrD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAC3D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAEnB;QACC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CACjC,yBAAyB,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,UAAU,CAAC,EAAE,CAChE,CAAC;QAEF,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACrD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,CAC3D,CAAC;IACJ,CAAC;IAEO,UAAU,CAAC,UAAmB;QACpC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,UAAU,EAAE,WAAW;YACvB,cAAc,EAAE,2BAA2B;YAC3C,aAAa,EAAE,eAAe;YAC9B,WAAW,EAAE,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC;SACnD,CAAC,CAAC;QAEH,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IACjC,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY;QAChC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,sBAAsB,IAAI,EAAE,EAAE;YACzD,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,IAAI,CAAC,WAAW,EAAE;gBAC3C,cAAc,EAAE,kBAAkB;aACnC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAc,CAAC;IAC9C,CAAC;IAEO,eAAe,CAAC,IAAa;QACnC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,aAAa,CAAC,IAAa;QACjC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACnE,CAAC;IAEO,OAAO,CAAC,KAAc;QAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,SAAS,GAAG,KAAgC,CAAC;QACnD,OAAO,CACL,OAAO,SAAS,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,SAAS,CAAC,IAAI,KAAK,QAAQ,CACvE,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,KAAa,EAAE,KAAuB;QAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;QAEjE,OAAO;YACL,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,SAAS,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;YACxD,GAAG,EAAE,iBAAiB,MAAM,EAAE,QAAQ,IAAI,GAAG,WAAW,KAAK,CAAC,EAAE,EAAE;YAClE,MAAM,EAAE;gBACN,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,KAAK,CAAC,SAAS,IAAI,SAAS;gBAC9C,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,SAAS;gBACvC,WAAW,EAAE,MAAM,EAAE,IAAI,IAAI,MAAM,EAAE,QAAQ,IAAI,SAAS;aAC3D;YACD,OAAO,EAAE;gBACP,KAAK,EAAE,KAAK,CAAC,cAAc,EAAE,UAAU,IAAI,CAAC;gBAC5C,OAAO,EAAE,KAAK,CAAC,cAAc,EAAE,WAAW,IAAI,CAAC;gBAC/C,OAAO,EAAE,KAAK,CAAC,cAAc,EAAE,aAAa,IAAI,CAAC;aAClD;SACF,CAAC;IACJ,CAAC;CACF;AA3HD,sCA2HC"}
package/dist/x/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export { XSocialClient } from "./XSocialClient.js";
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/x/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/x/index.js DELETED
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.XSocialClient = void 0;
4
- var XSocialClient_js_1 = require("./XSocialClient.js");
5
- Object.defineProperty(exports, "XSocialClient", { enumerable: true, get: function () { return XSocialClient_js_1.XSocialClient; } });
6
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/x/index.ts"],"names":[],"mappings":";;;AAAA,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}