@c4t4/heyamigo 0.9.5 → 0.9.6
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/dist/db/schema.js
CHANGED
|
@@ -128,3 +128,54 @@ export const crons = sqliteTable('crons', {
|
|
|
128
128
|
.on(t.name)
|
|
129
129
|
.where(sql `${t.recurrence} IS NOT NULL`),
|
|
130
130
|
}));
|
|
131
|
+
// ──────────────────────────────────────────────────────────────────
|
|
132
|
+
// Inbound queue (Phase 4)
|
|
133
|
+
// ──────────────────────────────────────────────────────────────────
|
|
134
|
+
// Messages received on any channel, waiting to be processed by a
|
|
135
|
+
// chat worker. The chat worker pool claims rows with per-address
|
|
136
|
+
// serialization (one in-flight job per chat preserves reply order),
|
|
137
|
+
// runs the AI provider, and enqueues outbound rows with the reply.
|
|
138
|
+
//
|
|
139
|
+
// `address` is the chat-level address (a group's address, or a DM's
|
|
140
|
+
// address). `actor_address` is the within-chat sender — for DMs it
|
|
141
|
+
// equals `address`; for groups it identifies which member sent the
|
|
142
|
+
// message. `person_id` and `actor_person_id` are the resolved
|
|
143
|
+
// person identities (nullable when resolution fails, e.g. an unknown
|
|
144
|
+
// member of a group).
|
|
145
|
+
//
|
|
146
|
+
// Idempotency: external_msg_id (channel-native message id, e.g.
|
|
147
|
+
// Baileys message key id). The same channel message arriving twice
|
|
148
|
+
// (network retransmit, replay on reconnect) maps to one row.
|
|
149
|
+
export const inbound = sqliteTable('inbound', {
|
|
150
|
+
id: integer('id').primaryKey({ autoIncrement: true }),
|
|
151
|
+
address: text('address').notNull(), // chat-level address
|
|
152
|
+
actorAddress: text('actor_address'), // sender within chat; null = self/system
|
|
153
|
+
personId: text('person_id'), // resolved chat owner (DM partner / group)
|
|
154
|
+
actorPersonId: text('actor_person_id'), // resolved sender
|
|
155
|
+
externalMsgId: text('external_msg_id'), // channel-native msg id (idempotency)
|
|
156
|
+
text: text('text').notNull(), // body or media tag
|
|
157
|
+
mediaPath: text('media_path'), // path relative to storage/ when media
|
|
158
|
+
mediaMime: text('media_mime'),
|
|
159
|
+
mediaBytes: integer('media_bytes'),
|
|
160
|
+
pushName: text('push_name'), // sender's display name at send time
|
|
161
|
+
triggerReason: text('trigger_reason'), // 'alias'|'mention'|'reply'|'owner'|...
|
|
162
|
+
// 'pending'|'claimed'|'done'|'failed'|'dlq'
|
|
163
|
+
status: text('status').notNull(),
|
|
164
|
+
attempts: integer('attempts').notNull().default(0),
|
|
165
|
+
nextAttemptAt: integer('next_attempt_at'),
|
|
166
|
+
lastError: text('last_error'),
|
|
167
|
+
claimedBy: text('claimed_by'),
|
|
168
|
+
claimedAt: integer('claimed_at'),
|
|
169
|
+
receivedAt: integer('received_at').notNull(), // unix sec when WA delivered
|
|
170
|
+
createdAt: integer('created_at').notNull(), // unix sec when row inserted
|
|
171
|
+
updatedAt: integer('updated_at').notNull(),
|
|
172
|
+
}, t => ({
|
|
173
|
+
byStatusNext: index('inbound_by_status_next').on(t.status, t.nextAttemptAt),
|
|
174
|
+
byAddress: index('inbound_by_address').on(t.address),
|
|
175
|
+
byPerson: index('inbound_by_person').on(t.personId, t.receivedAt),
|
|
176
|
+
// Sparse unique on external_msg_id: enforced only when set. Same
|
|
177
|
+
// pattern as outbound's idempotency_key.
|
|
178
|
+
uniqExtId: uniqueIndex('inbound_external_msg_id_uq')
|
|
179
|
+
.on(t.externalMsgId)
|
|
180
|
+
.where(sql `${t.externalMsgId} IS NOT NULL`),
|
|
181
|
+
}));
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Inbound queue helpers. Gateway (gateway/incoming.ts) calls
|
|
2
|
+
// enqueueInbound; chat workers (queue/chat-worker.ts) drain via
|
|
3
|
+
// claimNextInbound. Per-address serialization preserves reply order
|
|
4
|
+
// within a chat while different chats run in parallel.
|
|
5
|
+
//
|
|
6
|
+
// Same primitives as outbound (claim/done/retry/dlq) with the added
|
|
7
|
+
// `address NOT IN (claimed)` filter in the claim query.
|
|
8
|
+
import { and, asc, eq, isNull, lte, notInArray, or, sql } from 'drizzle-orm';
|
|
9
|
+
import { getDb } from '../db/index.js';
|
|
10
|
+
import { inbound } from '../db/schema.js';
|
|
11
|
+
// Idempotent on external_msg_id when set. Same channel message
|
|
12
|
+
// arriving twice (Baileys replay, network retransmit) returns the
|
|
13
|
+
// existing row instead of duplicating.
|
|
14
|
+
export function enqueueInbound(input) {
|
|
15
|
+
const db = getDb();
|
|
16
|
+
const now = Math.floor(Date.now() / 1000);
|
|
17
|
+
if (input.externalMsgId) {
|
|
18
|
+
const found = db
|
|
19
|
+
.select()
|
|
20
|
+
.from(inbound)
|
|
21
|
+
.where(eq(inbound.externalMsgId, input.externalMsgId))
|
|
22
|
+
.get();
|
|
23
|
+
if (found)
|
|
24
|
+
return { inserted: false, row: found };
|
|
25
|
+
}
|
|
26
|
+
const row = db
|
|
27
|
+
.insert(inbound)
|
|
28
|
+
.values({
|
|
29
|
+
address: input.address,
|
|
30
|
+
actorAddress: input.actorAddress ?? null,
|
|
31
|
+
personId: input.personId ?? null,
|
|
32
|
+
actorPersonId: input.actorPersonId ?? null,
|
|
33
|
+
externalMsgId: input.externalMsgId ?? null,
|
|
34
|
+
text: input.text,
|
|
35
|
+
mediaPath: input.mediaPath ?? null,
|
|
36
|
+
mediaMime: input.mediaMime ?? null,
|
|
37
|
+
mediaBytes: input.mediaBytes ?? null,
|
|
38
|
+
pushName: input.pushName ?? null,
|
|
39
|
+
triggerReason: input.triggerReason ?? null,
|
|
40
|
+
status: 'pending',
|
|
41
|
+
attempts: 0,
|
|
42
|
+
nextAttemptAt: null,
|
|
43
|
+
lastError: null,
|
|
44
|
+
claimedBy: null,
|
|
45
|
+
claimedAt: null,
|
|
46
|
+
receivedAt: input.receivedAt ?? now,
|
|
47
|
+
createdAt: now,
|
|
48
|
+
updatedAt: now,
|
|
49
|
+
})
|
|
50
|
+
.returning()
|
|
51
|
+
.get();
|
|
52
|
+
return { inserted: true, row };
|
|
53
|
+
}
|
|
54
|
+
// Atomic claim with per-address serialization. Skips any pending row
|
|
55
|
+
// whose address already has another row in `claimed` state →
|
|
56
|
+
// preserves reply order per chat while letting different chats run
|
|
57
|
+
// in parallel.
|
|
58
|
+
export function claimNextInbound(workerId) {
|
|
59
|
+
const db = getDb();
|
|
60
|
+
const now = Math.floor(Date.now() / 1000);
|
|
61
|
+
return db.transaction((tx) => {
|
|
62
|
+
// Subquery: addresses currently claimed (= one in-flight per chat).
|
|
63
|
+
const busyAddrs = tx
|
|
64
|
+
.select({ address: inbound.address })
|
|
65
|
+
.from(inbound)
|
|
66
|
+
.where(eq(inbound.status, 'claimed'))
|
|
67
|
+
.all()
|
|
68
|
+
.map((r) => r.address);
|
|
69
|
+
const conds = [
|
|
70
|
+
eq(inbound.status, 'pending'),
|
|
71
|
+
or(isNull(inbound.nextAttemptAt), lte(inbound.nextAttemptAt, now)),
|
|
72
|
+
];
|
|
73
|
+
if (busyAddrs.length > 0) {
|
|
74
|
+
conds.push(notInArray(inbound.address, busyAddrs));
|
|
75
|
+
}
|
|
76
|
+
const target = tx
|
|
77
|
+
.select({ id: inbound.id })
|
|
78
|
+
.from(inbound)
|
|
79
|
+
.where(and(...conds))
|
|
80
|
+
.orderBy(asc(inbound.id))
|
|
81
|
+
.limit(1)
|
|
82
|
+
.get();
|
|
83
|
+
if (!target)
|
|
84
|
+
return null;
|
|
85
|
+
const claimed = tx
|
|
86
|
+
.update(inbound)
|
|
87
|
+
.set({
|
|
88
|
+
status: 'claimed',
|
|
89
|
+
claimedBy: workerId,
|
|
90
|
+
claimedAt: now,
|
|
91
|
+
updatedAt: now,
|
|
92
|
+
})
|
|
93
|
+
.where(and(eq(inbound.id, target.id), eq(inbound.status, 'pending')))
|
|
94
|
+
.returning()
|
|
95
|
+
.get();
|
|
96
|
+
return claimed ?? null;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export function markInboundDone(id, workerId) {
|
|
100
|
+
const db = getDb();
|
|
101
|
+
const now = Math.floor(Date.now() / 1000);
|
|
102
|
+
const result = db
|
|
103
|
+
.update(inbound)
|
|
104
|
+
.set({ status: 'done', updatedAt: now })
|
|
105
|
+
.where(and(eq(inbound.id, id), eq(inbound.status, 'claimed'), eq(inbound.claimedBy, workerId)))
|
|
106
|
+
.returning({ id: inbound.id })
|
|
107
|
+
.all();
|
|
108
|
+
return result.length > 0;
|
|
109
|
+
}
|
|
110
|
+
// Backoff: 5s, 30s, 2min, 5min, give up. Bigger gaps than outbound
|
|
111
|
+
// because chat replies are expensive (AI call); a faster retry loop
|
|
112
|
+
// would just burn tokens on transient errors.
|
|
113
|
+
const BACKOFF_SECONDS = [5, 30, 120, 300];
|
|
114
|
+
const MAX_ATTEMPTS = BACKOFF_SECONDS.length;
|
|
115
|
+
export function markInboundRetryOrDlq(id, workerId, errorMessage) {
|
|
116
|
+
const db = getDb();
|
|
117
|
+
return db.transaction((tx) => {
|
|
118
|
+
const row = tx.select().from(inbound).where(eq(inbound.id, id)).get();
|
|
119
|
+
if (!row || row.status !== 'claimed' || row.claimedBy !== workerId) {
|
|
120
|
+
return { retried: false, deadLettered: false };
|
|
121
|
+
}
|
|
122
|
+
const now = Math.floor(Date.now() / 1000);
|
|
123
|
+
const nextAttempts = row.attempts + 1;
|
|
124
|
+
if (nextAttempts > MAX_ATTEMPTS) {
|
|
125
|
+
tx.update(inbound)
|
|
126
|
+
.set({
|
|
127
|
+
status: 'dlq',
|
|
128
|
+
attempts: nextAttempts,
|
|
129
|
+
lastError: errorMessage,
|
|
130
|
+
claimedBy: null,
|
|
131
|
+
claimedAt: null,
|
|
132
|
+
updatedAt: now,
|
|
133
|
+
})
|
|
134
|
+
.where(eq(inbound.id, id))
|
|
135
|
+
.run();
|
|
136
|
+
return { retried: false, deadLettered: true };
|
|
137
|
+
}
|
|
138
|
+
const backoff = BACKOFF_SECONDS[Math.min(row.attempts, BACKOFF_SECONDS.length - 1)];
|
|
139
|
+
tx.update(inbound)
|
|
140
|
+
.set({
|
|
141
|
+
status: 'pending',
|
|
142
|
+
attempts: nextAttempts,
|
|
143
|
+
nextAttemptAt: now + backoff,
|
|
144
|
+
lastError: errorMessage,
|
|
145
|
+
claimedBy: null,
|
|
146
|
+
claimedAt: null,
|
|
147
|
+
updatedAt: now,
|
|
148
|
+
})
|
|
149
|
+
.where(eq(inbound.id, id))
|
|
150
|
+
.run();
|
|
151
|
+
return { retried: true, deadLettered: false };
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
export function markInboundFailed(id, workerId, errorMessage) {
|
|
155
|
+
const db = getDb();
|
|
156
|
+
const now = Math.floor(Date.now() / 1000);
|
|
157
|
+
const result = db
|
|
158
|
+
.update(inbound)
|
|
159
|
+
.set({
|
|
160
|
+
status: 'failed',
|
|
161
|
+
lastError: errorMessage,
|
|
162
|
+
claimedBy: null,
|
|
163
|
+
claimedAt: null,
|
|
164
|
+
updatedAt: now,
|
|
165
|
+
})
|
|
166
|
+
.where(and(eq(inbound.id, id), eq(inbound.status, 'claimed'), eq(inbound.claimedBy, workerId)))
|
|
167
|
+
.returning({ id: inbound.id })
|
|
168
|
+
.all();
|
|
169
|
+
return result.length > 0;
|
|
170
|
+
}
|
|
171
|
+
// Orchestrator helper. Chat workers run longer than sender workers
|
|
172
|
+
// (AI calls + memory writes), so the TTL is more generous. 300s
|
|
173
|
+
// matches the typical chat-track timeout (5min).
|
|
174
|
+
const CLAIM_TTL_SECONDS = 360;
|
|
175
|
+
export function reclaimStuckInbound() {
|
|
176
|
+
const db = getDb();
|
|
177
|
+
const cutoff = Math.floor(Date.now() / 1000) - CLAIM_TTL_SECONDS;
|
|
178
|
+
const result = db
|
|
179
|
+
.update(inbound)
|
|
180
|
+
.set({
|
|
181
|
+
status: 'pending',
|
|
182
|
+
claimedBy: null,
|
|
183
|
+
claimedAt: null,
|
|
184
|
+
updatedAt: sql `${inbound.updatedAt}`,
|
|
185
|
+
})
|
|
186
|
+
.where(and(eq(inbound.status, 'claimed'), lte(inbound.claimedAt, cutoff)))
|
|
187
|
+
.returning({ id: inbound.id })
|
|
188
|
+
.all();
|
|
189
|
+
return result.length;
|
|
190
|
+
}
|
|
@@ -16,6 +16,7 @@ import { and, eq, lt, ne } from 'drizzle-orm';
|
|
|
16
16
|
import { getDb } from '../db/index.js';
|
|
17
17
|
import { workers } from '../db/schema.js';
|
|
18
18
|
import { logger } from '../logger.js';
|
|
19
|
+
import { reclaimStuckInbound } from './inbound.js';
|
|
19
20
|
import { reclaimStuckOutbound } from './outbound.js';
|
|
20
21
|
import { clearControl, readControl, requestControl } from './control.js';
|
|
21
22
|
import { listDueCrons, markCronFired } from './crons.js';
|
|
@@ -98,9 +99,13 @@ async function tick(id) {
|
|
|
98
99
|
.run();
|
|
99
100
|
}
|
|
100
101
|
// Cross-queue housekeeping. More queues land in later phases.
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
103
|
-
logger.info({ reclaimed }, 'reclaimed stuck outbound rows');
|
|
102
|
+
const reclaimedOutbound = reclaimStuckOutbound();
|
|
103
|
+
if (reclaimedOutbound > 0) {
|
|
104
|
+
logger.info({ reclaimed: reclaimedOutbound }, 'reclaimed stuck outbound rows');
|
|
105
|
+
}
|
|
106
|
+
const reclaimedInbound = reclaimStuckInbound();
|
|
107
|
+
if (reclaimedInbound > 0) {
|
|
108
|
+
logger.info({ reclaimed: reclaimedInbound }, 'reclaimed stuck inbound rows');
|
|
104
109
|
}
|
|
105
110
|
// Fire any due crons. Order: dispatch each in turn; if dispatch
|
|
106
111
|
// throws (it shouldn't — dispatch swallows), the cron is NOT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
CREATE TABLE `inbound` (
|
|
2
|
+
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
3
|
+
`address` text NOT NULL,
|
|
4
|
+
`actor_address` text,
|
|
5
|
+
`person_id` text,
|
|
6
|
+
`actor_person_id` text,
|
|
7
|
+
`external_msg_id` text,
|
|
8
|
+
`text` text NOT NULL,
|
|
9
|
+
`media_path` text,
|
|
10
|
+
`media_mime` text,
|
|
11
|
+
`media_bytes` integer,
|
|
12
|
+
`push_name` text,
|
|
13
|
+
`trigger_reason` text,
|
|
14
|
+
`status` text NOT NULL,
|
|
15
|
+
`attempts` integer DEFAULT 0 NOT NULL,
|
|
16
|
+
`next_attempt_at` integer,
|
|
17
|
+
`last_error` text,
|
|
18
|
+
`claimed_by` text,
|
|
19
|
+
`claimed_at` integer,
|
|
20
|
+
`received_at` integer NOT NULL,
|
|
21
|
+
`created_at` integer NOT NULL,
|
|
22
|
+
`updated_at` integer NOT NULL
|
|
23
|
+
);
|
|
24
|
+
--> statement-breakpoint
|
|
25
|
+
CREATE INDEX `inbound_by_status_next` ON `inbound` (`status`,`next_attempt_at`);--> statement-breakpoint
|
|
26
|
+
CREATE INDEX `inbound_by_address` ON `inbound` (`address`);--> statement-breakpoint
|
|
27
|
+
CREATE INDEX `inbound_by_person` ON `inbound` (`person_id`,`received_at`);--> statement-breakpoint
|
|
28
|
+
CREATE UNIQUE INDEX `inbound_external_msg_id_uq` ON `inbound` (`external_msg_id`) WHERE "inbound"."external_msg_id" IS NOT NULL;
|
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "6",
|
|
3
|
+
"dialect": "sqlite",
|
|
4
|
+
"id": "155b0aa7-93ec-4d7a-a853-e680b47d162c",
|
|
5
|
+
"prevId": "417b2708-624e-416f-af59-bee5d5e38556",
|
|
6
|
+
"tables": {
|
|
7
|
+
"control": {
|
|
8
|
+
"name": "control",
|
|
9
|
+
"columns": {
|
|
10
|
+
"key": {
|
|
11
|
+
"name": "key",
|
|
12
|
+
"type": "text",
|
|
13
|
+
"primaryKey": true,
|
|
14
|
+
"notNull": true,
|
|
15
|
+
"autoincrement": false
|
|
16
|
+
},
|
|
17
|
+
"value": {
|
|
18
|
+
"name": "value",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"primaryKey": false,
|
|
21
|
+
"notNull": false,
|
|
22
|
+
"autoincrement": false
|
|
23
|
+
},
|
|
24
|
+
"requested_by": {
|
|
25
|
+
"name": "requested_by",
|
|
26
|
+
"type": "text",
|
|
27
|
+
"primaryKey": false,
|
|
28
|
+
"notNull": false,
|
|
29
|
+
"autoincrement": false
|
|
30
|
+
},
|
|
31
|
+
"requested_at": {
|
|
32
|
+
"name": "requested_at",
|
|
33
|
+
"type": "integer",
|
|
34
|
+
"primaryKey": false,
|
|
35
|
+
"notNull": true,
|
|
36
|
+
"autoincrement": false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"indexes": {},
|
|
40
|
+
"foreignKeys": {},
|
|
41
|
+
"compositePrimaryKeys": {},
|
|
42
|
+
"uniqueConstraints": {},
|
|
43
|
+
"checkConstraints": {}
|
|
44
|
+
},
|
|
45
|
+
"crons": {
|
|
46
|
+
"name": "crons",
|
|
47
|
+
"columns": {
|
|
48
|
+
"id": {
|
|
49
|
+
"name": "id",
|
|
50
|
+
"type": "integer",
|
|
51
|
+
"primaryKey": true,
|
|
52
|
+
"notNull": true,
|
|
53
|
+
"autoincrement": true
|
|
54
|
+
},
|
|
55
|
+
"name": {
|
|
56
|
+
"name": "name",
|
|
57
|
+
"type": "text",
|
|
58
|
+
"primaryKey": false,
|
|
59
|
+
"notNull": true,
|
|
60
|
+
"autoincrement": false
|
|
61
|
+
},
|
|
62
|
+
"enqueue_into": {
|
|
63
|
+
"name": "enqueue_into",
|
|
64
|
+
"type": "text",
|
|
65
|
+
"primaryKey": false,
|
|
66
|
+
"notNull": true,
|
|
67
|
+
"autoincrement": false
|
|
68
|
+
},
|
|
69
|
+
"payload": {
|
|
70
|
+
"name": "payload",
|
|
71
|
+
"type": "text",
|
|
72
|
+
"primaryKey": false,
|
|
73
|
+
"notNull": true,
|
|
74
|
+
"autoincrement": false
|
|
75
|
+
},
|
|
76
|
+
"recurrence": {
|
|
77
|
+
"name": "recurrence",
|
|
78
|
+
"type": "text",
|
|
79
|
+
"primaryKey": false,
|
|
80
|
+
"notNull": false,
|
|
81
|
+
"autoincrement": false
|
|
82
|
+
},
|
|
83
|
+
"next_run_at": {
|
|
84
|
+
"name": "next_run_at",
|
|
85
|
+
"type": "integer",
|
|
86
|
+
"primaryKey": false,
|
|
87
|
+
"notNull": true,
|
|
88
|
+
"autoincrement": false
|
|
89
|
+
},
|
|
90
|
+
"last_run_at": {
|
|
91
|
+
"name": "last_run_at",
|
|
92
|
+
"type": "integer",
|
|
93
|
+
"primaryKey": false,
|
|
94
|
+
"notNull": false,
|
|
95
|
+
"autoincrement": false
|
|
96
|
+
},
|
|
97
|
+
"enabled": {
|
|
98
|
+
"name": "enabled",
|
|
99
|
+
"type": "integer",
|
|
100
|
+
"primaryKey": false,
|
|
101
|
+
"notNull": true,
|
|
102
|
+
"autoincrement": false,
|
|
103
|
+
"default": 1
|
|
104
|
+
},
|
|
105
|
+
"created_at": {
|
|
106
|
+
"name": "created_at",
|
|
107
|
+
"type": "integer",
|
|
108
|
+
"primaryKey": false,
|
|
109
|
+
"notNull": true,
|
|
110
|
+
"autoincrement": false
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"indexes": {
|
|
114
|
+
"crons_by_due": {
|
|
115
|
+
"name": "crons_by_due",
|
|
116
|
+
"columns": [
|
|
117
|
+
"enabled",
|
|
118
|
+
"next_run_at"
|
|
119
|
+
],
|
|
120
|
+
"isUnique": false
|
|
121
|
+
},
|
|
122
|
+
"crons_name_uq": {
|
|
123
|
+
"name": "crons_name_uq",
|
|
124
|
+
"columns": [
|
|
125
|
+
"name"
|
|
126
|
+
],
|
|
127
|
+
"isUnique": true,
|
|
128
|
+
"where": "\"crons\".\"recurrence\" IS NOT NULL"
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"foreignKeys": {},
|
|
132
|
+
"compositePrimaryKeys": {},
|
|
133
|
+
"uniqueConstraints": {},
|
|
134
|
+
"checkConstraints": {}
|
|
135
|
+
},
|
|
136
|
+
"identities": {
|
|
137
|
+
"name": "identities",
|
|
138
|
+
"columns": {
|
|
139
|
+
"person_id": {
|
|
140
|
+
"name": "person_id",
|
|
141
|
+
"type": "text",
|
|
142
|
+
"primaryKey": false,
|
|
143
|
+
"notNull": true,
|
|
144
|
+
"autoincrement": false
|
|
145
|
+
},
|
|
146
|
+
"address": {
|
|
147
|
+
"name": "address",
|
|
148
|
+
"type": "text",
|
|
149
|
+
"primaryKey": false,
|
|
150
|
+
"notNull": true,
|
|
151
|
+
"autoincrement": false
|
|
152
|
+
},
|
|
153
|
+
"added_at": {
|
|
154
|
+
"name": "added_at",
|
|
155
|
+
"type": "integer",
|
|
156
|
+
"primaryKey": false,
|
|
157
|
+
"notNull": true,
|
|
158
|
+
"autoincrement": false
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"indexes": {
|
|
162
|
+
"identities_address_unique": {
|
|
163
|
+
"name": "identities_address_unique",
|
|
164
|
+
"columns": [
|
|
165
|
+
"address"
|
|
166
|
+
],
|
|
167
|
+
"isUnique": true
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"foreignKeys": {
|
|
171
|
+
"identities_person_id_persons_id_fk": {
|
|
172
|
+
"name": "identities_person_id_persons_id_fk",
|
|
173
|
+
"tableFrom": "identities",
|
|
174
|
+
"tableTo": "persons",
|
|
175
|
+
"columnsFrom": [
|
|
176
|
+
"person_id"
|
|
177
|
+
],
|
|
178
|
+
"columnsTo": [
|
|
179
|
+
"id"
|
|
180
|
+
],
|
|
181
|
+
"onDelete": "no action",
|
|
182
|
+
"onUpdate": "no action"
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"compositePrimaryKeys": {
|
|
186
|
+
"identities_person_id_address_pk": {
|
|
187
|
+
"columns": [
|
|
188
|
+
"person_id",
|
|
189
|
+
"address"
|
|
190
|
+
],
|
|
191
|
+
"name": "identities_person_id_address_pk"
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
"uniqueConstraints": {},
|
|
195
|
+
"checkConstraints": {}
|
|
196
|
+
},
|
|
197
|
+
"inbound": {
|
|
198
|
+
"name": "inbound",
|
|
199
|
+
"columns": {
|
|
200
|
+
"id": {
|
|
201
|
+
"name": "id",
|
|
202
|
+
"type": "integer",
|
|
203
|
+
"primaryKey": true,
|
|
204
|
+
"notNull": true,
|
|
205
|
+
"autoincrement": true
|
|
206
|
+
},
|
|
207
|
+
"address": {
|
|
208
|
+
"name": "address",
|
|
209
|
+
"type": "text",
|
|
210
|
+
"primaryKey": false,
|
|
211
|
+
"notNull": true,
|
|
212
|
+
"autoincrement": false
|
|
213
|
+
},
|
|
214
|
+
"actor_address": {
|
|
215
|
+
"name": "actor_address",
|
|
216
|
+
"type": "text",
|
|
217
|
+
"primaryKey": false,
|
|
218
|
+
"notNull": false,
|
|
219
|
+
"autoincrement": false
|
|
220
|
+
},
|
|
221
|
+
"person_id": {
|
|
222
|
+
"name": "person_id",
|
|
223
|
+
"type": "text",
|
|
224
|
+
"primaryKey": false,
|
|
225
|
+
"notNull": false,
|
|
226
|
+
"autoincrement": false
|
|
227
|
+
},
|
|
228
|
+
"actor_person_id": {
|
|
229
|
+
"name": "actor_person_id",
|
|
230
|
+
"type": "text",
|
|
231
|
+
"primaryKey": false,
|
|
232
|
+
"notNull": false,
|
|
233
|
+
"autoincrement": false
|
|
234
|
+
},
|
|
235
|
+
"external_msg_id": {
|
|
236
|
+
"name": "external_msg_id",
|
|
237
|
+
"type": "text",
|
|
238
|
+
"primaryKey": false,
|
|
239
|
+
"notNull": false,
|
|
240
|
+
"autoincrement": false
|
|
241
|
+
},
|
|
242
|
+
"text": {
|
|
243
|
+
"name": "text",
|
|
244
|
+
"type": "text",
|
|
245
|
+
"primaryKey": false,
|
|
246
|
+
"notNull": true,
|
|
247
|
+
"autoincrement": false
|
|
248
|
+
},
|
|
249
|
+
"media_path": {
|
|
250
|
+
"name": "media_path",
|
|
251
|
+
"type": "text",
|
|
252
|
+
"primaryKey": false,
|
|
253
|
+
"notNull": false,
|
|
254
|
+
"autoincrement": false
|
|
255
|
+
},
|
|
256
|
+
"media_mime": {
|
|
257
|
+
"name": "media_mime",
|
|
258
|
+
"type": "text",
|
|
259
|
+
"primaryKey": false,
|
|
260
|
+
"notNull": false,
|
|
261
|
+
"autoincrement": false
|
|
262
|
+
},
|
|
263
|
+
"media_bytes": {
|
|
264
|
+
"name": "media_bytes",
|
|
265
|
+
"type": "integer",
|
|
266
|
+
"primaryKey": false,
|
|
267
|
+
"notNull": false,
|
|
268
|
+
"autoincrement": false
|
|
269
|
+
},
|
|
270
|
+
"push_name": {
|
|
271
|
+
"name": "push_name",
|
|
272
|
+
"type": "text",
|
|
273
|
+
"primaryKey": false,
|
|
274
|
+
"notNull": false,
|
|
275
|
+
"autoincrement": false
|
|
276
|
+
},
|
|
277
|
+
"trigger_reason": {
|
|
278
|
+
"name": "trigger_reason",
|
|
279
|
+
"type": "text",
|
|
280
|
+
"primaryKey": false,
|
|
281
|
+
"notNull": false,
|
|
282
|
+
"autoincrement": false
|
|
283
|
+
},
|
|
284
|
+
"status": {
|
|
285
|
+
"name": "status",
|
|
286
|
+
"type": "text",
|
|
287
|
+
"primaryKey": false,
|
|
288
|
+
"notNull": true,
|
|
289
|
+
"autoincrement": false
|
|
290
|
+
},
|
|
291
|
+
"attempts": {
|
|
292
|
+
"name": "attempts",
|
|
293
|
+
"type": "integer",
|
|
294
|
+
"primaryKey": false,
|
|
295
|
+
"notNull": true,
|
|
296
|
+
"autoincrement": false,
|
|
297
|
+
"default": 0
|
|
298
|
+
},
|
|
299
|
+
"next_attempt_at": {
|
|
300
|
+
"name": "next_attempt_at",
|
|
301
|
+
"type": "integer",
|
|
302
|
+
"primaryKey": false,
|
|
303
|
+
"notNull": false,
|
|
304
|
+
"autoincrement": false
|
|
305
|
+
},
|
|
306
|
+
"last_error": {
|
|
307
|
+
"name": "last_error",
|
|
308
|
+
"type": "text",
|
|
309
|
+
"primaryKey": false,
|
|
310
|
+
"notNull": false,
|
|
311
|
+
"autoincrement": false
|
|
312
|
+
},
|
|
313
|
+
"claimed_by": {
|
|
314
|
+
"name": "claimed_by",
|
|
315
|
+
"type": "text",
|
|
316
|
+
"primaryKey": false,
|
|
317
|
+
"notNull": false,
|
|
318
|
+
"autoincrement": false
|
|
319
|
+
},
|
|
320
|
+
"claimed_at": {
|
|
321
|
+
"name": "claimed_at",
|
|
322
|
+
"type": "integer",
|
|
323
|
+
"primaryKey": false,
|
|
324
|
+
"notNull": false,
|
|
325
|
+
"autoincrement": false
|
|
326
|
+
},
|
|
327
|
+
"received_at": {
|
|
328
|
+
"name": "received_at",
|
|
329
|
+
"type": "integer",
|
|
330
|
+
"primaryKey": false,
|
|
331
|
+
"notNull": true,
|
|
332
|
+
"autoincrement": false
|
|
333
|
+
},
|
|
334
|
+
"created_at": {
|
|
335
|
+
"name": "created_at",
|
|
336
|
+
"type": "integer",
|
|
337
|
+
"primaryKey": false,
|
|
338
|
+
"notNull": true,
|
|
339
|
+
"autoincrement": false
|
|
340
|
+
},
|
|
341
|
+
"updated_at": {
|
|
342
|
+
"name": "updated_at",
|
|
343
|
+
"type": "integer",
|
|
344
|
+
"primaryKey": false,
|
|
345
|
+
"notNull": true,
|
|
346
|
+
"autoincrement": false
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
"indexes": {
|
|
350
|
+
"inbound_by_status_next": {
|
|
351
|
+
"name": "inbound_by_status_next",
|
|
352
|
+
"columns": [
|
|
353
|
+
"status",
|
|
354
|
+
"next_attempt_at"
|
|
355
|
+
],
|
|
356
|
+
"isUnique": false
|
|
357
|
+
},
|
|
358
|
+
"inbound_by_address": {
|
|
359
|
+
"name": "inbound_by_address",
|
|
360
|
+
"columns": [
|
|
361
|
+
"address"
|
|
362
|
+
],
|
|
363
|
+
"isUnique": false
|
|
364
|
+
},
|
|
365
|
+
"inbound_by_person": {
|
|
366
|
+
"name": "inbound_by_person",
|
|
367
|
+
"columns": [
|
|
368
|
+
"person_id",
|
|
369
|
+
"received_at"
|
|
370
|
+
],
|
|
371
|
+
"isUnique": false
|
|
372
|
+
},
|
|
373
|
+
"inbound_external_msg_id_uq": {
|
|
374
|
+
"name": "inbound_external_msg_id_uq",
|
|
375
|
+
"columns": [
|
|
376
|
+
"external_msg_id"
|
|
377
|
+
],
|
|
378
|
+
"isUnique": true,
|
|
379
|
+
"where": "\"inbound\".\"external_msg_id\" IS NOT NULL"
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
"foreignKeys": {},
|
|
383
|
+
"compositePrimaryKeys": {},
|
|
384
|
+
"uniqueConstraints": {},
|
|
385
|
+
"checkConstraints": {}
|
|
386
|
+
},
|
|
387
|
+
"outbound": {
|
|
388
|
+
"name": "outbound",
|
|
389
|
+
"columns": {
|
|
390
|
+
"id": {
|
|
391
|
+
"name": "id",
|
|
392
|
+
"type": "integer",
|
|
393
|
+
"primaryKey": true,
|
|
394
|
+
"notNull": true,
|
|
395
|
+
"autoincrement": true
|
|
396
|
+
},
|
|
397
|
+
"address": {
|
|
398
|
+
"name": "address",
|
|
399
|
+
"type": "text",
|
|
400
|
+
"primaryKey": false,
|
|
401
|
+
"notNull": true,
|
|
402
|
+
"autoincrement": false
|
|
403
|
+
},
|
|
404
|
+
"kind": {
|
|
405
|
+
"name": "kind",
|
|
406
|
+
"type": "text",
|
|
407
|
+
"primaryKey": false,
|
|
408
|
+
"notNull": true,
|
|
409
|
+
"autoincrement": false
|
|
410
|
+
},
|
|
411
|
+
"text": {
|
|
412
|
+
"name": "text",
|
|
413
|
+
"type": "text",
|
|
414
|
+
"primaryKey": false,
|
|
415
|
+
"notNull": false,
|
|
416
|
+
"autoincrement": false
|
|
417
|
+
},
|
|
418
|
+
"media_path": {
|
|
419
|
+
"name": "media_path",
|
|
420
|
+
"type": "text",
|
|
421
|
+
"primaryKey": false,
|
|
422
|
+
"notNull": false,
|
|
423
|
+
"autoincrement": false
|
|
424
|
+
},
|
|
425
|
+
"media_mime": {
|
|
426
|
+
"name": "media_mime",
|
|
427
|
+
"type": "text",
|
|
428
|
+
"primaryKey": false,
|
|
429
|
+
"notNull": false,
|
|
430
|
+
"autoincrement": false
|
|
431
|
+
},
|
|
432
|
+
"media_bytes": {
|
|
433
|
+
"name": "media_bytes",
|
|
434
|
+
"type": "integer",
|
|
435
|
+
"primaryKey": false,
|
|
436
|
+
"notNull": false,
|
|
437
|
+
"autoincrement": false
|
|
438
|
+
},
|
|
439
|
+
"quote_msg_id": {
|
|
440
|
+
"name": "quote_msg_id",
|
|
441
|
+
"type": "text",
|
|
442
|
+
"primaryKey": false,
|
|
443
|
+
"notNull": false,
|
|
444
|
+
"autoincrement": false
|
|
445
|
+
},
|
|
446
|
+
"idempotency_key": {
|
|
447
|
+
"name": "idempotency_key",
|
|
448
|
+
"type": "text",
|
|
449
|
+
"primaryKey": false,
|
|
450
|
+
"notNull": false,
|
|
451
|
+
"autoincrement": false
|
|
452
|
+
},
|
|
453
|
+
"status": {
|
|
454
|
+
"name": "status",
|
|
455
|
+
"type": "text",
|
|
456
|
+
"primaryKey": false,
|
|
457
|
+
"notNull": true,
|
|
458
|
+
"autoincrement": false
|
|
459
|
+
},
|
|
460
|
+
"attempts": {
|
|
461
|
+
"name": "attempts",
|
|
462
|
+
"type": "integer",
|
|
463
|
+
"primaryKey": false,
|
|
464
|
+
"notNull": true,
|
|
465
|
+
"autoincrement": false,
|
|
466
|
+
"default": 0
|
|
467
|
+
},
|
|
468
|
+
"next_attempt_at": {
|
|
469
|
+
"name": "next_attempt_at",
|
|
470
|
+
"type": "integer",
|
|
471
|
+
"primaryKey": false,
|
|
472
|
+
"notNull": false,
|
|
473
|
+
"autoincrement": false
|
|
474
|
+
},
|
|
475
|
+
"last_error": {
|
|
476
|
+
"name": "last_error",
|
|
477
|
+
"type": "text",
|
|
478
|
+
"primaryKey": false,
|
|
479
|
+
"notNull": false,
|
|
480
|
+
"autoincrement": false
|
|
481
|
+
},
|
|
482
|
+
"claimed_by": {
|
|
483
|
+
"name": "claimed_by",
|
|
484
|
+
"type": "text",
|
|
485
|
+
"primaryKey": false,
|
|
486
|
+
"notNull": false,
|
|
487
|
+
"autoincrement": false
|
|
488
|
+
},
|
|
489
|
+
"claimed_at": {
|
|
490
|
+
"name": "claimed_at",
|
|
491
|
+
"type": "integer",
|
|
492
|
+
"primaryKey": false,
|
|
493
|
+
"notNull": false,
|
|
494
|
+
"autoincrement": false
|
|
495
|
+
},
|
|
496
|
+
"created_at": {
|
|
497
|
+
"name": "created_at",
|
|
498
|
+
"type": "integer",
|
|
499
|
+
"primaryKey": false,
|
|
500
|
+
"notNull": true,
|
|
501
|
+
"autoincrement": false
|
|
502
|
+
},
|
|
503
|
+
"updated_at": {
|
|
504
|
+
"name": "updated_at",
|
|
505
|
+
"type": "integer",
|
|
506
|
+
"primaryKey": false,
|
|
507
|
+
"notNull": true,
|
|
508
|
+
"autoincrement": false
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
"indexes": {
|
|
512
|
+
"outbound_by_status_next": {
|
|
513
|
+
"name": "outbound_by_status_next",
|
|
514
|
+
"columns": [
|
|
515
|
+
"status",
|
|
516
|
+
"next_attempt_at"
|
|
517
|
+
],
|
|
518
|
+
"isUnique": false
|
|
519
|
+
},
|
|
520
|
+
"outbound_by_address": {
|
|
521
|
+
"name": "outbound_by_address",
|
|
522
|
+
"columns": [
|
|
523
|
+
"address"
|
|
524
|
+
],
|
|
525
|
+
"isUnique": false
|
|
526
|
+
},
|
|
527
|
+
"outbound_idempotency_key_uq": {
|
|
528
|
+
"name": "outbound_idempotency_key_uq",
|
|
529
|
+
"columns": [
|
|
530
|
+
"idempotency_key"
|
|
531
|
+
],
|
|
532
|
+
"isUnique": true,
|
|
533
|
+
"where": "\"outbound\".\"idempotency_key\" IS NOT NULL"
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
"foreignKeys": {},
|
|
537
|
+
"compositePrimaryKeys": {},
|
|
538
|
+
"uniqueConstraints": {},
|
|
539
|
+
"checkConstraints": {}
|
|
540
|
+
},
|
|
541
|
+
"persons": {
|
|
542
|
+
"name": "persons",
|
|
543
|
+
"columns": {
|
|
544
|
+
"id": {
|
|
545
|
+
"name": "id",
|
|
546
|
+
"type": "text",
|
|
547
|
+
"primaryKey": true,
|
|
548
|
+
"notNull": true,
|
|
549
|
+
"autoincrement": false
|
|
550
|
+
},
|
|
551
|
+
"display_name": {
|
|
552
|
+
"name": "display_name",
|
|
553
|
+
"type": "text",
|
|
554
|
+
"primaryKey": false,
|
|
555
|
+
"notNull": false,
|
|
556
|
+
"autoincrement": false
|
|
557
|
+
},
|
|
558
|
+
"timezone": {
|
|
559
|
+
"name": "timezone",
|
|
560
|
+
"type": "text",
|
|
561
|
+
"primaryKey": false,
|
|
562
|
+
"notNull": false,
|
|
563
|
+
"autoincrement": false
|
|
564
|
+
},
|
|
565
|
+
"created_at": {
|
|
566
|
+
"name": "created_at",
|
|
567
|
+
"type": "integer",
|
|
568
|
+
"primaryKey": false,
|
|
569
|
+
"notNull": true,
|
|
570
|
+
"autoincrement": false
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
"indexes": {},
|
|
574
|
+
"foreignKeys": {},
|
|
575
|
+
"compositePrimaryKeys": {},
|
|
576
|
+
"uniqueConstraints": {},
|
|
577
|
+
"checkConstraints": {}
|
|
578
|
+
},
|
|
579
|
+
"workers": {
|
|
580
|
+
"name": "workers",
|
|
581
|
+
"columns": {
|
|
582
|
+
"id": {
|
|
583
|
+
"name": "id",
|
|
584
|
+
"type": "text",
|
|
585
|
+
"primaryKey": true,
|
|
586
|
+
"notNull": true,
|
|
587
|
+
"autoincrement": false
|
|
588
|
+
},
|
|
589
|
+
"kind": {
|
|
590
|
+
"name": "kind",
|
|
591
|
+
"type": "text",
|
|
592
|
+
"primaryKey": false,
|
|
593
|
+
"notNull": true,
|
|
594
|
+
"autoincrement": false
|
|
595
|
+
},
|
|
596
|
+
"status": {
|
|
597
|
+
"name": "status",
|
|
598
|
+
"type": "text",
|
|
599
|
+
"primaryKey": false,
|
|
600
|
+
"notNull": true,
|
|
601
|
+
"autoincrement": false
|
|
602
|
+
},
|
|
603
|
+
"current_job": {
|
|
604
|
+
"name": "current_job",
|
|
605
|
+
"type": "text",
|
|
606
|
+
"primaryKey": false,
|
|
607
|
+
"notNull": false,
|
|
608
|
+
"autoincrement": false
|
|
609
|
+
},
|
|
610
|
+
"last_seen": {
|
|
611
|
+
"name": "last_seen",
|
|
612
|
+
"type": "integer",
|
|
613
|
+
"primaryKey": false,
|
|
614
|
+
"notNull": true,
|
|
615
|
+
"autoincrement": false
|
|
616
|
+
},
|
|
617
|
+
"started_at": {
|
|
618
|
+
"name": "started_at",
|
|
619
|
+
"type": "integer",
|
|
620
|
+
"primaryKey": false,
|
|
621
|
+
"notNull": true,
|
|
622
|
+
"autoincrement": false
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
"indexes": {
|
|
626
|
+
"workers_by_kind_status": {
|
|
627
|
+
"name": "workers_by_kind_status",
|
|
628
|
+
"columns": [
|
|
629
|
+
"kind",
|
|
630
|
+
"status"
|
|
631
|
+
],
|
|
632
|
+
"isUnique": false
|
|
633
|
+
},
|
|
634
|
+
"workers_by_last_seen": {
|
|
635
|
+
"name": "workers_by_last_seen",
|
|
636
|
+
"columns": [
|
|
637
|
+
"last_seen"
|
|
638
|
+
],
|
|
639
|
+
"isUnique": false
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
"foreignKeys": {},
|
|
643
|
+
"compositePrimaryKeys": {},
|
|
644
|
+
"uniqueConstraints": {},
|
|
645
|
+
"checkConstraints": {}
|
|
646
|
+
}
|
|
647
|
+
},
|
|
648
|
+
"views": {},
|
|
649
|
+
"enums": {},
|
|
650
|
+
"_meta": {
|
|
651
|
+
"schemas": {},
|
|
652
|
+
"tables": {},
|
|
653
|
+
"columns": {}
|
|
654
|
+
},
|
|
655
|
+
"internal": {
|
|
656
|
+
"indexes": {}
|
|
657
|
+
}
|
|
658
|
+
}
|