multibot
This commit is contained in:
parent
c48f109d15
commit
35a66a1f29
217
index.mjs
217
index.mjs
@ -12,39 +12,50 @@ global.fetch = function fetch(url, options) {
|
||||
|
||||
|
||||
|
||||
var memberlist = []; // list of conversation ids
|
||||
var members = [];
|
||||
try {
|
||||
memberlist = JSON.parse(readFileSync("memberlist.json", "utf8"));
|
||||
members = JSON.parse(readFileSync("members.json", "utf8"));
|
||||
} catch (error) {}
|
||||
|
||||
function saveMemberlist() {
|
||||
writeFileSync("memberlist.json", JSON.stringify(memberlist));
|
||||
function saveMembers() {
|
||||
writeFileSync("members.json", JSON.stringify(members));
|
||||
}
|
||||
|
||||
var bots = new Map(await Promise.all(members.map(async ([memberdid, botidentifier]) => {
|
||||
let bot = await newbot(botidentifier, process.env.PASSWORD2, "https://lamp.wtf");
|
||||
return [memberdid, bot];
|
||||
})));
|
||||
|
||||
|
||||
async function newbot(identifier, password, service) {
|
||||
let bot = new Bot({
|
||||
emitEvents: false,
|
||||
emitChatEvents: true,
|
||||
service
|
||||
});
|
||||
bot.on("error", error => {
|
||||
console.error(`bot ${identifier} error`, error);
|
||||
});
|
||||
bot.on("message", messageHandler);
|
||||
await bot.login({
|
||||
identifier,
|
||||
password
|
||||
});
|
||||
console.log(`logged in bot ${identifier}`);
|
||||
await bot.setChatPreference(IncomingChatPreference.All);
|
||||
return bot;
|
||||
}
|
||||
|
||||
var mainbot = await newbot("groupchatbot.bsky.social", process.env.PASSWORD);
|
||||
|
||||
|
||||
|
||||
var bot = new Bot({
|
||||
emitEvents: false,
|
||||
emitChatEvents: true
|
||||
});
|
||||
bot.on("error", error => {
|
||||
console.error("bot error", error);
|
||||
});
|
||||
|
||||
await bot.login({
|
||||
identifier: process.env.IDENTIFIER,
|
||||
password: process.env.PASSWORD
|
||||
});
|
||||
await bot.setChatPreference(IncomingChatPreference.All);
|
||||
|
||||
|
||||
bot.on("message", async message => {
|
||||
console.debug(new Date().toISOString(), message.senderDid, message.text);
|
||||
async function messageHandler(message) {
|
||||
console.debug('m', new Date().toISOString(), message.bot.profile.did, message.senderDid, message.text);
|
||||
try {
|
||||
var sender = await message.getSender();
|
||||
var conversation = await message.getConversation();
|
||||
var respond = text => sendMessage({conversationId: conversation.id, text});
|
||||
var respond = text => sender.sendMessage({text});
|
||||
|
||||
if (message.text.startsWith('/')) {
|
||||
let args = message.text.split(' ');
|
||||
@ -55,33 +66,70 @@ bot.on("message", async message => {
|
||||
await respond("pong");
|
||||
return;
|
||||
case "list":
|
||||
let memberConversations = await Promise.all(memberlist.map(convoid => bot.getConversation(convoid)));
|
||||
await respond(`${memberConversations.length} members in group chat: ${memberConversations.map(c => {
|
||||
let handle = c.members.find(m => m.did != bot.profile.did).handle;
|
||||
if (handle != "missing.invalid") { // work around https://github.com/skyware-js/bot/issues/19
|
||||
handle = `@${handle}`;
|
||||
}
|
||||
return handle;
|
||||
}).join(', ')}`);
|
||||
let m = await Promise.all(members.map(async ([memberdid]) => {
|
||||
try {
|
||||
return await message.bot.getProfile(memberdid);
|
||||
} catch(error) {}
|
||||
return memberdid;
|
||||
}));
|
||||
let rt = new RichText();
|
||||
rt.addText(`${m.length} members in group chat: `);
|
||||
for (let p of m) {
|
||||
rt.addMention(p.displayName || p.handle || p.did || p, p.did || p);
|
||||
rt.addText(", ");
|
||||
}
|
||||
await respond(rt);
|
||||
return;
|
||||
case "leave":
|
||||
broadcast(new RichText().addMention(sender.displayName || sender.handle || message.senderDid, message.senderDid).addText(" left the group chat.")).catch(console.error);
|
||||
memberlist = memberlist.filter(convoid => convoid != conversation.id);
|
||||
saveMemberlist();
|
||||
let e = members.find(x => x[0] == message.senderDid);
|
||||
let i = members.indexOf(e);
|
||||
members.splice(i, 1);
|
||||
saveMembers();
|
||||
await respond(`You left the group chat. Rejoin at any time with /join`);
|
||||
return;
|
||||
case "join":
|
||||
if (memberlist.includes(conversation.id)) {
|
||||
await respond(`You're already in the group chat.`);
|
||||
if (bots.has(message.senderDid)) {
|
||||
await (await bots.get(message.senderDid).getProfile(message.senderDid)).sendMessage({
|
||||
text: `Use this bot to access group chat.`
|
||||
});
|
||||
await respond(`You are already in the group chat, use @${bots.get(message.senderDid).profile.handle} to access it.`);
|
||||
return;
|
||||
}
|
||||
if (members.some(entry => entry[0] == message.senderDid)) {
|
||||
await respond(`You're already a member of the group chat.`);
|
||||
//todo missing bot retry make
|
||||
} else {
|
||||
broadcast(new RichText().addMention(sender.displayName || sender.handle || message.senderDid, message.senderDid).addText(" joined the group chat.")).catch(console.error);
|
||||
memberlist.push(conversation.id);
|
||||
saveMemberlist();
|
||||
await respond(`Welcome to the group chat; you are now chatting with ${memberlist.length} other people. Use /list to see them.`);
|
||||
let u = Array.from(bots).map(bot => bot.profile.handle).map(h => Number(h.match(/\d+/)[0]));
|
||||
let i;
|
||||
for (i = 0; i < 100; i++) {
|
||||
if (!u.includes(i)) break;
|
||||
}
|
||||
let h = `groupchatbot${i}.lamp.wtf`;
|
||||
let p = await message.bot.getProfile(h);
|
||||
members.push([message.senderDid, p.did]);
|
||||
saveMembers();
|
||||
let b;
|
||||
if (bots.has(message.senderDid)) {
|
||||
b = bots.get(message.senderDid);
|
||||
} else {
|
||||
b = await newbot(p.did);
|
||||
bots.set(message.senderDid, b);
|
||||
}
|
||||
let s = await b.getProfile(message.senderDid);
|
||||
try {
|
||||
await s.sendMessage({
|
||||
text: `Welcome to the group chat; you are now chatting with ${members.length} other people. Use /list to see them.`
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
await respond(`You are now in the group chat but your groupchat bot failed to DM you. Please allow DMs from everyone or open a DM with @${b.profile.handle} to access the group chat.\n\n(${error.message}\n${error.cause})`);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case "invite":
|
||||
if (!memberlist.includes(conversation.id)) {
|
||||
if (!members.some(entry => entry[0] == message.senderDid)) {
|
||||
await respond(`You are not allowed to invite people to the group chat when you are not in the group chat yourself. Use /join first.`);
|
||||
return;
|
||||
}
|
||||
@ -91,9 +139,10 @@ bot.on("message", async message => {
|
||||
return;
|
||||
}
|
||||
handle = handle.replace('@','');
|
||||
let profile = await bot.getProfile(handle);
|
||||
await profile.sendMessage({text: `@${sender.handle} invited you to the group chat! Use /join to accept!`});
|
||||
await broadcast(new RichText().addMention(sender.displayName || sender.handle || message.senderDid, message.senderDid).addText(" invited ").addMention(profile.displayName||profile.handle,profile.did).addText(" to the group chat."));
|
||||
await respond("todo");
|
||||
//let profile = await bot.getProfile(handle);
|
||||
//await profile.sendMessage({text: `@${sender.handle} invited you to the group chat! Use /join to accept!`});
|
||||
//await broadcast(new RichText().addMention(sender.displayName || sender.handle || message.senderDid, message.senderDid).addText(" invited ").addMention(profile.displayName||profile.handle,profile.did).addText(" to the group chat."));
|
||||
return;
|
||||
case "help":
|
||||
await respond(`Commands: ${commandList}`);
|
||||
@ -104,11 +153,20 @@ bot.on("message", async message => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!memberlist.includes(conversation.id)) {
|
||||
if (!members.some(entry => entry[0] == message.senderDid)) {
|
||||
await respond("Use /join to join the group chat.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bots.has(message.senderDid)) {
|
||||
throw new Error("sender is member but missing bot");
|
||||
}
|
||||
|
||||
if (bots.get(message.senderDid) != message.bot) {
|
||||
await respond(`Wrong bot. Use @${bots.get(message.senderDid).profile.handle} to access the group chat.`);
|
||||
return;
|
||||
}
|
||||
|
||||
var logline = `${message.sentAt.toISOString()},${message.senderDid},${sender.displayName},${sender.handle},${JSON.stringify(message.text)}`;
|
||||
console.log(logline);
|
||||
appendFileSync(`chatlog.csv`, logline + '\n');
|
||||
@ -139,81 +197,34 @@ bot.on("message", async message => {
|
||||
}]
|
||||
});
|
||||
|
||||
var otherconvoids = memberlist.filter(x => x != conversation.id);
|
||||
var messages = otherconvoids.map(conversationId => ({
|
||||
conversationId,
|
||||
text,
|
||||
facets,
|
||||
embed: message.embed
|
||||
}));
|
||||
await sendMessages(messages, {resolveFacets: false});
|
||||
await broadcast({text, facets, embed: message.embed}, {resolveFacets: false}, [message.senderDid]);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (conversation) sendMessage({conversationId: conversation.id, text: `${error.message}\ncause: ${error.cause.message}`}).catch(error => console.error(error));
|
||||
sender.sendMessage({conversationId: conversation.id, text: `${error.message}\ncause: ${error.cause.message}`}).catch(error => console.error(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async function broadcast(text) {
|
||||
var logline = `${new Date().toISOString()},,,,${JSON.stringify(text.text||text)}`;
|
||||
async function broadcast(message, options, excludeDids) {
|
||||
var logline = `${new Date().toISOString()},,,,${JSON.stringify(message.text.text||message.text)}`;
|
||||
console.log(logline);
|
||||
appendFileSync(`chatlog.csv`, logline + '\n');
|
||||
var messages = memberlist.map(conversationId => ({
|
||||
conversationId,
|
||||
text
|
||||
}));
|
||||
await sendMessages(messages);
|
||||
}
|
||||
|
||||
|
||||
// presere order
|
||||
var queue = [];
|
||||
var ratelimited = false;
|
||||
function sendMessages(messages, options) {
|
||||
queue.push([messages, options]);
|
||||
if (!ratelimited) return flushQueue();
|
||||
}
|
||||
|
||||
async function flushQueue() {
|
||||
if (!queue.length) return;
|
||||
var [messages, options] = queue.shift();
|
||||
try {
|
||||
await bot.sendMessages(messages, options);
|
||||
ratelimited = false;
|
||||
} catch (error) {
|
||||
if (error.cause?.kind == "RateLimitExceeded") {
|
||||
console.warn("ratelimited");
|
||||
ratelimited = true;
|
||||
queue.unshift([messages, options]);
|
||||
} else throw error;
|
||||
for (let [memberdid, bot] of bots) {
|
||||
if (excludeDids?.includes(memberdid)) continue;
|
||||
if (!members.some(([m]) => m == memberdid)) continue;
|
||||
trySendMessageToDid(bot, memberdid, message, options);
|
||||
}
|
||||
}
|
||||
|
||||
(function flushQueueLoop() {
|
||||
flushQueue().catch(console.error).then(() => {
|
||||
setTimeout(flushQueueLoop, 10000);
|
||||
});
|
||||
})();
|
||||
|
||||
|
||||
// no preserve order
|
||||
async function sendMessage(message, options) {
|
||||
async function trySendMessageToDid(bot, did, message, options) {
|
||||
try {
|
||||
return await bot.sendMessage(message, options);
|
||||
let profile = await bot.getProfile(did);
|
||||
await profile.sendMessage(message, options);
|
||||
} catch (error) {
|
||||
if (error.cause?.kind == "RateLimitExceeded") {
|
||||
console.warn("ratelimited");
|
||||
await new Promise(r => setTimeout(r, 10000));
|
||||
return await sendMessage(message, options);
|
||||
} else throw error;
|
||||
console.error(`fail send to ${did} ${error.message} cause ${error.cause.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
global.bot = bot;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user