@uniquedivine/tg-sai 0.0.1 → 0.0.3
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/main.ts +53 -9
- package/package.json +1 -1
- package/profiles.ts +7 -2
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/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)
|
|
@@ -565,6 +589,7 @@ async function runSaiFind(options: SaiFindOptions): Promise<void> {
|
|
|
565
589
|
async function applyTargetPlan(params: {
|
|
566
590
|
client: Awaited<ReturnType<typeof createMtcuteClient>>
|
|
567
591
|
chat: ChatInput
|
|
592
|
+
actorUsername: string
|
|
568
593
|
targetUserId: number | string
|
|
569
594
|
targetPeer: number | string
|
|
570
595
|
rights: Record<string, boolean>
|
|
@@ -580,6 +605,7 @@ async function applyTargetPlan(params: {
|
|
|
580
605
|
printJsonEvent("target_plan", {
|
|
581
606
|
chatId: params.chat.chatId,
|
|
582
607
|
title: params.chat.title,
|
|
608
|
+
actorUsername: params.actorUsername,
|
|
583
609
|
targetUserId: params.targetUserId,
|
|
584
610
|
actionPrefix: params.actionPrefix,
|
|
585
611
|
run: params.run,
|
|
@@ -591,6 +617,7 @@ async function applyTargetPlan(params: {
|
|
|
591
617
|
if (!beforePlan.actorCanApply) {
|
|
592
618
|
printJsonEvent("target_skip", {
|
|
593
619
|
chatId: params.chat.chatId,
|
|
620
|
+
actorUsername: params.actorUsername,
|
|
594
621
|
actionPrefix: params.actionPrefix,
|
|
595
622
|
reason: "actor_lacks_owner_or_add_admins_right",
|
|
596
623
|
})
|
|
@@ -601,7 +628,7 @@ async function applyTargetPlan(params: {
|
|
|
601
628
|
return
|
|
602
629
|
}
|
|
603
630
|
|
|
604
|
-
const deps = actionExecutorDeps()
|
|
631
|
+
const deps = actionExecutorDeps(actorBatchLogger(params.actorUsername))
|
|
605
632
|
if (shouldRunAction(beforePlan.proposedActions, "add target user")) {
|
|
606
633
|
await withFloodWaitRetry(
|
|
607
634
|
{
|
|
@@ -647,6 +674,7 @@ async function maybeTransferOwner(params: {
|
|
|
647
674
|
client: Awaited<ReturnType<typeof createMtcuteClient>>
|
|
648
675
|
env: TelegramEnv
|
|
649
676
|
chat: ChatInput
|
|
677
|
+
actorUsername: string
|
|
650
678
|
targetUser: number | string
|
|
651
679
|
enabled: boolean
|
|
652
680
|
run: boolean
|
|
@@ -664,6 +692,7 @@ async function maybeTransferOwner(params: {
|
|
|
664
692
|
if (!plan.actorCanTransferOwnership) {
|
|
665
693
|
printJsonEvent("ownership_skip", {
|
|
666
694
|
chatId: params.chat.chatId,
|
|
695
|
+
actorUsername: params.actorUsername,
|
|
667
696
|
reason: "actor_not_owner",
|
|
668
697
|
})
|
|
669
698
|
return
|
|
@@ -671,6 +700,7 @@ async function maybeTransferOwner(params: {
|
|
|
671
700
|
if (!plan.targetCanReceiveOwnership) {
|
|
672
701
|
printJsonEvent("ownership_skip", {
|
|
673
702
|
chatId: params.chat.chatId,
|
|
703
|
+
actorUsername: params.actorUsername,
|
|
674
704
|
reason: "target_not_eligible",
|
|
675
705
|
})
|
|
676
706
|
return
|
|
@@ -678,6 +708,7 @@ async function maybeTransferOwner(params: {
|
|
|
678
708
|
if (params.env.password === null) {
|
|
679
709
|
printJsonEvent("ownership_skip", {
|
|
680
710
|
chatId: params.chat.chatId,
|
|
711
|
+
actorUsername: params.actorUsername,
|
|
681
712
|
reason: "missing_telegram_password",
|
|
682
713
|
})
|
|
683
714
|
return
|
|
@@ -685,6 +716,7 @@ async function maybeTransferOwner(params: {
|
|
|
685
716
|
if (!params.run) {
|
|
686
717
|
printJsonEvent("ownership_plan", {
|
|
687
718
|
chatId: params.chat.chatId,
|
|
719
|
+
actorUsername: params.actorUsername,
|
|
688
720
|
action: "transfer_owner",
|
|
689
721
|
})
|
|
690
722
|
return
|
|
@@ -701,7 +733,7 @@ async function maybeTransferOwner(params: {
|
|
|
701
733
|
targetPeer: params.targetUser,
|
|
702
734
|
password: params.env.password ?? "",
|
|
703
735
|
}),
|
|
704
|
-
actionExecutorDeps(),
|
|
736
|
+
actionExecutorDeps(actorBatchLogger(params.actorUsername)),
|
|
705
737
|
)
|
|
706
738
|
}
|
|
707
739
|
|
|
@@ -727,18 +759,23 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
727
759
|
|
|
728
760
|
await withClient(options.profile, async (client, env) => {
|
|
729
761
|
activeJsonLogPath = logPath
|
|
762
|
+
const actor = normalizeUser(await client.getMe())
|
|
763
|
+
const actorUsername = actor.username ?? ""
|
|
764
|
+
const profileName = await selectedProfileName(options.profile)
|
|
730
765
|
const ownershipTransferEnabled =
|
|
731
766
|
options.transferOwner !== false && env.password !== null
|
|
732
767
|
console.log(`wrote_run_log: ${logPath}`)
|
|
733
768
|
printJsonEvent("run_start", {
|
|
734
769
|
file: options.file,
|
|
735
770
|
logPath,
|
|
771
|
+
profileName,
|
|
772
|
+
actor,
|
|
736
773
|
chatCount: chats.length,
|
|
737
774
|
run: options.run === true,
|
|
738
775
|
transferOwner: ownershipTransferEnabled,
|
|
739
776
|
passwordAvailable: env.password !== null,
|
|
740
777
|
delayMs,
|
|
741
|
-
targetUser: uniqueDivine.id,
|
|
778
|
+
targetUser: USERS.uniqueDivine.id,
|
|
742
779
|
targetBot: saiBot.id,
|
|
743
780
|
})
|
|
744
781
|
|
|
@@ -749,12 +786,14 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
749
786
|
total: chats.length,
|
|
750
787
|
chatId: chat.chatId,
|
|
751
788
|
title: chat.title,
|
|
789
|
+
actorUsername,
|
|
752
790
|
})
|
|
753
791
|
|
|
754
792
|
try {
|
|
755
793
|
await applyTargetPlan({
|
|
756
794
|
client,
|
|
757
795
|
chat,
|
|
796
|
+
actorUsername,
|
|
758
797
|
targetUserId: saiBot.id,
|
|
759
798
|
targetPeer: saiBot.handle,
|
|
760
799
|
rights: saiBotAdminRights,
|
|
@@ -764,8 +803,9 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
764
803
|
await applyTargetPlan({
|
|
765
804
|
client,
|
|
766
805
|
chat,
|
|
767
|
-
|
|
768
|
-
|
|
806
|
+
actorUsername,
|
|
807
|
+
targetUserId: USERS.uniqueDivine.id,
|
|
808
|
+
targetPeer: USERS.uniqueDivine.handle,
|
|
769
809
|
rights: ownerTargetAdminRights,
|
|
770
810
|
actionPrefix: "owner_target",
|
|
771
811
|
run: options.run === true,
|
|
@@ -774,16 +814,19 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
774
814
|
client,
|
|
775
815
|
env,
|
|
776
816
|
chat,
|
|
777
|
-
|
|
817
|
+
actorUsername,
|
|
818
|
+
targetUser: USERS.uniqueDivine.handle,
|
|
778
819
|
enabled: ownershipTransferEnabled,
|
|
779
820
|
run: options.run === true,
|
|
780
821
|
})
|
|
781
822
|
printJsonEvent("chat_complete", {
|
|
782
823
|
chatId: chat.chatId,
|
|
824
|
+
actorUsername,
|
|
783
825
|
})
|
|
784
826
|
} catch (error) {
|
|
785
827
|
printJsonEvent("chat_error", {
|
|
786
828
|
chatId: chat.chatId,
|
|
829
|
+
actorUsername,
|
|
787
830
|
errorName: error instanceof Error ? error.name : "UnknownError",
|
|
788
831
|
errorMessage:
|
|
789
832
|
error instanceof Error ? error.message : String(error),
|
|
@@ -793,6 +836,7 @@ async function runSaiBootstrap(options: SaiBootstrapOptions): Promise<void> {
|
|
|
793
836
|
if (delayMs > 0 && i < chats.length - 1) {
|
|
794
837
|
printJsonEvent("chat_delay", {
|
|
795
838
|
chatId: chat.chatId,
|
|
839
|
+
actorUsername,
|
|
796
840
|
delayMs,
|
|
797
841
|
})
|
|
798
842
|
await sleep(delayMs)
|
|
@@ -1087,7 +1131,7 @@ export function createProgram(): Command {
|
|
|
1087
1131
|
program
|
|
1088
1132
|
.name("tg-sai")
|
|
1089
1133
|
.description("Audit and manage Sai Telegram chat admin state")
|
|
1090
|
-
.version(
|
|
1134
|
+
.version(packageJson.version)
|
|
1091
1135
|
.helpOption("--help", "display help for command")
|
|
1092
1136
|
|
|
1093
1137
|
program.action(() => {
|
package/package.json
CHANGED
package/profiles.ts
CHANGED
|
@@ -291,9 +291,14 @@ export async function addProfile(
|
|
|
291
291
|
const previous = config.profiles[input.name]
|
|
292
292
|
const timestamp = now.toISOString()
|
|
293
293
|
config.profiles[input.name] = {
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
name: input.name,
|
|
295
|
+
apiId: input.apiId ?? previous?.apiId ?? null,
|
|
296
|
+
apiHash: input.apiHash || previous?.apiHash || "",
|
|
297
|
+
sessionString: input.sessionString || previous?.sessionString || "",
|
|
296
298
|
password: input.password || previous?.password || "",
|
|
299
|
+
handle: input.handle ?? previous?.handle ?? null,
|
|
300
|
+
userId: input.userId ?? previous?.userId ?? null,
|
|
301
|
+
displayName: input.displayName || previous?.displayName || "",
|
|
297
302
|
createdAt: previous?.createdAt ?? timestamp,
|
|
298
303
|
updatedAt: timestamp,
|
|
299
304
|
}
|