nanoclaw init
Some checks failed
Sync upstream & merge-forward skill branches / sync-and-merge (push) Has been cancelled
Merge-forward skill branches / merge-forward (push) Has been cancelled
Bump version / bump-version (push) Has been cancelled
Update token count / update-tokens (push) Has been cancelled
Some checks failed
Sync upstream & merge-forward skill branches / sync-and-merge (push) Has been cancelled
Merge-forward skill branches / merge-forward (push) Has been cancelled
Bump version / bump-version (push) Has been cancelled
Update token count / update-tokens (push) Has been cancelled
This commit is contained in:
@@ -12,6 +12,98 @@ import {
|
||||
RegisteredGroup,
|
||||
} from '../types.js';
|
||||
|
||||
// Bot pool for agent teams: send-only Api instances (no polling)
|
||||
const poolApis: Api[] = [];
|
||||
// Maps "{groupFolder}:{senderName}" → pool Api index for stable assignment
|
||||
const senderBotMap = new Map<string, number>();
|
||||
let nextPoolIndex = 0;
|
||||
|
||||
/**
|
||||
* Initialize send-only Api instances for the bot pool.
|
||||
* Each pool bot can send messages but doesn't poll for updates.
|
||||
*/
|
||||
export async function initBotPool(tokens: string[]): Promise<void> {
|
||||
for (const token of tokens) {
|
||||
try {
|
||||
const api = new Api(token);
|
||||
const me = await api.getMe();
|
||||
poolApis.push(api);
|
||||
logger.info(
|
||||
{ username: me.username, id: me.id, poolSize: poolApis.length },
|
||||
'Pool bot initialized',
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Failed to initialize pool bot');
|
||||
}
|
||||
}
|
||||
if (poolApis.length > 0) {
|
||||
logger.info({ count: poolApis.length }, 'Telegram bot pool ready');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message via a pool bot assigned to the given sender name.
|
||||
* Assigns bots round-robin on first use; subsequent messages from the
|
||||
* same sender in the same group always use the same bot.
|
||||
* On first assignment, renames the bot to match the sender's role.
|
||||
*/
|
||||
export async function sendPoolMessage(
|
||||
chatId: string,
|
||||
text: string,
|
||||
sender: string,
|
||||
groupFolder: string,
|
||||
): Promise<void> {
|
||||
if (poolApis.length === 0) {
|
||||
// No pool bots — fall back to main bot sendMessage path
|
||||
return;
|
||||
}
|
||||
|
||||
const key = `${groupFolder}:${sender}`;
|
||||
let idx = senderBotMap.get(key);
|
||||
if (idx === undefined) {
|
||||
idx = nextPoolIndex % poolApis.length;
|
||||
nextPoolIndex++;
|
||||
senderBotMap.set(key, idx);
|
||||
// Rename the bot to match the sender's role, then wait for Telegram to propagate
|
||||
try {
|
||||
await poolApis[idx].setMyName(sender);
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
logger.info(
|
||||
{ sender, groupFolder, poolIndex: idx },
|
||||
'Assigned and renamed pool bot',
|
||||
);
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
{ sender, err },
|
||||
'Failed to rename pool bot (sending anyway)',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const api = poolApis[idx];
|
||||
try {
|
||||
const numericId = chatId.replace(/^tg:/, '');
|
||||
const MAX_LENGTH = 4096;
|
||||
if (text.length <= MAX_LENGTH) {
|
||||
await sendTelegramMessage(api, numericId, text);
|
||||
} else {
|
||||
for (let i = 0; i < text.length; i += MAX_LENGTH) {
|
||||
await sendTelegramMessage(
|
||||
api,
|
||||
numericId,
|
||||
text.slice(i, i + MAX_LENGTH),
|
||||
);
|
||||
}
|
||||
}
|
||||
logger.info(
|
||||
{ chatId, sender, poolIndex: idx, length: text.length },
|
||||
'Pool message sent',
|
||||
);
|
||||
} catch (err) {
|
||||
logger.error({ chatId, sender, err }, 'Failed to send pool message');
|
||||
}
|
||||
}
|
||||
|
||||
export interface TelegramChannelOpts {
|
||||
onMessage: OnInboundMessage;
|
||||
onChatMetadata: OnChatMetadata;
|
||||
|
||||
Reference in New Issue
Block a user