@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 +84 -178
- package/dist/index.d.ts +5 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -21
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +26 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/x/createXSocial.d.ts +4 -0
- package/dist/x/createXSocial.d.ts.map +1 -0
- package/dist/x/createXSocial.js +183 -0
- package/dist/x/createXSocial.js.map +1 -0
- package/package.json +2 -2
- package/dist/SocialClient.d.ts +0 -19
- package/dist/SocialClient.d.ts.map +0 -1
- package/dist/SocialClient.js +0 -27
- package/dist/SocialClient.js.map +0 -1
- package/dist/SocialLikeWatcher.d.ts +0 -20
- package/dist/SocialLikeWatcher.d.ts.map +0 -1
- package/dist/SocialLikeWatcher.js +0 -80
- package/dist/SocialLikeWatcher.js.map +0 -1
- package/dist/SocialProviderClient.d.ts +0 -11
- package/dist/SocialProviderClient.d.ts.map +0 -1
- package/dist/SocialProviderClient.js +0 -3
- package/dist/SocialProviderClient.js.map +0 -1
- package/dist/mastodon/MastodonSocialClient.d.ts +0 -16
- package/dist/mastodon/MastodonSocialClient.d.ts.map +0 -1
- package/dist/mastodon/MastodonSocialClient.js +0 -20
- package/dist/mastodon/MastodonSocialClient.js.map +0 -1
- package/dist/x/XSocialClient.d.ts +0 -24
- package/dist/x/XSocialClient.d.ts.map +0 -1
- package/dist/x/XSocialClient.js +0 -95
- package/dist/x/XSocialClient.js.map +0 -1
- package/dist/x/index.d.ts +0 -2
- package/dist/x/index.d.ts.map +0 -1
- package/dist/x/index.js +0 -6
- package/dist/x/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
# @hardlydifficult/social
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Opinionated social read client for X.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package is intentionally narrow:
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
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
|
-
```
|
|
24
|
-
import {
|
|
20
|
+
```ts
|
|
21
|
+
import { createSocial } from "@hardlydifficult/social";
|
|
25
22
|
|
|
26
|
-
const
|
|
27
|
-
|
|
23
|
+
const social = createSocial({
|
|
24
|
+
token: process.env.X_BEARER_TOKEN,
|
|
28
25
|
});
|
|
29
26
|
|
|
30
|
-
const timeline = await
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
+
## API
|
|
96
36
|
|
|
97
|
-
###
|
|
37
|
+
### `createSocial(options?)`
|
|
98
38
|
|
|
99
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
+
- `token?: string`
|
|
51
|
+
- `bearerToken?: string`
|
|
52
|
+
- `defaultLimit?: number`
|
|
53
|
+
- `limit?: number`
|
|
54
|
+
- `maxResults?: number`
|
|
55
|
+
- `type?: "x"`
|
|
133
56
|
|
|
134
|
-
|
|
135
|
-
|
|
57
|
+
`bearerToken`, `limit`, and `maxResults` are accepted as compatibility aliases.
|
|
58
|
+
`type` is optional because X is the only supported provider.
|
|
136
59
|
|
|
137
|
-
|
|
138
|
-
const watcher = new SocialLikeWatcher(client, { pollIntervalMs: 30_000 });
|
|
60
|
+
### `social.posts.get(id)`
|
|
139
61
|
|
|
140
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
// watcher.stop();
|
|
64
|
+
```ts
|
|
65
|
+
const post = await social.posts.get("123");
|
|
151
66
|
```
|
|
152
67
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
The package exports convenience factory functions to construct platform-specific clients:
|
|
68
|
+
### `social.me.timeline(options?)`
|
|
156
69
|
|
|
157
|
-
|
|
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
|
-
```
|
|
163
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
For a more opinionated interface, use `createSocial`:
|
|
77
|
+
### `social.me.likes(options?)`
|
|
172
78
|
|
|
173
|
-
|
|
174
|
-
import { createSocial } from "@hardlydifficult/social";
|
|
79
|
+
Fetch posts liked by the authenticated user.
|
|
175
80
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
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
|
-
###
|
|
86
|
+
### `social.me.watchLikes(options?)`
|
|
192
87
|
|
|
193
|
-
|
|
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
|
-
|
|
196
|
-
|
|
92
|
+
```ts
|
|
93
|
+
const controller = new AbortController();
|
|
197
94
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
103
|
+
Options:
|
|
208
104
|
|
|
209
|
-
|
|
105
|
+
- `everyMs?: number`
|
|
106
|
+
- `pollIntervalMs?: number`
|
|
107
|
+
- `signal?: AbortSignal`
|
|
210
108
|
|
|
211
|
-
|
|
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
|
-
|
|
111
|
+
## Types
|
|
216
112
|
|
|
217
|
-
|
|
218
|
-
npm test
|
|
219
|
-
```
|
|
113
|
+
Core types exported by the package:
|
|
220
114
|
|
|
221
|
-
|
|
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
|
-
|
|
129
|
+
## Scope
|
|
224
130
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
137
|
+
Posting, liking, and reposting are intentionally out of scope.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
export type { Provider,
|
|
2
|
-
|
|
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
|
|
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
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,
|
|
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
|
-
|
|
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
|
|
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
|
|
20
|
-
return
|
|
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":"
|
|
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
|
|
2
|
-
|
|
3
|
-
readonly
|
|
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
|
|
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
|
|
38
|
+
export interface WatchLikesOptions {
|
|
39
|
+
readonly everyMs?: number;
|
|
31
40
|
readonly pollIntervalMs?: number;
|
|
32
|
-
readonly
|
|
33
|
-
|
|
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
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,
|
|
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 @@
|
|
|
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.
|
|
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.
|
|
36
|
+
"@hardlydifficult/date-time": "1.0.25"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/dist/SocialClient.d.ts
DELETED
|
@@ -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"}
|
package/dist/SocialClient.js
DELETED
|
@@ -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
|
package/dist/SocialClient.js.map
DELETED
|
@@ -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 +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"}
|
package/dist/x/XSocialClient.js
DELETED
|
@@ -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":"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"}
|
package/dist/x/index.d.ts
DELETED
package/dist/x/index.d.ts.map
DELETED
|
@@ -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
|
package/dist/x/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/x/index.ts"],"names":[],"mappings":";;;AAAA,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}
|