Compare commits

..

No commits in common. "62247b81bc775ea915eb6d713729529c1de0f190" and "18d9bf7b9e15588486e91a9fb5dfaa18ec71c445" have entirely different histories.

8 changed files with 31 additions and 134 deletions

View File

@ -136,7 +136,7 @@ async function processGenerationJob(
await bot.api.editMessageText(
state.replyMessage.chat.id,
state.replyMessage.message_id,
`Generating your prompt now... 0% using ${sdInstance.name || sdInstance.id}`,
`Generating your prompt now... 0% using ${sdInstance.name}`,
{ maxAttempts: 1 },
);
@ -221,9 +221,9 @@ async function processGenerationJob(
await bot.api.editMessageText(
state.replyMessage.chat.id,
state.replyMessage.message_id,
`Generating your prompt now... ${(progressResponse.data.progress * 100).toFixed(0)}% using ${
sdInstance.name || sdInstance.id
}`,
`Generating your prompt now... ${
(progressResponse.data.progress * 100).toFixed(0)
}% using ${sdInstance.name}`,
{ maxAttempts: 1 },
).catch(() => undefined);

View File

@ -1,71 +0,0 @@
import { CommandContext } from "grammy";
import { bold, fmt, FormattedString } from "grammy_parse_mode";
import { distinctBy } from "std/collections";
import { getConfig } from "../app/config.ts";
import { generationStore } from "../app/generationStore.ts";
import { ErisContext, logger } from "./mod.ts";
import { formatUserChat } from "../utils/formatUserChat.ts";
export async function broadcastCommand(ctx: CommandContext<ErisContext>) {
if (!ctx.from?.username) {
return ctx.reply("I don't know who you are.");
}
const config = await getConfig();
if (!config.adminUsernames.includes(ctx.from.username)) {
return ctx.reply("Only a bot admin can use this command.");
}
const text = ctx.match.trim();
if (!text) {
return ctx.reply("Please specify a message to broadcast.");
}
// find users who interacted with bot in the last 24 hours
const gens = await generationStore.getAll(
{ after: new Date(Date.now() - 24 * 60 * 60 * 1000) },
{ reverse: true },
).then((gens) => distinctBy(gens, (gen) => gen.value.from.id));
let sentCount = 0;
const errors: FormattedString[] = [];
const getMessage = () =>
fmt([
fmt`Broadcasted to ${sentCount}/${gens.length} users.\n\n`,
errors.length > 0 ? fmt([bold("Errors:"), "\n", ...errors]) : "",
]);
const replyMessage = await ctx.replyFmt(getMessage(), {
reply_to_message_id: ctx.message?.message_id,
});
// send message to each user
for (const gen of gens) {
try {
await ctx.api.sendMessage(gen.value.from.id, text);
logger().info(`Broadcasted to ${formatUserChat({ from: gen.value.from })}`);
sentCount++;
} catch (err) {
logger().error(`Broadcasting to ${formatUserChat({ from: gen.value.from })} failed: ${err}`);
errors.push(fmt`${bold(formatUserChat({ from: gen.value.from }))} - ${err.message}\n`);
}
const fmtMessage = getMessage();
if (sentCount % 20 === 0) {
await ctx.api.editMessageText(
replyMessage.chat.id,
replyMessage.message_id,
fmtMessage.text,
{ entities: fmtMessage.entities },
).catch(() => undefined);
}
}
const fmtMessage = getMessage();
await ctx.api.editMessageText(
replyMessage.chat.id,
replyMessage.message_id,
fmtMessage.text,
{ entities: fmtMessage.entities },
).catch(() => undefined);
}

View File

@ -7,7 +7,5 @@ export async function cancelCommand(ctx: ErisContext) {
.filter((job) => job.lockUntil < new Date())
.filter((j) => j.state.from.id === ctx.from?.id);
for (const job of userJobs) await generationQueue.deleteJob(job.id);
await ctx.reply(`Cancelled ${userJobs.length} jobs`, {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply(`Cancelled ${userJobs.length} jobs`);
}

View File

@ -28,34 +28,30 @@ async function img2img(
state: QuestionState = {},
): Promise<void> {
if (!ctx.message?.from?.id) {
await ctx.reply("I don't know who you are", {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply("I don't know who you are");
return;
}
const config = await getConfig();
if (config.pausedReason != null) {
await ctx.reply(`I'm paused: ${config.pausedReason || "No reason given"}`, {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply(`I'm paused: ${config.pausedReason || "No reason given"}`);
return;
}
const jobs = await generationQueue.getAllJobs();
if (jobs.length >= config.maxJobs) {
await ctx.reply(`The queue is full. Try again later. (Max queue size: ${config.maxJobs})`, {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply(
`The queue is full. Try again later. (Max queue size: ${config.maxJobs})`,
);
return;
}
const userJobs = jobs.filter((job) => job.state.from.id === ctx.message?.from?.id);
if (userJobs.length >= config.maxUserJobs) {
await ctx.reply(`You already have ${config.maxUserJobs} jobs in queue. Try again later.`, {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply(
`You already have ${config.maxUserJobs} jobs in queue. Try again later.`,
);
return;
}
@ -95,11 +91,7 @@ async function img2img(
await ctx.reply(
"Please show me a picture to repaint." +
img2imgQuestion.messageSuffixMarkdown(JSON.stringify(state satisfies QuestionState)),
{
reply_markup: { force_reply: true, selective: true },
parse_mode: "Markdown",
reply_to_message_id: ctx.message?.message_id,
},
{ reply_markup: { force_reply: true, selective: true }, parse_mode: "Markdown" },
);
return;
}
@ -108,18 +100,12 @@ async function img2img(
await ctx.reply(
"Please describe the picture you want to repaint." +
img2imgQuestion.messageSuffixMarkdown(JSON.stringify(state satisfies QuestionState)),
{
reply_markup: { force_reply: true, selective: true },
parse_mode: "Markdown",
reply_to_message_id: ctx.message?.message_id,
},
{ reply_markup: { force_reply: true, selective: true }, parse_mode: "Markdown" },
);
return;
}
const replyMessage = await ctx.reply("Accepted. You are now in queue.", {
reply_to_message_id: ctx.message?.message_id,
});
const replyMessage = await ctx.reply("Accepted. You are now in queue.");
await generationQueue.pushJob({
task: { type: "img2img", fileId: state.fileId, params: state.params },

View File

@ -1,10 +1,10 @@
import { Api, Bot, Context, RawApi, session, SessionFlavor } from "grammy";
import { autoQuote } from "grammy_autoquote";
import { FileFlavor, hydrateFiles } from "grammy_files";
import { hydrateReply, ParseModeFlavor } from "grammy_parse_mode";
import { getLogger } from "std/log";
import { getConfig, setConfig } from "../app/config.ts";
import { formatUserChat } from "../utils/formatUserChat.ts";
import { broadcastCommand } from "./broadcastCommand.ts";
import { cancelCommand } from "./cancelCommand.ts";
import { img2imgCommand, img2imgQuestion } from "./img2imgCommand.ts";
import { pnginfoCommand, pnginfoQuestion } from "./pnginfoCommand.ts";
@ -45,6 +45,7 @@ export const bot = new Bot<ErisContext, ErisApi>(
},
);
bot.use(autoQuote);
bot.use(hydrateReply);
bot.use(session<SessionData, ErisContext>({
type: "multi",
@ -128,8 +129,6 @@ bot.command("queue", queueCommand);
bot.command("cancel", cancelCommand);
bot.command("broadcast", broadcastCommand);
bot.command("pause", async (ctx) => {
if (!ctx.from?.username) return;
const config = await getConfig();

View File

@ -23,11 +23,7 @@ async function pnginfo(ctx: ErisContext, includeRepliedTo: boolean): Promise<voi
await ctx.reply(
"Please send me a PNG file." +
pnginfoQuestion.messageSuffixMarkdown(),
{
reply_markup: { force_reply: true, selective: true },
parse_mode: "Markdown",
reply_to_message_id: ctx.message?.message_id,
},
{ reply_markup: { force_reply: true, selective: true }, parse_mode: "Markdown" },
);
return;
}
@ -47,7 +43,7 @@ async function pnginfo(ctx: ErisContext, includeRepliedTo: boolean): Promise<voi
]);
await ctx.reply(paramsText.text, {
entities: paramsText.entities,
reply_to_message_id: ctx.message?.message_id,
entities: paramsText.entities,
});
}

View File

@ -7,10 +7,7 @@ import { ErisContext } from "./mod.ts";
export async function queueCommand(ctx: CommandContext<ErisContext>) {
let formattedMessage = await getMessageText();
const queueMessage = await ctx.replyFmt(formattedMessage, {
disable_notification: true,
reply_to_message_id: ctx.message?.message_id,
});
const queueMessage = await ctx.replyFmt(formattedMessage, { disable_notification: true });
handleFutureUpdates().catch(() => undefined);
async function getMessageText() {

View File

@ -20,32 +20,30 @@ export async function txt2imgCommand(ctx: CommandContext<ErisContext>) {
async function txt2img(ctx: ErisContext, match: string, includeRepliedTo: boolean): Promise<void> {
if (!ctx.message?.from?.id) {
await ctx.reply("I don't know who you are", { reply_to_message_id: ctx.message?.message_id });
await ctx.reply("I don't know who you are");
return;
}
const config = await getConfig();
if (config.pausedReason != null) {
await ctx.reply(`I'm paused: ${config.pausedReason || "No reason given"}`, {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply(`I'm paused: ${config.pausedReason || "No reason given"}`);
return;
}
const jobs = await generationQueue.getAllJobs();
if (jobs.length >= config.maxJobs) {
await ctx.reply(`The queue is full. Try again later. (Max queue size: ${config.maxJobs})`, {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply(
`The queue is full. Try again later. (Max queue size: ${config.maxJobs})`,
);
return;
}
const userJobs = jobs.filter((job) => job.state.from.id === ctx.message?.from?.id);
if (userJobs.length >= config.maxUserJobs) {
await ctx.reply(`You already have ${config.maxUserJobs} jobs in queue. Try again later.`, {
reply_to_message_id: ctx.message?.message_id,
});
await ctx.reply(
`You already have ${config.maxUserJobs} jobs in queue. Try again later.`,
);
return;
}
@ -71,18 +69,12 @@ async function txt2img(ctx: ErisContext, match: string, includeRepliedTo: boolea
await ctx.reply(
"Please tell me what you want to see." +
txt2imgQuestion.messageSuffixMarkdown(),
{
reply_markup: { force_reply: true, selective: true },
parse_mode: "Markdown",
reply_to_message_id: ctx.message?.message_id,
},
{ reply_markup: { force_reply: true, selective: true }, parse_mode: "Markdown" },
);
return;
}
const replyMessage = await ctx.reply("Accepted. You are now in queue.", {
reply_to_message_id: ctx.message?.message_id,
});
const replyMessage = await ctx.reply("Accepted. You are now in queue.");
await generationQueue.pushJob({
task: { type: "txt2img", params },