@imessaging/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +32 -0
- package/src/index.ts +13 -0
- package/src/memory-peer-store.ts +40 -0
- package/src/peer-key.ts +25 -0
- package/src/types.ts +77 -0
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imessaging/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core messaging transport contracts and Telegram peer storage",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"messaging",
|
|
7
|
+
"peer-cache",
|
|
8
|
+
"telegram",
|
|
9
|
+
"transport"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/iconicompany/imessaging.git",
|
|
15
|
+
"directory": "packages/core"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"src"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./src/index.ts"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"type-check": "bunx tsgo --noEmit -p tsconfig.json",
|
|
30
|
+
"test": "bun test"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { MemoryPeerStore } from "./memory-peer-store";
|
|
2
|
+
export { createPeerKey } from "./peer-key";
|
|
3
|
+
export type {
|
|
4
|
+
MessageRecipient,
|
|
5
|
+
MessageTransport,
|
|
6
|
+
OutboundButton,
|
|
7
|
+
OutboundDocument,
|
|
8
|
+
OutboundMessage,
|
|
9
|
+
ResolvedPeer,
|
|
10
|
+
SendResult,
|
|
11
|
+
TelegramPeerStore,
|
|
12
|
+
TransportStatus,
|
|
13
|
+
} from "./types";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createPeerKey } from "./peer-key";
|
|
2
|
+
import type { MessageRecipient, ResolvedPeer, TelegramPeerStore } from "./types";
|
|
3
|
+
|
|
4
|
+
type StoredPeer = {
|
|
5
|
+
peer: ResolvedPeer;
|
|
6
|
+
expiresAt?: number;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export class MemoryPeerStore implements TelegramPeerStore {
|
|
10
|
+
private readonly peers = new Map<string, StoredPeer>();
|
|
11
|
+
|
|
12
|
+
async get(accountId: string, recipient: MessageRecipient): Promise<ResolvedPeer | null> {
|
|
13
|
+
const key = createPeerKey(accountId, recipient);
|
|
14
|
+
const stored = this.peers.get(key);
|
|
15
|
+
if (!stored) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (stored.expiresAt && stored.expiresAt <= Date.now()) {
|
|
19
|
+
this.peers.delete(key);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return stored.peer;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async set(
|
|
26
|
+
accountId: string,
|
|
27
|
+
recipient: MessageRecipient,
|
|
28
|
+
peer: ResolvedPeer,
|
|
29
|
+
ttlSeconds?: number,
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
this.peers.set(createPeerKey(accountId, recipient), {
|
|
32
|
+
peer,
|
|
33
|
+
expiresAt: ttlSeconds ? Date.now() + ttlSeconds * 1000 : undefined,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async delete(accountId: string, recipient: MessageRecipient): Promise<void> {
|
|
38
|
+
this.peers.delete(createPeerKey(accountId, recipient));
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/peer-key.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MessageRecipient } from "./types";
|
|
2
|
+
|
|
3
|
+
function normalizeUsername(username: string): string {
|
|
4
|
+
return username.trim().replace(/^@/, "").toLowerCase();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createPeerKey(accountId: string, recipient: MessageRecipient): string {
|
|
8
|
+
const normalizedAccountId = accountId.trim();
|
|
9
|
+
if (!normalizedAccountId) {
|
|
10
|
+
throw new Error("accountId must not be empty");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (recipient.type === "username") {
|
|
14
|
+
const username = normalizeUsername(recipient.username);
|
|
15
|
+
if (!username) {
|
|
16
|
+
throw new Error("username must not be empty");
|
|
17
|
+
}
|
|
18
|
+
return `telegram-peer:${normalizedAccountId}:username:${username}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!recipient.id.trim()) {
|
|
22
|
+
throw new Error("recipient id must not be empty");
|
|
23
|
+
}
|
|
24
|
+
return `telegram-peer:${normalizedAccountId}:${recipient.type}:${recipient.id}`;
|
|
25
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export type UsernameRecipient = {
|
|
2
|
+
type: "username";
|
|
3
|
+
username: string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type TelegramIdRecipient = {
|
|
7
|
+
type: "user" | "group" | "channel";
|
|
8
|
+
id: string;
|
|
9
|
+
username?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type MessageRecipient = UsernameRecipient | TelegramIdRecipient;
|
|
13
|
+
|
|
14
|
+
export type OutboundDocument = {
|
|
15
|
+
data: Uint8Array;
|
|
16
|
+
filename: string;
|
|
17
|
+
caption?: string;
|
|
18
|
+
mimeType?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type OutboundButton = {
|
|
22
|
+
text: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
callbackData?: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type OutboundMessage = {
|
|
28
|
+
recipient: MessageRecipient;
|
|
29
|
+
text: string;
|
|
30
|
+
replyToMessageId?: number;
|
|
31
|
+
threadId?: number;
|
|
32
|
+
documents?: OutboundDocument[];
|
|
33
|
+
buttons?: OutboundButton[][];
|
|
34
|
+
parseMode?: "HTML" | "Markdown" | "MarkdownV2";
|
|
35
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type SendResult = {
|
|
39
|
+
transportId: string;
|
|
40
|
+
messageId: string;
|
|
41
|
+
recipientId: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type TransportStatus = {
|
|
45
|
+
connected: boolean;
|
|
46
|
+
transportId: string;
|
|
47
|
+
accountId?: string;
|
|
48
|
+
lastActivityAt?: Date;
|
|
49
|
+
error?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export interface MessageTransport {
|
|
53
|
+
readonly id: string;
|
|
54
|
+
connect(): Promise<void>;
|
|
55
|
+
disconnect(): Promise<void>;
|
|
56
|
+
send(message: OutboundMessage): Promise<SendResult>;
|
|
57
|
+
getStatus(): Promise<TransportStatus>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type ResolvedPeer = {
|
|
61
|
+
type: "user" | "group" | "channel";
|
|
62
|
+
id: string;
|
|
63
|
+
accessHash?: string;
|
|
64
|
+
username?: string;
|
|
65
|
+
resolvedAt: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export interface TelegramPeerStore {
|
|
69
|
+
get(accountId: string, recipient: MessageRecipient): Promise<ResolvedPeer | null>;
|
|
70
|
+
set(
|
|
71
|
+
accountId: string,
|
|
72
|
+
recipient: MessageRecipient,
|
|
73
|
+
peer: ResolvedPeer,
|
|
74
|
+
ttlSeconds?: number,
|
|
75
|
+
): Promise<void>;
|
|
76
|
+
delete(accountId: string, recipient: MessageRecipient): Promise<void>;
|
|
77
|
+
}
|