mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-06-08 13:03:18 +00:00
support-bot: Fix basic functionality
This commit is contained in:
@@ -96,6 +96,15 @@ export class SupportBot {
|
||||
log(`Member connected in group ${evt.groupInfo.groupId}: ${evt.member.memberProfile.displayName}`)
|
||||
}
|
||||
|
||||
onMemberContactReceivedInv(evt: CEvt.NewMemberContactReceivedInv): void {
|
||||
const {contact, groupInfo, member} = evt
|
||||
if (groupInfo.groupId === this.config.teamGroup.id) {
|
||||
log(`Accepted DM contact from team group member: ${contact.contactId}:${member.memberProfile.displayName}`)
|
||||
} else {
|
||||
log(`DM contact received from non-team group ${groupInfo.groupId}, member ${member.memberProfile.displayName}`)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Event Handler (Grok agent) ---
|
||||
|
||||
async onGrokGroupInvitation(evt: CEvt.ReceivedGroupInvitation): Promise<void> {
|
||||
@@ -114,12 +123,20 @@ export class SupportBot {
|
||||
return
|
||||
}
|
||||
|
||||
// Join succeeded — set maps and resolve waiter
|
||||
// Join request sent — set maps, but don't resolve waiter yet.
|
||||
// The waiter resolves when grokChat fires connectedToGroupMember (see onGrokMemberConnected).
|
||||
this.grokGroupMap.set(mainGroupId, evt.groupInfo.groupId)
|
||||
this.reverseGrokMap.set(evt.groupInfo.groupId, mainGroupId)
|
||||
}
|
||||
|
||||
onGrokMemberConnected(evt: CEvt.ConnectedToGroupMember): void {
|
||||
const grokGroupId = evt.groupInfo.groupId
|
||||
const mainGroupId = this.reverseGrokMap.get(grokGroupId)
|
||||
if (mainGroupId === undefined) return
|
||||
const resolver = this.grokJoinResolvers.get(mainGroupId)
|
||||
if (resolver) {
|
||||
this.grokJoinResolvers.delete(mainGroupId)
|
||||
log(`Grok fully connected in group: mainGroupId=${mainGroupId}, grokGroupId=${grokGroupId}`)
|
||||
resolver()
|
||||
}
|
||||
}
|
||||
@@ -132,8 +149,13 @@ export class SupportBot {
|
||||
const groupInfo = chatInfo.groupInfo
|
||||
if (!groupInfo.businessChat) return
|
||||
const groupId = groupInfo.groupId
|
||||
const state = this.conversations.get(groupId)
|
||||
if (!state) return
|
||||
let state = this.conversations.get(groupId)
|
||||
if (!state) {
|
||||
// After restart, re-initialize state for existing business chats
|
||||
state = {type: "teamQueue", userMessages: []}
|
||||
this.conversations.set(groupId, state)
|
||||
log(`Re-initialized conversation state for group ${groupId} after restart`)
|
||||
}
|
||||
|
||||
if (chatItem.chatDir.type === "groupSnd") return
|
||||
if (chatItem.chatDir.type !== "groupRcv") return
|
||||
@@ -227,7 +249,11 @@ export class SupportBot {
|
||||
groupId: number,
|
||||
state: {type: "teamQueue"; userMessages: string[]},
|
||||
): Promise<void> {
|
||||
const grokContactId = this.config.grokContact!.id
|
||||
if (this.config.grokContactId === null) {
|
||||
await this.sendToGroup(groupId, "Grok is temporarily unavailable. Please try again or click /team for a team member.")
|
||||
return
|
||||
}
|
||||
const grokContactId = this.config.grokContactId
|
||||
let member: T.GroupMember | undefined
|
||||
try {
|
||||
member = await this.mainChat.apiAddMember(groupId, grokContactId, T.GroupMemberRole.Member)
|
||||
@@ -366,9 +392,24 @@ export class SupportBot {
|
||||
}
|
||||
this.cleanupGrokMaps(groupId)
|
||||
}
|
||||
if (this.config.teamMembers.length === 0) {
|
||||
logError(`No team members configured, cannot add team member to group ${groupId}`, new Error("no team members"))
|
||||
if (wasGrokMode) {
|
||||
this.conversations.set(groupId, {type: "teamQueue", userMessages: []})
|
||||
}
|
||||
await this.sendToGroup(groupId, "No team members are available yet. Please try again later or click /grok.")
|
||||
return
|
||||
}
|
||||
try {
|
||||
const teamContactId = this.config.teamMembers[0].id
|
||||
const member = await this.mainChat.apiAddMember(groupId, teamContactId, T.GroupMemberRole.Member)
|
||||
const member = await this.addOrFindTeamMember(groupId, teamContactId)
|
||||
if (!member) {
|
||||
if (wasGrokMode) {
|
||||
this.conversations.set(groupId, {type: "teamQueue", userMessages: []})
|
||||
}
|
||||
await this.sendToGroup(groupId, "Sorry, there was an error adding a team member. Please try again.")
|
||||
return
|
||||
}
|
||||
this.conversations.set(groupId, {
|
||||
type: "teamPending",
|
||||
teamMemberGId: member.groupMemberId,
|
||||
@@ -387,10 +428,13 @@ export class SupportBot {
|
||||
// --- Helpers ---
|
||||
|
||||
private async addReplacementTeamMember(groupId: number): Promise<void> {
|
||||
if (this.config.teamMembers.length === 0) return
|
||||
try {
|
||||
const teamContactId = this.config.teamMembers[0].id
|
||||
const member = await this.mainChat.apiAddMember(groupId, teamContactId, T.GroupMemberRole.Member)
|
||||
this.conversations.set(groupId, {type: "teamLocked", teamMemberGId: member.groupMemberId})
|
||||
const member = await this.addOrFindTeamMember(groupId, teamContactId)
|
||||
if (member) {
|
||||
this.conversations.set(groupId, {type: "teamLocked", teamMemberGId: member.groupMemberId})
|
||||
}
|
||||
} catch (err) {
|
||||
logError(`Failed to add replacement team member to group ${groupId}`, err)
|
||||
// Stay in teamLocked with stale teamMemberGId — one-way gate must hold
|
||||
@@ -398,6 +442,26 @@ export class SupportBot {
|
||||
}
|
||||
}
|
||||
|
||||
private async addOrFindTeamMember(groupId: number, teamContactId: number): Promise<T.GroupMember | null> {
|
||||
try {
|
||||
return await this.mainChat.apiAddMember(groupId, teamContactId, T.GroupMemberRole.Member)
|
||||
} catch (err: any) {
|
||||
if (err?.chatError?.errorType?.type === "groupDuplicateMember") {
|
||||
// Team member already in group (e.g., from previous session) — find existing member
|
||||
log(`Team member already in group ${groupId}, looking up existing member`)
|
||||
const members = await this.mainChat.apiListMembers(groupId)
|
||||
const existing = members.find(m => m.memberContactId === teamContactId)
|
||||
if (existing) {
|
||||
log(`Found existing team member: groupMemberId=${existing.groupMemberId}`)
|
||||
return existing
|
||||
}
|
||||
logError(`Team member contact ${teamContactId} reported as duplicate but not found in group ${groupId}`, err)
|
||||
return null
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
private async sendToGroup(groupId: number, text: string): Promise<void> {
|
||||
try {
|
||||
await this.mainChat.apiSendTextMessage([T.ChatType.Group, groupId], text)
|
||||
|
||||
@@ -6,13 +6,12 @@ export interface IdName {
|
||||
export interface Config {
|
||||
dbPrefix: string
|
||||
grokDbPrefix: string
|
||||
teamGroup: IdName
|
||||
teamMembers: IdName[]
|
||||
grokContact: IdName | null // null during first-run
|
||||
teamGroup: IdName // name from CLI, id resolved at startup from state file
|
||||
teamMembers: IdName[] // optional, empty if not provided
|
||||
grokContactId: number | null // resolved at startup from state file
|
||||
groupLinks: string
|
||||
timezone: string
|
||||
grokApiKey: string
|
||||
firstRun: boolean
|
||||
}
|
||||
|
||||
export function parseIdName(s: string): IdName {
|
||||
@@ -36,26 +35,15 @@ function optionalArg(args: string[], flag: string, defaultValue: string): string
|
||||
}
|
||||
|
||||
export function parseConfig(args: string[]): Config {
|
||||
const firstRun = args.includes("--first-run")
|
||||
|
||||
const grokApiKey = process.env.GROK_API_KEY
|
||||
if (!grokApiKey) throw new Error("Missing environment variable: GROK_API_KEY")
|
||||
|
||||
const dbPrefix = optionalArg(args, "--db-prefix", "./data/bot")
|
||||
const grokDbPrefix = optionalArg(args, "--grok-db-prefix", "./data/grok")
|
||||
const teamGroup = parseIdName(requiredArg(args, "--team-group"))
|
||||
const teamMembers = requiredArg(args, "--team-members").split(",").map(parseIdName)
|
||||
if (teamMembers.length === 0) throw new Error("--team-members must have at least one member")
|
||||
|
||||
let grokContact: IdName | null = null
|
||||
if (!firstRun) {
|
||||
grokContact = parseIdName(requiredArg(args, "--grok-contact"))
|
||||
} else {
|
||||
const i = args.indexOf("--grok-contact")
|
||||
if (i >= 0 && i + 1 < args.length) {
|
||||
grokContact = parseIdName(args[i + 1])
|
||||
}
|
||||
}
|
||||
const teamGroupName = requiredArg(args, "--team-group")
|
||||
const teamGroup: IdName = {id: 0, name: teamGroupName} // id resolved at startup
|
||||
const teamMembersRaw = optionalArg(args, "--team-members", "")
|
||||
const teamMembers = teamMembersRaw ? teamMembersRaw.split(",").map(parseIdName) : []
|
||||
|
||||
const groupLinks = optionalArg(args, "--group-links", "")
|
||||
const timezone = optionalArg(args, "--timezone", "UTC")
|
||||
@@ -65,10 +53,9 @@ export function parseConfig(args: string[]): Config {
|
||||
grokDbPrefix,
|
||||
teamGroup,
|
||||
teamMembers,
|
||||
grokContact,
|
||||
grokContactId: null, // resolved at startup from state file
|
||||
groupLinks,
|
||||
timezone,
|
||||
grokApiKey,
|
||||
firstRun,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
import {readFileSync} from "fs"
|
||||
import {readFileSync, writeFileSync, existsSync} from "fs"
|
||||
import {join} from "path"
|
||||
import {bot, api} from "simplex-chat"
|
||||
import {T} from "@simplex-chat/types"
|
||||
import {parseConfig} from "./config.js"
|
||||
import {SupportBot} from "./bot.js"
|
||||
import {GrokApiClient} from "./grok.js"
|
||||
import {welcomeMessage} from "./messages.js"
|
||||
import {log, logError} from "./util.js"
|
||||
|
||||
interface BotState {
|
||||
teamGroupId?: number
|
||||
grokContactId?: number
|
||||
}
|
||||
|
||||
function readState(path: string): BotState {
|
||||
if (!existsSync(path)) return {}
|
||||
try { return JSON.parse(readFileSync(path, "utf-8")) } catch { return {} }
|
||||
}
|
||||
|
||||
function writeState(path: string, state: BotState): void {
|
||||
writeFileSync(path, JSON.stringify(state), "utf-8")
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const config = parseConfig(process.argv.slice(2))
|
||||
log("Config parsed", {
|
||||
@@ -14,11 +29,12 @@ async function main(): Promise<void> {
|
||||
grokDbPrefix: config.grokDbPrefix,
|
||||
teamGroup: config.teamGroup,
|
||||
teamMembers: config.teamMembers,
|
||||
grokContact: config.grokContact,
|
||||
firstRun: config.firstRun,
|
||||
timezone: config.timezone,
|
||||
})
|
||||
|
||||
const stateFilePath = `${config.dbPrefix}_state.json`
|
||||
const state = readState(stateFilePath)
|
||||
|
||||
// --- Init Grok agent (direct ChatApi) ---
|
||||
log("Initializing Grok agent...")
|
||||
const grokChat = await api.ChatApi.init(config.grokDbPrefix)
|
||||
@@ -30,42 +46,6 @@ async function main(): Promise<void> {
|
||||
log(`Grok user: ${grokUser.profile.displayName}`)
|
||||
await grokChat.startChat()
|
||||
|
||||
// --- First-run mode: establish contact between bot and Grok agent ---
|
||||
if (config.firstRun) {
|
||||
log("First-run mode: establishing bot↔Grok contact...")
|
||||
// We need to init the main bot first to create the invitation link
|
||||
const mainChat = await api.ChatApi.init(config.dbPrefix)
|
||||
let mainUser = await mainChat.apiGetActiveUser()
|
||||
if (!mainUser) {
|
||||
log("No main bot user, creating...")
|
||||
mainUser = await mainChat.apiCreateActiveUser({displayName: "SimpleX Support", fullName: ""})
|
||||
}
|
||||
await mainChat.startChat()
|
||||
|
||||
const invLink = await mainChat.apiCreateLink(mainUser.userId)
|
||||
log(`Invitation link created: ${invLink}`)
|
||||
|
||||
await grokChat.apiConnectActiveUser(invLink)
|
||||
log("Grok agent connecting...")
|
||||
|
||||
const evt = await mainChat.wait("contactConnected", 60000)
|
||||
if (!evt) {
|
||||
console.error("Timeout waiting for Grok agent to connect (60s). Exiting.")
|
||||
process.exit(1)
|
||||
}
|
||||
const contactId = evt.contact.contactId
|
||||
const displayName = evt.contact.profile.displayName
|
||||
log(`Grok contact established. ContactId=${contactId}`)
|
||||
console.log(`\nGrok contact established. Use: --grok-contact ${contactId}:${displayName}\n`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// --- Normal mode: validate config, init main bot ---
|
||||
if (!config.grokContact) {
|
||||
console.error("--grok-contact is required (unless --first-run)")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// SupportBot forward-reference: assigned after bot.run returns.
|
||||
// Events use optional chaining so any events during init are safely skipped.
|
||||
let supportBot: SupportBot | undefined
|
||||
@@ -77,6 +57,7 @@ async function main(): Promise<void> {
|
||||
deletedMemberUser: (evt) => supportBot?.onDeletedMemberUser(evt),
|
||||
groupDeleted: (evt) => supportBot?.onGroupDeleted(evt),
|
||||
connectedToGroupMember: (evt) => supportBot?.onMemberConnected(evt),
|
||||
newMemberContactReceivedInv: (evt) => supportBot?.onMemberContactReceivedInv(evt),
|
||||
}
|
||||
|
||||
log("Initializing main bot...")
|
||||
@@ -99,49 +80,133 @@ async function main(): Promise<void> {
|
||||
})
|
||||
log(`Main bot user: ${mainUser.profile.displayName}`)
|
||||
|
||||
// --- Startup validation ---
|
||||
log("Validating config against live data...")
|
||||
// --- Auto-accept direct messages from group members ---
|
||||
await mainChat.sendChatCmd(`/_set accept member contacts ${mainUser.userId} on`)
|
||||
log("Auto-accept member contacts enabled")
|
||||
|
||||
// Validate team group
|
||||
const groups = await mainChat.apiListGroups(mainUser.userId)
|
||||
const teamGroup = groups.find(g => g.groupId === config.teamGroup.id)
|
||||
if (!teamGroup) {
|
||||
console.error(`Team group not found: ID=${config.teamGroup.id}. Available groups: ${groups.map(g => `${g.groupId}:${g.groupProfile.displayName}`).join(", ") || "(none)"}`)
|
||||
process.exit(1)
|
||||
}
|
||||
if (teamGroup.groupProfile.displayName !== config.teamGroup.name) {
|
||||
console.error(`Team group name mismatch: expected "${config.teamGroup.name}", got "${teamGroup.groupProfile.displayName}" (ID=${config.teamGroup.id})`)
|
||||
process.exit(1)
|
||||
}
|
||||
log(`Team group validated: ${config.teamGroup.id}:${config.teamGroup.name}`)
|
||||
|
||||
// Validate contacts (team members + Grok)
|
||||
// --- List contacts ---
|
||||
const contacts = await mainChat.apiListContacts(mainUser.userId)
|
||||
for (const member of config.teamMembers) {
|
||||
const contact = contacts.find(c => c.contactId === member.id)
|
||||
if (!contact) {
|
||||
console.error(`Team member not found: ID=${member.id}. Available contacts: ${contacts.map(c => `${c.contactId}:${c.profile.displayName}`).join(", ") || "(none)"}`)
|
||||
process.exit(1)
|
||||
log(`Contacts (${contacts.length}):`, contacts.map(c => `${c.contactId}:${c.profile.displayName}`))
|
||||
|
||||
// --- Resolve Grok contact: from state file or auto-establish ---
|
||||
log("Resolving Grok contact...")
|
||||
|
||||
if (typeof state.grokContactId === "number") {
|
||||
const found = contacts.find(c => c.contactId === state.grokContactId)
|
||||
if (found) {
|
||||
config.grokContactId = found.contactId
|
||||
log(`Grok contact resolved from state file: ID=${config.grokContactId}`)
|
||||
} else {
|
||||
log(`Persisted Grok contact ID=${state.grokContactId} no longer exists, will re-establish`)
|
||||
}
|
||||
if (contact.profile.displayName !== member.name) {
|
||||
console.error(`Team member name mismatch: expected "${member.name}", got "${contact.profile.displayName}" (ID=${member.id})`)
|
||||
process.exit(1)
|
||||
}
|
||||
log(`Team member validated: ${member.id}:${member.name}`)
|
||||
}
|
||||
|
||||
const grokContact = contacts.find(c => c.contactId === config.grokContact!.id)
|
||||
if (!grokContact) {
|
||||
console.error(`Grok contact not found: ID=${config.grokContact.id}. Available contacts: ${contacts.map(c => `${c.contactId}:${c.profile.displayName}`).join(", ") || "(none)"}`)
|
||||
process.exit(1)
|
||||
}
|
||||
if (grokContact.profile.displayName !== config.grokContact.name) {
|
||||
console.error(`Grok contact name mismatch: expected "${config.grokContact.name}", got "${grokContact.profile.displayName}" (ID=${config.grokContact.id})`)
|
||||
process.exit(1)
|
||||
}
|
||||
log(`Grok contact validated: ${config.grokContact.id}:${config.grokContact.name}`)
|
||||
if (config.grokContactId === null) {
|
||||
log("Establishing bot↔Grok contact...")
|
||||
const invLink = await mainChat.apiCreateLink(mainUser.userId)
|
||||
await grokChat.apiConnectActiveUser(invLink)
|
||||
log("Grok agent connecting...")
|
||||
|
||||
log("All config validated.")
|
||||
const evt = await mainChat.wait("contactConnected", 60000)
|
||||
if (!evt) {
|
||||
console.error("Timeout waiting for Grok agent to connect (60s). Exiting.")
|
||||
process.exit(1)
|
||||
}
|
||||
config.grokContactId = evt.contact.contactId
|
||||
state.grokContactId = config.grokContactId
|
||||
writeState(stateFilePath, state)
|
||||
log(`Grok contact established: ID=${config.grokContactId} (persisted)`)
|
||||
}
|
||||
|
||||
// --- Resolve team group: from state file or auto-create ---
|
||||
log("Resolving team group...")
|
||||
|
||||
// Workaround: apiListGroups sends "/_groups {userId}" but the native parser
|
||||
// expects "/_groups{userId}" (no space). Send the command directly.
|
||||
const groupsResult = await mainChat.sendChatCmd(`/_groups${mainUser.userId}`)
|
||||
if (groupsResult.type !== "groupsList") {
|
||||
console.error("Failed to list groups:", groupsResult)
|
||||
process.exit(1)
|
||||
}
|
||||
const groups = groupsResult.groups
|
||||
|
||||
if (typeof state.teamGroupId === "number") {
|
||||
const found = groups.find(g => g.groupId === state.teamGroupId)
|
||||
if (found) {
|
||||
config.teamGroup.id = found.groupId
|
||||
log(`Team group resolved from state file: ${config.teamGroup.id}:${found.groupProfile.displayName}`)
|
||||
} else {
|
||||
log(`Persisted team group ID=${state.teamGroupId} no longer exists, will create new`)
|
||||
}
|
||||
}
|
||||
|
||||
const teamGroupPreferences: T.GroupPreferences = {
|
||||
directMessages: {enable: T.GroupFeatureEnabled.On},
|
||||
}
|
||||
|
||||
if (config.teamGroup.id === 0) {
|
||||
log(`Creating team group "${config.teamGroup.name}"...`)
|
||||
const newGroup = await mainChat.apiNewGroup(mainUser.userId, {
|
||||
displayName: config.teamGroup.name,
|
||||
fullName: "",
|
||||
groupPreferences: teamGroupPreferences,
|
||||
})
|
||||
config.teamGroup.id = newGroup.groupId
|
||||
state.teamGroupId = config.teamGroup.id
|
||||
writeState(stateFilePath, state)
|
||||
log(`Team group created: ${config.teamGroup.id}:${config.teamGroup.name} (persisted)`)
|
||||
} else {
|
||||
// Ensure direct messages are enabled on existing team group
|
||||
await mainChat.apiUpdateGroupProfile(config.teamGroup.id, {
|
||||
displayName: config.teamGroup.name,
|
||||
fullName: "",
|
||||
groupPreferences: teamGroupPreferences,
|
||||
})
|
||||
}
|
||||
|
||||
// --- Create invite link for team group (for team members to join) ---
|
||||
// Delete any stale link from a previous run (e.g., crash without graceful shutdown)
|
||||
try { await mainChat.apiDeleteGroupLink(config.teamGroup.id) } catch {}
|
||||
const teamGroupInviteLink = await mainChat.apiCreateGroupLink(config.teamGroup.id, T.GroupMemberRole.Member)
|
||||
log(`Team group invite link created`)
|
||||
console.log(`\nTeam group invite link (expires in 10 min):\n${teamGroupInviteLink}\n`)
|
||||
|
||||
// Schedule invite link deletion after 10 minutes
|
||||
let inviteLinkDeleted = false
|
||||
async function deleteInviteLink(): Promise<void> {
|
||||
if (inviteLinkDeleted) return
|
||||
inviteLinkDeleted = true
|
||||
try {
|
||||
await mainChat.apiDeleteGroupLink(config.teamGroup.id)
|
||||
log("Team group invite link deleted")
|
||||
} catch (err) {
|
||||
logError("Failed to delete team group invite link", err)
|
||||
}
|
||||
}
|
||||
const inviteLinkTimer = setTimeout(async () => {
|
||||
log("10 minutes elapsed, deleting team group invite link...")
|
||||
await deleteInviteLink()
|
||||
}, 10 * 60 * 1000)
|
||||
inviteLinkTimer.unref() // don't keep process alive for the timer
|
||||
|
||||
// --- Validate team member contacts (if provided) ---
|
||||
if (config.teamMembers.length > 0) {
|
||||
log("Validating team member contacts...")
|
||||
for (const member of config.teamMembers) {
|
||||
const contact = contacts.find(c => c.contactId === member.id)
|
||||
if (!contact) {
|
||||
console.error(`Team member not found: ID=${member.id}. Available contacts: ${contacts.map(c => `${c.contactId}:${c.profile.displayName}`).join(", ") || "(none)"}`)
|
||||
process.exit(1)
|
||||
}
|
||||
if (contact.profile.displayName !== member.name) {
|
||||
console.error(`Team member name mismatch: expected "${member.name}", got "${contact.profile.displayName}" (ID=${member.id})`)
|
||||
process.exit(1)
|
||||
}
|
||||
log(`Team member validated: ${member.id}:${member.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
log("Startup complete.")
|
||||
|
||||
// Load Grok context docs
|
||||
let docsContext = ""
|
||||
@@ -161,16 +226,19 @@ async function main(): Promise<void> {
|
||||
grokChat.on("receivedGroupInvitation", async (evt) => {
|
||||
await supportBot?.onGrokGroupInvitation(evt)
|
||||
})
|
||||
grokChat.on("connectedToGroupMember", (evt) => {
|
||||
supportBot?.onGrokMemberConnected(evt)
|
||||
})
|
||||
|
||||
// Keep process alive
|
||||
process.on("SIGINT", () => {
|
||||
log("Received SIGINT, shutting down...")
|
||||
// Graceful shutdown: delete invite link before exit
|
||||
async function shutdown(signal: string): Promise<void> {
|
||||
log(`Received ${signal}, shutting down...`)
|
||||
clearTimeout(inviteLinkTimer)
|
||||
await deleteInviteLink()
|
||||
process.exit(0)
|
||||
})
|
||||
process.on("SIGTERM", () => {
|
||||
log("Received SIGTERM, shutting down...")
|
||||
process.exit(0)
|
||||
})
|
||||
}
|
||||
process.on("SIGINT", () => shutdown("SIGINT"))
|
||||
process.on("SIGTERM", () => shutdown("SIGTERM"))
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
|
||||
Reference in New Issue
Block a user