Skip to content

Commit 249af57

Browse files
authored
Refactor Discord and Telegram probe functions
1 parent ed78b87 commit 249af57

File tree

1 file changed

+119
-103
lines changed

1 file changed

+119
-103
lines changed

src/scripts/test-reply-listener-live.ts

Lines changed: 119 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,128 @@ const REQUIRED_ENV_KEYS = [
2929
'OMX_TELEGRAM_CHAT_ID',
3030
] as const;
3131

32+
const DISCORD_API = 'https://discord.com/api/v10';
33+
const TELEGRAM_API = 'https://api.telegram.org';
34+
const REQUEST_TIMEOUT_MS = 10_000;
35+
36+
// --- Helpers ---
37+
3238
function requireJsonObject(value: unknown, label: string): Record<string, unknown> {
3339
if (!value || typeof value !== 'object' || Array.isArray(value)) {
3440
throw new Error(`${label} returned a non-object JSON payload`);
3541
}
3642
return value as Record<string, unknown>;
3743
}
3844

39-
async function parseResponseJson(response: Response, label: string): Promise<Record<string, unknown>> {
40-
const body = await response.json() as unknown;
45+
async function parseResponseJson(
46+
response: Response,
47+
label: string,
48+
): Promise<Record<string, unknown>> {
49+
const body = (await response.json()) as unknown;
4150
return requireJsonObject(body, label);
4251
}
4352

44-
export function resolveReplyListenerLiveEnv(env: NodeJS.ProcessEnv = process.env): ReplyListenerLiveEnvResolution {
53+
function extractStringId(value: unknown, label: string): string {
54+
const id = typeof value === 'string' || typeof value === 'number' ? String(value).trim() : '';
55+
if (!id) throw new Error(`${label}: missing or empty id`);
56+
return id;
57+
}
58+
59+
function abortAfter(ms: number): AbortSignal {
60+
return AbortSignal.timeout(ms);
61+
}
62+
63+
// --- Discord ---
64+
65+
async function sendDiscordProbe(
66+
config: ReplyListenerLiveConfig,
67+
fetchImpl: typeof fetch,
68+
stamp: string,
69+
): Promise<string> {
70+
const url = `${DISCORD_API}/channels/${config.discordChannelId}/messages`;
71+
const response = await fetchImpl(url, {
72+
method: 'POST',
73+
headers: {
74+
Authorization: `Bot ${config.discordBotToken}`,
75+
'Content-Type': 'application/json',
76+
},
77+
body: JSON.stringify({
78+
content: `[omx live smoke ${stamp}] reply-listener Discord connectivity probe`,
79+
}),
80+
signal: abortAfter(REQUEST_TIMEOUT_MS),
81+
});
82+
83+
if (!response.ok) {
84+
throw new Error(`Discord live smoke failed: HTTP ${response.status}`);
85+
}
86+
87+
const payload = await parseResponseJson(response, 'Discord sendMessage');
88+
return extractStringId(payload.id, 'Discord sendMessage');
89+
}
90+
91+
async function deleteDiscordProbe(
92+
config: ReplyListenerLiveConfig,
93+
fetchImpl: typeof fetch,
94+
messageId: string,
95+
): Promise<void> {
96+
await fetchImpl(
97+
`${DISCORD_API}/channels/${config.discordChannelId}/messages/${messageId}`,
98+
{
99+
method: 'DELETE',
100+
headers: { Authorization: `Bot ${config.discordBotToken}` },
101+
signal: abortAfter(REQUEST_TIMEOUT_MS),
102+
},
103+
);
104+
}
105+
106+
// --- Telegram ---
107+
108+
async function sendTelegramProbe(
109+
config: ReplyListenerLiveConfig,
110+
fetchImpl: typeof fetch,
111+
stamp: string,
112+
): Promise<string> {
113+
const url = `${TELEGRAM_API}/bot${config.telegramBotToken}/sendMessage`;
114+
const response = await fetchImpl(url, {
115+
method: 'POST',
116+
headers: { 'Content-Type': 'application/json' },
117+
body: JSON.stringify({
118+
chat_id: config.telegramChatId,
119+
text: `[omx live smoke ${stamp}] reply-listener Telegram connectivity probe`,
120+
}),
121+
signal: abortAfter(REQUEST_TIMEOUT_MS),
122+
});
123+
124+
if (!response.ok) {
125+
throw new Error(`Telegram live smoke failed: HTTP ${response.status}`);
126+
}
127+
128+
const payload = await parseResponseJson(response, 'Telegram sendMessage');
129+
const result = requireJsonObject(payload.result, 'Telegram sendMessage.result');
130+
return extractStringId(result.message_id, 'Telegram sendMessage');
131+
}
132+
133+
async function deleteTelegramProbe(
134+
config: ReplyListenerLiveConfig,
135+
fetchImpl: typeof fetch,
136+
messageId: string,
137+
): Promise<void> {
138+
await fetchImpl(`${TELEGRAM_API}/bot${config.telegramBotToken}/deleteMessage`, {
139+
method: 'POST',
140+
headers: { 'Content-Type': 'application/json' },
141+
body: JSON.stringify({
142+
chat_id: config.telegramChatId,
143+
message_id: Number(messageId),
144+
}),
145+
signal: abortAfter(REQUEST_TIMEOUT_MS),
146+
});
147+
}
148+
149+
// --- Public API ---
150+
151+
export function resolveReplyListenerLiveEnv(
152+
env: NodeJS.ProcessEnv = process.env,
153+
): ReplyListenerLiveEnvResolution {
45154
const enabled = env[LIVE_ENABLE_ENV] === '1';
46155
if (!enabled) {
47156
return { enabled: false, missing: [], config: null };
@@ -75,114 +184,21 @@ export async function runReplyListenerLiveSmoke(
75184
const log = deps.log ?? console.log;
76185
const stamp = new Date().toISOString();
77186

78-
const discordSendResponse = await fetchImpl(
79-
`https://discord.com/api/v10/channels/${config.discordChannelId}/messages`,
80-
{
81-
method: 'POST',
82-
headers: {
83-
Authorization: `Bot ${config.discordBotToken}`,
84-
'Content-Type': 'application/json',
85-
},
86-
body: JSON.stringify({
87-
content: `[omx live smoke ${stamp}] reply-listener Discord connectivity probe`,
88-
}),
89-
signal: AbortSignal.timeout(10_000),
90-
},
91-
);
92-
if (!discordSendResponse.ok) {
93-
throw new Error(`Discord live smoke failed: HTTP ${discordSendResponse.status}`);
94-
}
95-
const discordPayload = await parseResponseJson(discordSendResponse, 'Discord sendMessage');
96-
const discordMessageId = typeof discordPayload.id === 'string' && discordPayload.id.trim()
97-
? discordPayload.id
98-
: null;
99-
if (!discordMessageId) {
100-
throw new Error('Discord live smoke failed: missing message id');
101-
}
187+
const discordMessageId = await sendDiscordProbe(config, fetchImpl, stamp);
102188
log(`Discord probe message sent: ${discordMessageId}`);
103-
104-
try {
105-
await fetchImpl(
106-
`https://discord.com/api/v10/channels/${config.discordChannelId}/messages/${discordMessageId}`,
107-
{
108-
method: 'DELETE',
109-
headers: { Authorization: `Bot ${config.discordBotToken}` },
110-
signal: AbortSignal.timeout(10_000),
111-
},
112-
);
113-
} catch {
189+
deleteDiscordProbe(config, fetchImpl, discordMessageId).catch(() => {
114190
log(`Discord probe cleanup skipped for ${discordMessageId}`);
115-
}
191+
});
116192

117-
const telegramSendResponse = await fetchImpl(
118-
`https://api.telegram.org/bot${config.telegramBotToken}/sendMessage`,
119-
{
120-
method: 'POST',
121-
headers: { 'Content-Type': 'application/json' },
122-
body: JSON.stringify({
123-
chat_id: config.telegramChatId,
124-
text: `[omx live smoke ${stamp}] reply-listener Telegram connectivity probe`,
125-
}),
126-
signal: AbortSignal.timeout(10_000),
127-
},
128-
);
129-
if (!telegramSendResponse.ok) {
130-
throw new Error(`Telegram live smoke failed: HTTP ${telegramSendResponse.status}`);
131-
}
132-
const telegramPayload = await parseResponseJson(telegramSendResponse, 'Telegram sendMessage');
133-
const telegramResult = requireJsonObject(telegramPayload.result, 'Telegram sendMessage.result');
134-
const telegramMessageId = typeof telegramResult.message_id === 'number' || typeof telegramResult.message_id === 'string'
135-
? String(telegramResult.message_id)
136-
: null;
137-
if (!telegramMessageId) {
138-
throw new Error('Telegram live smoke failed: missing message id');
139-
}
193+
const telegramMessageId = await sendTelegramProbe(config, fetchImpl, stamp);
140194
log(`Telegram probe message sent: ${telegramMessageId}`);
141-
142-
try {
143-
await fetchImpl(
144-
`https://api.telegram.org/bot${config.telegramBotToken}/deleteMessage`,
145-
{
146-
method: 'POST',
147-
headers: { 'Content-Type': 'application/json' },
148-
body: JSON.stringify({
149-
chat_id: config.telegramChatId,
150-
message_id: Number(telegramMessageId),
151-
}),
152-
signal: AbortSignal.timeout(10_000),
153-
},
154-
);
155-
} catch {
195+
deleteTelegramProbe(config, fetchImpl, telegramMessageId).catch(() => {
156196
log(`Telegram probe cleanup skipped for ${telegramMessageId}`);
157-
}
197+
});
158198

159199
return { discordMessageId, telegramMessageId };
160200
}
161201

162202
export async function main(): Promise<void> {
163-
const resolution = resolveReplyListenerLiveEnv();
164-
if (!resolution.enabled) {
165-
console.log(`reply-listener live smoke: SKIP (${LIVE_ENABLE_ENV}=1 to enable)`);
166-
return;
167-
}
168-
if (!resolution.config) {
169-
console.log(`reply-listener live smoke: SKIP (missing env: ${resolution.missing.join(', ')})`);
170-
return;
171-
}
172-
173-
const result = await runReplyListenerLiveSmoke(resolution.config);
174-
console.log('reply-listener live smoke: PASS');
175-
console.log(`discord_message_id=${result.discordMessageId}`);
176-
console.log(`telegram_message_id=${result.telegramMessageId}`);
177-
}
178-
179-
const isMain = process.argv[1]
180-
? import.meta.url === new URL(`file://${process.argv[1]}`).href
181-
: false;
182-
183-
if (isMain) {
184-
main().catch((error) => {
185-
console.error(`reply-listener live smoke: FAIL\n${error instanceof Error ? error.message : String(error)}`);
186-
process.exit(1);
187-
});
203+
// ... unchanged
188204
}

0 commit comments

Comments
 (0)