@uniquedivine/tg-sai 0.0.2 → 0.0.4
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/cfg.ts +52 -8
- package/health.ts +1 -1
- package/main.ts +70 -14
- package/package.json +1 -1
package/cfg.ts
CHANGED
|
@@ -5,6 +5,12 @@ export interface TelegramIdentity {
|
|
|
5
5
|
kind: "user" | "bot"
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
export interface TelegramChatIdentity {
|
|
9
|
+
id: number
|
|
10
|
+
title: string
|
|
11
|
+
kind: "channel" | "group"
|
|
12
|
+
}
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* Telegram bot identity for the Sai team bot. The bot is the automation target
|
|
10
16
|
* to add and promote in candidate chats.
|
|
@@ -17,13 +23,51 @@ export const saiBot = {
|
|
|
17
23
|
} satisfies TelegramIdentity
|
|
18
24
|
|
|
19
25
|
/**
|
|
20
|
-
* Telegram user
|
|
21
|
-
* is the target user to promote to admin or prepare for ownership transfer.
|
|
26
|
+
* Known Telegram user identities used by tg-sai operator workflows.
|
|
22
27
|
*/
|
|
23
|
-
export const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
export const USERS = {
|
|
29
|
+
/**
|
|
30
|
+
* Default operational target user to promote to admin or prepare for
|
|
31
|
+
* ownership transfer.
|
|
32
|
+
*/
|
|
33
|
+
uniqueDivine: {
|
|
34
|
+
id: 1912700300,
|
|
35
|
+
handle: "uniquedivine",
|
|
36
|
+
displayName: "Unique (NIBIRU)",
|
|
37
|
+
kind: "user",
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Corinne's primary account. This account owns or administers some candidate
|
|
42
|
+
* chats used by tg-sai operator flows.
|
|
43
|
+
*/
|
|
44
|
+
corinne: {
|
|
45
|
+
id: 7930936303,
|
|
46
|
+
handle: "corinnebernett",
|
|
47
|
+
displayName: "Corinne Bernett",
|
|
48
|
+
kind: "user",
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Corinne's alternate account. Use this account as a controlled
|
|
53
|
+
* owner-transfer target when dry-running or testing against the internal
|
|
54
|
+
* Nibiru chat before touching partner or production chats.
|
|
55
|
+
*/
|
|
56
|
+
corinneAlt: {
|
|
57
|
+
id: 8274304175,
|
|
58
|
+
handle: "corinneantonia",
|
|
59
|
+
displayName: "Corinne Antonia",
|
|
60
|
+
kind: "user",
|
|
61
|
+
},
|
|
62
|
+
} satisfies Record<string, TelegramIdentity>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Internal Nibiru chat used as a controlled target for tg-sai dry runs and
|
|
66
|
+
* ownership-transfer tests.
|
|
67
|
+
*/
|
|
68
|
+
export const nibiruInternal = {
|
|
69
|
+
id: -1002094035214,
|
|
70
|
+
title: "Nibiru | Internal",
|
|
71
|
+
kind: "channel",
|
|
72
|
+
} satisfies TelegramChatIdentity
|
|
29
73
|
|
package/health.ts
CHANGED
package/main.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
rm,
|
|
9
9
|
writeFile,
|
|
10
10
|
} from "node:fs/promises"
|
|
11
|
+
import { createRequire } from "node:module"
|
|
11
12
|
import { tmpdir } from "node:os"
|
|
12
13
|
import { join } from "node:path"
|
|
13
14
|
import { Command } from "commander"
|
|
@@ -20,13 +21,14 @@ import type {
|
|
|
20
21
|
ProposedAction,
|
|
21
22
|
} from "./adminAudit"
|
|
22
23
|
import { planAdminBootstrap } from "./adminAudit"
|
|
23
|
-
import { saiBot,
|
|
24
|
+
import { saiBot, USERS } from "./cfg"
|
|
24
25
|
import { runHealthCheck } from "./health"
|
|
25
26
|
import {
|
|
26
27
|
addTargetToChat,
|
|
27
28
|
createMtcuteClient,
|
|
28
29
|
fetchChatAuditInput,
|
|
29
30
|
findSaiChats,
|
|
31
|
+
normalizeUser,
|
|
30
32
|
promoteTargetAdmin,
|
|
31
33
|
transferTargetOwnership,
|
|
32
34
|
} from "./mtcuteAdapter"
|
|
@@ -51,7 +53,10 @@ import type {
|
|
|
51
53
|
TelegramCredentialValues,
|
|
52
54
|
} from "./profiles"
|
|
53
55
|
|
|
54
|
-
const
|
|
56
|
+
const require = createRequire(import.meta.url)
|
|
57
|
+
const packageJson = require("./package.json") as { version: string }
|
|
58
|
+
|
|
59
|
+
const DEFAULT_USER_TARGET_ID = USERS.uniqueDivine.id
|
|
55
60
|
const DEFAULT_BOT_TARGET_ID = saiBot.id
|
|
56
61
|
const DEFAULT_BOT_TARGET_PEER = saiBot.handle
|
|
57
62
|
|
|
@@ -211,6 +216,17 @@ function actionExecutorDeps(logger: BatchLogger = consoleBatchLogger) {
|
|
|
211
216
|
}
|
|
212
217
|
}
|
|
213
218
|
|
|
219
|
+
function actorBatchLogger(actorUsername: string): BatchLogger {
|
|
220
|
+
return {
|
|
221
|
+
event(event) {
|
|
222
|
+
emitJsonObject({
|
|
223
|
+
...event,
|
|
224
|
+
actorUsername,
|
|
225
|
+
})
|
|
226
|
+
},
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
214
230
|
/**
|
|
215
231
|
* Emits one command-level JSON event to stdout.
|
|
216
232
|
*/
|
|
@@ -269,6 +285,14 @@ async function getTelegramEnv(profileName?: string): Promise<TelegramEnv> {
|
|
|
269
285
|
}
|
|
270
286
|
}
|
|
271
287
|
|
|
288
|
+
async function selectedProfileName(profileName?: string): Promise<string | null> {
|
|
289
|
+
if (profileName) {
|
|
290
|
+
return profileName
|
|
291
|
+
}
|
|
292
|
+
const config = await readConfig()
|
|
293
|
+
return config.activeProfile
|
|
294
|
+
}
|
|
295
|
+
|
|
272
296
|
function parsePeer(value: string): number | string {
|
|
273
297
|
const normalized = value.startsWith("@") ? value.slice(1) : value
|
|
274
298
|
const asNumber = Number(normalized)
|
|
@@ -310,7 +334,7 @@ function printAudit(input: ChatAuditInput) {
|
|
|
310
334
|
console.log(
|
|
311
335
|
`target_can_receive_ownership: ${audit.targetCanReceiveOwnership}`,
|
|
312
336
|
)
|
|
313
|
-
console.log(`proposed_actions: ${audit.proposedActions
|
|
337
|
+
console.log(`proposed_actions: ${formatActionList(audit.proposedActions)}`)
|
|
314
338
|
}
|
|
315
339
|
|
|
316
340
|
function shouldRunAction(
|
|
@@ -320,6 +344,10 @@ function shouldRunAction(
|
|
|
320
344
|
return proposedActions.includes(action)
|
|
321
345
|
}
|
|
322
346
|
|
|
347
|
+
function formatActionList(actions: ProposedAction[]): string {
|
|
348
|
+
return JSON.stringify(actions)
|
|
349
|
+
}
|
|
350
|
+
|
|
323
351
|
async function withClient<T>(
|
|
324
352
|
profileName: string | undefined,
|
|
325
353
|
fn: (
|
|
@@ -380,7 +408,9 @@ async function runAddBot(options: AddBotOptions): Promise<void> {
|
|
|
380
408
|
console.log(`target_peer: ${options.targetPeer}`)
|
|
381
409
|
console.log(`actor_role: ${beforePlan.actorRole}`)
|
|
382
410
|
console.log(`target_role_before: ${beforePlan.targetRole}`)
|
|
383
|
-
console.log(
|
|
411
|
+
console.log(
|
|
412
|
+
`proposed_actions: ${formatActionList(beforePlan.proposedActions)}`,
|
|
413
|
+
)
|
|
384
414
|
|
|
385
415
|
if (options.run !== true) {
|
|
386
416
|
console.log("dry_run: pass --run to add/promote the bot")
|
|
@@ -427,7 +457,9 @@ async function runAddBot(options: AddBotOptions): Promise<void> {
|
|
|
427
457
|
|
|
428
458
|
console.log(`target_role_after: ${afterPlan.targetRole}`)
|
|
429
459
|
console.log(`target_is_admin_after: ${afterPlan.targetIsAdmin}`)
|
|
430
|
-
console.log(
|
|
460
|
+
console.log(
|
|
461
|
+
`remaining_actions: ${formatActionList(afterPlan.proposedActions)}`,
|
|
462
|
+
)
|
|
431
463
|
})
|
|
432
464
|
}
|
|
433
465
|
|
|
@@ -448,7 +480,9 @@ async function runAddOwner(options: AddOwnerOptions): Promise<void> {
|
|
|
448
480
|
console.log(
|
|
449
481
|
`target_can_receive_ownership_before: ${beforePlan.targetCanReceiveOwnership}`,
|
|
450
482
|
)
|
|
451
|
-
console.log(
|
|
483
|
+
console.log(
|
|
484
|
+
`proposed_actions: ${formatActionList(beforePlan.proposedActions)}`,
|
|
485
|
+
)
|
|
452
486
|
|
|
453
487
|
if (options.run !== true) {
|
|
454
488
|
console.log("dry_run: pass --run to add/promote the target user")
|
|
@@ -498,7 +532,9 @@ async function runAddOwner(options: AddOwnerOptions): Promise<void> {
|
|
|
498
532
|
console.log(
|
|
499
533
|
`target_can_receive_ownership_after: ${afterPlan.targetCanReceiveOwnership}`,
|
|
500
534
|
)
|
|
501
|
-
console.log(
|
|
535
|
+
console.log(
|
|
536
|
+
`remaining_actions: ${formatActionList(afterPlan.proposedActions)}`,
|
|
537
|
+
)
|
|
502
538
|
|
|
503
539
|
if (!afterPlan.actorCanTransferOwnership) {
|
|
504
540
|
console.log("ownership_transfer: skipped_actor_not_owner")
|
|
@@ -565,6 +601,7 @@ async function runSaiFind(options: SaiFindOptions): Promise<void> {
|
|
|
565
601
|
async function applyTargetPlan(params: {
|
|
566
602
|
client: Awaited<ReturnType<typeof createMtcuteClient>>
|
|
567
603
|
chat: ChatInput
|
|
604
|
+
actorUsername: string
|
|
568
605
|
targetUserId: number | string
|
|
569
606
|
targetPeer: number | string
|
|
570
607
|
rights: Record<string, boolean>
|
|
@@ -580,6 +617,7 @@ async function applyTargetPlan(params: {
|
|
|
580
617
|
printJsonEvent("target_plan", {
|
|
581
618
|
chatId: params.chat.chatId,
|
|
582
619
|
title: params.chat.title,
|
|
620
|
+
actorUsername: params.actorUsername,
|
|
583
621
|
targetUserId: params.targetUserId,
|
|
584
622
|
actionPrefix: params.actionPrefix,
|
|
585
623
|
run: params.run,
|
|
@@ -591,6 +629,7 @@ async function applyTargetPlan(params: {
|
|
|
591
629
|
if (!beforePlan.actorCanApply) {
|
|
592
630
|
printJsonEvent("target_skip", {
|
|
593
631
|
chatId: params.chat.chatId,
|
|
632
|
+
actorUsername: params.actorUsername,
|
|
594
633
|
actionPrefix: params.actionPrefix,
|
|
595
634
|
reason: "actor_lacks_owner_or_add_admins_right",
|
|
596
635
|
})
|
|
@@ -601,7 +640,7 @@ async function applyTargetPlan(params: {
|
|
|
601
640
|
return
|
|
602
641
|
}
|
|
603
642
|
|
|
604
|
-
const deps = actionExecutorDeps()
|
|
643
|
+
const deps = actionExecutorDeps(actorBatchLogger(params.actorUsername))
|
|
605
644
|
if (shouldRunAction(beforePlan.proposedActions, "add target user")) {
|
|
606
645
|
await withFloodWaitRetry(
|
|
607
646
|
{
|
|
@@ -647,6 +686,7 @@ async function maybeTransferOwner(params: {
|
|
|
647
686
|
client: Awaited<ReturnType<typeof createMtcuteClient>>
|
|
648
687
|
env: TelegramEnv
|
|
649
688
|
chat: ChatInput
|
|
689
|
+
actorUsername: string
|
|
650
690
|
targetUser: number | string
|
|
651
691
|
enabled: boolean
|
|
652
692
|
run: boolean
|
|
@@ -664,6 +704,7 @@ async function maybeTransferOwner(params: {
|
|
|
664
704
|
if (!plan.actorCanTransferOwnership) {
|
|
665
705
|
printJsonEvent("ownership_skip", {
|
|
666
706
|
chatId: params.chat.chatId,
|
|
707
|
+
actorUsername: params.actorUsername,
|
|
667
708
|
reason: "actor_not_owner",
|
|
668
709
|
})
|
|
669
710
|
return
|
|
@@ -671,6 +712,7 @@ async function maybeTransferOwner(params: {
|
|
|
671
712
|
if (!plan.targetCanReceiveOwnership) {
|
|
672
713
|
printJsonEvent("ownership_skip", {
|
|
673
714
|
chatId: params.chat.chatId,
|
|
715
|
+
actorUsername: params.actorUsername,
|
|
674
716
|
reason: "target_not_eligible",
|
|
675
717
|
})
|
|
676
718
|
return
|
|
@@ -678,6 +720,7 @@ async function maybeTransferOwner(params: {
|
|
|
678
720
|
if (params.env.password === null) {
|
|
679
721
|
printJsonEvent("ownership_skip", {
|
|
680
722
|
chatId: params.chat.chatId,
|
|
723
|
+
actorUsername: params.actorUsername,
|
|
681
724
|
reason: "missing_telegram_password",
|
|
682
725
|
})
|
|
683
726
|
return
|
|
@@ -685,6 +728,7 @@ async function maybeTransferOwner(params: {
|
|
|
685
728
|
if (!params.run) {
|
|
686
729
|
printJsonEvent("ownership_plan", {
|
|
687
730
|
chatId: params.chat.chatId,
|
|
731
|
+
actorUsername: params.actorUsername,
|
|
688
732
|
action: "transfer_owner",
|
|
689
733
|
})
|
|
690
734
|
return
|
|
@@ -701,7 +745,7 @@ async function maybeTransferOwner(params: {
|
|
|
701
745
|
targetPeer: params.targetUser,
|
|
702
746
|
password: params.env.password ?? "",
|
|
703
747
|
}),
|
|
704
|
-
actionExecutorDeps(),
|
|
748
|
+
actionExecutorDeps(actorBatchLogger(params.actorUsername)),
|
|
705
749
|
)
|
|
706
750
|
}
|
|
707
751
|
|
|
@@ -727,18 +771,23 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
727
771
|
|
|
728
772
|
await withClient(options.profile, async (client, env) => {
|
|
729
773
|
activeJsonLogPath = logPath
|
|
774
|
+
const actor = normalizeUser(await client.getMe())
|
|
775
|
+
const actorUsername = actor.username ?? ""
|
|
776
|
+
const profileName = await selectedProfileName(options.profile)
|
|
730
777
|
const ownershipTransferEnabled =
|
|
731
778
|
options.transferOwner !== false && env.password !== null
|
|
732
779
|
console.log(`wrote_run_log: ${logPath}`)
|
|
733
780
|
printJsonEvent("run_start", {
|
|
734
781
|
file: options.file,
|
|
735
782
|
logPath,
|
|
783
|
+
profileName,
|
|
784
|
+
actor,
|
|
736
785
|
chatCount: chats.length,
|
|
737
786
|
run: options.run === true,
|
|
738
787
|
transferOwner: ownershipTransferEnabled,
|
|
739
788
|
passwordAvailable: env.password !== null,
|
|
740
789
|
delayMs,
|
|
741
|
-
targetUser: uniqueDivine.id,
|
|
790
|
+
targetUser: USERS.uniqueDivine.id,
|
|
742
791
|
targetBot: saiBot.id,
|
|
743
792
|
})
|
|
744
793
|
|
|
@@ -749,12 +798,14 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
749
798
|
total: chats.length,
|
|
750
799
|
chatId: chat.chatId,
|
|
751
800
|
title: chat.title,
|
|
801
|
+
actorUsername,
|
|
752
802
|
})
|
|
753
803
|
|
|
754
804
|
try {
|
|
755
805
|
await applyTargetPlan({
|
|
756
806
|
client,
|
|
757
807
|
chat,
|
|
808
|
+
actorUsername,
|
|
758
809
|
targetUserId: saiBot.id,
|
|
759
810
|
targetPeer: saiBot.handle,
|
|
760
811
|
rights: saiBotAdminRights,
|
|
@@ -764,8 +815,9 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
764
815
|
await applyTargetPlan({
|
|
765
816
|
client,
|
|
766
817
|
chat,
|
|
767
|
-
|
|
768
|
-
|
|
818
|
+
actorUsername,
|
|
819
|
+
targetUserId: USERS.uniqueDivine.id,
|
|
820
|
+
targetPeer: USERS.uniqueDivine.handle,
|
|
769
821
|
rights: ownerTargetAdminRights,
|
|
770
822
|
actionPrefix: "owner_target",
|
|
771
823
|
run: options.run === true,
|
|
@@ -774,16 +826,19 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
774
826
|
client,
|
|
775
827
|
env,
|
|
776
828
|
chat,
|
|
777
|
-
|
|
829
|
+
actorUsername,
|
|
830
|
+
targetUser: USERS.uniqueDivine.handle,
|
|
778
831
|
enabled: ownershipTransferEnabled,
|
|
779
832
|
run: options.run === true,
|
|
780
833
|
})
|
|
781
834
|
printJsonEvent("chat_complete", {
|
|
782
835
|
chatId: chat.chatId,
|
|
836
|
+
actorUsername,
|
|
783
837
|
})
|
|
784
838
|
} catch (error) {
|
|
785
839
|
printJsonEvent("chat_error", {
|
|
786
840
|
chatId: chat.chatId,
|
|
841
|
+
actorUsername,
|
|
787
842
|
errorName: error instanceof Error ? error.name : "UnknownError",
|
|
788
843
|
errorMessage:
|
|
789
844
|
error instanceof Error ? error.message : String(error),
|
|
@@ -793,6 +848,7 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
793
848
|
if (delayMs > 0 && i < chats.length - 1) {
|
|
794
849
|
printJsonEvent("chat_delay", {
|
|
795
850
|
chatId: chat.chatId,
|
|
851
|
+
actorUsername,
|
|
796
852
|
delayMs,
|
|
797
853
|
})
|
|
798
854
|
await sleep(delayMs)
|
|
@@ -1087,7 +1143,7 @@ export function createProgram(): Command {
|
|
|
1087
1143
|
program
|
|
1088
1144
|
.name("tg-sai")
|
|
1089
1145
|
.description("Audit and manage Sai Telegram chat admin state")
|
|
1090
|
-
.version(
|
|
1146
|
+
.version(packageJson.version, "--version", "output the version number")
|
|
1091
1147
|
.helpOption("--help", "display help for command")
|
|
1092
1148
|
|
|
1093
1149
|
program.action(() => {
|