@hasna/connectors 1.3.23 → 1.3.25
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/bin/index.js +492 -153
- package/bin/mcp.js +1078 -595
- package/bin/serve.js +774 -436
- package/connectors/connect-cloudflare/bun.lock +32 -0
- package/connectors/connect-gmail/bin/index.js +7027 -0
- package/connectors/connect-googledrive/bin/index.js +5958 -0
- package/connectors/connect-imessage/bin/.gitkeep +2 -0
- package/connectors/connect-imessage/bin/index.js +3016 -0
- package/connectors/connect-imessage/bun.lock +32 -0
- package/connectors/connect-imessage/package.json +46 -0
- package/connectors/connect-imessage/src/api/client.ts +139 -0
- package/connectors/connect-imessage/src/api/conversations.ts +49 -0
- package/connectors/connect-imessage/src/api/health.ts +16 -0
- package/connectors/connect-imessage/src/api/index.ts +62 -0
- package/connectors/connect-imessage/src/api/messages.ts +119 -0
- package/connectors/connect-imessage/src/cli/index.ts +258 -0
- package/connectors/connect-imessage/src/index.ts +26 -0
- package/connectors/connect-imessage/src/types/index.ts +134 -0
- package/connectors/connect-imessage/src/utils/config.ts +219 -0
- package/connectors/connect-imessage/src/utils/output.ts +121 -0
- package/connectors/connect-imessage/tsconfig.json +16 -0
- package/dist/core/builtins.test.d.ts +1 -0
- package/dist/core/connectors/googledrive.d.ts +6 -0
- package/dist/core/connectors/internal-operations.test.d.ts +1 -0
- package/dist/core/connectors/internal-runtime.test.d.ts +1 -0
- package/dist/core/errors.test.d.ts +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +640 -86
- package/dist/lib/runner-auth.test.d.ts +1 -0
- package/dist/lib/runner.d.ts +31 -0
- package/dist/lib/test-endpoints.test.d.ts +1 -0
- package/dist/lib/workflow-runner.d.ts +7 -0
- package/dist/server/auth-exchange.test.d.ts +1 -0
- package/dist/server/auth-refresh.test.d.ts +1 -0
- package/dist/server/auth.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "@hasna/connect-imessage",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"chalk": "^5.3.0",
|
|
9
|
+
"commander": "^12.1.0",
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@types/bun": "latest",
|
|
13
|
+
"typescript": "^5",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
"packages": {
|
|
18
|
+
"@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
|
|
19
|
+
|
|
20
|
+
"@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
|
|
21
|
+
|
|
22
|
+
"bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
|
|
23
|
+
|
|
24
|
+
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
|
25
|
+
|
|
26
|
+
"commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
|
27
|
+
|
|
28
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
29
|
+
|
|
30
|
+
"undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasna/connect-imessage",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "iMessage connector CLI - bridge-first iMessage transport for send, reply, and conversation management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"connect-imessage": "bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun && bun build ./src/cli/index.ts --outdir ./bin --target bun",
|
|
19
|
+
"dev": "bun run ./src/cli/index.ts",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "bun run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"imessage",
|
|
25
|
+
"apple",
|
|
26
|
+
"messages",
|
|
27
|
+
"connector",
|
|
28
|
+
"cli",
|
|
29
|
+
"typescript",
|
|
30
|
+
"bun",
|
|
31
|
+
"hasna"
|
|
32
|
+
],
|
|
33
|
+
"author": "Hasna",
|
|
34
|
+
"license": "Apache-2.0",
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/bun": "latest",
|
|
37
|
+
"typescript": "^5"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"commander": "^12.1.0",
|
|
41
|
+
"chalk": "^5.3.0"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"bun": ">=1.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { IMessageConfig, IMessageApiResponse } from '../types';
|
|
2
|
+
import { IMessageApiError } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface ImessageRequestOptions {
|
|
5
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
6
|
+
params?: Record<string, string | number | boolean | undefined>;
|
|
7
|
+
body?: Record<string, unknown> | unknown[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* HTTP client for the iMessage bridge API
|
|
12
|
+
*/
|
|
13
|
+
export class ImessageClient {
|
|
14
|
+
private readonly bridgeUrl: string;
|
|
15
|
+
private readonly apiKey?: string;
|
|
16
|
+
private readonly deviceId?: string;
|
|
17
|
+
|
|
18
|
+
constructor(config: IMessageConfig) {
|
|
19
|
+
if (!config.bridgeUrl) {
|
|
20
|
+
throw new Error('Bridge URL is required');
|
|
21
|
+
}
|
|
22
|
+
// Normalize URL - strip trailing slash
|
|
23
|
+
this.bridgeUrl = config.bridgeUrl.replace(/\/+$/, '');
|
|
24
|
+
this.apiKey = config.apiKey;
|
|
25
|
+
this.deviceId = config.deviceId;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {
|
|
29
|
+
const url = new URL(this.bridgeUrl + path);
|
|
30
|
+
if (params) {
|
|
31
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
32
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
33
|
+
url.searchParams.append(key, String(value));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return url.toString();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private getHeaders(): Record<string, string> {
|
|
41
|
+
const headers: Record<string, string> = {
|
|
42
|
+
'Accept': 'application/json',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (this.apiKey) {
|
|
46
|
+
headers['Authorization'] = 'Bearer ' + this.apiKey;
|
|
47
|
+
}
|
|
48
|
+
if (this.deviceId) {
|
|
49
|
+
headers['X-API-Key'] = this.deviceId;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return headers;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async request<T>(
|
|
56
|
+
path: string,
|
|
57
|
+
options: ImessageRequestOptions = {}
|
|
58
|
+
): Promise<T> {
|
|
59
|
+
const { method = 'GET', params, body } = options;
|
|
60
|
+
|
|
61
|
+
const url = this.buildUrl(path, method === 'GET' ? params : undefined);
|
|
62
|
+
const headers = this.getHeaders();
|
|
63
|
+
|
|
64
|
+
const fetchOptions: RequestInit = {
|
|
65
|
+
method,
|
|
66
|
+
headers,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
|
|
70
|
+
headers['Content-Type'] = 'application/json';
|
|
71
|
+
fetchOptions.body = JSON.stringify(body);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let response: Response;
|
|
75
|
+
try {
|
|
76
|
+
response = await fetch(url, fetchOptions);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
throw new IMessageApiError('Failed to reach bridge: ' + error.message, 0);
|
|
80
|
+
}
|
|
81
|
+
throw new IMessageApiError('Failed to reach bridge', 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let data: unknown;
|
|
85
|
+
const contentType = response.headers.get('content-type') || '';
|
|
86
|
+
if (contentType.includes('application/json')) {
|
|
87
|
+
const text = await response.text();
|
|
88
|
+
if (text) {
|
|
89
|
+
try {
|
|
90
|
+
data = JSON.parse(text);
|
|
91
|
+
} catch {
|
|
92
|
+
data = text;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
data = await response.text();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!response.ok) {
|
|
100
|
+
const errorData = data as IMessageApiResponse;
|
|
101
|
+
const message = errorData?.error || (typeof data === 'string' ? data : response.statusText);
|
|
102
|
+
throw new IMessageApiError(message || 'Bridge request failed', response.status, errorData?.code);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return (data as IMessageApiResponse<T>)?.data ?? (data as T);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async get<T>(path: string, params?: Record<string, string | number | boolean | undefined>): Promise<T> {
|
|
109
|
+
return this.request<T>(path, { method: 'GET', params });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async post<T>(path: string, body?: Record<string, unknown>): Promise<T> {
|
|
113
|
+
return this.request<T>(path, { method: 'POST', body });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async put<T>(path: string, body?: Record<string, unknown>): Promise<T> {
|
|
117
|
+
return this.request<T>(path, { method: 'PUT', body });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async patch<T>(path: string, body?: Record<string, unknown>): Promise<T> {
|
|
121
|
+
return this.request<T>(path, { method: 'PATCH', body });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async delete<T>(path: string): Promise<T> {
|
|
125
|
+
return this.request<T>(path, { method: 'DELETE' });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get a preview of the bridge URL (for debugging)
|
|
130
|
+
*/
|
|
131
|
+
getBridgeUrlPreview(): string {
|
|
132
|
+
try {
|
|
133
|
+
const url = new URL(this.bridgeUrl);
|
|
134
|
+
return url.host;
|
|
135
|
+
} catch {
|
|
136
|
+
return 'invalid-url';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { ImessageClient } from './client';
|
|
2
|
+
import type { IMessageConversation } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface ListConversationsOptions {
|
|
5
|
+
limit?: number;
|
|
6
|
+
offset?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Conversation API module - list and retrieve iMessage conversations
|
|
11
|
+
*/
|
|
12
|
+
export class ConversationsApi {
|
|
13
|
+
constructor(private readonly client: ImessageClient) {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* List all conversations
|
|
17
|
+
*/
|
|
18
|
+
async list(options: ListConversationsOptions = {}): Promise<IMessageConversation[]> {
|
|
19
|
+
const params: Record<string, number | undefined> = {};
|
|
20
|
+
if (options.limit) params.limit = options.limit;
|
|
21
|
+
if (options.offset) params.offset = options.offset;
|
|
22
|
+
return this.client.get<IMessageConversation[]>('/conversations', params);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Iterate over all conversations (handles pagination)
|
|
27
|
+
*/
|
|
28
|
+
async *listAll(options: ListConversationsOptions = {}): AsyncGenerator<IMessageConversation> {
|
|
29
|
+
let offset = options.offset || 0;
|
|
30
|
+
const limit = options.limit || 50;
|
|
31
|
+
|
|
32
|
+
while (true) {
|
|
33
|
+
const conversations = await this.list({ limit, offset });
|
|
34
|
+
if (conversations.length === 0) break;
|
|
35
|
+
for (const conversation of conversations) {
|
|
36
|
+
yield conversation;
|
|
37
|
+
}
|
|
38
|
+
if (conversations.length < limit) break;
|
|
39
|
+
offset += conversations.length;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get a specific conversation by chat GUID
|
|
45
|
+
*/
|
|
46
|
+
async get(chatGuid: string): Promise<IMessageConversation> {
|
|
47
|
+
return this.client.get<IMessageConversation>(`/conversations/${encodeURIComponent(chatGuid)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ImessageClient } from './client';
|
|
2
|
+
import type { IMessageHealth } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Health API module - check bridge and iMessage service status
|
|
6
|
+
*/
|
|
7
|
+
export class HealthApi {
|
|
8
|
+
constructor(private readonly client: ImessageClient) {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Check bridge and iMessage health
|
|
12
|
+
*/
|
|
13
|
+
async check(): Promise<IMessageHealth> {
|
|
14
|
+
return this.client.get<IMessageHealth>('/health');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { IMessageConfig } from '../types';
|
|
2
|
+
import { ImessageClient } from './client';
|
|
3
|
+
import { HealthApi } from './health';
|
|
4
|
+
import { ConversationsApi } from './conversations';
|
|
5
|
+
import { MessagesApi } from './messages';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Main iMessage Connector class
|
|
9
|
+
* Provides access to iMessage via a bridge API
|
|
10
|
+
*/
|
|
11
|
+
export class IMessage {
|
|
12
|
+
private readonly client: ImessageClient;
|
|
13
|
+
|
|
14
|
+
// Service APIs
|
|
15
|
+
public readonly health: HealthApi;
|
|
16
|
+
public readonly conversations: ConversationsApi;
|
|
17
|
+
public readonly messages: MessagesApi;
|
|
18
|
+
|
|
19
|
+
constructor(config: IMessageConfig) {
|
|
20
|
+
this.client = new ImessageClient(config);
|
|
21
|
+
this.health = new HealthApi(this.client);
|
|
22
|
+
this.conversations = new ConversationsApi(this.client);
|
|
23
|
+
this.messages = new MessagesApi(this.client);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create a client from environment variables
|
|
28
|
+
* Looks for IMESSAGE_BRIDGE_URL, IMESSAGE_API_KEY, IMESSAGE_DEVICE_ID
|
|
29
|
+
*/
|
|
30
|
+
static fromEnv(): IMessage {
|
|
31
|
+
const bridgeUrl = process.env.IMESSAGE_BRIDGE_URL;
|
|
32
|
+
|
|
33
|
+
if (!bridgeUrl) {
|
|
34
|
+
throw new Error('IMESSAGE_BRIDGE_URL environment variable is required');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return new IMessage({
|
|
38
|
+
bridgeUrl,
|
|
39
|
+
apiKey: process.env.IMESSAGE_API_KEY,
|
|
40
|
+
deviceId: process.env.IMESSAGE_DEVICE_ID,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get the bridge URL preview
|
|
46
|
+
*/
|
|
47
|
+
getBridgeUrlPreview(): string {
|
|
48
|
+
return this.client.getBridgeUrlPreview();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the underlying client for direct API access
|
|
53
|
+
*/
|
|
54
|
+
getClient(): ImessageClient {
|
|
55
|
+
return this.client;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { ImessageClient } from './client';
|
|
60
|
+
export { HealthApi } from './health';
|
|
61
|
+
export { ConversationsApi } from './conversations';
|
|
62
|
+
export { MessagesApi } from './messages';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ImessageClient } from './client';
|
|
2
|
+
import type { IMessage, IMessageAttachment } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface ListMessagesOptions {
|
|
5
|
+
chatGuid: string;
|
|
6
|
+
limit?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
before?: string;
|
|
9
|
+
after?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface SendMessageOptions {
|
|
13
|
+
chatGuid?: string;
|
|
14
|
+
recipient?: string;
|
|
15
|
+
text: string;
|
|
16
|
+
method?: string;
|
|
17
|
+
effectId?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ReplyMessageOptions {
|
|
21
|
+
text: string;
|
|
22
|
+
selectedMessageGuid: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Message API module - send, receive, and manage iMessages
|
|
27
|
+
*/
|
|
28
|
+
export class MessagesApi {
|
|
29
|
+
constructor(private readonly client: ImessageClient) {}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* List messages for a conversation
|
|
33
|
+
*/
|
|
34
|
+
async list(options: ListMessagesOptions): Promise<IMessage[]> {
|
|
35
|
+
const params: Record<string, string | number | undefined> = {
|
|
36
|
+
chatGuid: options.chatGuid,
|
|
37
|
+
};
|
|
38
|
+
if (options.limit) params.limit = options.limit;
|
|
39
|
+
if (options.offset) params.offset = options.offset;
|
|
40
|
+
if (options.before) params.before = options.before;
|
|
41
|
+
if (options.after) params.after = options.after;
|
|
42
|
+
return this.client.get<IMessage[]>('/messages', params);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Iterate over all messages (handles pagination)
|
|
47
|
+
*/
|
|
48
|
+
async *listAll(options: ListMessagesOptions): AsyncGenerator<IMessage> {
|
|
49
|
+
let offset = options.offset || 0;
|
|
50
|
+
const limit = options.limit || 50;
|
|
51
|
+
|
|
52
|
+
while (true) {
|
|
53
|
+
const messages = await this.list({ ...options, limit, offset });
|
|
54
|
+
if (messages.length === 0) break;
|
|
55
|
+
for (const message of messages) {
|
|
56
|
+
yield message;
|
|
57
|
+
}
|
|
58
|
+
if (messages.length < limit) break;
|
|
59
|
+
offset += messages.length;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Send a message to a conversation
|
|
65
|
+
*/
|
|
66
|
+
async send(options: SendMessageOptions): Promise<IMessage> {
|
|
67
|
+
const body: Record<string, unknown> = {
|
|
68
|
+
text: options.text,
|
|
69
|
+
};
|
|
70
|
+
if (options.chatGuid) body.chatGuid = options.chatGuid;
|
|
71
|
+
if (options.recipient) body.recipient = options.recipient;
|
|
72
|
+
if (options.method) body.method = options.method;
|
|
73
|
+
if (options.effectId) body.effectId = options.effectId;
|
|
74
|
+
return this.client.post<IMessage>('/messages', body);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Send a reply to a specific message (tapback or quoted reply)
|
|
79
|
+
*/
|
|
80
|
+
async reply(messageGuid: string, options: ReplyMessageOptions): Promise<IMessage> {
|
|
81
|
+
const body: Record<string, unknown> = {
|
|
82
|
+
text: options.text,
|
|
83
|
+
selectedMessageGuid: options.selectedMessageGuid,
|
|
84
|
+
};
|
|
85
|
+
return this.client.post<IMessage>(`/messages/${messageGuid}/reply`, body);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get a specific message by GUID
|
|
90
|
+
*/
|
|
91
|
+
async get(messageGuid: string): Promise<IMessage> {
|
|
92
|
+
return this.client.get<IMessage>(`/messages/${messageGuid}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Download a message attachment
|
|
97
|
+
*/
|
|
98
|
+
async downloadAttachment(attachmentGuid: string): Promise<ArrayBuffer> {
|
|
99
|
+
return this.client.request<ArrayBuffer>(`/attachments/${attachmentGuid}`, {
|
|
100
|
+
method: 'GET',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Send a tapback (reaction) to a message
|
|
106
|
+
*/
|
|
107
|
+
async tapback(messageGuid: string, tapback: string): Promise<IMessage> {
|
|
108
|
+
return this.client.post<IMessage>(`/messages/${messageGuid}/tapback`, {
|
|
109
|
+
tapback,
|
|
110
|
+
} as unknown as Record<string, unknown>);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Mark messages as read in a conversation
|
|
115
|
+
*/
|
|
116
|
+
async markRead(chatGuid: string): Promise<void> {
|
|
117
|
+
await this.client.post<void>(`/conversations/${encodeURIComponent(chatGuid)}/read`);
|
|
118
|
+
}
|
|
119
|
+
}
|